Reviewed By: mityal Differential Revision: D20919670 fbshipit-source-id: c7ceb517cmaster
parent
db965085b9
commit
b22f7c83d5
@ -1,13 +0,0 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
|
||||
(** this is a naive/temporary implementation in a near diff, we will 1) parse the source file to
|
||||
collect location datas for all class names 2) cache the result for later uses but we may have to
|
||||
adapt a litle some signatures *)
|
||||
let class_name_location file _cn : Location.t = {line= 0; col= 0; file}
|
@ -1,12 +0,0 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
|
||||
val class_name_location : SourceFile.t -> string -> Location.t
|
||||
(** [class_name_location source class_name] searches in file [source] the declaration location for
|
||||
class name [class_name] *)
|
@ -0,0 +1,401 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
{
|
||||
open! IStd
|
||||
|
||||
open Lexing
|
||||
|
||||
module Array = struct
|
||||
include Array
|
||||
let make len = create ~len
|
||||
end
|
||||
|
||||
(** classic Ocamllex function to update current lexbuf line at each end of
|
||||
line *)
|
||||
let incr_linenum lexbuf =
|
||||
let pos = lexbuf.Lexing.lex_curr_p in
|
||||
lexbuf.Lexing.lex_curr_p <- { pos with
|
||||
Lexing.pos_lnum = pos.Lexing.pos_lnum + 1;
|
||||
Lexing.pos_bol = pos.Lexing.pos_cnum;
|
||||
}
|
||||
|
||||
(** position of the char just after lexbuf *)
|
||||
let end_pos lexbuf =
|
||||
lexbuf.lex_curr_p.pos_lnum,
|
||||
lexbuf.lex_curr_p.pos_cnum - lexbuf.lex_curr_p.pos_bol
|
||||
|
||||
(** return the exact position start of the suffix [classname] in [lexbuf] *)
|
||||
let location_suffix suffix lexbuf =
|
||||
let length_suffix = String.length suffix in
|
||||
let l, c = end_pos lexbuf in
|
||||
l, c - length_suffix
|
||||
|
||||
(** return the position start of [lexbuf] *)
|
||||
let location_start lexbuf =
|
||||
let l, c = end_pos lexbuf in
|
||||
let lexbuf_length = lexeme_end lexbuf - lexeme_start lexbuf in
|
||||
l, c - lexbuf_length
|
||||
|
||||
(** We traverse the structure of the source file by recording the
|
||||
encompassing blocks in a stack of frame.
|
||||
We use the stack to recover the full inner class name at bytecode level *)
|
||||
type expr = | AllocExpr | OtherExpr
|
||||
type frame =
|
||||
{ short_class_name: string
|
||||
; is_enum : bool
|
||||
; next_anonymous_class: int
|
||||
; opened_blocks: int
|
||||
; exprs: expr list }
|
||||
type state =
|
||||
{ stack: frame list
|
||||
; record_location: classname:string -> col:int -> line:int -> unit }
|
||||
|
||||
let push frame state = { state with stack = frame :: state.stack; }
|
||||
|
||||
exception Missing_opening_bracket
|
||||
|
||||
exception Missing_opening_parenthesis
|
||||
|
||||
let add_package package state =
|
||||
let record_location ~classname =
|
||||
let classname = package^"."^classname in
|
||||
state.record_location ~classname in
|
||||
{ state with record_location; }
|
||||
|
||||
let pop_class state =
|
||||
match state.stack with
|
||||
| [] -> raise Missing_opening_bracket
|
||||
| _ :: stack -> { state with stack; }
|
||||
|
||||
let incr_next_anonymous state =
|
||||
match state.stack with
|
||||
| [] -> { state with stack = []; }
|
||||
| fr :: stack ->
|
||||
let stack =
|
||||
{fr with next_anonymous_class = fr.next_anonymous_class+1; } :: stack in
|
||||
{ state with stack; }
|
||||
|
||||
let add_expr e (state:state) : state =
|
||||
match state.stack with
|
||||
| [] -> state
|
||||
| fr :: stack ->
|
||||
let stack = {fr with exprs = e :: fr.exprs; } :: stack in
|
||||
{ state with stack; }
|
||||
|
||||
let pop_exprs state =
|
||||
match state.stack with
|
||||
| [] -> raise Missing_opening_parenthesis
|
||||
| fr :: stack -> (
|
||||
match fr.exprs with
|
||||
| [] -> raise Missing_opening_parenthesis
|
||||
| e :: exprs ->
|
||||
let stack = {fr with exprs; } :: stack in
|
||||
(e, { state with stack; }))
|
||||
|
||||
let in_field_declaration_area state =
|
||||
match state.stack with
|
||||
| [] -> false
|
||||
| fr :: _ -> Int.equal fr.opened_blocks 0
|
||||
|
||||
let get_opened_blocks state =
|
||||
match state.stack with
|
||||
| [] -> raise Missing_opening_bracket
|
||||
| fr :: _ -> fr.opened_blocks
|
||||
|
||||
let is_enum state =
|
||||
match state.stack with
|
||||
| [] -> false
|
||||
| fr :: _ -> fr.is_enum && Int.equal fr.opened_blocks 0
|
||||
|
||||
let get_next_anonymous_class state =
|
||||
match state.stack with
|
||||
| [] -> raise Missing_opening_bracket
|
||||
| fr :: _ -> string_of_int fr.next_anonymous_class
|
||||
|
||||
let decr_opened_blocks state =
|
||||
let stack =
|
||||
match state.stack with
|
||||
| [] -> []
|
||||
| fr :: stack ->
|
||||
{fr with opened_blocks = fr.opened_blocks-1; } :: stack in
|
||||
{ state with stack; }
|
||||
|
||||
let incr_opened_blocks state =
|
||||
let stack =
|
||||
match state.stack with
|
||||
| [] -> []
|
||||
| fr :: stack ->
|
||||
{fr with opened_blocks = fr.opened_blocks+1; } :: stack in
|
||||
{ state with stack; }
|
||||
|
||||
let long_class_name name state =
|
||||
let f name frame = Printf.sprintf "%s$%s" frame.short_class_name name in
|
||||
List.fold ~f ~init:name state.stack
|
||||
|
||||
}
|
||||
|
||||
let whitespace = [' ' '\t']
|
||||
let eol = whitespace*("\r")?"\n" (* end of line *)
|
||||
let eol_comment = "//" [^'\n']*
|
||||
let id = ['a'-'z' 'A'-'Z' '_' '$'] ['a'-'z' 'A'-'Z' '0'-'9' '_' '$']*
|
||||
let char = "'\\''" | "'\"'" | "'" [ ^'\'' ]+ "'"
|
||||
let class_keyword = "class"|"interface"|"enum"|"@" whitespace+ "interface"
|
||||
|
||||
(* We follow an abstraction of the official grammar described here:
|
||||
https://docs.oracle.com/javase/specs/jls/se14/html/jls-19.html *)
|
||||
rule class_scan state = parse
|
||||
| whitespace+
|
||||
{ class_scan state lexbuf }
|
||||
| eol_comment
|
||||
{ class_scan state lexbuf }
|
||||
| "/*"
|
||||
{ skip_comments (class_scan state) lexbuf }
|
||||
| eol
|
||||
{ incr_linenum lexbuf;
|
||||
class_scan state lexbuf }
|
||||
| "package" whitespace+ (id ("." id)* as package) whitespace* ";"
|
||||
{ class_scan (add_package package state) lexbuf }
|
||||
| id
|
||||
{ class_scan state lexbuf }
|
||||
| class_keyword whitespace+ (id as name)
|
||||
{
|
||||
let line, col = location_suffix name lexbuf in
|
||||
let classname = long_class_name name state in
|
||||
state.record_location ~classname ~col ~line ;
|
||||
let frame : frame =
|
||||
{ short_class_name = name;
|
||||
is_enum = false;
|
||||
next_anonymous_class = 1;
|
||||
exprs = [];
|
||||
opened_blocks = 0 } in
|
||||
(* we jump to the next left bracket, skipping annotations and
|
||||
generics <...> contents *)
|
||||
do_at_next_left_bracket
|
||||
(fun lexbuf ->
|
||||
class_scan (push frame state) lexbuf) lexbuf
|
||||
}
|
||||
| "new" whitespace+
|
||||
{ (* may be a declaration of an anonymous class
|
||||
```new [TypeArguments] ClassOrInterfaceTypeToInstantiate
|
||||
( [ArgumentList] ) [ClassBody] ```
|
||||
^
|
||||
so we jump this position | *)
|
||||
search_anonymous_class_body state lexbuf
|
||||
}
|
||||
| id
|
||||
{ if is_enum state
|
||||
then found_entrance_of_anonymous_class state lexbuf
|
||||
else class_scan state lexbuf }
|
||||
| (id as _field) whitespace* ";"
|
||||
{ if in_field_declaration_area state then
|
||||
(* we only reach this situation in class/interface bodies, and
|
||||
never inside method bodies *)
|
||||
() ; (* TODO : record field location *)
|
||||
class_scan state lexbuf
|
||||
}
|
||||
| "\""
|
||||
{ skip_string (class_scan state) lexbuf }
|
||||
| char
|
||||
{ class_scan state lexbuf }
|
||||
| "{"
|
||||
{ class_scan (incr_opened_blocks state) lexbuf }
|
||||
| "("
|
||||
{ class_scan (add_expr OtherExpr state) lexbuf }
|
||||
| ")"
|
||||
{ match pop_exprs state with
|
||||
| AllocExpr, state -> found_entrance_of_anonymous_class state lexbuf
|
||||
| OtherExpr, state -> class_scan state lexbuf }
|
||||
| "@" whitespace* id ("." id)* "("
|
||||
{ skip_well_parenthesized_parentheses 1
|
||||
(class_scan state) lexbuf }
|
||||
| "}"
|
||||
{
|
||||
if Int.equal (get_opened_blocks state) 0
|
||||
then class_scan (pop_class state) lexbuf
|
||||
else class_scan (decr_opened_blocks state) lexbuf
|
||||
}
|
||||
| _
|
||||
{ class_scan state lexbuf }
|
||||
| eof
|
||||
{ () }
|
||||
|
||||
(* we search for the next left bracket *)
|
||||
and do_at_next_left_bracket action = parse
|
||||
| eol
|
||||
{ incr_linenum lexbuf;
|
||||
do_at_next_left_bracket action lexbuf }
|
||||
| eol_comment
|
||||
{ do_at_next_left_bracket action lexbuf }
|
||||
| "/*"
|
||||
{ skip_comments (do_at_next_left_bracket action) lexbuf }
|
||||
| "{"
|
||||
{ action lexbuf }
|
||||
| "<"
|
||||
{ skip_well_parenthesized_angles 1
|
||||
(do_at_next_left_bracket action) lexbuf }
|
||||
| "@" whitespace* id "("
|
||||
{ skip_well_parenthesized_parentheses 1
|
||||
(do_at_next_left_bracket action) lexbuf }
|
||||
| "\""
|
||||
{ skip_string (do_at_next_left_bracket action) lexbuf }
|
||||
| _
|
||||
{ do_at_next_left_bracket action lexbuf }
|
||||
|
||||
(* we search for (...) parentheses *)
|
||||
and search_anonymous_class_body state = parse
|
||||
| eol
|
||||
{ incr_linenum lexbuf;
|
||||
search_anonymous_class_body state lexbuf }
|
||||
| eol_comment
|
||||
{ search_anonymous_class_body state lexbuf }
|
||||
| "/*"
|
||||
{ skip_comments
|
||||
(search_anonymous_class_body state) lexbuf }
|
||||
| "("
|
||||
{ class_scan (add_expr AllocExpr state) lexbuf }
|
||||
| "<"
|
||||
{ skip_well_parenthesized_angles 1
|
||||
(search_anonymous_class_body state) lexbuf }
|
||||
| "@" whitespace* id "("
|
||||
{ skip_well_parenthesized_parentheses 1
|
||||
(search_anonymous_class_body state) lexbuf }
|
||||
| "\""
|
||||
{ skip_string
|
||||
(search_anonymous_class_body state) lexbuf }
|
||||
| "["
|
||||
{ (* this is an array allocation, not an anonymous class *)
|
||||
class_scan state lexbuf
|
||||
}
|
||||
| _
|
||||
{ search_anonymous_class_body state lexbuf }
|
||||
|
||||
(* we test if there is an opening anonymous class body here *)
|
||||
and found_entrance_of_anonymous_class state = parse
|
||||
| eol
|
||||
{ incr_linenum lexbuf;
|
||||
found_entrance_of_anonymous_class state lexbuf }
|
||||
| eol_comment
|
||||
{ found_entrance_of_anonymous_class state lexbuf }
|
||||
| "/*"
|
||||
{ skip_comments
|
||||
(found_entrance_of_anonymous_class state) lexbuf }
|
||||
| whitespace+
|
||||
{ found_entrance_of_anonymous_class state lexbuf }
|
||||
| "{"
|
||||
{ (* this is an anonymous class *)
|
||||
let line, col = location_start lexbuf in
|
||||
let name = get_next_anonymous_class state in
|
||||
let classname = long_class_name name state in
|
||||
state.record_location ~classname ~col ~line ;
|
||||
let frame : frame =
|
||||
{ short_class_name = name;
|
||||
is_enum = false;
|
||||
next_anonymous_class = 1;
|
||||
exprs = [];
|
||||
opened_blocks = 0 } in
|
||||
class_scan (push frame (incr_next_anonymous state)) lexbuf
|
||||
}
|
||||
| _
|
||||
{ (* this is not an anonymous class *)
|
||||
class_scan state lexbuf
|
||||
}
|
||||
|
||||
(* we skip type arguments <...> because they may contain brackets *)
|
||||
and skip_well_parenthesized_angles width action = parse
|
||||
| eol
|
||||
{ incr_linenum lexbuf;
|
||||
skip_well_parenthesized_angles width action lexbuf }
|
||||
| "<"
|
||||
{ skip_well_parenthesized_angles (width+1) action lexbuf }
|
||||
| ">"
|
||||
{ if width <= 1 then action lexbuf
|
||||
else skip_well_parenthesized_angles (width-1) action lexbuf }
|
||||
| eol_comment
|
||||
{ skip_well_parenthesized_angles width action lexbuf }
|
||||
| "/*"
|
||||
{ skip_comments
|
||||
(skip_well_parenthesized_angles width action) lexbuf }
|
||||
| "\""
|
||||
{ skip_string (skip_well_parenthesized_angles width action) lexbuf }
|
||||
| _
|
||||
{ skip_well_parenthesized_angles width action lexbuf }
|
||||
|
||||
(* we skip type annotation arguments (...) because they may contain brackets *)
|
||||
and skip_well_parenthesized_parentheses width action = parse
|
||||
| eol
|
||||
{ incr_linenum lexbuf;
|
||||
skip_well_parenthesized_parentheses width action lexbuf }
|
||||
| "("
|
||||
{ skip_well_parenthesized_parentheses (width+1) action lexbuf }
|
||||
| ")"
|
||||
{ if width <= 1 then action lexbuf
|
||||
else skip_well_parenthesized_parentheses (width-1) action lexbuf }
|
||||
| eol_comment
|
||||
{ skip_well_parenthesized_parentheses width action lexbuf }
|
||||
| "/*"
|
||||
{ skip_comments
|
||||
(skip_well_parenthesized_parentheses width action) lexbuf }
|
||||
| "\""
|
||||
{ skip_string (skip_well_parenthesized_parentheses width action) lexbuf }
|
||||
| char
|
||||
{ skip_well_parenthesized_parentheses width action lexbuf }
|
||||
| _
|
||||
{ skip_well_parenthesized_parentheses width action lexbuf }
|
||||
|
||||
and skip_string action = parse
|
||||
| "\\\\"
|
||||
{ skip_string action lexbuf }
|
||||
| "\\\""
|
||||
{ skip_string action lexbuf }
|
||||
| "\""
|
||||
{ action lexbuf }
|
||||
| _
|
||||
{ skip_string action lexbuf }
|
||||
|
||||
and skip_comments action = parse
|
||||
| "*/"
|
||||
{ action lexbuf }
|
||||
| eol
|
||||
{ incr_linenum lexbuf;
|
||||
skip_comments action lexbuf }
|
||||
| _
|
||||
{ skip_comments action lexbuf }
|
||||
|
||||
{
|
||||
|
||||
open Javalib_pack
|
||||
|
||||
(** We scan source file [file] and record location of each class declaration *)
|
||||
let collect_class_location (program:JClasspath.program) (file:SourceFile.t) =
|
||||
let cin = In_channel.create (SourceFile.to_abs_path file) in
|
||||
let stack = [] in
|
||||
let record_location ~classname ~col ~line =
|
||||
let loc : Location.t = { line; col; file } in
|
||||
let cn : JBasics.class_name = JBasics.make_cn classname in
|
||||
Logging.debug Capture Verbose "set_java_location %s with location %a@."
|
||||
(JBasics.cn_name cn) Location.pp_file_pos loc;
|
||||
JClasspath.set_java_location program cn loc in
|
||||
try (
|
||||
class_scan { record_location; stack; } (from_channel cin) ;
|
||||
In_channel.close cin )
|
||||
with
|
||||
| Failure s ->
|
||||
raise
|
||||
(Failure
|
||||
(Printf.sprintf "Error parsing source file %s\n%s" (SourceFile.to_abs_path file) s))
|
||||
| Missing_opening_bracket ->
|
||||
raise
|
||||
(Failure (Printf.sprintf "Missing opening bracket error while parsing source file %s\n"
|
||||
(SourceFile.to_abs_path file)))
|
||||
| Missing_opening_parenthesis ->
|
||||
raise
|
||||
(Failure
|
||||
(Printf.sprintf "Missing opening parenthesis error while parsing source file %s\n"
|
||||
(SourceFile.to_abs_path file)))
|
||||
|
||||
}
|
Loading…
Reference in new issue