-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Frontend: when emitting loaded module trace file, also emit a JSON file tracking Objc method calls issued from the source code #77217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,12 +17,15 @@ | |
#include "swift/AST/Module.h" | ||
#include "swift/AST/ModuleLoader.h" | ||
#include "swift/AST/PluginRegistry.h" | ||
#include "swift/AST/SourceFile.h" | ||
#include "swift/Basic/Assertions.h" | ||
#include "swift/Basic/FileTypes.h" | ||
#include "swift/Basic/JSONSerialization.h" | ||
#include "swift/Frontend/FrontendOptions.h" | ||
#include "swift/IDE/SourceEntityWalker.h" | ||
|
||
#include "clang/Basic/Module.h" | ||
#include "clang/AST/DeclObjC.h" | ||
|
||
#include "llvm/ADT/SmallPtrSet.h" | ||
#include "llvm/ADT/SmallString.h" | ||
|
@@ -810,3 +813,69 @@ bool swift::emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, | |
} | ||
return false; | ||
} | ||
|
||
|
||
bool swift::emitObjCMessageSendTraceIfNeeded(ModuleDecl *mainModule, | ||
const FrontendOptions &opts) { | ||
ASTContext &ctxt = mainModule->getASTContext(); | ||
assert(!ctxt.hadError() && | ||
"We should've already exited earlier if there was an error."); | ||
class ObjcMethodRefereceCollector: public SourceEntityWalker { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd pull out of this function personally |
||
std::set<const clang::ObjCMethodDecl*> results; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
bool visitDeclReference(ValueDecl *D, CharSourceRange Range, | ||
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, | ||
Type T, ReferenceMetaData Data) override { | ||
if (!Range.isValid()) | ||
return true; | ||
if (auto *clangD = dyn_cast_or_null<clang::ObjCMethodDecl>(D->getClangDecl())) { | ||
results.insert(clangD); | ||
} | ||
return true; | ||
} | ||
public: | ||
void dump(llvm::raw_ostream &OS) { | ||
OS << "[\n"; | ||
for (const clang::ObjCMethodDecl* clangD: results) { | ||
auto &SM = clangD->getASTContext().getSourceManager(); | ||
clang::SourceLocation Loc = clangD->getLocation(); | ||
if (!Loc.isValid()) { | ||
continue; | ||
} | ||
OS << "\t{\n"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For small JSON output like this, it would be nice to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! Will update. |
||
OS << "\t\t\"method_name\": \"" << clangD->getNameAsString() << "\",\n"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is just the method name really all that's needed? Is the expectation that the location will unique them? |
||
OS << "\t\t\"location\": \"" << Loc.printToString(SM) << "\"\n"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure exactly what you're after here, but note this will be both the spelling + expansion + line + column. If you only need offset, you could make this a fair bit simpler. |
||
OS << "\t}"; | ||
OS << ",\n"; | ||
} | ||
OS << "{} ]\n"; | ||
} | ||
}; | ||
opts.InputsAndOutputs.forEachInput([&](const InputFile &input) { | ||
auto loadedModuleTracePath = input.getLoadedModuleTracePath(); | ||
if (loadedModuleTracePath.empty()) | ||
return false; | ||
llvm::SmallString<128> tracePath {loadedModuleTracePath}; | ||
llvm::sys::path::remove_filename(tracePath); | ||
llvm::sys::path::append(tracePath, "SWIFT_OBJC_MESSAGE_TRACE"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Caps is a little weird but module trace does the same so 🤷. Maybe |
||
if (!llvm::sys::fs::exists(tracePath)) { | ||
if (llvm::sys::fs::create_directory(tracePath)) | ||
return false; | ||
} | ||
llvm::sys::path::append(tracePath, "%%%%-%%%%-%%%%.json"); | ||
int tmpFD; | ||
if (llvm::sys::fs::createUniqueFile(tracePath.str(), tmpFD, tracePath)) { | ||
return false; | ||
} | ||
// Write the contents of the buffer. | ||
llvm::raw_fd_ostream out(tmpFD, /*shouldClose=*/true); | ||
ObjcMethodRefereceCollector collector; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
for (auto *FU : mainModule->getFiles()) { | ||
if (auto *SF = dyn_cast<SourceFile>(FU)) { | ||
collector.walk(*SF); | ||
} | ||
} | ||
collector.dump(out); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When I see |
||
return true; | ||
}); | ||
return false; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/usr/bin/env python3 | ||
# | ||
# Usage: print_first_file_under_director.py /dir/ | ||
|
||
import os | ||
import sys | ||
|
||
directory = sys.argv[1] | ||
|
||
for p in os.listdir(directory): | ||
with open(directory + '/' + p, 'r') as f: | ||
print(f.read()) | ||
exit(0) | ||
|
||
exit(1) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// RUN: %empty-directory(%t) | ||
|
||
// RUN: %target-swift-frontend -I %t/lib/swift -typecheck %s -module-name main -swift-version 5 -F %S/Inputs/mock-sdk -emit-loaded-module-trace-path %t/.MODULE_TRACE | ||
// RUN: %{python} %S/Inputs/print_first_file_under_directory.py %t/SWIFT_OBJC_MESSAGE_TRACE | %FileCheck %s | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just |
||
|
||
// REQUIRES: objc_interop | ||
|
||
// EXPECTED OUTPUT STARTS BELOW THIS LINE. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't really the output 🤔. Also does this test really belong under |
||
|
||
|
||
import Foo | ||
|
||
public func testProperties(_ x: FooClassBase) { | ||
_ = x.fooBaseInstanceFunc0() | ||
x.fooBaseInstanceFunc1(1.2) | ||
} | ||
|
||
// CHECK: fooBaseInstanceFunc0 | ||
// CHECK: fooBaseInstanceFunc1 | ||
// CHECK: SOURCE_DIR/test/IDE/Inputs/mock-sdk/Foo.framework/Headers/Foo.h |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang-format?