You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
6.4 KiB

/*
* 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.
*/
package codetoanalyze.java.checkers;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.concurrent.ThreadSafe;
import android.support.annotation.UiThread;
import com.facebook.infer.annotation.AssumeThreadSafe;
import com.facebook.infer.annotation.Functional;
import com.facebook.infer.annotation.ThreadConfined;
import com.facebook.infer.annotation.ReturnsOwnership;
/** tests for classes and method annotations that are meaningful w.r.t thread-safety */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
@interface OnBind {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
@interface OnEvent {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
@interface OnMount {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
@interface OnUnbind {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
@interface OnUnmount {
}
interface Interface {
@Functional Object functionalMethod();
@ReturnsOwnership Obj returnsOwnershipMethod();
}
@ThreadSafe
class Annotations implements Interface {
Object f;
@UiThread
public void setF(Object newF) {
this.f = newF; // shouldn't report here
}
public void callSetFOnMethodOk(Annotations obj) {
obj.setF(new Object()); // or here
}
public void mutateOffUiThreadBad() {
this.f = new Object();
}
// anything annotated with OnEvent is modeled as running on the UI thread, should not warn
@OnEvent
public void onClick() {
this.f = new Object();
}
Confined con;
public void confinedCallerOk(){
con.foo();
}
public void writeFieldOfConfinedClassOk() {
con.x = 7;
}
@ThreadConfined(ThreadConfined.UI)
class Confined {
Integer x;
void foo(){
x = 22;
}
}
@ThreadConfined(ThreadConfined.ANY) Obj encapsulatedField;
public void mutateConfinedFieldDirectlyOk() {
this.encapsulatedField = new Obj();
}
public static void mutateConfinedFieldIndirectlyOk(Annotations a) {
a.encapsulatedField = new Obj();
}
public void mutateSubfieldOfConfinedBad() {
this.encapsulatedField.f = new Object();
}
@ThreadConfined("some_custom_string")
public void threadConfinedMethodOk() {
this.f = new Object();
}
@OnBind
public void onBindMethodOk() {
this.f = new Object();
}
@OnMount
public void onMountMethodOk() {
this.f = new Object();
}
@OnUnmount
public void onUnmountMethodOk() {
this.f = new Object();
}
@OnUnbind
public void onUnbindMethodOk() {
this.f = new Object();
}
@AssumeThreadSafe(because = "it's a test")
public void assumeThreadSafeOk() {
this.f = new Object();
}
@Functional native Object returnFunctional1();
@Functional Object returnFunctional2() { return null; }
// marked @Functional in interface
@Override public Object functionalMethod() { return null; }
Object mAssignToFunctional;
public Object functionalOk1() {
if (mAssignToFunctional == null) {
mAssignToFunctional = returnFunctional1();
}
return mAssignToFunctional;
}
public Object functionalOk2() {
if (mAssignToFunctional == null) {
mAssignToFunctional = returnFunctional2();
}
return mAssignToFunctional;
}
public Object functionalOk3() {
if (mAssignToFunctional == null) {
mAssignToFunctional = functionalMethod();
}
return mAssignToFunctional;
}
@Functional native double returnDouble();
@Functional native long returnLong();
double mDouble;
long mLong;
// writes to doubles are not atomic on all platforms, so this is not a benign race
public double functionalDoubleBad() {
if (mDouble == 0.0) {
mDouble = returnDouble();
}
return mDouble;
}
// writes to longs are not atomic on all platforms, so this is not a benign race
public long functionaLongBad() {
if (mLong == 0L) {
mLong = returnLong();
}
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;
}
public boolean propagateFunctional() {
return returnBool();
}
// show that we can handle indirect returns of procedures marked @Functional
public void propagateFunctionalOk() {
boolean returnedFunctional = propagateFunctional();
mBool = returnedFunctional;
}
@Functional native int returnInt();
int mInt;
public void functionalAcrossLogicalOpsOk() {
boolean functionalBool = returnBool();
int functionalInt = returnInt();
boolean propagated = functionalBool && true || 2 < returnInt() && 3 == functionalInt;
mBool = propagated;
}
public void functionalAcrossArithmeticOpsOk() {
int functional = returnInt();
int propagated = functional + 1 - returnInt() * 7 % 2;
mInt = functional;
}
native int returnNonFunctionalInt();
public void functionalAndNonfunctionalBad() {
mInt = returnNonFunctionalInt() + returnInt();
}
@ReturnsOwnership native Obj returnsOwned();
@Override
public native Obj returnsOwnershipMethod(); // marked @ReturnsOwnership in interface
void mutateAnnotatedOwnedOk() {
Obj owned = returnsOwned();
owned.f = new Object();
}
void mutateAnnotatedOverrideOwnedOk() {
Obj owned = returnsOwnershipMethod();
owned.f = new Object();
}
}