зеркало из https://github.com/microsoft/clang-1.git
objc-arc: Perform null check on receiver before sending methods which
consume one or more of their arguments. If not done, this will cause a leak as method will not consume the argument when receiver is null. In this patch, the null path releases consumed argument. // rdar://10444474 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149279 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
7fc800356f
Коммит
3c267e6262
|
@ -1414,8 +1414,16 @@ struct NullReturnState {
|
|||
CGF.EmitBlock(callBB);
|
||||
}
|
||||
|
||||
RValue complete(CodeGenFunction &CGF, RValue result, QualType resultType) {
|
||||
RValue complete(CodeGenFunction &CGF, RValue result, QualType resultType,
|
||||
const CallArgList &CallArgs,
|
||||
const ObjCMethodDecl *Method) {
|
||||
if (!NullBB) return result;
|
||||
|
||||
llvm::Value *NullInitPtr = 0;
|
||||
if (result.isScalar() && !resultType->isVoidType()) {
|
||||
NullInitPtr = CGF.CreateTempAlloca(result.getScalarVal()->getType());
|
||||
CGF.Builder.CreateStore(result.getScalarVal(), NullInitPtr);
|
||||
}
|
||||
|
||||
// Finish the call path.
|
||||
llvm::BasicBlock *contBB = CGF.createBasicBlock("msgSend.cont");
|
||||
|
@ -1423,6 +1431,31 @@ struct NullReturnState {
|
|||
|
||||
// Emit the null-init block and perform the null-initialization there.
|
||||
CGF.EmitBlock(NullBB);
|
||||
|
||||
// Release consumed arguments along the null-receiver path.
|
||||
if (Method) {
|
||||
CallArgList::const_iterator I = CallArgs.begin();
|
||||
for (ObjCMethodDecl::param_const_iterator i = Method->param_begin(),
|
||||
e = Method->param_end(); i != e; ++i, ++I) {
|
||||
const ParmVarDecl *ParamDecl = (*i);
|
||||
if (ParamDecl->hasAttr<NSConsumedAttr>()) {
|
||||
RValue RV = I->RV;
|
||||
assert(RV.isScalar() &&
|
||||
"NullReturnState::complete - arg not on object");
|
||||
CGF.EmitARCRelease(RV.getScalarVal(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.isScalar()) {
|
||||
if (NullInitPtr)
|
||||
CGF.EmitNullInitialization(NullInitPtr, resultType);
|
||||
// Jump to the continuation block.
|
||||
CGF.EmitBlock(contBB);
|
||||
return NullInitPtr ? RValue::get(CGF.Builder.CreateLoad(NullInitPtr))
|
||||
: result;
|
||||
}
|
||||
|
||||
if (!resultType->isAnyComplexType()) {
|
||||
assert(result.isAggregate() && "null init of non-aggregate result?");
|
||||
CGF.EmitNullInitialization(result.getAggregateAddr(), resultType);
|
||||
|
@ -1668,9 +1701,24 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
|
|||
Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper)
|
||||
: ObjCTypes.getSendFn(IsSuper);
|
||||
}
|
||||
|
||||
bool requiresnullCheck = false;
|
||||
if (CGM.getLangOptions().ObjCAutoRefCount && Method)
|
||||
for (ObjCMethodDecl::param_const_iterator i = Method->param_begin(),
|
||||
e = Method->param_end(); i != e; ++i) {
|
||||
const ParmVarDecl *ParamDecl = (*i);
|
||||
if (ParamDecl->hasAttr<NSConsumedAttr>()) {
|
||||
if (!nullReturn.NullBB)
|
||||
nullReturn.init(CGF, Arg0);
|
||||
requiresnullCheck = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Fn = llvm::ConstantExpr::getBitCast(Fn, llvm::PointerType::getUnqual(FTy));
|
||||
RValue rvalue = CGF.EmitCall(FnInfo, Fn, Return, ActualArgs);
|
||||
return nullReturn.complete(CGF, rvalue, ResultType);
|
||||
return nullReturn.complete(CGF, rvalue, ResultType, CallArgs,
|
||||
requiresnullCheck ? Method : 0);
|
||||
}
|
||||
|
||||
static Qualifiers::GC GetGCAttrTypeForType(ASTContext &Ctx, QualType FQT) {
|
||||
|
@ -5785,7 +5833,8 @@ CGObjCNonFragileABIMac::EmitVTableMessageSend(CodeGenFunction &CGF,
|
|||
llvm::PointerType::getUnqual(fnType));
|
||||
|
||||
RValue result = CGF.EmitCall(fnInfo, callee, returnSlot, args);
|
||||
return nullReturn.complete(CGF, result, resultType);
|
||||
CallArgList CallArgs;
|
||||
return nullReturn.complete(CGF, result, resultType, CallArgs, 0);
|
||||
}
|
||||
|
||||
/// Generate code for a message send expression in the nonfragile abi.
|
||||
|
|
|
@ -1489,8 +1489,13 @@ void test66(void) {
|
|||
// CHECK-NEXT: [[T5:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T4]])
|
||||
// CHECK-NEXT: [[T6:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES
|
||||
// CHECK-NEXT: [[T7:%.*]] = bitcast [[TEST66]]* [[T3]] to i8*
|
||||
// CHECK-NEXT: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i8*)*)(i8* [[T7]], i8* [[T6]], i8* [[T5]])
|
||||
// CHECK-NEXT: [[T8:%.*]] = bitcast [[TEST66]]* [[T3]] to i8*
|
||||
// CHECK-NEXT: [[SIX:%.*]] = icmp eq i8* [[T7]], null
|
||||
// CHECK-NEXT: br i1 [[SIX]], label [[NULINIT:%.*]], label [[CALL:%.*]]
|
||||
// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i8*)*)(i8* [[T7]], i8* [[T6]], i8* [[T5]])
|
||||
// CHECK-NEXT: br label [[CONT:%.*]]
|
||||
// CHECK: call void @objc_release(i8* [[T5]]) nounwind
|
||||
// CHECK-NEXT: br label [[CONT:%.*]]
|
||||
// CHECK: [[T8:%.*]] = bitcast [[TEST66]]* [[T3]] to i8*
|
||||
// CHECK-NEXT: call void @objc_release(i8* [[T8]])
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче