Skip to content

Commit 7bf0107

Browse files
committed
Frontend: when emitting loaded module trace file, also emit a JSON file tracking Objc method calls issued from the source code.
1 parent cd83ee1 commit 7bf0107

File tree

5 files changed

+108
-0
lines changed

5 files changed

+108
-0
lines changed

lib/FrontendTool/Dependencies.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
3232
const FrontendOptions &opts,
3333
const InputFile &input);
3434

35+
bool emitObjCMessageSendTraceIfNeeded(ModuleDecl *mainModule,
36+
const FrontendOptions &opts);
37+
3538
} // end namespace swift
3639

3740
#endif

lib/FrontendTool/FrontendTool.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,7 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) {
10011001
Instance.getMainModule(), Instance.getDependencyTracker(), opts);
10021002

10031003
dumpAPIIfNeeded(Instance);
1004+
swift::emitObjCMessageSendTraceIfNeeded(Instance.getMainModule(), opts);
10041005
}
10051006

10061007
// Contains the hadError checks internally, we still want to output the

lib/FrontendTool/LoadedModuleTrace.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
#include "swift/AST/Module.h"
1818
#include "swift/AST/ModuleLoader.h"
1919
#include "swift/AST/PluginRegistry.h"
20+
#include "swift/AST/SourceFile.h"
2021
#include "swift/Basic/Assertions.h"
2122
#include "swift/Basic/FileTypes.h"
2223
#include "swift/Basic/JSONSerialization.h"
2324
#include "swift/Frontend/FrontendOptions.h"
25+
#include "swift/IDE/SourceEntityWalker.h"
2426

2527
#include "clang/Basic/Module.h"
28+
#include "clang/AST/DeclObjC.h"
2629

2730
#include "llvm/ADT/SmallPtrSet.h"
2831
#include "llvm/ADT/SmallString.h"
@@ -810,3 +813,69 @@ bool swift::emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
810813
}
811814
return false;
812815
}
816+
817+
818+
bool swift::emitObjCMessageSendTraceIfNeeded(ModuleDecl *mainModule,
819+
const FrontendOptions &opts) {
820+
ASTContext &ctxt = mainModule->getASTContext();
821+
assert(!ctxt.hadError() &&
822+
"We should've already exited earlier if there was an error.");
823+
class ObjcMethodRefereceCollector: public SourceEntityWalker {
824+
std::set<const clang::ObjCMethodDecl*> results;
825+
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
826+
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef,
827+
Type T, ReferenceMetaData Data) override {
828+
if (!Range.isValid())
829+
return true;
830+
if (auto *clangD = dyn_cast_or_null<clang::ObjCMethodDecl>(D->getClangDecl())) {
831+
results.insert(clangD);
832+
}
833+
return true;
834+
}
835+
public:
836+
void dump(llvm::raw_ostream &OS) {
837+
OS << "[\n";
838+
for (const clang::ObjCMethodDecl* clangD: results) {
839+
auto &SM = clangD->getASTContext().getSourceManager();
840+
clang::SourceLocation Loc = clangD->getLocation();
841+
if (!Loc.isValid()) {
842+
continue;
843+
}
844+
OS << "\t{\n";
845+
OS << "\t\t\"method_name\": \"" << clangD->getNameAsString() << "\",\n";
846+
OS << "\t\t\"location\": \"" << Loc.printToString(SM) << "\"\n";
847+
OS << "\t}";
848+
OS << ",\n";
849+
}
850+
OS << "{} ]\n";
851+
}
852+
};
853+
opts.InputsAndOutputs.forEachInput([&](const InputFile &input) {
854+
auto loadedModuleTracePath = input.getLoadedModuleTracePath();
855+
if (loadedModuleTracePath.empty())
856+
return false;
857+
llvm::SmallString<128> tracePath {loadedModuleTracePath};
858+
llvm::sys::path::remove_filename(tracePath);
859+
llvm::sys::path::append(tracePath, "SWIFT_OBJC_MESSAGE_TRACE");
860+
if (!llvm::sys::fs::exists(tracePath)) {
861+
if (llvm::sys::fs::create_directory(tracePath))
862+
return false;
863+
}
864+
llvm::sys::path::append(tracePath, "%%%%-%%%%-%%%%.json");
865+
int tmpFD;
866+
if (llvm::sys::fs::createUniqueFile(tracePath.str(), tmpFD, tracePath)) {
867+
return false;
868+
}
869+
// Write the contents of the buffer.
870+
llvm::raw_fd_ostream out(tmpFD, /*shouldClose=*/true);
871+
ObjcMethodRefereceCollector collector;
872+
for (auto *FU : mainModule->getFiles()) {
873+
if (auto *SF = dyn_cast<SourceFile>(FU)) {
874+
collector.walk(*SF);
875+
}
876+
}
877+
collector.dump(out);
878+
return true;
879+
});
880+
return false;
881+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Usage: print_first_file_under_director.py /dir/
4+
5+
import os
6+
import sys
7+
8+
directory = sys.argv[1]
9+
10+
for p in os.listdir(directory):
11+
with open(directory + '/' + p, 'r') as f:
12+
print(f.read())
13+
exit(0)
14+
15+
exit(1)

test/IDE/objc_send_collector.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// 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
4+
// RUN: %{python} %S/Inputs/print_first_file_under_directory.py %t/SWIFT_OBJC_MESSAGE_TRACE | %FileCheck %s
5+
6+
// REQUIRES: objc_interop
7+
8+
// EXPECTED OUTPUT STARTS BELOW THIS LINE.
9+
10+
11+
import Foo
12+
13+
public func testProperties(_ x: FooClassBase) {
14+
_ = x.fooBaseInstanceFunc0()
15+
x.fooBaseInstanceFunc1(1.2)
16+
}
17+
18+
// CHECK: fooBaseInstanceFunc0
19+
// CHECK: fooBaseInstanceFunc1
20+
// CHECK: SOURCE_DIR/test/IDE/Inputs/mock-sdk/Foo.framework/Headers/Foo.h

0 commit comments

Comments
 (0)