//===--- PlaygroundTransform.cpp - Playground Transform -------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements the playground transform for Swift. // //===----------------------------------------------------------------------===// #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/Pattern.h" #include "swift/AST/Stmt.h" #include "swift/AST/Types.h" #include "swift/Parse/Lexer.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Subsystems.h" #include "TypeChecker.h" #include #include #include using namespace swift; //===----------------------------------------------------------------------===// // performPlaygroundTransform //===----------------------------------------------------------------------===// namespace { template class Added { private: E Contents; public: Added() { } Added(E NewContents) { Contents = NewContents; } const Added &operator=(const Added &rhs) { Contents = rhs.Contents; return *this; } E &operator*() { return Contents; } E &operator->() { return Contents; } }; class Instrumenter { private: std::mt19937_64 &RNG; ASTContext &Context; DeclContext *TypeCheckDC; unsigned &TmpNameIndex; bool HighPerformance; struct BracePair { public: SourceRange BraceRange; enum class TargetKinds { None = 0, Break, Return, Fallthrough }; TargetKinds TargetKind = TargetKinds::None; BracePair(const SourceRange &BR) : BraceRange(BR) { } }; typedef std::forward_list BracePairStack; BracePairStack BracePairs; class BracePairPusher { BracePairStack &BracePairs; bool Valid = false; public: BracePairPusher(BracePairStack &BPS, const SourceRange &BR) : BracePairs(BPS) { BracePairs.push_front(BracePair(BR)); Valid = true; } ~BracePairPusher() { if (isValid()) { Valid = false; BracePairs.pop_front(); } } void invalidate() { if (isValid()) { Valid = false; BracePairs.pop_front(); } } bool isValid() { return Valid; } }; class TargetKindSetter { BracePairStack &BracePairs; public: TargetKindSetter(BracePairStack &BPS, BracePair::TargetKinds Kind) : BracePairs(BPS) { assert(!BracePairs.empty()); assert(BracePairs.front().TargetKind == BracePair::TargetKinds::None); BracePairs.front().TargetKind = Kind; } ~TargetKindSetter() { BracePairs.front().TargetKind = BracePair::TargetKinds::None; } }; typedef SmallVector ElementVector; // Before a "return," "continue" or similar statement, emit pops of // all the braces up to its target. size_t escapeToTarget(BracePair::TargetKinds TargetKind, ElementVector &Elements, size_t EI) { if (HighPerformance) return EI; for (const BracePair &BP : BracePairs) { if (BP.TargetKind == TargetKind) { break; } Elements.insert(Elements.begin() + EI, *buildScopeExit(BP.BraceRange)); ++EI; } return EI; } class ClosureFinder : public ASTWalker { private: Instrumenter &I; public: ClosureFinder (Instrumenter &Inst) : I(Inst) { } virtual std::pair walkToStmtPre(Stmt *S) { if (isa(S)) { return { false, S }; // don't walk into brace statements; we // need to respect nesting! } else { return { true, S }; } } virtual std::pair walkToExprPre(Expr *E) { if (ClosureExpr *CE = dyn_cast(E)) { BraceStmt *B = CE->getBody(); if (B) { BraceStmt *NB = I.transformBraceStmt(B); CE->setBody(NB, false); // just with the entry and exit logging this is going to // be more than a single expression! } } return { true, E }; } }; ClosureFinder CF; public: Instrumenter(ASTContext &C, DeclContext *DC, std::mt19937_64 &RNG, bool HP, unsigned &TmpNameIndex) : RNG(RNG), Context(C), TypeCheckDC(DC), TmpNameIndex(TmpNameIndex), HighPerformance(HP), CF(*this) {} Stmt *transformStmt(Stmt *S) { switch (S->getKind()) { default: return S; case StmtKind::Brace: return transformBraceStmt(cast(S)); case StmtKind::If: return transformIfStmt(cast(S)); case StmtKind::Guard: return transformGuardStmt(cast(S)); case StmtKind::While: { TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break); return transformWhileStmt(cast(S)); } case StmtKind::RepeatWhile: { TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break); return transformRepeatWhileStmt(cast(S)); } case StmtKind::For: { TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break); return transformForStmt(cast(S)); } case StmtKind::ForEach: { TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break); return transformForEachStmt(cast(S)); } case StmtKind::Switch: { TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Fallthrough); return transformSwitchStmt(cast(S)); } case StmtKind::Do: return transformDoStmt(llvm::cast(S)); case StmtKind::DoCatch: return transformDoCatchStmt(cast(S)); } } // transform*() return their input if it's unmodified, // or a modified copy of their input otherwise. IfStmt *transformIfStmt(IfStmt *IS) { if (Stmt *TS = IS->getThenStmt()) { Stmt *NTS = transformStmt(TS); if (NTS != TS) { IS->setThenStmt(NTS); } } if (Stmt *ES = IS->getElseStmt()) { Stmt *NES = transformStmt(ES); if (NES != ES) { IS->setElseStmt(NES); } } return IS; } GuardStmt *transformGuardStmt(GuardStmt *GS) { if (Stmt *BS = GS->getBody()) GS->setBody(transformStmt(BS)); return GS; } WhileStmt *transformWhileStmt(WhileStmt *WS) { if (Stmt *B = WS->getBody()) { Stmt *NB = transformStmt(B); if (NB != B) { WS->setBody(NB); } } return WS; } RepeatWhileStmt *transformRepeatWhileStmt(RepeatWhileStmt *RWS) { if (Stmt *B = RWS->getBody()) { Stmt *NB = transformStmt(B); if (NB != B) { RWS->setBody(NB); } } return RWS; } ForStmt *transformForStmt(ForStmt *FS) { if (Stmt *B = FS->getBody()) { Stmt *NB = transformStmt(B); if (NB != B) { FS->setBody(NB); } } return FS; } ForEachStmt *transformForEachStmt(ForEachStmt *FES) { if (BraceStmt *B = FES->getBody()) { BraceStmt *NB = transformBraceStmt(B); if (NB != B) { FES->setBody(NB); } } return FES; } SwitchStmt *transformSwitchStmt(SwitchStmt *SS) { for (CaseStmt *CS : SS->getCases()) { if (Stmt *S = CS->getBody()) { if (auto *B = dyn_cast(S)) { BraceStmt *NB = transformBraceStmt(B); if (NB != B) { CS->setBody(NB); } } } } return SS; } DoStmt *transformDoStmt(DoStmt *DS) { if (BraceStmt *B = dyn_cast_or_null(DS->getBody())) { BraceStmt *NB = transformBraceStmt(B); if (NB != B) { DS->setBody(NB); } } return DS; } DoCatchStmt *transformDoCatchStmt(DoCatchStmt *DCS) { if (BraceStmt *B = dyn_cast_or_null(DCS->getBody())) { BraceStmt *NB = transformBraceStmt(B); if (NB != B) { DCS->setBody(NB); } } for (CatchStmt *C : DCS->getCatches()) { if (BraceStmt *CB = dyn_cast_or_null(C->getBody())) { BraceStmt *NCB = transformBraceStmt(CB); if (NCB != CB) { C->setBody(NCB); } } } return DCS; } Decl *transformDecl(Decl *D) { if (D->isImplicit()) return D; if (FuncDecl *FD = dyn_cast(D)) { if (BraceStmt *B = FD->getBody()) { TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Return); BraceStmt *NB = transformBraceStmt(B); if (NB != B) { FD->setBody(NB); } } } else if (auto *NTD = dyn_cast(D)) { for (Decl *Member : NTD->getMembers()) { transformDecl(Member); } } return D; } std::pair, ValueDecl *> digForVariable(Expr *E) { switch (E->getKind()) { default: if (auto *ICE = dyn_cast(E)) return digForVariable(ICE->getSubExpr()); return std::make_pair(Added(nullptr), nullptr); case ExprKind::DeclRef: { ValueDecl *D = cast(E)->getDecl(); Added DRE = new (Context) DeclRefExpr(ConcreteDeclRef(D), cast(E)->getNameLoc(), true, // implicit AccessSemantics::Ordinary, cast(E)->getType()); return std::make_pair(DRE, D); } case ExprKind::MemberRef: { std::pair, ValueDecl *> BaseVariable = digForVariable(cast(E)->getBase()); if (!*BaseVariable.first || !BaseVariable.second) return std::make_pair(nullptr, nullptr); ValueDecl *M = cast(E)->getMember().getDecl(); Added MRE( new (Context) MemberRefExpr(*BaseVariable.first, cast(E)->getDotLoc(), ConcreteDeclRef(M), cast(E)->getNameLoc(), true, // implicit AccessSemantics::Ordinary)); return std::make_pair(MRE, M); } case ExprKind::Load: return digForVariable(cast(E)->getSubExpr()); case ExprKind::ForceValue: return digForVariable(cast(E)->getSubExpr()); case ExprKind::InOut: return digForVariable(cast(E)->getSubExpr()); } } std::string digForName(Expr *E) { Added RE = nullptr; ValueDecl *VD = nullptr; std::tie(RE, VD) = digForVariable(E); if (VD) { return VD->getName().str(); } else { return std::string(""); } } static DeclRefExpr *digForInoutDeclRef(Expr *E) { if (auto inout = dyn_cast(E)) { return dyn_cast( inout->getSubExpr()->getSemanticsProvidingExpr()); // Drill through tuple shuffles, ignoring non-default-argument inouts. } else if (auto shuffle = dyn_cast(E)) { return digForInoutDeclRef(shuffle->getSubExpr()); // Try to find a unique inout argument in a tuple. } else if (auto tuple = dyn_cast(E)) { DeclRefExpr *uniqueRef = nullptr; for (Expr *elt : tuple->getElements()) { if (auto ref = digForInoutDeclRef(elt)) { // If we already have a reference, it's not unique. if (uniqueRef) return nullptr; uniqueRef = ref; } } return uniqueRef; // Look through parens. } else { auto subExpr = E->getSemanticsProvidingExpr(); return (E == subExpr ? nullptr : digForInoutDeclRef(subExpr)); } } class ErrorGatherer : public DiagnosticConsumer { private: bool error = false; DiagnosticEngine &diags; public: ErrorGatherer(DiagnosticEngine &diags) : diags(diags) { diags.addConsumer(*this); } ~ErrorGatherer() { diags.takeConsumers(); } virtual void handleDiagnostic(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, StringRef Text, const DiagnosticInfo &Info) { if (Kind == swift::DiagnosticKind::Error) { error = true; } llvm::errs() << Text << "\n"; } bool hadError() { return error; } }; class ErrorFinder : public ASTWalker { bool error = false; public: ErrorFinder () { } virtual std::pair walkToExprPre(Expr *E) { if (isa(E) || !E->getType() || E->getType()->is()) { error = true; return { false, E }; } return { true, E }; } virtual bool walkToDeclPre(Decl *D) { if (ValueDecl *VD = dyn_cast(D)) { if (!VD->getType() || VD->getType()->is()) { error = true; return false; } } return true; } bool hadError() { return error; } }; template bool doTypeCheck(ASTContext &Ctx, DeclContext *DC, Added &parsedExpr) { DiagnosticEngine diags(Ctx.SourceMgr); ErrorGatherer errorGatherer(diags); TypeChecker TC(Ctx, diags); Expr *E = *parsedExpr; TC.typeCheckExpression(E, DC); parsedExpr = Added(dyn_cast(E)); if (*parsedExpr) { ErrorFinder errorFinder; parsedExpr->walk(errorFinder); if (!errorFinder.hadError() && !errorGatherer.hadError()) { return true; } } return false; } BraceStmt *transformBraceStmt(BraceStmt *BS, bool TopLevel = false) { ArrayRef OriginalElements = BS->getElements(); typedef SmallVector ElementVector; ElementVector Elements(OriginalElements.begin(), OriginalElements.end()); SourceRange SR = BS->getSourceRange(); BracePairPusher BPP(BracePairs, SR); for (size_t EI = 0; EI != Elements.size(); ++EI) { swift::ASTNode &Element = Elements[EI]; if (Expr *E = Element.dyn_cast()) { E->walk(CF); if (AssignExpr *AE = dyn_cast(E)) { if (auto *MRE = dyn_cast(AE->getDest())) { // an assignment to a property of an object counts as a mutation of // that object Added Base_RE = nullptr; ValueDecl *BaseVD = nullptr; std::tie(Base_RE, BaseVD) = digForVariable(MRE->getBase()); if (*Base_RE) { Added Log = logDeclOrMemberRef(Base_RE); if (*Log) { Elements.insert(Elements.begin() + (EI + 1), *Log); ++EI; } } } else { std::pair PV = buildPatternAndVariable(AE->getSrc()); DeclRefExpr *DRE = new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, AE->getSrc()->getType()); AssignExpr *NAE = new (Context) AssignExpr(AE->getDest(), SourceLoc(), DRE, true); // implicit NAE->setType(Context.TheEmptyTupleType); AE->setImplicit(true); std::string Name = digForName(AE->getDest()); Added Log(buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, AE->getSrc()->getType()), AE->getSrc()->getSourceRange(), Name.c_str())); if (*Log) { Elements[EI] = PV.first; Elements.insert(Elements.begin() + (EI + 1), PV.second); Elements.insert(Elements.begin() + (EI + 2), *Log); Elements.insert(Elements.begin() + (EI + 3), NAE); EI += 3; } } } else if (ApplyExpr *AE = dyn_cast(E)) { bool Handled = false; if (DeclRefExpr *DRE = dyn_cast(AE->getFn())) { auto *FnD = dyn_cast(DRE->getDecl()); if (FnD && FnD->getModuleContext() == Context.TheStdlibModule) { StringRef FnName = FnD->getNameStr(); if (FnName.equals("print") || FnName.equals("debugPrint")) { const bool isOldStyle = false; if (isOldStyle) { const bool isDebugPrint = FnName.equals("debugPrint"); PatternBindingDecl *ArgPattern = nullptr; VarDecl *ArgVariable = nullptr; Added Log = logPrint(isDebugPrint, AE, ArgPattern, ArgVariable); if (*Log) { if (ArgPattern) { assert(ArgVariable); Elements[EI] = ArgPattern; Elements.insert(Elements.begin() + (EI + 1), ArgVariable); Elements.insert(Elements.begin() + (EI + 2), *Log); EI += 2; } else { Elements[EI] = *Log; } } } else { Added Log = logPostPrint(AE->getSourceRange()); Elements.insert(Elements.begin() + (EI + 1), *Log); EI += 1; } Handled = true; } } } if (!Handled && AE->getType()->getCanonicalType() == Context.TheEmptyTupleType) { if (auto *DSCE = dyn_cast(AE->getFn())) { Expr *TargetExpr = DSCE->getArg(); Added Target_RE; ValueDecl *TargetVD = nullptr; std::tie(Target_RE, TargetVD) = digForVariable(TargetExpr); if (TargetVD) { Added Log = logDeclOrMemberRef(Target_RE); if (*Log) { Elements.insert(Elements.begin() + (EI + 1), *Log); ++EI; } } } else if (DeclRefExpr *DRE = digForInoutDeclRef(AE->getArg())) { Added Log = logDeclOrMemberRef(DRE); if (*Log) { Elements.insert(Elements.begin() + (EI + 1), *Log); ++EI; } } Handled = true; } if (!Handled) { // do the same as for all other expressions std::pair PV = buildPatternAndVariable(E); Added Log = buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, E->getType()), E->getSourceRange(), ""); if (*Log) { Elements[EI] = PV.first; Elements.insert(Elements.begin() + (EI + 1), PV.second); Elements.insert(Elements.begin() + (EI + 2), *Log); EI += 2; } } } else { if (E->getType()->getCanonicalType() != Context.TheEmptyTupleType) { std::pair PV = buildPatternAndVariable(E); Added Log = buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, E->getType()), E->getSourceRange(), ""); if (*Log) { Elements[EI] = PV.first; Elements.insert(Elements.begin() + (EI + 1), PV.second); Elements.insert(Elements.begin() + (EI + 2), *Log); EI += 2; } } } } else if (Stmt *S = Element.dyn_cast()) { S->walk(CF); if (ReturnStmt *RS = dyn_cast(S)) { if (RS->hasResult()) { std::pair PV = buildPatternAndVariable(RS->getResult()); DeclRefExpr *DRE = new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, RS->getResult()->getType()); ReturnStmt *NRS = new (Context) ReturnStmt(SourceLoc(), DRE, true); // implicit Added Log = buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, RS->getResult()->getType()), RS->getResult()->getSourceRange(), ""); if (*Log) { Elements[EI] = PV.first; Elements.insert(Elements.begin() + (EI + 1), PV.second); Elements.insert(Elements.begin() + (EI + 2), *Log); Elements.insert(Elements.begin() + (EI + 3), NRS); EI += 3; } } EI = escapeToTarget(BracePair::TargetKinds::Return, Elements, EI); } else { if (isa(S) || isa(S)) { EI = escapeToTarget(BracePair::TargetKinds::Break, Elements, EI); } else if (isa(S)) { EI = escapeToTarget(BracePair::TargetKinds::Fallthrough, Elements, EI); } Stmt *NS = transformStmt(S); if (NS != S) { Elements[EI] = NS; } } } else if (Decl *D = Element.dyn_cast()) { D->walk(CF); if (auto *PBD = dyn_cast(D)) { if (VarDecl *VD = PBD->getSingleVar()) { if (VD->getParentInitializer()) { Added Log = logVarDecl(VD); if (*Log) { Elements.insert(Elements.begin() + (EI + 1), *Log); ++EI; } } } } else { transformDecl(D); } } } if (!TopLevel && !HighPerformance) { Elements.insert(Elements.begin(), *buildScopeEntry(BS->getSourceRange())); Elements.insert(Elements.end(), *buildScopeExit(BS->getSourceRange())); } // Remove null elements from the list. // FIXME: This is a band-aid used to work around the fact that the // above code can introduce null elements into the vector. The // right fix is to avoid doing that above. Elements.erase(std::remove_if(Elements.begin(), Elements.end(), [](ASTNode node) { return node.isNull(); }), Elements.end()); return swift::BraceStmt::create(Context, BS->getLBraceLoc(), Context.AllocateCopy(Elements), BS->getRBraceLoc()); } // log*() functions return a newly-created log expression to be inserted // after or instead of the expression they're looking at. Only call this // if the variable has an initializer. Added logVarDecl(VarDecl *VD) { if (isa(TypeCheckDC) && VD->getNameStr().equals("self")) { // Don't log "self" in a constructor return nullptr; } return buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(VD), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, Type()), VD->getSourceRange(), VD->getName().str().str().c_str()); } Added logDeclOrMemberRef(Added RE) { if (DeclRefExpr *DRE = dyn_cast(*RE)) { VarDecl *VD = cast(DRE->getDecl()); if (isa(TypeCheckDC) && VD->getNameStr().equals("self")){ // Don't log "self" in a constructor return nullptr; } return buildLoggerCall( new (Context) DeclRefExpr(ConcreteDeclRef(VD), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, Type()), DRE->getSourceRange(), VD->getName().str().str().c_str()); } else if (MemberRefExpr *MRE = dyn_cast(*RE)) { Expr *B = MRE->getBase(); ConcreteDeclRef M = MRE->getMember(); if (isa(TypeCheckDC) && !digForName(B).compare("self")) { // Don't log attributes of "self" in a constructor return nullptr; } return buildLoggerCall( new (Context) MemberRefExpr(B, SourceLoc(), M, DeclNameLoc(), true, // implicit AccessSemantics::Ordinary), MRE->getSourceRange(), M.getDecl()->getName().str().str().c_str()); } else { return nullptr; } } std::pair maybeFixupPrintArgument(ApplyExpr *Print) { Expr *ArgTuple = Print->getArg(); if (ParenExpr *PE = dyn_cast(ArgTuple)) { std::pair PV = buildPatternAndVariable(PE->getSubExpr()); PE->setSubExpr(new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, PE->getSubExpr()->getType())); return PV; } else if (TupleExpr *TE = dyn_cast(ArgTuple)) { if (TE->getNumElements() == 0) { return std::make_pair(nullptr, nullptr); } else { // Are we using print() specialized to handle a single argument, // or is actually only the first argument of interest and the rest are // extra information for print()? bool useJustFirst = false; if (TE->hasElementNames()) { useJustFirst = true; } else { for (Expr *Arg : TE->getElements()) { if (Arg->getType()->getAs()) { useJustFirst = true; break; } } } if (useJustFirst) { std::pair PV = buildPatternAndVariable(TE->getElement(0)); TE->setElement(0, new (Context) DeclRefExpr( ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, TE->getElement(0)->getType())); return PV; } else { std::pair PV = buildPatternAndVariable(TE); Print->setArg(new (Context) DeclRefExpr( ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, TE->getType())); return PV; } } } else { return std::make_pair(nullptr, nullptr); } } Added logPrint(bool isDebugPrint, ApplyExpr *AE, PatternBindingDecl *&ArgPattern, VarDecl *&ArgVariable) { const char *LoggerName = isDebugPrint ? "$builtin_debugPrint" : "$builtin_print"; UnresolvedDeclRefExpr *LoggerRef = new (Context) UnresolvedDeclRefExpr( Context.getIdentifier(LoggerName), DeclRefKind::Ordinary, DeclNameLoc(AE->getSourceRange().End)); std::tie(ArgPattern, ArgVariable) = maybeFixupPrintArgument(AE); AE->setFn(LoggerRef); Added AddedApply(AE); // safe because we've fixed up the args if (!doTypeCheck(Context, TypeCheckDC, AddedApply)) { return nullptr; } return buildLoggerCallWithApply(AddedApply, AE->getSourceRange()); } Added logPostPrint(SourceRange SR) { return buildLoggerCallWithArgs("$builtin_postPrint", MutableArrayRef(), SR); } std::pair buildPatternAndVariable(Expr *InitExpr) { char NameBuf[11] = { 0 }; snprintf(NameBuf, 11, "tmp%u", TmpNameIndex); TmpNameIndex++; Expr *MaybeLoadInitExpr = nullptr; if (LValueType *LVT = dyn_cast(InitExpr->getType().getPointer())) { MaybeLoadInitExpr = new (Context) LoadExpr (InitExpr, LVT->getObjectType()); } else { MaybeLoadInitExpr = InitExpr; } VarDecl *VD = new (Context) VarDecl(false, // static true, // let SourceLoc(), Context.getIdentifier(NameBuf), MaybeLoadInitExpr->getType(), TypeCheckDC); VD->setImplicit(); NamedPattern *NP = new (Context) NamedPattern(VD, /*implicit*/true); PatternBindingDecl *PBD = PatternBindingDecl::create(Context, SourceLoc(), StaticSpellingKind::None, SourceLoc(), NP, MaybeLoadInitExpr, TypeCheckDC); PBD->setImplicit(); return std::make_pair(PBD, VD); } Added buildLoggerCall(Added E, SourceRange SR, const char *Name) { assert(Name); std::string *NameInContext = Context.AllocateObjectCopy(std::string(Name)); Expr *NameExpr = new (Context) StringLiteralExpr(NameInContext->c_str(), SourceRange()); NameExpr->setImplicit(true); const size_t buf_size = 11; char * const id_buf = (char*)Context.Allocate(buf_size, 1); std::uniform_int_distribution Distribution(0, 0x7fffffffu); const unsigned id_num = Distribution(RNG); ::snprintf(id_buf, buf_size, "%u", id_num); Expr *IDExpr = new (Context) IntegerLiteralExpr(id_buf, SourceLoc(), true); Expr *LoggerArgExprs[] = { *E, NameExpr, IDExpr }; return buildLoggerCallWithArgs("$builtin_log_with_id", MutableArrayRef(LoggerArgExprs), SR); } Added buildScopeEntry(SourceRange SR) { return buildScopeCall(SR, false); } Added buildScopeExit(SourceRange SR) { return buildScopeCall(SR, true); } Added buildScopeCall(SourceRange SR, bool IsExit) { const char *LoggerName = IsExit ? "$builtin_log_scope_exit" : "$builtin_log_scope_entry"; return buildLoggerCallWithArgs(LoggerName, MutableArrayRef(), SR); } Added buildLoggerCallWithArgs(const char *LoggerName, MutableArrayRef Args, SourceRange SR) { Expr *LoggerArgs = nullptr; if (Args.size() == 1) { LoggerArgs = new (Context) ParenExpr(SourceLoc(), Args[0], SourceLoc(), false); } else { LoggerArgs = TupleExpr::createImplicit(Context, Args, { }); } UnresolvedDeclRefExpr *LoggerRef = new (Context) UnresolvedDeclRefExpr( Context.getIdentifier(LoggerName), DeclRefKind::Ordinary, DeclNameLoc(SR.End)); LoggerRef->setImplicit(true); ApplyExpr *LoggerCall = new (Context) CallExpr(LoggerRef, LoggerArgs, true, Type()); Added AddedLogger(LoggerCall); if (!doTypeCheck(Context, TypeCheckDC, AddedLogger)) { return nullptr; } return buildLoggerCallWithApply(AddedLogger, SR); } // Assumes Apply has already been type-checked. Added buildLoggerCallWithApply(Added Apply, SourceRange SR) { std::pair StartLC = Context.SourceMgr.getLineAndColumn(SR.Start); std::pair EndLC = Context.SourceMgr.getLineAndColumn( Lexer::getLocForEndOfToken(Context.SourceMgr, SR.End)); const size_t buf_size = 8; char *start_line_buf = (char*)Context.Allocate(buf_size, 1); char *end_line_buf = (char*)Context.Allocate(buf_size, 1); char *start_column_buf = (char*)Context.Allocate(buf_size, 1); char *end_column_buf = (char*)Context.Allocate(buf_size, 1); ::snprintf(start_line_buf, buf_size, "%d", StartLC.first); ::snprintf(start_column_buf, buf_size, "%d", StartLC.second); ::snprintf(end_line_buf, buf_size, "%d", EndLC.first); ::snprintf(end_column_buf, buf_size, "%d", EndLC.second); Expr *StartLine = new (Context) IntegerLiteralExpr(start_line_buf, SR.End, true); Expr *EndLine = new (Context) IntegerLiteralExpr(end_line_buf, SR.End, true); Expr *StartColumn = new (Context) IntegerLiteralExpr(start_column_buf, SR.End, true); Expr *EndColumn = new (Context) IntegerLiteralExpr(end_column_buf, SR.End, true); std::pair PV = buildPatternAndVariable(*Apply); DeclRefExpr *DRE = new (Context) DeclRefExpr(ConcreteDeclRef(PV.second), DeclNameLoc(), true, // implicit AccessSemantics::Ordinary, Apply->getType()); Expr *SendDataArgExprs[] = { DRE, StartLine, EndLine, StartColumn, EndColumn }; TupleExpr *SendDataArgs = TupleExpr::createImplicit(Context, SendDataArgExprs, { }); UnresolvedDeclRefExpr *SendDataRef = new (Context) UnresolvedDeclRefExpr( Context.getIdentifier("$builtin_send_data"), DeclRefKind::Ordinary, DeclNameLoc()); SendDataRef->setImplicit(true); Expr * SendDataCall = new (Context) CallExpr(SendDataRef, SendDataArgs, true, Type()); Added AddedSendData(SendDataCall); if (!doTypeCheck(Context, TypeCheckDC, AddedSendData)) { return nullptr; } ASTNode Elements[] = { PV.first, PV.second, SendDataCall }; BraceStmt *BS = BraceStmt::create(Context, SourceLoc(), Elements, SourceLoc(), true); return BS; } }; } // end anonymous namespace void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) { class ExpressionFinder : public ASTWalker { private: std::mt19937_64 RNG; bool HighPerformance; unsigned TmpNameIndex = 0; public: ExpressionFinder(bool HP) : HighPerformance(HP) {} virtual bool walkToDeclPre(Decl *D) { if (AbstractFunctionDecl *FD = dyn_cast(D)) { if (!FD->isImplicit()) { if (BraceStmt *Body = FD->getBody()) { ASTContext &ctx = FD->getASTContext(); Instrumenter I(ctx, FD, RNG, HighPerformance, TmpNameIndex); BraceStmt *NewBody = I.transformBraceStmt(Body); if (NewBody != Body) { FD->setBody(NewBody); TypeChecker(ctx).checkFunctionErrorHandling(FD); } return false; } } } else if (TopLevelCodeDecl *TLCD = dyn_cast(D)) { if (!TLCD->isImplicit()) { if (BraceStmt *Body = TLCD->getBody()) { ASTContext &ctx = static_cast(TLCD)->getASTContext(); Instrumenter I(ctx, TLCD, RNG, HighPerformance, TmpNameIndex); BraceStmt *NewBody = I.transformBraceStmt(Body, true); if (NewBody != Body) { TLCD->setBody(NewBody); TypeChecker(ctx).checkTopLevelErrorHandling(TLCD); } return false; } } } return true; } }; ExpressionFinder EF(HighPerformance); for (Decl* D : SF.Decls) { D->walk(EF); } }