Files
swift-mirror/lib/Sema/InstrumenterSupport.h
Anders Bertelrud 8eb931d617 [Sema] Playground transform should also log function and closure parameter values (#63929)
Add logging of function and closure parameter values when the playground transform is enabled and its "high-performance" mode is off.

MOTIVATION

The goal of the optional "playground transform" step in Sema is to instrument the code by inserting calls to `__builtin_log()` and similar log functions, in order to record the execution of the compiled code. Some IDEs (such as Xcode) enable this transform by passing -playground and provide implementations of the logger functions that record information that can then be shown in the IDE.

The playground transform currently logs variable assignments and return statements, but it doesn't log the input parameters received by functions and closures. Knowing these values can be very useful in order to understand the behaviour of functions and closures.

CHANGES

- add a `ParameterList` parameter to `InstrumenterSupport::transformBraceStmt()`
  - this is an optional parameter list that, if given, specifies the parameters that should be logged at the start of the brace statement
  - this has to be passed into the function because it comes from the owner of the BraceStmt
- adjust `PlaygroundTransform.cpp` to make use of this list
  - the transform will insert calls to `__builtin_log()` for each of the parameters, in order
- adjust `PCMacro.cpp` to accept the parameter, though this instrumenter doesn't currently make use of the new information
- add two new unit tests (one for functions and one for closures)
- adjust four existing unit tests to account for the new log calls

REMARKS

- this is currently guarded by the existing "high performance" option (parameter logging is omitted in that case)

rdar://104974072
2023-03-03 12:37:31 -08:00

109 lines
3.2 KiB
C++

//===--- InstrumenterSupport.h - Instrumenter Support -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements the supporting functions for writing instrumenters of
// the Swift AST.
//
//===----------------------------------------------------------------------===//
#include "TypeChecker.h"
#include "swift/AST/ASTWalker.h"
namespace swift {
namespace instrumenter_support {
template <class E> class Added {
private:
E Contents;
public:
Added() {}
Added(E NewContents) : Contents(NewContents) {}
Added(const Added<E> &rhs) : Contents(rhs.Contents) {}
const Added<E> &operator=(const Added<E> &rhs) {
Contents = rhs.Contents;
return *this;
}
E &operator*() { return Contents; }
E &operator->() { return Contents; }
};
class InstrumenterBase {
protected:
ASTContext &Context;
DeclContext *TypeCheckDC;
Optional<DeclNameRef> ModuleIdentifier;
Optional<DeclNameRef> FileIdentifier;
InstrumenterBase(ASTContext &C, DeclContext *DC);
virtual ~InstrumenterBase() = default;
virtual void anchor();
virtual BraceStmt *transformBraceStmt(BraceStmt *BS,
const ParameterList *PL = nullptr,
bool TopLevel = false) = 0;
/// Create an expression which retrieves a valid ModuleIdentifier or
/// FileIdentifier, if available.
Expr *buildIDArgumentExpr(Optional<DeclNameRef> name, SourceRange SR);
class ClosureFinder : public ASTWalker {
private:
InstrumenterBase &I;
public:
ClosureFinder(InstrumenterBase &Inst) : I(Inst) {}
/// Walk only the expansion of the macro.
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Expansion;
}
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
if (isa<BraceStmt>(S)) {
return Action::SkipChildren(S); // don't walk into brace statements; we
// need to respect nesting!
} else {
return Action::Continue(S);
}
}
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
if (auto *CE = dyn_cast<ClosureExpr>(E)) {
BraceStmt *B = CE->getBody();
if (B) {
const ParameterList *PL = CE->getParameters();
BraceStmt *NB = I.transformBraceStmt(B, PL);
CE->setBody(NB, false);
// just with the entry and exit logging this is going to
// be more than a single expression!
}
}
return Action::Continue(E);
}
};
ClosureFinder CF;
template <class T>
bool doTypeCheck(ASTContext &Ctx, DeclContext *DC, Added<T *> &parsedExpr) {
Expr *E = *parsedExpr;
bool result = doTypeCheckImpl(Ctx, DC, E);
parsedExpr = Added<T *>(dyn_cast<T>(E));
return result;
}
private:
bool doTypeCheckImpl(ASTContext &Ctx, DeclContext *DC, Expr * &parsedExpr);
};
}
}