1 1.1 joerg //===--- TransAutoreleasePool.cpp - Transformations to ARC mode -----------===// 2 1.1 joerg // 3 1.1 joerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 1.1 joerg // See https://llvm.org/LICENSE.txt for license information. 5 1.1 joerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 1.1 joerg // 7 1.1 joerg //===----------------------------------------------------------------------===// 8 1.1 joerg // 9 1.1 joerg // rewriteAutoreleasePool: 10 1.1 joerg // 11 1.1 joerg // Calls to NSAutoreleasePools will be rewritten as an @autorelease scope. 12 1.1 joerg // 13 1.1 joerg // NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 14 1.1 joerg // ... 15 1.1 joerg // [pool release]; 16 1.1 joerg // ----> 17 1.1 joerg // @autorelease { 18 1.1 joerg // ... 19 1.1 joerg // } 20 1.1 joerg // 21 1.1 joerg // An NSAutoreleasePool will not be touched if: 22 1.1 joerg // - There is not a corresponding -release/-drain in the same scope 23 1.1 joerg // - Not all references of the NSAutoreleasePool variable can be removed 24 1.1 joerg // - There is a variable that is declared inside the intended @autorelease scope 25 1.1 joerg // which is also used outside it. 26 1.1 joerg // 27 1.1 joerg //===----------------------------------------------------------------------===// 28 1.1 joerg 29 1.1 joerg #include "Transforms.h" 30 1.1 joerg #include "Internals.h" 31 1.1 joerg #include "clang/AST/ASTContext.h" 32 1.1 joerg #include "clang/Basic/SourceManager.h" 33 1.1 joerg #include "clang/Sema/SemaDiagnostic.h" 34 1.1 joerg #include <map> 35 1.1 joerg 36 1.1 joerg using namespace clang; 37 1.1 joerg using namespace arcmt; 38 1.1 joerg using namespace trans; 39 1.1 joerg 40 1.1 joerg namespace { 41 1.1 joerg 42 1.1 joerg class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> { 43 1.1 joerg Decl *Dcl; 44 1.1 joerg SmallVectorImpl<ObjCMessageExpr *> &Releases; 45 1.1 joerg 46 1.1 joerg public: 47 1.1 joerg ReleaseCollector(Decl *D, SmallVectorImpl<ObjCMessageExpr *> &releases) 48 1.1 joerg : Dcl(D), Releases(releases) { } 49 1.1 joerg 50 1.1 joerg bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 51 1.1 joerg if (!E->isInstanceMessage()) 52 1.1 joerg return true; 53 1.1 joerg if (E->getMethodFamily() != OMF_release) 54 1.1 joerg return true; 55 1.1 joerg Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts(); 56 1.1 joerg if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) { 57 1.1 joerg if (DE->getDecl() == Dcl) 58 1.1 joerg Releases.push_back(E); 59 1.1 joerg } 60 1.1 joerg return true; 61 1.1 joerg } 62 1.1 joerg }; 63 1.1 joerg 64 1.1 joerg } 65 1.1 joerg 66 1.1 joerg namespace { 67 1.1 joerg 68 1.1 joerg class AutoreleasePoolRewriter 69 1.1 joerg : public RecursiveASTVisitor<AutoreleasePoolRewriter> { 70 1.1 joerg public: 71 1.1 joerg AutoreleasePoolRewriter(MigrationPass &pass) 72 1.1 joerg : Body(nullptr), Pass(pass) { 73 1.1 joerg PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool"); 74 1.1 joerg DrainSel = pass.Ctx.Selectors.getNullarySelector( 75 1.1 joerg &pass.Ctx.Idents.get("drain")); 76 1.1 joerg } 77 1.1 joerg 78 1.1 joerg void transformBody(Stmt *body, Decl *ParentD) { 79 1.1 joerg Body = body; 80 1.1 joerg TraverseStmt(body); 81 1.1 joerg } 82 1.1 joerg 83 1.1 joerg ~AutoreleasePoolRewriter() { 84 1.1 joerg SmallVector<VarDecl *, 8> VarsToHandle; 85 1.1 joerg 86 1.1 joerg for (std::map<VarDecl *, PoolVarInfo>::iterator 87 1.1 joerg I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) { 88 1.1 joerg VarDecl *var = I->first; 89 1.1 joerg PoolVarInfo &info = I->second; 90 1.1 joerg 91 1.1 joerg // Check that we can handle/rewrite all references of the pool. 92 1.1 joerg 93 1.1 joerg clearRefsIn(info.Dcl, info.Refs); 94 1.1 joerg for (SmallVectorImpl<PoolScope>::iterator 95 1.1 joerg scpI = info.Scopes.begin(), 96 1.1 joerg scpE = info.Scopes.end(); scpI != scpE; ++scpI) { 97 1.1 joerg PoolScope &scope = *scpI; 98 1.1 joerg clearRefsIn(*scope.Begin, info.Refs); 99 1.1 joerg clearRefsIn(*scope.End, info.Refs); 100 1.1 joerg clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs); 101 1.1 joerg } 102 1.1 joerg 103 1.1 joerg // Even if one reference is not handled we will not do anything about that 104 1.1 joerg // pool variable. 105 1.1 joerg if (info.Refs.empty()) 106 1.1 joerg VarsToHandle.push_back(var); 107 1.1 joerg } 108 1.1 joerg 109 1.1 joerg for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) { 110 1.1 joerg PoolVarInfo &info = PoolVars[VarsToHandle[i]]; 111 1.1 joerg 112 1.1 joerg Transaction Trans(Pass.TA); 113 1.1 joerg 114 1.1 joerg clearUnavailableDiags(info.Dcl); 115 1.1 joerg Pass.TA.removeStmt(info.Dcl); 116 1.1 joerg 117 1.1 joerg // Add "@autoreleasepool { }" 118 1.1 joerg for (SmallVectorImpl<PoolScope>::iterator 119 1.1 joerg scpI = info.Scopes.begin(), 120 1.1 joerg scpE = info.Scopes.end(); scpI != scpE; ++scpI) { 121 1.1 joerg PoolScope &scope = *scpI; 122 1.1 joerg clearUnavailableDiags(*scope.Begin); 123 1.1 joerg clearUnavailableDiags(*scope.End); 124 1.1 joerg if (scope.IsFollowedBySimpleReturnStmt) { 125 1.1 joerg // Include the return in the scope. 126 1.1 joerg Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {"); 127 1.1 joerg Pass.TA.removeStmt(*scope.End); 128 1.1 joerg Stmt::child_iterator retI = scope.End; 129 1.1 joerg ++retI; 130 1.1 joerg SourceLocation afterSemi = 131 1.1 joerg findLocationAfterSemi((*retI)->getEndLoc(), Pass.Ctx); 132 1.1 joerg assert(afterSemi.isValid() && 133 1.1 joerg "Didn't we check before setting IsFollowedBySimpleReturnStmt " 134 1.1 joerg "to true?"); 135 1.1 joerg Pass.TA.insertAfterToken(afterSemi, "\n}"); 136 1.1 joerg Pass.TA.increaseIndentation( 137 1.1 joerg SourceRange(scope.getIndentedRange().getBegin(), 138 1.1 joerg (*retI)->getEndLoc()), 139 1.1 joerg scope.CompoundParent->getBeginLoc()); 140 1.1 joerg } else { 141 1.1 joerg Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {"); 142 1.1 joerg Pass.TA.replaceStmt(*scope.End, "}"); 143 1.1 joerg Pass.TA.increaseIndentation(scope.getIndentedRange(), 144 1.1 joerg scope.CompoundParent->getBeginLoc()); 145 1.1 joerg } 146 1.1 joerg } 147 1.1 joerg 148 1.1 joerg // Remove rest of pool var references. 149 1.1 joerg for (SmallVectorImpl<PoolScope>::iterator 150 1.1 joerg scpI = info.Scopes.begin(), 151 1.1 joerg scpE = info.Scopes.end(); scpI != scpE; ++scpI) { 152 1.1 joerg PoolScope &scope = *scpI; 153 1.1 joerg for (SmallVectorImpl<ObjCMessageExpr *>::iterator 154 1.1 joerg relI = scope.Releases.begin(), 155 1.1 joerg relE = scope.Releases.end(); relI != relE; ++relI) { 156 1.1 joerg clearUnavailableDiags(*relI); 157 1.1 joerg Pass.TA.removeStmt(*relI); 158 1.1 joerg } 159 1.1 joerg } 160 1.1 joerg } 161 1.1 joerg } 162 1.1 joerg 163 1.1 joerg bool VisitCompoundStmt(CompoundStmt *S) { 164 1.1 joerg SmallVector<PoolScope, 4> Scopes; 165 1.1 joerg 166 1.1 joerg for (Stmt::child_iterator 167 1.1 joerg I = S->body_begin(), E = S->body_end(); I != E; ++I) { 168 1.1 joerg Stmt *child = getEssential(*I); 169 1.1 joerg if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) { 170 1.1 joerg if (DclS->isSingleDecl()) { 171 1.1 joerg if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) { 172 1.1 joerg if (isNSAutoreleasePool(VD->getType())) { 173 1.1 joerg PoolVarInfo &info = PoolVars[VD]; 174 1.1 joerg info.Dcl = DclS; 175 1.1 joerg collectRefs(VD, S, info.Refs); 176 1.1 joerg // Does this statement follow the pattern: 177 1.1 joerg // NSAutoreleasePool * pool = [NSAutoreleasePool new]; 178 1.1 joerg if (isPoolCreation(VD->getInit())) { 179 1.1 joerg Scopes.push_back(PoolScope()); 180 1.1 joerg Scopes.back().PoolVar = VD; 181 1.1 joerg Scopes.back().CompoundParent = S; 182 1.1 joerg Scopes.back().Begin = I; 183 1.1 joerg } 184 1.1 joerg } 185 1.1 joerg } 186 1.1 joerg } 187 1.1 joerg } else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) { 188 1.1 joerg if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) { 189 1.1 joerg if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) { 190 1.1 joerg // Does this statement follow the pattern: 191 1.1 joerg // pool = [NSAutoreleasePool new]; 192 1.1 joerg if (isNSAutoreleasePool(VD->getType()) && 193 1.1 joerg isPoolCreation(bop->getRHS())) { 194 1.1 joerg Scopes.push_back(PoolScope()); 195 1.1 joerg Scopes.back().PoolVar = VD; 196 1.1 joerg Scopes.back().CompoundParent = S; 197 1.1 joerg Scopes.back().Begin = I; 198 1.1 joerg } 199 1.1 joerg } 200 1.1 joerg } 201 1.1 joerg } 202 1.1 joerg 203 1.1 joerg if (Scopes.empty()) 204 1.1 joerg continue; 205 1.1 joerg 206 1.1 joerg if (isPoolDrain(Scopes.back().PoolVar, child)) { 207 1.1 joerg PoolScope &scope = Scopes.back(); 208 1.1 joerg scope.End = I; 209 1.1 joerg handlePoolScope(scope, S); 210 1.1 joerg Scopes.pop_back(); 211 1.1 joerg } 212 1.1 joerg } 213 1.1 joerg return true; 214 1.1 joerg } 215 1.1 joerg 216 1.1 joerg private: 217 1.1 joerg void clearUnavailableDiags(Stmt *S) { 218 1.1 joerg if (S) 219 1.1 joerg Pass.TA.clearDiagnostic(diag::err_unavailable, 220 1.1 joerg diag::err_unavailable_message, 221 1.1 joerg S->getSourceRange()); 222 1.1 joerg } 223 1.1 joerg 224 1.1 joerg struct PoolScope { 225 1.1 joerg VarDecl *PoolVar; 226 1.1 joerg CompoundStmt *CompoundParent; 227 1.1 joerg Stmt::child_iterator Begin; 228 1.1 joerg Stmt::child_iterator End; 229 1.1 joerg bool IsFollowedBySimpleReturnStmt; 230 1.1 joerg SmallVector<ObjCMessageExpr *, 4> Releases; 231 1.1 joerg 232 1.1 joerg PoolScope() : PoolVar(nullptr), CompoundParent(nullptr), Begin(), End(), 233 1.1 joerg IsFollowedBySimpleReturnStmt(false) { } 234 1.1 joerg 235 1.1 joerg SourceRange getIndentedRange() const { 236 1.1 joerg Stmt::child_iterator rangeS = Begin; 237 1.1 joerg ++rangeS; 238 1.1 joerg if (rangeS == End) 239 1.1 joerg return SourceRange(); 240 1.1 joerg Stmt::child_iterator rangeE = Begin; 241 1.1 joerg for (Stmt::child_iterator I = rangeS; I != End; ++I) 242 1.1 joerg ++rangeE; 243 1.1 joerg return SourceRange((*rangeS)->getBeginLoc(), (*rangeE)->getEndLoc()); 244 1.1 joerg } 245 1.1 joerg }; 246 1.1 joerg 247 1.1 joerg class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{ 248 1.1 joerg ASTContext &Ctx; 249 1.1 joerg SourceRange ScopeRange; 250 1.1 joerg SourceLocation &referenceLoc, &declarationLoc; 251 1.1 joerg 252 1.1 joerg public: 253 1.1 joerg NameReferenceChecker(ASTContext &ctx, PoolScope &scope, 254 1.1 joerg SourceLocation &referenceLoc, 255 1.1 joerg SourceLocation &declarationLoc) 256 1.1 joerg : Ctx(ctx), referenceLoc(referenceLoc), 257 1.1 joerg declarationLoc(declarationLoc) { 258 1.1 joerg ScopeRange = SourceRange((*scope.Begin)->getBeginLoc(), 259 1.1 joerg (*scope.End)->getBeginLoc()); 260 1.1 joerg } 261 1.1 joerg 262 1.1 joerg bool VisitDeclRefExpr(DeclRefExpr *E) { 263 1.1 joerg return checkRef(E->getLocation(), E->getDecl()->getLocation()); 264 1.1 joerg } 265 1.1 joerg 266 1.1 joerg bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { 267 1.1 joerg return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation()); 268 1.1 joerg } 269 1.1 joerg 270 1.1 joerg bool VisitTagTypeLoc(TagTypeLoc TL) { 271 1.1 joerg return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation()); 272 1.1 joerg } 273 1.1 joerg 274 1.1 joerg private: 275 1.1 joerg bool checkRef(SourceLocation refLoc, SourceLocation declLoc) { 276 1.1 joerg if (isInScope(declLoc)) { 277 1.1 joerg referenceLoc = refLoc; 278 1.1 joerg declarationLoc = declLoc; 279 1.1 joerg return false; 280 1.1 joerg } 281 1.1 joerg return true; 282 1.1 joerg } 283 1.1 joerg 284 1.1 joerg bool isInScope(SourceLocation loc) { 285 1.1 joerg if (loc.isInvalid()) 286 1.1 joerg return false; 287 1.1 joerg 288 1.1 joerg SourceManager &SM = Ctx.getSourceManager(); 289 1.1 joerg if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin())) 290 1.1 joerg return false; 291 1.1 joerg return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd()); 292 1.1 joerg } 293 1.1 joerg }; 294 1.1 joerg 295 1.1 joerg void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) { 296 1.1 joerg // Check that all names declared inside the scope are not used 297 1.1 joerg // outside the scope. 298 1.1 joerg { 299 1.1 joerg bool nameUsedOutsideScope = false; 300 1.1 joerg SourceLocation referenceLoc, declarationLoc; 301 1.1 joerg Stmt::child_iterator SI = scope.End, SE = compoundS->body_end(); 302 1.1 joerg ++SI; 303 1.1 joerg // Check if the autoreleasepool scope is followed by a simple return 304 1.1 joerg // statement, in which case we will include the return in the scope. 305 1.1 joerg if (SI != SE) 306 1.1 joerg if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI)) 307 1.1 joerg if ((retS->getRetValue() == nullptr || 308 1.1 joerg isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) && 309 1.1 joerg findLocationAfterSemi(retS->getEndLoc(), Pass.Ctx).isValid()) { 310 1.1 joerg scope.IsFollowedBySimpleReturnStmt = true; 311 1.1 joerg ++SI; // the return will be included in scope, don't check it. 312 1.1 joerg } 313 1.1 joerg 314 1.1 joerg for (; SI != SE; ++SI) { 315 1.1 joerg nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope, 316 1.1 joerg referenceLoc, 317 1.1 joerg declarationLoc).TraverseStmt(*SI); 318 1.1 joerg if (nameUsedOutsideScope) 319 1.1 joerg break; 320 1.1 joerg } 321 1.1 joerg 322 1.1 joerg // If not all references were cleared it means some variables/typenames/etc 323 1.1 joerg // declared inside the pool scope are used outside of it. 324 1.1 joerg // We won't try to rewrite the pool. 325 1.1 joerg if (nameUsedOutsideScope) { 326 1.1 joerg Pass.TA.reportError("a name is referenced outside the " 327 1.1 joerg "NSAutoreleasePool scope that it was declared in", referenceLoc); 328 1.1 joerg Pass.TA.reportNote("name declared here", declarationLoc); 329 1.1 joerg Pass.TA.reportNote("intended @autoreleasepool scope begins here", 330 1.1 joerg (*scope.Begin)->getBeginLoc()); 331 1.1 joerg Pass.TA.reportNote("intended @autoreleasepool scope ends here", 332 1.1 joerg (*scope.End)->getBeginLoc()); 333 1.1 joerg return; 334 1.1 joerg } 335 1.1 joerg } 336 1.1 joerg 337 1.1 joerg // Collect all releases of the pool; they will be removed. 338 1.1 joerg { 339 1.1 joerg ReleaseCollector releaseColl(scope.PoolVar, scope.Releases); 340 1.1 joerg Stmt::child_iterator I = scope.Begin; 341 1.1 joerg ++I; 342 1.1 joerg for (; I != scope.End; ++I) 343 1.1 joerg releaseColl.TraverseStmt(*I); 344 1.1 joerg } 345 1.1 joerg 346 1.1 joerg PoolVars[scope.PoolVar].Scopes.push_back(scope); 347 1.1 joerg } 348 1.1 joerg 349 1.1 joerg bool isPoolCreation(Expr *E) { 350 1.1 joerg if (!E) return false; 351 1.1 joerg E = getEssential(E); 352 1.1 joerg ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E); 353 1.1 joerg if (!ME) return false; 354 1.1 joerg if (ME->getMethodFamily() == OMF_new && 355 1.1 joerg ME->getReceiverKind() == ObjCMessageExpr::Class && 356 1.1 joerg isNSAutoreleasePool(ME->getReceiverInterface())) 357 1.1 joerg return true; 358 1.1 joerg if (ME->getReceiverKind() == ObjCMessageExpr::Instance && 359 1.1 joerg ME->getMethodFamily() == OMF_init) { 360 1.1 joerg Expr *rec = getEssential(ME->getInstanceReceiver()); 361 1.1 joerg if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) { 362 1.1 joerg if (recME->getMethodFamily() == OMF_alloc && 363 1.1 joerg recME->getReceiverKind() == ObjCMessageExpr::Class && 364 1.1 joerg isNSAutoreleasePool(recME->getReceiverInterface())) 365 1.1 joerg return true; 366 1.1 joerg } 367 1.1 joerg } 368 1.1 joerg 369 1.1 joerg return false; 370 1.1 joerg } 371 1.1 joerg 372 1.1 joerg bool isPoolDrain(VarDecl *poolVar, Stmt *S) { 373 1.1 joerg if (!S) return false; 374 1.1 joerg S = getEssential(S); 375 1.1 joerg ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S); 376 1.1 joerg if (!ME) return false; 377 1.1 joerg if (ME->getReceiverKind() == ObjCMessageExpr::Instance) { 378 1.1 joerg Expr *rec = getEssential(ME->getInstanceReceiver()); 379 1.1 joerg if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec)) 380 1.1 joerg if (dref->getDecl() == poolVar) 381 1.1 joerg return ME->getMethodFamily() == OMF_release || 382 1.1 joerg ME->getSelector() == DrainSel; 383 1.1 joerg } 384 1.1 joerg 385 1.1 joerg return false; 386 1.1 joerg } 387 1.1 joerg 388 1.1 joerg bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) { 389 1.1 joerg return IDecl && IDecl->getIdentifier() == PoolII; 390 1.1 joerg } 391 1.1 joerg 392 1.1 joerg bool isNSAutoreleasePool(QualType Ty) { 393 1.1 joerg QualType pointee = Ty->getPointeeType(); 394 1.1 joerg if (pointee.isNull()) 395 1.1 joerg return false; 396 1.1 joerg if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>()) 397 1.1 joerg return isNSAutoreleasePool(interT->getDecl()); 398 1.1 joerg return false; 399 1.1 joerg } 400 1.1 joerg 401 1.1 joerg static Expr *getEssential(Expr *E) { 402 1.1 joerg return cast<Expr>(getEssential((Stmt*)E)); 403 1.1 joerg } 404 1.1 joerg static Stmt *getEssential(Stmt *S) { 405 1.1 joerg if (FullExpr *FE = dyn_cast<FullExpr>(S)) 406 1.1 joerg S = FE->getSubExpr(); 407 1.1 joerg if (Expr *E = dyn_cast<Expr>(S)) 408 1.1 joerg S = E->IgnoreParenCasts(); 409 1.1 joerg return S; 410 1.1 joerg } 411 1.1 joerg 412 1.1 joerg Stmt *Body; 413 1.1 joerg MigrationPass &Pass; 414 1.1 joerg 415 1.1 joerg IdentifierInfo *PoolII; 416 1.1 joerg Selector DrainSel; 417 1.1 joerg 418 1.1 joerg struct PoolVarInfo { 419 1.1 joerg DeclStmt *Dcl; 420 1.1 joerg ExprSet Refs; 421 1.1 joerg SmallVector<PoolScope, 2> Scopes; 422 1.1 joerg 423 1.1 joerg PoolVarInfo() : Dcl(nullptr) { } 424 1.1 joerg }; 425 1.1 joerg 426 1.1 joerg std::map<VarDecl *, PoolVarInfo> PoolVars; 427 1.1 joerg }; 428 1.1 joerg 429 1.1 joerg } // anonymous namespace 430 1.1 joerg 431 1.1 joerg void trans::rewriteAutoreleasePool(MigrationPass &pass) { 432 1.1 joerg BodyTransform<AutoreleasePoolRewriter> trans(pass); 433 1.1 joerg trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 434 1.1 joerg } 435