Reviewed By: jeremydubreil Differential Revision: D6411833 fbshipit-source-id: 4609a6bmaster
parent
303b79b6ae
commit
f37344358b
@ -0,0 +1,178 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2017 - present Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*)
|
||||||
|
|
||||||
|
module F = Format
|
||||||
|
module L = Logging
|
||||||
|
module Domain = LithoDomain
|
||||||
|
|
||||||
|
module Summary = Summary.Make (struct
|
||||||
|
type payload = Domain.astate
|
||||||
|
|
||||||
|
let update_payload astate (summary: Specs.summary) =
|
||||||
|
{summary with payload= {summary.payload with litho= Some astate}}
|
||||||
|
|
||||||
|
|
||||||
|
let read_payload (summary: Specs.summary) = summary.payload.litho
|
||||||
|
end)
|
||||||
|
|
||||||
|
module TransferFunctions (CFG : ProcCfg.S) = struct
|
||||||
|
module CFG = CFG
|
||||||
|
module Domain = Domain
|
||||||
|
|
||||||
|
type extras = ProcData.no_extras
|
||||||
|
|
||||||
|
let is_graphql_getter procname summary =
|
||||||
|
Option.is_none summary
|
||||||
|
(* we skip analysis of all GraphQL procs *)
|
||||||
|
&&
|
||||||
|
match procname with
|
||||||
|
| Typ.Procname.Java java_procname
|
||||||
|
-> (
|
||||||
|
PatternMatch.is_getter java_procname
|
||||||
|
&&
|
||||||
|
match Typ.Procname.java_get_package java_procname with
|
||||||
|
| Some package ->
|
||||||
|
String.is_prefix ~prefix:"com.facebook.graphql.model" package
|
||||||
|
| None ->
|
||||||
|
false )
|
||||||
|
| _ ->
|
||||||
|
false
|
||||||
|
|
||||||
|
|
||||||
|
let apply_callee_summary summary_opt caller_pname ret_opt actuals astate =
|
||||||
|
match summary_opt with
|
||||||
|
| Some summary ->
|
||||||
|
(* TODO: append paths if the footprint access path is an actual path instead of a var *)
|
||||||
|
let f_sub {Domain.LocalAccessPath.access_path= (var, _), _} =
|
||||||
|
match Var.get_footprint_index var with
|
||||||
|
| Some footprint_index -> (
|
||||||
|
match List.nth actuals footprint_index with
|
||||||
|
| Some HilExp.AccessPath actual_access_path ->
|
||||||
|
Some (Domain.LocalAccessPath.make actual_access_path caller_pname)
|
||||||
|
| _ ->
|
||||||
|
None )
|
||||||
|
| None ->
|
||||||
|
if Var.is_return var then
|
||||||
|
match ret_opt with
|
||||||
|
| Some ret ->
|
||||||
|
Some (Domain.LocalAccessPath.make (ret, []) caller_pname)
|
||||||
|
| None ->
|
||||||
|
assert false
|
||||||
|
else None
|
||||||
|
in
|
||||||
|
Domain.substitute ~f_sub summary
|
||||||
|
| None ->
|
||||||
|
astate
|
||||||
|
|
||||||
|
|
||||||
|
let exec_instr astate (proc_data: extras ProcData.t) _ (instr: HilInstr.t) : Domain.astate =
|
||||||
|
let caller_pname = Procdesc.get_proc_name proc_data.pdesc in
|
||||||
|
match instr with
|
||||||
|
| Call
|
||||||
|
( (Some return_base as ret_opt)
|
||||||
|
, Direct callee_procname
|
||||||
|
, ((HilExp.AccessPath receiver_ap) :: _ as actuals)
|
||||||
|
, _
|
||||||
|
, _ ) ->
|
||||||
|
let summary = Summary.read_summary proc_data.pdesc callee_procname in
|
||||||
|
(* track the call if the callee is a graphql getter *or* the receiver is already tracked *)
|
||||||
|
(* TODO: we should probably track all formals as well *)
|
||||||
|
let receiver = Domain.LocalAccessPath.make receiver_ap caller_pname in
|
||||||
|
if is_graphql_getter callee_procname summary || Domain.mem receiver astate then
|
||||||
|
let receiver = Domain.LocalAccessPath.make receiver_ap caller_pname in
|
||||||
|
let return_access_path = Domain.LocalAccessPath.make (return_base, []) caller_pname in
|
||||||
|
let return_calls =
|
||||||
|
(try Domain.find return_access_path astate with Not_found -> Domain.CallSet.empty)
|
||||||
|
|> Domain.CallSet.add {receiver; procname= callee_procname}
|
||||||
|
in
|
||||||
|
Domain.add return_access_path return_calls astate
|
||||||
|
else
|
||||||
|
(* treat it like a normal call *)
|
||||||
|
apply_callee_summary summary caller_pname ret_opt actuals astate
|
||||||
|
| Call (ret_opt, Direct callee_procname, actuals, _, _) ->
|
||||||
|
let summary = Summary.read_summary proc_data.pdesc callee_procname in
|
||||||
|
apply_callee_summary summary caller_pname ret_opt actuals astate
|
||||||
|
| Assign (lhs_ap, HilExp.AccessPath rhs_ap, _)
|
||||||
|
-> (
|
||||||
|
(* creating an alias for the rhs binding; assume all reads will now occur through the
|
||||||
|
alias. this helps us keep track of chains in cases like tmp = getFoo(); x = tmp;
|
||||||
|
tmp.getBar() *)
|
||||||
|
let lhs_access_path = Domain.LocalAccessPath.make lhs_ap caller_pname in
|
||||||
|
let rhs_access_path = Domain.LocalAccessPath.make rhs_ap caller_pname in
|
||||||
|
try
|
||||||
|
let call_set = Domain.find rhs_access_path astate in
|
||||||
|
Domain.remove rhs_access_path astate |> Domain.add lhs_access_path call_set
|
||||||
|
with Not_found -> astate )
|
||||||
|
| _ ->
|
||||||
|
astate
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
module Analyzer = LowerHil.MakeAbstractInterpreter (ProcCfg.Exceptional) (TransferFunctions)
|
||||||
|
|
||||||
|
let report access_path call_chain summary =
|
||||||
|
let call_strings = List.map ~f:(Typ.Procname.to_simplified_string ~withclass:false) call_chain in
|
||||||
|
let call_string = String.concat ~sep:"." call_strings in
|
||||||
|
let message = F.asprintf "%a.%s" AccessPath.pp access_path call_string in
|
||||||
|
let exn = Exceptions.Checkers (IssueType.graphql_field_access, Localise.verbatim_desc message) in
|
||||||
|
let loc = Specs.get_loc summary in
|
||||||
|
let ltr = [Errlog.make_trace_element 0 loc message []] in
|
||||||
|
Reporting.log_error summary ~loc ~ltr exn
|
||||||
|
|
||||||
|
|
||||||
|
let unroll_call call astate summary =
|
||||||
|
let max_depth = Domain.cardinal astate in
|
||||||
|
let rec unroll_call_ ({receiver; procname}: Domain.MethodCall.t) (acc, depth) =
|
||||||
|
let acc' = procname :: acc in
|
||||||
|
let depth' = depth + 1 in
|
||||||
|
let is_cycle access_path =
|
||||||
|
(* detect direct cycles and cycles due to mutual recursion *)
|
||||||
|
Domain.LocalAccessPath.equal access_path receiver || depth' > max_depth
|
||||||
|
in
|
||||||
|
try
|
||||||
|
let calls' = Domain.find receiver astate in
|
||||||
|
Domain.CallSet.iter
|
||||||
|
(fun call ->
|
||||||
|
if not (is_cycle call.receiver) then unroll_call_ call (acc', depth')
|
||||||
|
else report receiver.access_path acc' summary)
|
||||||
|
calls'
|
||||||
|
with Not_found -> report receiver.access_path acc' summary
|
||||||
|
in
|
||||||
|
unroll_call_ call ([], 0)
|
||||||
|
|
||||||
|
|
||||||
|
let should_report proc_desc =
|
||||||
|
match Procdesc.get_proc_name proc_desc with
|
||||||
|
| Typ.Procname.Java java_pname -> (
|
||||||
|
match Typ.Procname.java_get_method java_pname with "onCreateLayout" -> true | _ -> false )
|
||||||
|
| _ ->
|
||||||
|
false
|
||||||
|
|
||||||
|
|
||||||
|
let report_call_chains post summary =
|
||||||
|
Domain.iter
|
||||||
|
(fun _ call_set -> Domain.CallSet.iter (fun call -> unroll_call call post summary) call_set)
|
||||||
|
post
|
||||||
|
|
||||||
|
|
||||||
|
let postprocess astate proc_desc : Domain.astate =
|
||||||
|
let formal_map = FormalMap.make proc_desc in
|
||||||
|
let f_sub access_path = Domain.LocalAccessPath.to_formal_option access_path formal_map in
|
||||||
|
Domain.substitute ~f_sub astate
|
||||||
|
|
||||||
|
|
||||||
|
let checker {Callbacks.summary; proc_desc; tenv} =
|
||||||
|
let proc_data = ProcData.make_default proc_desc tenv in
|
||||||
|
match Analyzer.compute_post proc_data ~initial:Domain.empty with
|
||||||
|
| Some post ->
|
||||||
|
if should_report proc_desc then report_call_chains post summary ;
|
||||||
|
let payload = postprocess post proc_desc in
|
||||||
|
Summary.update_summary payload summary
|
||||||
|
| None ->
|
||||||
|
summary
|
@ -0,0 +1,67 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2017 - present Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*)
|
||||||
|
|
||||||
|
module F = Format
|
||||||
|
module L = Logging
|
||||||
|
|
||||||
|
(** Access path + its parent procedure *)
|
||||||
|
module LocalAccessPath = struct
|
||||||
|
type t = {access_path: AccessPath.t; parent: Typ.Procname.t} [@@deriving compare]
|
||||||
|
|
||||||
|
let equal = [%compare.equal : t]
|
||||||
|
|
||||||
|
let make access_path parent = {access_path; parent}
|
||||||
|
|
||||||
|
let is_rooted_in_footprint {access_path= (base_var, _), _} = Var.is_footprint base_var
|
||||||
|
|
||||||
|
let to_formal_option {access_path= ((_, base_typ) as base), accesses; parent} formal_map =
|
||||||
|
match FormalMap.get_formal_index base formal_map with
|
||||||
|
| Some formal_index ->
|
||||||
|
Some (make ((Var.of_formal_index formal_index, base_typ), accesses) parent)
|
||||||
|
| None ->
|
||||||
|
None
|
||||||
|
|
||||||
|
|
||||||
|
let pp fmt t = AccessPath.pp fmt t.access_path
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Called procedure + it's receiver *)
|
||||||
|
module MethodCall = struct
|
||||||
|
type t = {receiver: LocalAccessPath.t; procname: Typ.Procname.t} [@@deriving compare]
|
||||||
|
|
||||||
|
let pp fmt {receiver; procname} =
|
||||||
|
F.fprintf fmt "%a.%a" LocalAccessPath.pp receiver Typ.Procname.pp procname
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
module CallSet = AbstractDomain.FiniteSet (MethodCall)
|
||||||
|
include AbstractDomain.Map (LocalAccessPath) (CallSet)
|
||||||
|
|
||||||
|
let substitute ~(f_sub: LocalAccessPath.t -> LocalAccessPath.t option) astate =
|
||||||
|
fold
|
||||||
|
(fun original_access_path call_set acc ->
|
||||||
|
let access_path' =
|
||||||
|
match f_sub original_access_path with
|
||||||
|
| Some access_path ->
|
||||||
|
access_path
|
||||||
|
| None ->
|
||||||
|
original_access_path
|
||||||
|
in
|
||||||
|
let call_set' =
|
||||||
|
CallSet.fold
|
||||||
|
(fun ({procname} as call) call_set_acc ->
|
||||||
|
let receiver =
|
||||||
|
match f_sub call.receiver with Some receiver' -> receiver' | None -> call.receiver
|
||||||
|
in
|
||||||
|
CallSet.add {receiver; procname} call_set_acc)
|
||||||
|
call_set CallSet.empty
|
||||||
|
in
|
||||||
|
add access_path' call_set' acc)
|
||||||
|
astate empty
|
||||||
|
|
@ -1,117 +0,0 @@
|
|||||||
(*
|
|
||||||
* Copyright (c) 2017 - present Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*)
|
|
||||||
|
|
||||||
module F = Format
|
|
||||||
module L = Logging
|
|
||||||
module Domain = ShouldUpdateDomain
|
|
||||||
|
|
||||||
module Summary = Summary.Make (struct
|
|
||||||
type payload = Domain.astate
|
|
||||||
|
|
||||||
let update_payload astate (summary: Specs.summary) =
|
|
||||||
{summary with payload= {summary.payload with should_update= Some astate}}
|
|
||||||
|
|
||||||
|
|
||||||
let read_payload (summary: Specs.summary) = summary.payload.should_update
|
|
||||||
end)
|
|
||||||
|
|
||||||
module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
||||||
module CFG = CFG
|
|
||||||
module Domain = Domain
|
|
||||||
|
|
||||||
type extras = ProcData.no_extras
|
|
||||||
|
|
||||||
let is_getter = function
|
|
||||||
| Typ.Procname.Java procname ->
|
|
||||||
PatternMatch.is_getter procname
|
|
||||||
| _ ->
|
|
||||||
false
|
|
||||||
|
|
||||||
|
|
||||||
let exec_instr astate _ _ (instr: HilInstr.t) : Domain.astate =
|
|
||||||
match instr with
|
|
||||||
| Call (Some return_base, Direct procname, (HilExp.AccessPath receiver) :: _, _, _)
|
|
||||||
when is_getter procname ->
|
|
||||||
let return_access_path = (return_base, []) in
|
|
||||||
let return_calls =
|
|
||||||
(try Domain.find return_access_path astate with Not_found -> Domain.CallSet.empty)
|
|
||||||
|> Domain.CallSet.add {receiver; procname}
|
|
||||||
in
|
|
||||||
Domain.add return_access_path return_calls astate
|
|
||||||
| Call _ ->
|
|
||||||
(* TODO: interprocedural analysis
|
|
||||||
(1) add caller actuals to their callee call set (according to the summary)
|
|
||||||
(2) bind the caller return value to its callee call set (according to the summary
|
|
||||||
*)
|
|
||||||
astate
|
|
||||||
| Assign (lhs_access_path, HilExp.AccessPath rhs_access_path, _) -> (
|
|
||||||
try
|
|
||||||
(* creating an alias for the rhs binding; assume all reads will now occur through the
|
|
||||||
alias. this helps us keep track of chains in cases like tmp = getFoo(); x = tmp;
|
|
||||||
tmp.getBar() *)
|
|
||||||
let call_set = Domain.find rhs_access_path astate in
|
|
||||||
Domain.remove rhs_access_path astate |> Domain.add lhs_access_path call_set
|
|
||||||
with Not_found -> astate )
|
|
||||||
| _ ->
|
|
||||||
astate
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
module Analyzer = LowerHil.MakeAbstractInterpreter (ProcCfg.Exceptional) (TransferFunctions)
|
|
||||||
|
|
||||||
let report receiver call_chain summary =
|
|
||||||
let call_strings = List.map ~f:(Typ.Procname.to_simplified_string ~withclass:false) call_chain in
|
|
||||||
let call_string = String.concat ~sep:"." call_strings in
|
|
||||||
let message = F.asprintf "GraphQL getter chain %a.%s" AccessPath.pp receiver call_string in
|
|
||||||
let exn = Exceptions.Checkers (IssueType.graphql_field_access, Localise.verbatim_desc message) in
|
|
||||||
let loc = Specs.get_loc summary in
|
|
||||||
Reporting.log_error summary ~loc exn
|
|
||||||
|
|
||||||
|
|
||||||
let unroll_call call astate summary =
|
|
||||||
let max_depth = Domain.cardinal astate in
|
|
||||||
let rec unroll_call_ ({receiver; procname}: Domain.CallWithReceiver.t) (acc, depth) =
|
|
||||||
let acc' = procname :: acc in
|
|
||||||
let depth' = depth + 1 in
|
|
||||||
let is_cycle access_path =
|
|
||||||
(* detect direct cycles and cycles due to mutual recursion *)
|
|
||||||
AccessPath.equal access_path receiver || depth' > max_depth
|
|
||||||
in
|
|
||||||
try
|
|
||||||
let calls' = Domain.find receiver astate in
|
|
||||||
Domain.CallSet.iter
|
|
||||||
(fun call -> if not (is_cycle call.receiver) then unroll_call_ call (acc', depth'))
|
|
||||||
calls'
|
|
||||||
with Not_found -> report receiver acc' summary
|
|
||||||
in
|
|
||||||
unroll_call_ call ([], 0)
|
|
||||||
|
|
||||||
|
|
||||||
let should_report proc_desc =
|
|
||||||
match Procdesc.get_proc_name proc_desc with
|
|
||||||
| Typ.Procname.Java java_pname -> (
|
|
||||||
match Typ.Procname.java_get_method java_pname with "onCreateLayout" -> true | _ -> false )
|
|
||||||
| _ ->
|
|
||||||
false
|
|
||||||
|
|
||||||
|
|
||||||
let report_call_chains post summary =
|
|
||||||
Domain.iter
|
|
||||||
(fun _ call_set -> Domain.CallSet.iter (fun call -> unroll_call call post summary) call_set)
|
|
||||||
post
|
|
||||||
|
|
||||||
|
|
||||||
let checker {Callbacks.summary; proc_desc; tenv} =
|
|
||||||
let proc_data = ProcData.make_default proc_desc tenv in
|
|
||||||
match Analyzer.compute_post proc_data ~initial:Domain.empty with
|
|
||||||
| Some post ->
|
|
||||||
if should_report proc_desc then report_call_chains post summary ;
|
|
||||||
summary
|
|
||||||
| None ->
|
|
||||||
summary
|
|
@ -1,22 +0,0 @@
|
|||||||
(*
|
|
||||||
* Copyright (c) 2017 - present Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*)
|
|
||||||
|
|
||||||
module F = Format
|
|
||||||
module L = Logging
|
|
||||||
|
|
||||||
module CallWithReceiver = struct
|
|
||||||
type t = {receiver: AccessPath.t; procname: Typ.Procname.t} [@@deriving compare]
|
|
||||||
|
|
||||||
let pp fmt {receiver; procname} =
|
|
||||||
F.fprintf fmt "%a.%a" AccessPath.pp receiver Typ.Procname.pp procname
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
module CallSet = AbstractDomain.FiniteSet (CallWithReceiver)
|
|
||||||
include AbstractDomain.Map (AccessPath) (CallSet)
|
|
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright (c) 2016 - present Facebook, Inc.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# This source code is licensed under the BSD style license found in the
|
||||||
|
# LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
# of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
|
||||||
|
TESTS_DIR = ../../..
|
||||||
|
|
||||||
|
ANALYZER = checkers
|
||||||
|
INFER_OPTIONS = --litho-only --debug-exceptions
|
||||||
|
INFERPRINT_OPTIONS = --issues-tests
|
||||||
|
SOURCES = $(wildcard *.java)
|
||||||
|
|
||||||
|
include $(TESTS_DIR)/javac.make
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - present Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
// makes it easier to mock graphql types
|
||||||
|
package com.facebook.graphql.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
abstract class A {
|
||||||
|
abstract A getA();
|
||||||
|
abstract B getB();
|
||||||
|
abstract C getC();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class B {
|
||||||
|
abstract C getC();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class C {
|
||||||
|
abstract D getD();
|
||||||
|
}
|
||||||
|
|
||||||
|
class D {}
|
||||||
|
|
||||||
|
abstract class GraphQLStory {
|
||||||
|
|
||||||
|
public abstract List getActors();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class LithoTest {
|
||||||
|
|
||||||
|
void /*basic chain*/onCreateLayout(A a) {
|
||||||
|
a.getB().getC().getD();
|
||||||
|
}
|
||||||
|
|
||||||
|
void /*sibling chain*/onCreateLayout(A a, int i) {
|
||||||
|
a.getB().getC().getD();
|
||||||
|
a.getC().getD();
|
||||||
|
}
|
||||||
|
|
||||||
|
void /*split chain*/onCreateLayout(A a, int i1, int i2) {
|
||||||
|
B b = a.getB();
|
||||||
|
C c = b.getC();
|
||||||
|
c.getD();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void chainFromActual1(B b) {
|
||||||
|
b.getC().getD();
|
||||||
|
}
|
||||||
|
|
||||||
|
void chainFromActual2(C c) {
|
||||||
|
c.getD();
|
||||||
|
}
|
||||||
|
|
||||||
|
void /*chain rooted in actual*/onCreateLayout(A a, boolean b) {
|
||||||
|
chainFromActual1(a.getB());
|
||||||
|
}
|
||||||
|
|
||||||
|
void /*local chain + interproc chain*/onCreateLayout(A a, char ch) {
|
||||||
|
C c = a.getB().getC();
|
||||||
|
chainFromActual2(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// conditional getters
|
||||||
|
static GraphQLStory getPrimaryActor(GraphQLStory story) {
|
||||||
|
List actors = story.getActors();
|
||||||
|
return actors != null && actors.size() > 0 ? (GraphQLStory) actors.get(0) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void /*conditional getters on formal*/onCreateLayout(GraphQLStory story) {
|
||||||
|
getPrimaryActor(story).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
native static GraphQLStory getStory();
|
||||||
|
|
||||||
|
void /*conditional getters on local*/onCreateLayout() {
|
||||||
|
GraphQLStory story = getStory();
|
||||||
|
getPrimaryActor(story).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void /*cycle*/onCreateLayout(A a, float f) {
|
||||||
|
a = a.getA();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cycle(A a) {
|
||||||
|
a = a.getA();
|
||||||
|
}
|
||||||
|
|
||||||
|
void /*interprocedural cycle*/onCreateLayout(A a, double d) {
|
||||||
|
cycle(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(), 0, GRAPHQL_FIELD_ACCESS, [&story.getActors().get(...)]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(), 0, GRAPHQL_FIELD_ACCESS, [&story.getActors().get(...).toString()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(), 0, GRAPHQL_FIELD_ACCESS, [&story.getActors().size()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(), 0, GRAPHQL_FIELD_ACCESS, [&story.getActors()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC().getD()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A), 0, GRAPHQL_FIELD_ACCESS, [&a.getB()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,boolean), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC().getD()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,boolean), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,boolean), 0, GRAPHQL_FIELD_ACCESS, [&a.getB()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,char), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC().getD()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,char), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,char), 0, GRAPHQL_FIELD_ACCESS, [&a.getB()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,double), 0, GRAPHQL_FIELD_ACCESS, [&a.getA()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,float), 0, GRAPHQL_FIELD_ACCESS, [&a.getA()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,int), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC().getD()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,int), 0, GRAPHQL_FIELD_ACCESS, [&a.getC().getD()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,int), 0, GRAPHQL_FIELD_ACCESS, [&a.getC()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,int), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,int), 0, GRAPHQL_FIELD_ACCESS, [&a.getB()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,int,int), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC().getD()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,int,int), 0, GRAPHQL_FIELD_ACCESS, [&a.getB().getC()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(A,int,int), 0, GRAPHQL_FIELD_ACCESS, [&a.getB()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(GraphQLStory), 0, GRAPHQL_FIELD_ACCESS, [&story.getActors().get(...).toString()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(GraphQLStory), 0, GRAPHQL_FIELD_ACCESS, [&story.getActors()]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(GraphQLStory), 0, GRAPHQL_FIELD_ACCESS, [&story.getActors().get(...)]
|
||||||
|
codetoanalyze/java/litho/ShouldUpdate.java, void LithoTest.onCreateLayout(GraphQLStory), 0, GRAPHQL_FIELD_ACCESS, [&story.getActors().size()]
|
Loading…
Reference in new issue