//===--- PlaygroundTransform.cpp - Playground Transform -------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 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/CodeCompletionTypeChecking.h"
#include "swift/Subsystems.h"
#include "TypeChecker.h"

#include <forward_list>
#include <random>

using namespace swift;

//===----------------------------------------------------------------------===//
// performPlaygroundTransform
//===----------------------------------------------------------------------===//

namespace {

template <class E> class Added {
private:
  E Contents;
public:
  Added() { }
  Added(E NewContents) { Contents = NewContents; }
  const Added<E> &operator=(const Added<E> &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 = 0;
  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<BracePair> 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<swift::ASTNode, 3> 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<bool, Stmt*> walkToStmtPre(Stmt *S) {
      if (isa<BraceStmt>(S)) {
        return { false, S }; // don't walk into brace statements; we
                             // need to respect nesting!
      } else {
        return { true, S };
      }
    }
    virtual std::pair<bool, Expr*> walkToExprPre(Expr *E) {
      if (ClosureExpr *CE = dyn_cast<ClosureExpr>(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) :
    RNG(RNG), Context(C), TypeCheckDC(DC), HighPerformance(HP), CF(*this) { }
    
  Stmt *transformStmt(Stmt *S) { 
    switch (S->getKind()) {
    default:
      return S;
    case StmtKind::Brace:
      return transformBraceStmt(cast<BraceStmt>(S));
    case StmtKind::If:
      return transformIfStmt(cast<IfStmt>(S));
    case StmtKind::Guard:
      return transformGuardStmt(cast<GuardStmt>(S));
    case StmtKind::While: {
        TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break);
        return transformWhileStmt(cast<WhileStmt>(S));
      }
    case StmtKind::RepeatWhile: {
        TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break);
        return transformRepeatWhileStmt(cast<RepeatWhileStmt>(S));
      }
    case StmtKind::For: {
        TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break);
        return transformForStmt(cast<ForStmt>(S));
      }
    case StmtKind::ForEach: {
        TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Break);
        return transformForEachStmt(cast<ForEachStmt>(S));
      }
    case StmtKind::Switch: {
        TargetKindSetter TKS(BracePairs, BracePair::TargetKinds::Fallthrough);
        return transformSwitchStmt(cast<SwitchStmt>(S));
      }
    case StmtKind::Do:
      return transformDoStmt(llvm::cast<DoStmt>(S));
    case StmtKind::DoCatch:
      return transformDoCatchStmt(cast<DoCatchStmt>(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<BraceStmt>(S)) {
          BraceStmt *NB = transformBraceStmt(B);
          if (NB != B) {
            CS->setBody(NB);
          }
        }
      }
    }
      
    return SS;
  }

  DoStmt *transformDoStmt(DoStmt *DS) {
    if (BraceStmt *B = dyn_cast_or_null<BraceStmt>(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<BraceStmt>(DCS->getBody())) {
      BraceStmt *NB = transformBraceStmt(B);
      if (NB != B) {
        DCS->setBody(NB);
      }
    }
    for (CatchStmt *C : DCS->getCatches()) {
      if (BraceStmt *CB = dyn_cast_or_null<BraceStmt>(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<FuncDecl>(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<NominalTypeDecl>(D)) {
      for (Decl *Member : NTD->getMembers()) {
        transformDecl(Member);
      }
    }
    
    return D;
  }

  std::pair<Added<Expr *>, ValueDecl *> digForVariable(Expr *E) {
    switch (E->getKind()) {
    default:
      if (auto *ICE = dyn_cast<ImplicitConversionExpr>(E))
        return digForVariable(ICE->getSubExpr());
      return std::make_pair(Added<Expr*>(nullptr), nullptr);
    case ExprKind::DeclRef: {
      ValueDecl *D = cast<DeclRefExpr>(E)->getDecl();
      Added<Expr *> DRE = 
        new (Context) DeclRefExpr(ConcreteDeclRef(D),
                                  cast<DeclRefExpr>(E)->getLoc(),
                                  true, // implicit
                                  AccessSemantics::Ordinary,
                                  cast<DeclRefExpr>(E)->getType());
      return std::make_pair(DRE, D);
    }
    case ExprKind::MemberRef: {
      std::pair<Added<Expr *>, ValueDecl *> BaseVariable =
        digForVariable(cast<MemberRefExpr>(E)->getBase());
      if (!*BaseVariable.first || !BaseVariable.second)
        return std::make_pair(nullptr, nullptr);
      ValueDecl *M = cast<MemberRefExpr>(E)->getMember().getDecl();
      Added<Expr *> MRE(
        new (Context) MemberRefExpr(*BaseVariable.first,
                                    cast<MemberRefExpr>(E)->getDotLoc(),
                                    ConcreteDeclRef(M),
                                    cast<MemberRefExpr>(E)->getSourceRange(),
                                    true, // implicit
                                    AccessSemantics::Ordinary));
      return std::make_pair(MRE, M);
    }
    case ExprKind::Load:
      return digForVariable(cast<LoadExpr>(E)->getSubExpr());
    case ExprKind::ForceValue:
      return digForVariable(cast<ForceValueExpr>(E)->getSubExpr());
    case ExprKind::InOut:
      return digForVariable(cast<InOutExpr>(E)->getSubExpr());
    }
  }
  
  std::string digForName(Expr *E) {
    Added <Expr *> 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<InOutExpr>(E)) {
      return dyn_cast<DeclRefExpr>(
                   inout->getSubExpr()->getSemanticsProvidingExpr());

    // Drill through tuple shuffles, ignoring non-default-argument inouts.
    } else if (auto shuffle = dyn_cast<TupleShuffleExpr>(E)) {
      return digForInoutDeclRef(shuffle->getSubExpr());

    // Try to find a unique inout argument in a tuple.
    } else if (auto tuple = dyn_cast<TupleExpr>(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<bool, Expr*> walkToExprPre(Expr *E) {
      if (isa<ErrorExpr>(E) || !E->getType() || E->getType()->is<ErrorType>()) {
        error = true;
        return { false, E };
      }
      return { true, E };
    }
    virtual bool walkToDeclPre(Decl *D) {
      if (ValueDecl *VD = dyn_cast<ValueDecl>(D)) {
        if (!VD->getType() || VD->getType()->is<ErrorType>()) {
          error = true;
          return false;
        }
      }
      return true;
    }
    bool hadError() { 
      return error;
    }
  };

  template <class T> bool doTypeCheck(ASTContext &Ctx,
                                      DeclContext *DC,
                                      Added<T*> &parsedExpr) {
    DiagnosticEngine diags(Ctx.SourceMgr);
    ErrorGatherer errorGatherer(diags);
  
    TypeChecker TC(Ctx, diags);

    Expr *E = *parsedExpr;
    TC.typeCheckExpression(E, DC);
    parsedExpr = Added<T*>(dyn_cast<T>(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<ASTNode> OriginalElements = BS->getElements();
    typedef SmallVector<swift::ASTNode, 3> 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<Expr*>()) {
        E->walk(CF);
        if (AssignExpr *AE = dyn_cast<AssignExpr>(E)) {
          if (auto *MRE = dyn_cast<MemberRefExpr>(AE->getDest())) {
            // an assignment to a property of an object counts as a mutation of
            // that object
            Added<Expr *> Base_RE = nullptr;
            ValueDecl *BaseVD = nullptr;
            std::tie(Base_RE, BaseVD) = digForVariable(MRE->getBase());
            
            if (*Base_RE) {
              Added<Stmt *> Log = logDeclOrMemberRef(Base_RE);
              if (*Log) {
                Elements.insert(Elements.begin() + (EI + 1), *Log);
                ++EI;
              }
            }
          } else {
            std::pair<PatternBindingDecl *, VarDecl *> PV =
              buildPatternAndVariable(AE->getSrc());
            DeclRefExpr *DRE =
              new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
                                        SourceLoc(),
                                        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<Stmt *> Log(buildLoggerCall(
              new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
                                        SourceLoc(),
                                        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<ApplyExpr>(E)) {
          bool Handled = false;
          if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(AE->getFn())) {
            auto *FnD = dyn_cast<AbstractFunctionDecl>(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<Stmt *> 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<Stmt *> 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<DotSyntaxCallExpr>(AE->getFn())) {
              Expr *TargetExpr = DSCE->getArg();
              Added <Expr *> Target_RE;
              ValueDecl *TargetVD = nullptr;

              std::tie(Target_RE, TargetVD) = digForVariable(TargetExpr);

              if (TargetVD) {
                Added<Stmt *> Log = logDeclOrMemberRef(Target_RE);
                if (*Log) {
                  Elements.insert(Elements.begin() + (EI + 1), *Log);
                  ++EI;
                }
              }
            } else if (DeclRefExpr *DRE = digForInoutDeclRef(AE->getArg())) {
              Added<Stmt *> 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<PatternBindingDecl *, VarDecl *> PV =
              buildPatternAndVariable(E);
            Added<Stmt *> Log = buildLoggerCall(
              new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
                                        SourceLoc(),
                                        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<PatternBindingDecl *, VarDecl *> PV =
              buildPatternAndVariable(E);
            Added<Stmt *> Log = buildLoggerCall(
              new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
                                        SourceLoc(),
                                        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<Stmt*>()) {
        S->walk(CF);
        if (ReturnStmt *RS = dyn_cast<ReturnStmt>(S)) {
          if (RS->hasResult()) {
            std::pair<PatternBindingDecl *, VarDecl *> PV =
              buildPatternAndVariable(RS->getResult());
            DeclRefExpr *DRE =
              new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
                                        SourceLoc(),
                                        true, // implicit
                                        AccessSemantics::Ordinary,
                                        RS->getResult()->getType());
            ReturnStmt *NRS = new (Context) ReturnStmt(SourceLoc(),
                                                       DRE,
                                                       true); // implicit
            Added<Stmt *> Log = buildLoggerCall(
              new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
                                        SourceLoc(),
                                        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<BreakStmt>(S) || isa<ContinueStmt>(S)) {
            EI = escapeToTarget(BracePair::TargetKinds::Break, Elements, EI);
          } else if (isa<FallthroughStmt>(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<Decl*>()) {
        D->walk(CF);
        if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
          if (VarDecl *VD = PBD->getSingleVar()) {
            if (VD->getParentInitializer()) {
              Added<Stmt *> 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<Stmt *> logVarDecl(VarDecl *VD) {
    if (isa<ConstructorDecl>(TypeCheckDC) && VD->getNameStr().equals("self")) {
      // Don't log "self" in a constructor
      return nullptr;
    }

    return buildLoggerCall(
      new (Context) DeclRefExpr(ConcreteDeclRef(VD),
                                SourceLoc(),
                                true, // implicit
                                AccessSemantics::Ordinary,
                                Type()),
      VD->getSourceRange(), VD->getName().str().str().c_str());
  }

  Added<Stmt *> logDeclOrMemberRef(Added<Expr *> RE) {
    if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(*RE)) {
      VarDecl *VD = cast<VarDecl>(DRE->getDecl());
  
      if (isa<ConstructorDecl>(TypeCheckDC) && VD->getNameStr().equals("self")){
        // Don't log "self" in a constructor
        return nullptr;
      }
  
      return buildLoggerCall(
        new (Context) DeclRefExpr(ConcreteDeclRef(VD),
                                  SourceLoc(),
                                  true, // implicit
                                  AccessSemantics::Ordinary,
                                  Type()),
        DRE->getSourceRange(), VD->getName().str().str().c_str());
    } else if (MemberRefExpr *MRE = dyn_cast<MemberRefExpr>(*RE)) {
      Expr *B = MRE->getBase();
      ConcreteDeclRef M = MRE->getMember();

      if (isa<ConstructorDecl>(TypeCheckDC) &&
          !digForName(B).compare("self")) {
        // Don't log attributes of "self" in a constructor
        return nullptr;
      }
  
      return buildLoggerCall(
        new (Context) MemberRefExpr(B,
                                    SourceLoc(),
                                    M,
                                    SourceRange(),
                                    true, // implicit
                                    AccessSemantics::Ordinary),
        MRE->getSourceRange(), M.getDecl()->getName().str().str().c_str());
    } else {
      return nullptr;
    }
  }

  std::pair<PatternBindingDecl*, VarDecl*>
    maybeFixupPrintArgument(ApplyExpr *Print) { 
    Expr *ArgTuple = Print->getArg();
    if (ParenExpr *PE = dyn_cast<ParenExpr>(ArgTuple)) {
      std::pair<PatternBindingDecl*, VarDecl*> PV =
        buildPatternAndVariable(PE->getSubExpr());
      PE->setSubExpr(new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
                                               SourceLoc(),
                                               true, // implicit
                                               AccessSemantics::Ordinary,
                                               PE->getSubExpr()->getType()));
      return PV;
    } else if (TupleExpr *TE = dyn_cast<TupleExpr>(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<InOutType>()) {
              useJustFirst = true;
              break;
            }
          }
        }
        if (useJustFirst) {
          std::pair<PatternBindingDecl*, VarDecl*> PV =
            buildPatternAndVariable(TE->getElement(0));
          TE->setElement(0,
                         new (Context) DeclRefExpr(
                           ConcreteDeclRef(PV.second),
                           SourceLoc(),
                           true, // implicit
                           AccessSemantics::Ordinary,
                           TE->getElement(0)->getType()));
          return PV;
        } else {
          std::pair<PatternBindingDecl*, VarDecl*> PV =
            buildPatternAndVariable(TE);
          Print->setArg(new (Context) DeclRefExpr(
                          ConcreteDeclRef(PV.second),
                          SourceLoc(),
                          true, // implicit
                          AccessSemantics::Ordinary,
                          TE->getType()));
          return PV;
        }
      }
    } else {
      return std::make_pair(nullptr, nullptr);
    }
  }

  Added<Stmt *> 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,
        AE->getSourceRange().End);

    std::tie(ArgPattern, ArgVariable) =
      maybeFixupPrintArgument(AE);

    AE->setFn(LoggerRef);
    Added<ApplyExpr*> AddedApply(AE); // safe because we've fixed up the args

    if (!doTypeCheck(Context, TypeCheckDC, AddedApply)) {
      return nullptr;
    }

    return buildLoggerCallWithApply(AddedApply, AE->getSourceRange());
  }

  Added<Stmt *> logPostPrint(SourceRange SR) {
    return buildLoggerCallWithArgs("$builtin_postPrint",
                                   MutableArrayRef<Expr *>(),
                                   SR);
  }

  std::pair<PatternBindingDecl*, VarDecl*>
    buildPatternAndVariable(Expr *InitExpr) {
    char NameBuf[11] = { 0 };
    snprintf(NameBuf, 11, "tmp%u", TmpNameIndex);
    TmpNameIndex++;
        
    Expr *MaybeLoadInitExpr = nullptr;
    
    if (LValueType *LVT =
          dyn_cast<LValueType>(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<Stmt *> buildLoggerCall(Added<Expr *> 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<unsigned int> Distribution(0, 0x7fffffffu);
    const unsigned int 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<Expr *>(LoggerArgExprs),
                                   SR);
  }

  Added<Stmt *> buildScopeEntry(SourceRange SR) {
    return buildScopeCall(SR, false);
  }

  Added<Stmt *> buildScopeExit(SourceRange SR) {
    return buildScopeCall(SR, true);
  }

  Added<Stmt *> buildScopeCall(SourceRange SR, bool IsExit) {
    const char *LoggerName = IsExit ? "$builtin_log_scope_exit"
                                    : "$builtin_log_scope_entry";

    return buildLoggerCallWithArgs(LoggerName,
                                   MutableArrayRef<Expr *>(),
                                   SR);
  }

  Added<Stmt *> buildLoggerCallWithArgs(const char *LoggerName,
                                        MutableArrayRef<Expr *> 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,
        SR.End);

    LoggerRef->setImplicit(true);

    ApplyExpr *LoggerCall = new (Context) CallExpr(LoggerRef, LoggerArgs, true,
                                                   Type());
    Added<ApplyExpr*> AddedLogger(LoggerCall);

    if (!doTypeCheck(Context, TypeCheckDC, AddedLogger)) {
      return nullptr;
    }

    return buildLoggerCallWithApply(AddedLogger, SR);
  }

  // Assumes Apply has already been type-checked.
  Added<Stmt *> buildLoggerCallWithApply(Added<ApplyExpr*> Apply,
                                         SourceRange SR) {
    std::pair<unsigned, unsigned> StartLC =
      Context.SourceMgr.getLineAndColumn(SR.Start);

    std::pair<unsigned, unsigned> 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<PatternBindingDecl *, VarDecl *> PV =
      buildPatternAndVariable(*Apply);

    DeclRefExpr *DRE = new (Context) DeclRefExpr(ConcreteDeclRef(PV.second),
                                                 SourceLoc(),
                                                 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,
        SourceLoc());

    SendDataRef->setImplicit(true);

    Expr * SendDataCall = new (Context) CallExpr(SendDataRef,
                                                 SendDataArgs, true,
                                                 Type());
    Added<Expr *> 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;
  public:
    ExpressionFinder(bool HP) : HighPerformance(HP) { }

    virtual bool walkToDeclPre(Decl *D) {
      if (AbstractFunctionDecl *FD = dyn_cast<AbstractFunctionDecl>(D)) {
        if (!FD->isImplicit()) {
          if (BraceStmt *Body = FD->getBody()) {
            ASTContext &ctx = FD->getASTContext();
            Instrumenter I(ctx, FD, RNG, HighPerformance);
            BraceStmt *NewBody = I.transformBraceStmt(Body);
            if (NewBody != Body) {
              FD->setBody(NewBody);
              TypeChecker(ctx).checkFunctionErrorHandling(FD);
            }
            return false;
          }
        }
      } else if (TopLevelCodeDecl *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
        if (!TLCD->isImplicit()) {
          if (BraceStmt *Body = TLCD->getBody()) {
            ASTContext &ctx = static_cast<Decl*>(TLCD)->getASTContext();
            Instrumenter I(ctx, TLCD, RNG, HighPerformance);
            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);
  }
}
