(* * This file is part of JavaLib * Copyright (c)2004-2012 Nicolas Cannasse and Caue Waneck * * 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 JData;; open IO.BigEndian;; open ExtString;; open ExtList;; exception Error_message of string let error msg = raise (Error_message msg) let get_reference_type i constid = match i with | 1 -> RGetField | 2 -> RGetStatic | 3 -> RPutField | 4 -> RPutStatic | 5 -> RInvokeVirtual | 6 -> RInvokeStatic | 7 -> RInvokeSpecial | 8 -> RNewInvokeSpecial | 9 -> RInvokeInterface | _ -> error (string_of_int constid ^ ": Invalid reference type " ^ string_of_int i) let parse_constant max idx ch = let cid = IO.read_byte ch in let error() = error (string_of_int idx ^ ": Invalid constant " ^ string_of_int cid) in let index() = let n = read_ui16 ch in if n = 0 || n >= max then error(); n in match cid with | 7 -> KClass (index()) | 9 -> let n1 = index() in let n2 = index() in KFieldRef (n1,n2) | 10 -> let n1 = index() in let n2 = index() in KMethodRef (n1,n2) | 11 -> let n1 = index() in let n2 = index() in KInterfaceMethodRef (n1,n2) | 8 -> KString (index()) | 3 -> KInt (read_real_i32 ch) | 4 -> let f = Int32.float_of_bits (read_real_i32 ch) in KFloat f | 5 -> KLong (read_i64 ch) | 6 -> KDouble (read_double ch) | 12 -> let n1 = index() in let n2 = index() in KNameAndType (n1, n2) | 1 -> let len = read_ui16 ch in let str = IO.nread_string ch len in (* TODO: correctly decode modified UTF8 *) KUtf8String str | 15 -> let reft = get_reference_type (IO.read_byte ch) idx in let dynref = index() in KMethodHandle (reft, dynref) | 16 -> KMethodType (index()) | 17 -> let bootstrapref = read_ui16 ch in (* not index *) let nametyperef = index() in KDynamic (bootstrapref, nametyperef) | 18 -> let bootstrapref = read_ui16 ch in (* not index *) let nametyperef = index() in KInvokeDynamic (bootstrapref, nametyperef) | 19 -> KModule (index()) | 20 -> KPackage (index()) | n -> error() let expand_path s = let rec loop remaining acc = match remaining with | name :: [] -> List.rev acc, name | v :: tl -> loop tl (v :: acc) | _ -> assert false in loop (String.nsplit s "/") [] let rec parse_type_parameter_part s = match s.[0] with | '*' -> TAny, 1 | c -> let wildcard, i = match c with | '+' -> WExtends, 1 | '-' -> WSuper, 1 | _ -> WNone, 0 in let jsig, l = parse_signature_part (String.sub s i (String.length s - 1)) in (TType (wildcard, jsig), l + i) and parse_signature_part s = let len = String.length s in if len = 0 then raise Exit; match s.[0] with | 'B' -> TByte, 1 | 'C' -> TChar, 1 | 'D' -> TDouble, 1 | 'F' -> TFloat, 1 | 'I' -> TInt, 1 | 'J' -> TLong, 1 | 'S' -> TShort, 1 | 'Z' -> TBool, 1 | 'L' -> (try let orig_s = s in let rec loop start i acc = match s.[i] with | '/' -> loop (i + 1) (i + 1) (String.sub s start (i - start) :: acc) | ';' | '.' -> List.rev acc, (String.sub s start (i - start)), [], (i) | '<' -> let name = String.sub s start (i - start) in let rec loop_params i acc = let s = String.sub s i (len - i) in match s.[0] with | '>' -> List.rev acc, i + 1 | _ -> let tp, l = parse_type_parameter_part s in loop_params (l + i) (tp :: acc) in let params, _end = loop_params (i + 1) [] in List.rev acc, name, params, (_end) | _ -> loop start (i+1) acc in let pack, name, params, _end = loop 1 1 [] in let rec loop_inner i acc = match s.[i] with | '.' -> let pack, name, params, _end = loop (i+1) (i+1) [] in if pack <> [] then error ("Inner types must not define packages. For '" ^ orig_s ^ "'."); loop_inner _end ( (name,params) :: acc ) | ';' -> List.rev acc, i + 1 | c -> error ("End of complex type signature expected after type parameter. Got '" ^ Char.escaped c ^ "' for '" ^ orig_s ^ "'." ); in let inners, _end = loop_inner _end [] in match inners with | [] -> TObject((pack,name), params), _end | _ -> TObjectInner( pack, (name,params) :: inners ), _end with Invalid_string -> raise Exit) | '[' -> let p = ref 1 in while !p < String.length s && s.[!p] >= '0' && s.[!p] <= '9' do incr p; done; let size = (if !p > 1 then Some (int_of_string (String.sub s 1 (!p - 1))) else None) in let s , l = parse_signature_part (String.sub s !p (String.length s - !p)) in TArray (s,size) , l + !p | '(' -> let p = ref 1 in let args = ref [] in while !p < String.length s && s.[!p] <> ')' do let a , l = parse_signature_part (String.sub s !p (String.length s - !p)) in args := a :: !args; p := !p + l; done; incr p; if !p >= String.length s then raise Exit; let ret , l = (match s.[!p] with 'V' -> None , 1 | _ -> let s, l = parse_signature_part (String.sub s !p (String.length s - !p)) in Some s, l ) in TMethod (List.rev !args,ret) , !p + l | 'T' -> (try let s1 , _ = String.split s ";" in let len = String.length s1 in TTypeParameter (String.sub s1 1 (len - 1)) , len + 1 with Invalid_string -> raise Exit) | _ -> raise Exit let parse_signature s = try let sign , l = parse_signature_part s in if String.length s <> l then raise Exit; sign with Exit -> error ("Invalid signature '" ^ s ^ "'") let parse_method_signature s = match parse_signature s with | (TMethod m) -> m | _ -> error ("Unexpected signature '" ^ s ^ "'. Expecting method") let parse_formal_type_params s = match s.[0] with | '<' -> let rec read_id i = match s.[i] with | ':' | '>' -> i | _ -> read_id (i + 1) in let len = String.length s in let rec parse_params idx acc = let idi = read_id (idx + 1) in let id = String.sub s (idx + 1) (idi - idx - 1) in (* next must be a : *) (match s.[idi] with | ':' -> () | _ -> error ("Invalid formal type signature character: " ^ Char.escaped s.[idi] ^ " ; from " ^ s)); let ext, l = match s.[idi + 1] with | ':' | '>' -> None, idi + 1 | _ -> let sgn, l = parse_signature_part (String.sub s (idi + 1) (len - idi - 1)) in Some sgn, l + idi + 1 in let rec loop idx acc = match s.[idx] with | ':' -> let ifacesig, ifacei = parse_signature_part (String.sub s (idx + 1) (len - idx - 1)) in loop (idx + ifacei + 1) (ifacesig :: acc) | _ -> acc, idx in let ifaces, idx = loop l [] in let acc = (id, ext, ifaces) :: acc in if s.[idx] = '>' then List.rev acc, idx + 1 else parse_params (idx - 1) acc in parse_params 0 [] | _ -> [], 0 let parse_throws s = let len = String.length s in let rec loop idx acc = if idx > len then raise Exit else if idx = len then acc, idx else match s.[idx] with | '^' -> let tsig, l = parse_signature_part (String.sub s (idx+1) (len - idx - 1)) in loop (idx + l + 1) (tsig :: acc) | _ -> acc, idx in loop 0 [] let parse_complete_method_signature s = try let len = String.length s in let tparams, i = parse_formal_type_params s in let sign, l = parse_signature_part (String.sub s i (len - i)) in let throws, l2 = parse_throws (String.sub s (i+l) (len - i - l)) in if (i + l + l2) <> len then raise Exit; match sign with | TMethod msig -> tparams, msig, throws | _ -> raise Exit with Exit -> error ("Invalid method extended signature '" ^ s ^ "'") let rec expand_constant consts i = let unexpected i = error (string_of_int i ^ ": Unexpected constant type") in let expand_path n = match Array.get consts n with | KUtf8String s -> expand_path s | _ -> unexpected n in let expand_cls n = match expand_constant consts n with | ConstClass p -> p | _ -> unexpected n in let expand_nametype n = match expand_constant consts n with | ConstNameAndType (s,jsig) -> s, jsig | _ -> unexpected n in let expand_string n = match Array.get consts n with | KUtf8String s -> s | _ -> unexpected n in let expand_nametype_m n = match expand_nametype n with | (n, TMethod m) -> n, m | _ -> unexpected n in let expand ncls nt = match expand_cls ncls, expand_nametype nt with | path, (n, m) -> path, n, m in let expand_m ncls nt = match expand_cls ncls, expand_nametype_m nt with | path, (n, m) -> path, n, m in match Array.get consts i with | KClass utf8ref -> ConstClass (expand_path utf8ref) | KFieldRef (classref, nametyperef) -> ConstField (expand classref nametyperef) | KMethodRef (classref, nametyperef) -> ConstMethod (expand_m classref nametyperef) | KInterfaceMethodRef (classref, nametyperef) -> ConstInterfaceMethod (expand_m classref nametyperef) | KString utf8ref -> ConstString (expand_string utf8ref) | KInt i32 -> ConstInt i32 | KFloat f -> ConstFloat f | KLong i64 -> ConstLong i64 | KDouble d -> ConstDouble d | KNameAndType (n, t) -> ConstNameAndType(expand_string n, parse_signature (expand_string t)) | KUtf8String s -> ConstUtf8 s (* TODO: expand UTF8 characters *) | KMethodHandle (reference_type, dynref) -> ConstMethodHandle (reference_type, expand_constant consts dynref) | KMethodType utf8ref -> ConstMethodType (parse_method_signature (expand_string utf8ref)) | KDynamic(bootstrapref, nametyperef) -> let n, t = expand_nametype nametyperef in ConstDynamic(bootstrapref, n, t) | KInvokeDynamic (bootstrapref, nametyperef) -> let n, t = expand_nametype nametyperef in ConstInvokeDynamic(bootstrapref, n, t) | KModule n -> ConstModule (expand_string n) | KPackage n -> ConstPackage (expand_string n) | KUnusable -> ConstUnusable let parse_access_flags ch all_flags = let fl = read_ui16 ch in let flags = ref [] in let fbit = ref 0 in List.iter (fun f -> if fl land (1 lsl !fbit) <> 0 then begin flags := f :: !flags; if f = JUnusable then error ("Unusable flag: " ^ string_of_int fl) end; incr fbit ) all_flags; (*if fl land (0x4000 - (1 lsl !fbit)) <> 0 then error ("Invalid access flags " ^ string_of_int fl);*) !flags let get_constant c n = if n < 1 || n >= Array.length c then error ("Invalid constant index " ^ string_of_int n); match c.(n) with | ConstUnusable -> error "Unusable constant index"; | x -> x let get_class consts ch = match get_constant consts (read_ui16 ch) with | ConstClass n -> n | _ -> error "Invalid class index" let get_string consts ch = let i = read_ui16 ch in match get_constant consts i with | ConstUtf8 s -> s | _ -> error ("Invalid string index " ^ string_of_int i) let rec parse_element_value consts ch = let tag = IO.read_byte ch in match Char.chr tag with | 'B' | 'C' | 'D' | 'F' | 'I' | 'J' | 'S' | 'Z' | 's' -> let jsig = match (Char.chr tag) with | 's' -> TObject( (["java";"lang"],"String"), [] ) | tag -> fst (parse_signature_part (Char.escaped tag)) in ValConst(jsig, get_constant consts (read_ui16 ch)) | 'e' -> let path = parse_signature (get_string consts ch) in let name = get_string consts ch in ValEnum (path, name) | 'c' -> let name = get_string consts ch in let jsig = if name = "V" then TObject(([], "Void"), []) else parse_signature name in ValClass jsig | '@' -> ValAnnotation (parse_annotation consts ch) | '[' -> let num_vals = read_ui16 ch in ValArray (List.init (num_vals) (fun _ -> parse_element_value consts ch)) | tag -> error ("Invalid element value: '" ^ Char.escaped tag ^ "'") and parse_ann_element consts ch = let name = get_string consts ch in let element_value = parse_element_value consts ch in name, element_value and parse_annotation consts ch = let anntype = parse_signature (get_string consts ch) in let count = read_ui16 ch in { ann_type = anntype; ann_elements = List.init count (fun _ -> parse_ann_element consts ch) } let parse_attribute on_special consts ch = let aname = get_string consts ch in let error() = error ("Malformed attribute " ^ aname) in let alen = read_i32 ch in match aname with | "Deprecated" -> if alen <> 0 then error(); Some (AttrDeprecated) | "LocalVariableTable" -> let len = read_ui16 ch in let locals = List.init len (fun _ -> let start_pc = read_ui16 ch in let length = read_ui16 ch in let name = get_string consts ch in let descriptor = get_string consts ch in let index = read_ui16 ch in { ld_start_pc = start_pc; ld_length = length; ld_name = name; ld_descriptor = descriptor; ld_index = index } ) in Some (AttrLocalVariableTable locals) | "MethodParameters" -> let len = IO.read_byte ch in let parameters = List.init len (fun _ -> let name = get_string consts ch in let flags = read_ui16 ch in (name,flags) ) in Some (AttrMethodParameters parameters) | "RuntimeVisibleAnnotations" -> let anncount = read_ui16 ch in Some (AttrVisibleAnnotations (List.init anncount (fun _ -> parse_annotation consts ch))) | "RuntimeInvisibleAnnotations" -> let anncount = read_ui16 ch in Some (AttrInvisibleAnnotations (List.init anncount (fun _ -> parse_annotation consts ch))) | _ -> let do_default () = Some (AttrUnknown (aname,IO.nread_string ch alen)) in match on_special with | None -> do_default() | Some fn -> fn consts ch aname alen do_default let parse_attributes ?on_special consts ch count = let rec loop i acc = if i >= count then List.rev acc else match parse_attribute on_special consts ch with | None -> loop (i + 1) acc | Some attrib -> loop (i + 1) (attrib :: acc) in loop 0 [] let parse_field kind consts ch = let all_flags = match kind with | JKField -> [JPublic; JPrivate; JProtected; JStatic; JFinal; JUnusable; JVolatile; JTransient; JSynthetic; JEnum] | JKMethod -> [JPublic; JPrivate; JProtected; JStatic; JFinal; JSynchronized; JBridge; JVarArgs; JNative; JUnusable; JAbstract; JStrict; JSynthetic] in let acc = ref (parse_access_flags ch all_flags) in let name = get_string consts ch in let sign = parse_signature (get_string consts ch) in let jsig = ref sign in let throws = ref [] in let types = ref [] in let constant = ref None in let code = ref None in let attrib_count = read_ui16 ch in let attribs = parse_attributes ~on_special:(fun _ _ aname alen do_default -> match kind, aname with | JKField, "ConstantValue" -> constant := Some (get_constant consts (read_ui16 ch)); None | JKField, "Synthetic" -> if not (List.mem JSynthetic !acc) then acc := !acc @ [JSynthetic]; None | JKField, "Signature" -> let s = get_string consts ch in jsig := parse_signature s; None | JKMethod, "Code" -> ignore(read_ui16 ch); (* max stack *) ignore(read_ui16 ch); (* max locals *) let len = read_i32 ch in ignore(IO.nread_string ch len); (* code *) let len = read_ui16 ch in for i = 0 to len - 1 do ignore(IO.nread_string ch 8); done; (* exceptions *) let attrib_count = read_ui16 ch in let attribs = parse_attributes consts ch attrib_count in code := Some attribs; None | JKMethod, "Exceptions" -> let num = read_ui16 ch in throws := List.init num (fun _ -> TObject(get_class consts ch,[])); None | JKMethod, "Signature" -> let s = get_string consts ch in let tp, sgn, thr = parse_complete_method_signature s in if thr <> [] then throws := thr; types := tp; jsig := TMethod(sgn); None | _ -> do_default() ) consts ch attrib_count in { jf_name = name; jf_kind = kind; (* signature, as used by the vm *) jf_vmsignature = sign; (* actual signature, as used in java code *) jf_signature = !jsig; jf_throws = !throws; jf_types = !types; jf_flags = !acc; jf_attributes = attribs; jf_constant = !constant; jf_code = !code; } let parse_class ch = if read_real_i32 ch <> 0xCAFEBABEl then error "Invalid header"; let minorv = read_ui16 ch in let majorv = read_ui16 ch in let constant_count = read_ui16 ch in let const_big = ref true in let consts = Array.init constant_count (fun idx -> if !const_big then begin const_big := false; KUnusable end else let c = parse_constant constant_count idx ch in (match c with KLong _ | KDouble _ -> const_big := true | _ -> ()); c ) in let consts = Array.mapi (fun i _ -> expand_constant consts i) consts in let flags = parse_access_flags ch [JPublic; JUnusable; JUnusable; JUnusable; JFinal; JSuper; JUnusable; JUnusable; JUnusable; JInterface; JAbstract; JUnusable; JSynthetic; JAnnotation; JEnum; JModule] in let this = get_class consts ch in let super_idx = read_ui16 ch in let super = match super_idx with | 0 -> TObject((["java";"lang"], "Object"), []); | idx -> match get_constant consts idx with | ConstClass path -> TObject(path,[]) | _ -> error "Invalid super index" in let interfaces = List.init (read_ui16 ch) (fun _ -> TObject (get_class consts ch, [])) in let fields = List.init (read_ui16 ch) (fun _ -> parse_field JKField consts ch) in let methods = List.init (read_ui16 ch) (fun _ -> parse_field JKMethod consts ch) in let inner = ref [] in let types = ref [] in let super = ref super in let interfaces = ref interfaces in let attribs = read_ui16 ch in let attribs = parse_attributes ~on_special:(fun _ _ aname alen do_default -> match aname with | "InnerClasses" -> let count = read_ui16 ch in let classes = List.init count (fun _ -> let inner_ci = get_class consts ch in let outeri = read_ui16 ch in let outer_ci = match outeri with | 0 -> None | _ -> match get_constant consts outeri with | ConstClass n -> Some n | _ -> error "Invalid class index" in let inner_namei = read_ui16 ch in let inner_name = match inner_namei with | 0 -> None | _ -> match get_constant consts inner_namei with | ConstUtf8 s -> Some s | _ -> error ("Invalid string index " ^ string_of_int inner_namei) in let flags = parse_access_flags ch [JPublic; JPrivate; JProtected; JStatic; JFinal; JUnusable; JUnusable; JUnusable; JUnusable; JInterface; JAbstract; JSynthetic; JAnnotation; JEnum] in inner_ci, outer_ci, inner_name, flags ) in inner := classes; None | "Signature" -> let s = get_string consts ch in let formal, idx = parse_formal_type_params s in types := formal; let s = String.sub s idx (String.length s - idx) in let len = String.length s in let sup, idx = parse_signature_part s in let rec loop idx acc = if idx = len then acc else begin let s = String.sub s idx (len - idx) in let iface, i2 = parse_signature_part s in loop (idx + i2) (iface :: acc) end in interfaces := loop idx []; super := sup; None | _ -> do_default() ) consts ch attribs in IO.close_in ch; { cversion = majorv, minorv; cpath = this; csuper = !super; cflags = flags; cinterfaces = !interfaces; cfields = fields; cmethods = methods; cattributes = attribs; cinner_types = !inner; ctypes = !types; }