[thread-safety] propagate @Functional attribute across boxing of primitive types

Reviewed By: jeremydubreil

Differential Revision: D4501552

fbshipit-source-id: c4cc9ec
master
Sam Blackshear 8 years ago committed by Facebook Github Bot
parent 6338997cf5
commit 0e77e36235

@ -174,6 +174,24 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
{ astate with unconditional_writes; } in
let is_unprotected is_locked =
not is_locked && not (Procdesc.is_java_synchronized pdesc) in
(* return true if the given procname boxes a primitive type into a reference type *)
let is_box = function
| Procname.Java java_pname ->
begin
match Procname.java_get_class_name java_pname, Procname.java_get_method java_pname with
| ("java.lang.Boolean" |
"java.lang.Byte" |
"java.lang.Char" |
"java.lang.Double" |
"java.lang.Float" |
"java.lang.Integer" |
"java.lang.Long" |
"java.lang.Short"),
"valueOf" -> true
| _ -> false
end
| _ ->
false in
let f_resolve_id = resolve_id astate.id_map in
let open Domain in
@ -293,7 +311,24 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
astate'.owned in
{ astate' with locks = locks'; owned; }
| None ->
astate in
if is_box callee_pname
then
match ret_opt, actuals with
| Some (ret_id, ret_typ), (actual_exp, actual_typ) :: _ ->
begin
match AccessPath.of_lhs_exp actual_exp actual_typ ~f_resolve_id with
| Some ap when AccessPathSetDomain.mem ap astate.functional ->
let functional =
AccessPathSetDomain.add
(AccessPath.of_id ret_id ret_typ) astate.functional in
{ astate with functional; }
| _ ->
astate
end
| _ ->
astate
else
astate in
begin
match ret_opt with
| Some (_, (Typ.Tint ILong | Tfloat FDouble)) ->

@ -194,4 +194,51 @@ class Annotations implements FunctionalInterface {
return mLong;
}
Boolean mBoxedBool;
@Functional native boolean returnBool();
public boolean functionalAcrossBoxingOk() {
if (mBoxedBool != null) {
mBoxedBool = returnBool();
}
return mBoxedBool;
}
boolean mBool;
@Functional native Boolean returnBoxedBool();
public boolean FP_functionalAcrossUnboxingOk() {
if (!mBool) {
mBool = returnBoxedBool();
}
return mBool;
}
Long mBoxedLong;
@Functional native Long returnBoxedLong();
public Long functionalBoxedLongOk() {
if (mBoxedLong == null) {
mBoxedLong = returnBoxedLong();
}
return mBoxedLong;
}
public long functionalAcrossUnboxingLongBad() {
if (mLong != 0L) {
mLong = returnBoxedLong();
}
return mLong;
}
public long FP_functionalAcrossBoxingLongOk() {
if (mBoxedLong != null) {
mBoxedLong = returnLong();
}
return mBoxedLong;
}
}

@ -1,5 +1,8 @@
codetoanalyze/java/threadsafety/Annotations.java, boolean Annotations.FP_functionalAcrossUnboxingOk(), 2, THREAD_SAFETY_VIOLATION, [access to codetoanalyze.java.checkers.Annotations.mBool]
codetoanalyze/java/threadsafety/Annotations.java, double Annotations.functionalDoubleBad(), 2, THREAD_SAFETY_VIOLATION, [access to codetoanalyze.java.checkers.Annotations.mDouble]
codetoanalyze/java/threadsafety/Annotations.java, long Annotations.FP_functionalAcrossBoxingLongOk(), 2, THREAD_SAFETY_VIOLATION, [access to codetoanalyze.java.checkers.Annotations.mBoxedLong]
codetoanalyze/java/threadsafety/Annotations.java, long Annotations.functionaLongBad(), 2, THREAD_SAFETY_VIOLATION, [access to codetoanalyze.java.checkers.Annotations.mLong]
codetoanalyze/java/threadsafety/Annotations.java, long Annotations.functionalAcrossUnboxingLongBad(), 2, THREAD_SAFETY_VIOLATION, [access to codetoanalyze.java.checkers.Annotations.mLong]
codetoanalyze/java/threadsafety/Annotations.java, void Annotations.mutateOffUiThreadBad(), 1, THREAD_SAFETY_VIOLATION, [access to codetoanalyze.java.checkers.Annotations.f]
codetoanalyze/java/threadsafety/Annotations.java, void Annotations.mutateSubfieldOfConfinedBad(), 1, THREAD_SAFETY_VIOLATION, [access to codetoanalyze.java.checkers.Annotations.encapsulatedField.codetoanalyze.java.checkers.Annotations$Obj.fld]
codetoanalyze/java/threadsafety/Builders.java, Builders$Obj Builders.buildThenMutateBad(Builders$Obj), 3, THREAD_SAFETY_VIOLATION, [access to codetoanalyze.java.checkers.Builders$Obj.g]

Loading…
Cancel
Save