Added a new AST transformation pass called the

"Playground Transform."  This is an
instrumentation pass that adds calls to a
function called playground_log at locations of
interest.  Roughly speaking, these locations are

- Initialization of variables
- Modification of variables
- Expressions returning values
- Application of mutating methods on objects

The playground transform currently only finds
modifications of variables, but the intent is to
make all of these cases work.

It is enabled by a frontend option, and can
also be invoked by calling

swift::performPlaygroundTransform(SF)

which is the way LLDB, its main client, will
use it.

The frontend option is intended for testing,
and indeed I will add tests for this
transformation in the coming week as I bring
more functionality online.


Swift SVN r14801
This commit is contained in:
Sean Callanan
2014-03-07 22:59:19 +00:00
parent dce0673db1
commit 3b95376949
7 changed files with 246 additions and 0 deletions

View File

@@ -101,6 +101,9 @@ def enable_objc_optional : Flag<["-"], "enable-objc-optional">,
def print_stats : Flag<["-"], "print-stats">, def print_stats : Flag<["-"], "print-stats">,
HelpText<"Print various statistics">; HelpText<"Print various statistics">;
def playground : Flag<["-"], "playground">,
HelpText<"Apply the playground transformation">;
def sil_inline_threshold : Separate<["-"], "sil-inline-threshold">, def sil_inline_threshold : Separate<["-"], "sil-inline-threshold">,
MetaVarName<"<50>">, MetaVarName<"<50>">,
HelpText<"Controls the aggressiveness of performance inlining">; HelpText<"Controls the aggressiveness of performance inlining">;

View File

@@ -145,6 +145,9 @@ public:
/// termination. /// termination.
bool PrintStats = false; bool PrintStats = false;
/// Indicates whether the playground transformation should be applied.
bool Playground = false;
/// Indicates whether standard help should be shown. /// Indicates whether standard help should be shown.
bool PrintHelp = false; bool PrintHelp = false;

View File

@@ -115,6 +115,10 @@ namespace swift {
/// \param StartElem Where to start for incremental name binding in the main /// \param StartElem Where to start for incremental name binding in the main
/// source file. /// source file.
void performNameBinding(SourceFile &SF, unsigned StartElem = 0); void performNameBinding(SourceFile &SF, unsigned StartElem = 0);
/// Once parsing and name-binding are complete, this optionally transforms the
/// ASTs to add calls to external logging functions.
void performPlaygroundTransform(SourceFile &SF);
/// Once parsing and name-binding are complete, this walks the AST to resolve /// Once parsing and name-binding are complete, this walks the AST to resolve
/// types and diagnose problems therein. /// types and diagnose problems therein.

View File

@@ -96,6 +96,10 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
Opts.PrintStats = true; Opts.PrintStats = true;
} }
if (Args.hasArg(OPT_playground)) {
Opts.Playground = true;
}
if (const Arg *A = Args.getLastArg(OPT_help, OPT_help_hidden)) { if (const Arg *A = Args.getLastArg(OPT_help, OPT_help_hidden)) {
if (A->getOption().matches(OPT_help)) { if (A->getOption().matches(OPT_help)) {
Opts.PrintHelp = true; Opts.PrintHelp = true;

View File

@@ -308,6 +308,9 @@ void CompilerInstance::performParse() {
CurTUElem); CurTUElem);
CurTUElem = MainFile.Decls.size(); CurTUElem = MainFile.Decls.size();
} while (!Done); } while (!Done);
if (Invocation.getFrontendOptions().Playground)
performPlaygroundTransform(MainFile);
} }
if (!Invocation.getParseOnly()) { if (!Invocation.getParseOnly()) {

View File

@@ -14,6 +14,7 @@ add_swift_library(swiftSema
DerivedConformanceEquatableHashable.cpp DerivedConformanceEquatableHashable.cpp
DerivedConformanceRawRepresentable.cpp DerivedConformanceRawRepresentable.cpp
MiscDiagnostics.cpp MiscDiagnostics.cpp
PlaygroundTransform.cpp
TypeChecker.cpp TypeChecker.cpp
TypeCheckConstraints.cpp TypeCheckConstraints.cpp
TypeCheckExpr.cpp TypeCheckExpr.cpp

View File

@@ -0,0 +1,228 @@
//===--- 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/Stmt.h"
#include "swift/AST/Types.h"
#include "swift/Sema/CodeCompletionTypeChecking.h"
#include "swift/Subsystems.h"
using namespace swift;
//===----------------------------------------------------------------------===//
// performPlaygroundTransform
//===----------------------------------------------------------------------===//
class Instrumenter {
private:
ASTContext &Context;
DeclContext *TypeCheckDC;
public:
Instrumenter (ASTContext &C, DeclContext *DC) :
Context(C), TypeCheckDC(DC) { }
Stmt *transformStmt(Stmt *S) {
switch (S->getKind()) {
default:
return S;
case StmtKind::Brace:
return transformBraceStmt(llvm::cast<BraceStmt>(S));
case StmtKind::If:
return transformIfStmt(llvm::cast<IfStmt>(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;
}
BraceStmt *transformBraceStmt(BraceStmt *BS) {
llvm::ArrayRef<ASTNode> OriginalElements = BS->getElements();
typedef llvm::SmallVector<swift::ASTNode, 3> ElementVector;
ElementVector Elements(OriginalElements.begin(),
OriginalElements.end());
bool Modified = false;
for (size_t EI = 0;
EI != Elements.size();
++EI) {
swift::ASTNode &Element = Elements[EI];
if (Expr *E = Element.dyn_cast<Expr*>()) {
if (AssignExpr *AE = llvm::dyn_cast<AssignExpr>(E)) {
Expr *Log = logAssignExpr(AE);
if (Log) {
Modified = true;
Elements.insert(Elements.begin() + (EI + 1), Log);
++EI;
}
}
} else if (Stmt *S = Element.dyn_cast<Stmt*>()) {
Stmt *NS = transformStmt(S);
if (NS != S) {
Modified = true;
Elements[EI] = NS;
}
} else if (Decl *D = Element.dyn_cast<Decl*>()) {
(void)D;
}
}
if (Modified) {
return swift::BraceStmt::create(Context, BS->getLBraceLoc(),
Context.AllocateCopy(Elements),
BS->getRBraceLoc());
} else {
return BS;
}
}
// log*() functions return a newly-created log expression to be inserted
// after the expression they're looking at.
Expr *logAssignExpr(AssignExpr *AE) {
Expr *Dest = AE->getDest();
if (DeclRefExpr *DRE = llvm::dyn_cast<DeclRefExpr>(Dest)) {
return buildLoggerCall(DRE->getDecl());
}
return nullptr;
}
Expr *buildLoggerCall(ValueDecl *VD) {
Expr *Header = new (Context) StringLiteralExpr("", SourceRange());
Header->setImplicit(true);
Expr *MaxChildren = new (Context) IntegerLiteralExpr("0", SourceLoc(),
true);
Expr *MaxDepth = new (Context) IntegerLiteralExpr("0", SourceLoc(),
true);
Expr *ArgExprs[] = {
new (Context) DeclRefExpr(ConcreteDeclRef(VD),
SourceLoc(),
true, // implicit
false, // uses direct property access
Type()),
Header,
MaxChildren,
MaxDepth
};
TupleExpr *Args = new (Context) TupleExpr(SourceLoc(),
Context.AllocateCopy(ArgExprs),
(Identifier*)nullptr,
SourceLoc(),
false, // hasTrailingClosure
true, // implicit
Type());
UnresolvedDeclRefExpr *LoggerRef =
new (Context) UnresolvedDeclRefExpr(
Context.getIdentifier("playground_log"),
DeclRefKind::Ordinary,
SourceLoc());
LoggerRef->setImplicit(true);
Expr *Call = new (Context) CallExpr(LoggerRef, Args, true, Type());
if (!typeCheckCompletionContextExpr(Context, TypeCheckDC, Call)) {
return nullptr;
}
return Call;
}
};
void swift::performPlaygroundTransform(SourceFile &SF) {
class ExpressionFinder : public ASTWalker {
FuncDecl *WrappedDecl = nullptr;
FuncDecl *ExpressionDecl = nullptr;
StringRef WrappedName = "__lldb_wrapped_expr";
StringRef ExpressionName = "__lldb_expr";
public:
virtual bool walkToDeclPre(Decl *D) {
if (FuncDecl *FD = llvm::dyn_cast<FuncDecl>(D)) {
StringRef FuncName = FD->getName().str();
if (!FuncName.endswith(WrappedName)) {
WrappedDecl = FD;
return false;
} else if (!FuncName.endswith(ExpressionName)) {
ExpressionDecl = FD;
return false;
}
}
return true;
}
FuncDecl *getFunctionToTransform() {
return WrappedDecl ? WrappedDecl : ExpressionDecl;
}
};
ExpressionFinder EF;
for (Decl* D : SF.Decls) {
D->walk(EF);
}
if (FuncDecl *FuncToTransform = EF.getFunctionToTransform()) {
if (BraceStmt *Body = FuncToTransform->getBody()) {
ASTContext &Context(SF.getASTContext());
Module *LoggerModule = Context.getModule(
std::make_pair(Context.getIdentifier("PlaygroundLoggerLibrary"),
SourceLoc()));
if (LoggerModule)
{
SmallVector<ValueDecl*, 1> LoggerDecls;
LoggerModule->lookupValue(Module::AccessPathTy(),
Context.getIdentifier("playground_log"),
NLKind::UnqualifiedLookup,
LoggerDecls);
if (LoggerDecls.size() == 1) {
Instrumenter I(Context, FuncToTransform);
BraceStmt *NewBody = I.transformBraceStmt(Body);
if (NewBody != Body) {
FuncToTransform->setBody(NewBody);
}
}
}
}
}
}