blob: b6ab3fe0f8ed00822d5e0a6ab15ad3a9da2ea153 [file] [log] [blame]
//===--- DebuggerContextChange.h --------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_PARSE_DEBUGGERCONTEXTCHANGE_H
#define SWIFT_PARSE_DEBUGGERCONTEXTCHANGE_H
#include "swift/AST/DebuggerClient.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/SourceFile.h"
#include "swift/Parse/Parser.h"
namespace swift {
/// A RAII object for deciding whether this DeclKind needs special
/// treatment when parsing in the "debugger context", and implementing
/// that treatment. The problem arises because, when lldb
/// uses swift to parse expressions, it needs to emulate the current
/// frame's scope. We do that, for instance, by making a class extension
/// and running the code in a function in that extension.
///
/// This causes two kinds of issues:
/// 1) Some DeclKinds require to be parsed in TopLevel contexts only.
/// 2) Sometimes the debugger wants a Decl to live beyond the current
/// function invocation, in which case it should be parsed at the
/// file scope level so it will be set up correctly for this purpose.
///
/// Creating an instance of this object will cause it to figure out
/// whether we are in the debugger function, whether it needs to swap
/// the Decl that is currently being parsed.
/// If you have created the object, instead of returning the result
/// with makeParserResult, use the object's fixupParserResult. If
/// no swap has occurred, these methods will work the same.
/// If the decl has been moved, then Parser::markWasHandled will be
/// called on the Decl, and you should call declWasHandledAlready
/// before you consume the Decl to see if you actually need to
/// consume it.
/// If you are making one of these objects to address issue 1, call
/// the constructor that only takes a DeclKind, and it will be moved
/// unconditionally. Otherwise pass in the Name and DeclKind and the
/// DebuggerClient will be asked whether to move it or not.
class DebuggerContextChange {
protected:
Parser &P;
Identifier Name;
SourceFile *SF;
Optional<Parser::ContextChange> CC;
public:
DebuggerContextChange(Parser &P) : P(P), SF(nullptr) {
if (!inDebuggerContext())
return;
else
switchContext();
}
DebuggerContextChange(Parser &P, Identifier &Name, DeclKind Kind)
: P(P), Name(Name), SF(nullptr) {
if (!inDebuggerContext())
return;
bool globalize = false;
DebuggerClient *debug_client = getDebuggerClient();
if (!debug_client)
return;
globalize = debug_client->shouldGlobalize(Name, Kind);
if (globalize)
switchContext();
}
bool movedToTopLevel() { return CC.hasValue(); }
template <typename T>
ParserResult<T> fixupParserResult(ParserResult<T> &Result) {
ParserStatus Status = Result;
return fixupParserResult(Status, Result.getPtrOrNull());
}
template <typename T> ParserResult<T> fixupParserResult(T *D) {
if (CC.hasValue()) {
swapDecl(D);
}
return ParserResult<T>(D);
}
template <typename T>
ParserResult<T> fixupParserResult(ParserStatus Status, T *D) {
if (CC.hasValue() && !Status.isError()) {
// If there is an error, don't do our splicing trick,
// just return the Decl and the status for reporting.
swapDecl(D);
}
return makeParserResult(Status, D);
}
// The destructor doesn't need to do anything, the CC's destructor will
// pop the context if we set it.
~DebuggerContextChange() {}
protected:
DebuggerClient *getDebuggerClient() {
ModuleDecl *PM = P.CurDeclContext->getParentModule();
if (!PM)
return nullptr;
else
return PM->getDebugClient();
}
bool inDebuggerContext() {
if (!P.Context.LangOpts.DebuggerSupport)
return false;
if (!P.CurDeclContext)
return false;
auto *func_decl = dyn_cast<FuncDecl>(P.CurDeclContext);
if (!func_decl)
return false;
if (!func_decl->getAttrs().hasAttribute<LLDBDebuggerFunctionAttr>())
return false;
return true;
}
void switchContext() {
SF = P.CurDeclContext->getParentSourceFile();
CC.emplace(P, SF);
}
void swapDecl(Decl *D) {
assert(SF);
DebuggerClient *debug_client = getDebuggerClient();
assert(debug_client);
debug_client->didGlobalize(D);
SF->Decls.push_back(D);
P.markWasHandled(D);
}
};
} // namespace swift
#endif