UAST(统一抽象语法树)是针对 JVM(Java 虚拟机)的不同编程语言的PSI上的抽象层。它提供了一个统一的 API,用于处理公共语言元素,如类和方法声明、文字值和控制流运算符。不同的 JVM 语言有它们自己的PSI,但许多 IDE 功能,如检查、装订线标记、引用注入和许多其他功能,对所有这些语言都以相同的方式工作。使用 UAST 允许使用单个实现提供可在所有受支持的 JVM 语言中工作的功能。比如spring框架插件就使用了这种技术,完全支持java和kotlin。
UAST 是只读 API。有实验UastCodeGenerationPlugin和JvmElementActionsFactory类,但目前不建议外部使用。
UAST 的基本元素是UElement。不到必要的时候不建议使用UAST,因为性能并不是太好。
- UastContextKt.toUElement(element);
-
- //转换PsiElement为特定的UElement,可选下列方法之一
- UastContextKt.toUElement(element, UCallExpression.class);
- UastFacade.INSTANCE.convertElementWithParent(element,
- new Class[]{UInjectionHost.class, UReferenceExpression.class});
- UastFacade.INSTANCE.convertToAlternatives(element,
- new Class[]{UField.class, UParameter.class});
UElement#sourcePsi属性返回对应PsiElement的原始语言。sourcePsi是一个“物理” PsiElement,它主要用于获取原始文件中的文本范围(例如,用于突出显示)。有些UElement是“虚拟的”,因此没有sourcePsi。
UElement#javaPsi返回 "Java-like" 型的PsiElement。PsiElement使不同的 JVM 语言模仿 Java 语言以保持与 Java-API 的兼容性的一种“伪造” 。例如,调用时MethodReferencesSearch.search(PsiMethod),只有 Java 原生提供PsiMethod;因此,其他 JVM 语言PsiMethod通过UMethod#javaPsi。
所以UElement#javaPsi仅适用于 Java。因此UElement#sourcePsi应该用于获取文本范围或用于检查警告/装订线标记放置的锚元素。简而言之:
在 UAST 中,没有统一的方法来获取children(UElement尽管可以通过 获取其父项UElement#uastParent)。因此,将 UAST 作为树遍历的唯一方法是传UastVisitor做为参数传递给UElement.accept()方法。
UastVisitor可以通过UastVisitorAdapter或UastHintedVisitorAdapter方法被转换为PsiElementVisitor。建议使用提供更好的性能和更可预测的结果的UastHintedVisitorAdapter类。
UastVisitor可以转换为PsiElementVisitor使用。后者更可取,因为它提供更好的性能和更可预测的结果。如果不需要处理很多种类型的UElement或比较复杂的结构,建议不要直接使用UastVisitor。建议使用PsiElementVisitor遍历PSI树,然后通过UastContext.toUElement()把PsiElement转换为UAST。
UAST提供了一种统一的方式来通过、UField、UClass等来表示 JVM 兼容声明。但同时,所有的 JVM 语言插件都实现了PsiMethod, PsiClass, 等等 以兼容 Java。这些实现可以通过属性获得UElement#javaPsi。
UAST 是不同语言 PSI 之上的抽象层,并试图构建一个统一的树(参见检查 UAST 树)。这导致 UAST 和原始语言之间的树结构可能严重不同,因此无法保证保留祖先-后代关系。