编辑
这个答案的基本思想是完全绕过(相当复杂的)Scala 编译器,.class
最后从生成的文件中提取图形。具有足够详细输出的反编译器似乎可以将问题减少到基本的文本操作。然而,经过更详细的检查,事实证明并非如此。一种是回到原点,但使用混淆的 Java 代码而不是原始的 Scala 代码。.class
所以这个提议并没有真正起作用,尽管使用最终文件而不是 Scala 编译器内部使用的中间结构背后有一些基本原理。
/编辑
我不知道是否有开箱即用的工具(我假设您已经检查过)。我只有一个非常粗略的想法,演示编译器是什么。但是,如果您只想提取一个图,其中方法作为节点,方法的潜在调用作为边,我有一个快速而简单的解决方案的建议。这仅在您想将其用于某种可视化时才有效,如果您想执行一些巧妙的重构操作,它根本无济于事。
如果您想尝试自己构建这样的图形生成器,结果可能比您想象的要简单得多。但是为此,您需要一路向下,甚至经过编译器。只需获取已编译 .class
的文件,然后在其上使用 CFR java 反编译器之类的东西。
在单个编译.class
文件上使用时,CFR 将生成当前类所依赖的类列表(这里我以我的小宠物项目为例):
import akka.actor.Actor;
import akka.actor.ActorContext;
import akka.actor.ActorLogging;
import akka.actor.ActorPath;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.ScalaActorRef;
import akka.actor.SupervisorStrategy;
import akka.actor.package;
import akka.event.LoggingAdapter;
import akka.pattern.PipeToSupport;
import akka.pattern.package;
import scala.Function1;
import scala.None;
import scala.Option;
import scala.PartialFunction;
...
(very long list with all the classes this one depends on)
...
import scavenger.backend.worker.WorkerCache$class;
import scavenger.backend.worker.WorkerScheduler;
import scavenger.backend.worker.WorkerScheduler$class;
import scavenger.categories.formalccc.Elem;
然后它会吐出一些看起来很可怕的代码,可能看起来像这样(小摘录):
public PartialFunction<Object, BoxedUnit> handleLocalResponses() {
return SimpleComputationExecutor.class.handleLocalResponses((SimpleComputationExecutor)this);
}
public Context provideComputationContext() {
return ContextProvider.class.provideComputationContext((ContextProvider)this);
}
public ActorRef scavenger$backend$worker$MasterJoin$$_master() {
return this.scavenger$backend$worker$MasterJoin$$_master;
}
@TraitSetter
public void scavenger$backend$worker$MasterJoin$$_master_$eq(ActorRef x$1) {
this.scavenger$backend$worker$MasterJoin$$_master = x$1;
}
public ActorRef scavenger$backend$worker$MasterJoin$$_masterProxy() {
return this.scavenger$backend$worker$MasterJoin$$_masterProxy;
}
@TraitSetter
public void scavenger$backend$worker$MasterJoin$$_masterProxy_$eq(ActorRef x$1) {
this.scavenger$backend$worker$MasterJoin$$_masterProxy = x$1;
}
public ActorRef master() {
return MasterJoin$class.master((MasterJoin)this);
}
这里应该注意的是,所有方法都带有完整的签名,包括定义它们的类,例如:
Scheduler.class.schedule(...)
ContextProvider.class.provideComputationContext(...)
SimpleComputationExecutor.class.fulfillPromise(...)
SimpleComputationExecutor.class.computeHere(...)
SimpleComputationExecutor.class.handleLocalResponses(...)
因此,如果您需要一个快速而简单的解决方案,您很可能只需要大约 10 行awk
、grep
和魔法就可以得到很好的邻接列表sort
,uniq
其中所有类都作为节点,方法作为边。
我从来没有尝试过,这只是一个想法。我不能保证 Java 反编译器在 Scala 代码上运行良好。