mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -101,6 +101,9 @@ def enable_objc_optional : Flag<["-"], "enable-objc-optional">,
|
||||
def print_stats : Flag<["-"], "print-stats">,
|
||||
HelpText<"Print various statistics">;
|
||||
|
||||
def playground : Flag<["-"], "playground">,
|
||||
HelpText<"Apply the playground transformation">;
|
||||
|
||||
def sil_inline_threshold : Separate<["-"], "sil-inline-threshold">,
|
||||
MetaVarName<"<50>">,
|
||||
HelpText<"Controls the aggressiveness of performance inlining">;
|
||||
|
||||
@@ -145,6 +145,9 @@ public:
|
||||
/// termination.
|
||||
bool PrintStats = false;
|
||||
|
||||
/// Indicates whether the playground transformation should be applied.
|
||||
bool Playground = false;
|
||||
|
||||
/// Indicates whether standard help should be shown.
|
||||
bool PrintHelp = false;
|
||||
|
||||
|
||||
@@ -116,6 +116,10 @@ namespace swift {
|
||||
/// source file.
|
||||
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
|
||||
/// types and diagnose problems therein.
|
||||
///
|
||||
|
||||
@@ -96,6 +96,10 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
|
||||
Opts.PrintStats = true;
|
||||
}
|
||||
|
||||
if (Args.hasArg(OPT_playground)) {
|
||||
Opts.Playground = true;
|
||||
}
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_help, OPT_help_hidden)) {
|
||||
if (A->getOption().matches(OPT_help)) {
|
||||
Opts.PrintHelp = true;
|
||||
|
||||
@@ -308,6 +308,9 @@ void CompilerInstance::performParse() {
|
||||
CurTUElem);
|
||||
CurTUElem = MainFile.Decls.size();
|
||||
} while (!Done);
|
||||
|
||||
if (Invocation.getFrontendOptions().Playground)
|
||||
performPlaygroundTransform(MainFile);
|
||||
}
|
||||
|
||||
if (!Invocation.getParseOnly()) {
|
||||
|
||||
@@ -14,6 +14,7 @@ add_swift_library(swiftSema
|
||||
DerivedConformanceEquatableHashable.cpp
|
||||
DerivedConformanceRawRepresentable.cpp
|
||||
MiscDiagnostics.cpp
|
||||
PlaygroundTransform.cpp
|
||||
TypeChecker.cpp
|
||||
TypeCheckConstraints.cpp
|
||||
TypeCheckExpr.cpp
|
||||
|
||||
228
lib/Sema/PlaygroundTransform.cpp
Normal file
228
lib/Sema/PlaygroundTransform.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user