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">,
|
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">;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
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