New J2K: always print real fqNames for static calls

As fqNames of some symbols may change during conversion
E.g, when moving Java static method to Kotlin companion object

#KT-19607 fixed
#KT-32903 fixed
#KT-33743 fixed
#KT-33556 fixed
This commit is contained in:
Ilya Kirillov
2019-09-24 12:00:51 +03:00
parent f9fac0acf5
commit 704b3bd2e6
20 changed files with 258 additions and 98 deletions
@@ -17,10 +17,12 @@
package org.jetbrains.kotlin.nj2k
import org.jetbrains.kotlin.nj2k.conversions.*
import org.jetbrains.kotlin.nj2k.tree.JKLambdaExpression
import org.jetbrains.kotlin.nj2k.tree.JKParameter
import org.jetbrains.kotlin.nj2k.tree.JKTreeRoot
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object ConversionsRunner {
private fun createConversions(context: NewJ2kConverterContext) = listOf(
NonCodeElementsConversion(context),
JavaModifiersConversion(context),
@@ -38,7 +40,6 @@ object ConversionsRunner {
ArrayInitializerConversion(context),
TryStatementConversion(context),
EnumFieldAccessConversion(context),
StaticMemberAccessConversion(context),
SynchronizedStatementConversion(context),
JetbrainsNullableAnnotationsConverter(context),
DefaultArgumentsConversion(context),
@@ -69,9 +70,14 @@ object ConversionsRunner {
ClassToObjectPromotionConversion(context),
RemoveWrongExtraModifiersForSingleFunctionsConversion(context),
MethodReferenceToLambdaConversion(context),
TypeMappingConversion(context) { typeElement ->
typeElement.parent.safeAs<JKParameter>()?.parent is JKLambdaExpression
},
BuiltinMembersConversion(context),
ImplicitCastsConversion(context),
LiteralConversion(context),
StaticMemberAccessConversion(context),
RemoveRedundantQualifiersForCallsConversion(context),
FilterImportsConversion(context),
MoveInitBlocksToTheEndConversion(context),
AddElementsInfoConversion(context)
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.psi.*
class JKSymbolProvider(project: Project, module: Module, contextElement: PsiElement) {
private val symbolsByFqName = mutableMapOf<String, JKSymbol>()
val symbolsByPsi = mutableMapOf<PsiElement, JKSymbol>()
val symbolsByJK = mutableMapOf<JKDeclaration, JKSymbol>()
private val symbolsByJK = mutableMapOf<JKDeclaration, JKSymbol>()
private val resolver = JKResolver(project, module, contextElement)
private val elementVisitor = ElementVisitor()
@@ -68,14 +68,15 @@ class JKSymbolProvider(project: Project, module: Module, contextElement: PsiElem
) else JKUnresolvedMethod(reference, typeFactory)) as T
}
fun provideUniverseSymbol(psi: PsiElement, jk: JKDeclaration): JKSymbol = provideUniverseSymbol(psi).also {
when (it) {
is JKUniverseClassSymbol -> it.target = jk as JKClass
is JKUniverseFieldSymbol -> it.target = jk as JKVariable
is JKUniverseMethodSymbol -> it.target = jk as JKMethod
is JKUniverseTypeParameterSymbol -> it.target = jk as JKTypeParameter
fun provideUniverseSymbol(psi: PsiElement, declaration: JKDeclaration): JKSymbol = provideUniverseSymbol(psi).also { symbol ->
when (symbol) {
is JKUniverseClassSymbol -> symbol.target = declaration as JKClass
is JKUniverseFieldSymbol -> symbol.target = declaration as JKVariable
is JKUniverseMethodSymbol -> symbol.target = declaration as JKMethod
is JKUniverseTypeParameterSymbol -> symbol.target = declaration as JKTypeParameter
is JKUniversePackageSymbol -> symbol.target = declaration as JKPackageDeclaration
}
symbolsByJK[jk] = it
symbolsByJK[declaration] = symbol
}
fun provideUniverseSymbol(psi: PsiElement): JKSymbol =
@@ -85,6 +86,7 @@ class JKSymbolProvider(project: Project, module: Module, contextElement: PsiElem
is PsiMethod -> JKUniverseMethodSymbol(typeFactory)
is PsiTypeParameter -> JKUniverseTypeParameterSymbol(typeFactory)
is PsiClass -> JKUniverseClassSymbol(typeFactory)
is PsiPackageStatement -> JKUniversePackageSymbol(typeFactory)
else -> TODO()
}
}
@@ -108,6 +110,22 @@ class JKSymbolProvider(project: Project, module: Module, contextElement: PsiElem
JKUniverseMethodSymbol(typeFactory).also { it.target = jk }
} as JKMethodSymbol
fun provideUniverseSymbol(jk: JKTypeParameter): JKTypeParameterSymbol = symbolsByJK.getOrPut(jk) {
JKUniverseTypeParameterSymbol(typeFactory).also { it.target = jk }
} as JKTypeParameterSymbol
fun provideUniverseSymbol(jk: JKPackageDeclaration): JKPackageSymbol = symbolsByJK.getOrPut(jk) {
JKUniversePackageSymbol(typeFactory).also { it.target = jk }
} as JKPackageSymbol
fun provideUniverseSymbol(jk: JKDeclaration): JKUniverseSymbol<*>? = when (jk) {
is JKClass -> provideUniverseSymbol(jk)
is JKVariable -> provideUniverseSymbol(jk)
is JKMethod -> provideUniverseSymbol(jk)
is JKTypeParameter -> provideUniverseSymbol(jk)
else -> null
} as? JKUniverseSymbol<*>
fun provideClassSymbol(fqName: FqName): JKClassSymbol =
symbolsByFqName.getOrPutIfNotNull(fqName.asString()) {
@@ -175,6 +193,10 @@ class JKSymbolProvider(project: Project, module: Module, contextElement: PsiElem
provideUniverseSymbol(classParameter)
}
override fun visitPackageStatement(statement: PsiPackageStatement) {
provideUniverseSymbol(statement)
}
override fun visitFile(file: PsiFile) {
file.acceptChildren(this)
}
@@ -6,9 +6,7 @@
package org.jetbrains.kotlin.nj2k
import org.jetbrains.kotlin.j2k.ast.Nullability
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.nj2k.symbols.JKClassSymbol
import org.jetbrains.kotlin.nj2k.symbols.getDisplayName
import org.jetbrains.kotlin.nj2k.symbols.*
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.tree.visitors.JKVisitorWithCommentsPrinting
import org.jetbrains.kotlin.nj2k.types.*
@@ -160,7 +158,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
}
override fun visitClassAccessExpressionRaw(classAccessExpression: JKClassAccessExpression) {
printer.renderClassSymbol(classAccessExpression.identifier, classAccessExpression)
printer.renderSymbol(classAccessExpression.identifier, classAccessExpression)
}
override fun visitFileRaw(file: JKFile) {
@@ -522,7 +520,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
}
override fun visitCallExpressionRaw(callExpression: JKCallExpression) {
printer.printWithNoIndent(FqName(callExpression.identifier.fqName).shortName().asString().escaped())
printer.renderSymbol(callExpression.identifier, callExpression)
callExpression.typeArgumentList.accept(this)
printer.par {
callExpression.arguments.accept(this)
@@ -603,7 +601,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
if (newExpression.isAnonymousClass) {
printer.printWithNoIndent("object : ")
}
printer.renderClassSymbol(newExpression.classSymbol, newExpression)
printer.renderSymbol(newExpression.classSymbol, newExpression)
newExpression.typeArgumentList.accept(this)
if (!newExpression.classSymbol.isInterface() || newExpression.arguments.arguments.isNotEmpty()) {
printer.par(ParenthesisKind.ROUND) {
@@ -667,11 +665,11 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
}
override fun visitFieldAccessExpressionRaw(fieldAccessExpression: JKFieldAccessExpression) {
printer.printWithNoIndent(fieldAccessExpression.identifier.name.escaped())
printer.renderSymbol(fieldAccessExpression.identifier, fieldAccessExpression)
}
override fun visitPackageAccessExpressionRaw(packageAccessExpression: JKPackageAccessExpression) {
printer.printWithNoIndent(packageAccessExpression.identifier.name.escaped())
printer.renderSymbol(packageAccessExpression.identifier, packageAccessExpression)
}
override fun visitMethodReferenceExpressionRaw(methodReferenceExpression: JKMethodReferenceExpression) {
@@ -896,8 +894,10 @@ private class JKPrinter(
printWithNoIndent(kind.close)
}
private fun JKClassSymbol.needFqName(): Boolean =
fqName !in mappedToKotlinFqNames
private fun JKSymbol.needFqName(): Boolean {
if (this !is JKClassSymbol && !isStaticMember && !isEnumConstant) return false
return fqName !in mappedToKotlinFqNames
}
private fun JKType.renderTypeInfo() {
print(elementInfoStorage.getOrCreateInfoForElement(this).render())
@@ -920,7 +920,7 @@ private class JKPrinter(
type.renderTypeInfo()
when (type) {
is JKClassType -> {
renderClassSymbol(type.classReference, owner)
renderSymbol(type.classReference, owner)
}
is JKContextType -> return
is JKStarProjectionType ->
@@ -951,9 +951,9 @@ private class JKPrinter(
}
}
fun renderClassSymbol(classSymbol: JKClassSymbol, owner: JKTreeElement?) {
val needFqName = classSymbol.needFqName() && owner?.isSelectorOfQualifiedExpression() != true
val displayName = if (needFqName) classSymbol.getDisplayName() else classSymbol.name
fun renderSymbol(symbol: JKSymbol, owner: JKTreeElement?) {
val needFqName = symbol.needFqName() && owner?.isSelectorOfQualifiedExpression() != true
val displayName = if (needFqName) symbol.getDisplayName() else symbol.name
printWithNoIndent(displayName.escapedAsQualifiedName())
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.nj2k.conversions
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.identifier
import org.jetbrains.kotlin.nj2k.symbols.JKUniverseClassSymbol
import org.jetbrains.kotlin.nj2k.symbols.isStaticMember
import org.jetbrains.kotlin.nj2k.tree.*
class RemoveRedundantQualifiersForCallsConversion(context: NewJ2kConverterContext) : RecursiveApplicableConversionBase(context) {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
if (element !is JKQualifiedExpression) return recurse(element)
val needRemoveQualifier = when (val receiver = element.receiver.receiverExpression()) {
is JKClassAccessExpression -> receiver.identifier is JKUniverseClassSymbol
is JKFieldAccessExpression, is JKCallExpression ->
receiver !is JKClassAccessExpression && element.selector.identifier?.isStaticMember == true
else -> false
}
if (needRemoveQualifier) {
element.invalidate()
return recurse(element.selector.withNonCodeElementsFrom(element.receiver).withNonCodeElementsFrom(element))
}
return recurse(element)
}
private fun JKExpression.receiverExpression() = when (this) {
is JKQualifiedExpression -> selector
else -> this
}
}
@@ -5,35 +5,23 @@
package org.jetbrains.kotlin.nj2k.conversions
import com.intellij.lang.jvm.JvmModifier
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiModifierListOwner
import org.jetbrains.kotlin.j2k.getContainingClass
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.asQualifierWithThisAsSelector
import org.jetbrains.kotlin.nj2k.symbols.JKClassSymbol
import org.jetbrains.kotlin.nj2k.symbols.JKSymbol
import org.jetbrains.kotlin.nj2k.symbols.isStaticMember
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class StaticMemberAccessConversion(context: NewJ2kConverterContext) : RecursiveApplicableConversionBase(context) {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
val symbol =
when (element) {
is JKFieldAccessExpression -> element.identifier
is JKCallExpression -> element.identifier
else -> null
} ?: return recurse(element)
if (element !is JKQualifiedExpression) return recurse(element)
val symbol = when (element) {
is JKFieldAccessExpression -> element.identifier
is JKCallExpression -> element.identifier
else -> null
} ?: return recurse(element)
if (element.asQualifierWithThisAsSelector() != null) return recurse(element)
if (symbol.isStaticMember()) {
val containingClassSymbol = symbol.containingClassSymbol() ?: return recurse(element)
if (symbol.isStaticMember) {
val containingClassSymbol = symbol.declaredIn as? JKClassSymbol ?: return recurse(element)
return recurse(
JKQualifiedExpression(
JKClassAccessExpression(containingClassSymbol),
@@ -43,39 +31,5 @@ class StaticMemberAccessConversion(context: NewJ2kConverterContext) : RecursiveA
}
return recurse(element)
}
private fun JKSymbol.isStaticMember(): Boolean = when (val target = target) {
is PsiModifierListOwner -> target.hasModifier(JvmModifier.STATIC)
is KtElement -> target.getStrictParentOfType<KtClassOrObject>()
?.safeAs<KtObjectDeclaration>()
?.isCompanion() == true
is JKTreeElement ->
target.safeAs<JKOtherModifiersOwner>()?.hasOtherModifier(OtherModifier.STATIC) == true
|| target.parentOfType<JKClass>()?.classKind == JKClass.ClassKind.OBJECT
else -> false
}
private fun JKSymbol.containingClassSymbol(): JKClassSymbol? {
val target = target
return when (target) {
is KtElement ->
target.getStrictParentOfType<KtClassOrObject>()?.let { klass ->
if (klass is KtObjectDeclaration && klass.isCompanion()) klass.containingClass()
else klass
}?.let { symbolProvider.provideDirectSymbol(it) }
is PsiElement -> target.getContainingClass()?.let {
symbolProvider.provideDirectSymbol(it)
}
is JKTreeElement ->
target.parentOfType<JKClass>()?.let { klass ->
if (klass.classKind == JKClass.ClassKind.COMPANION) klass.parentOfType<JKClass>()
else klass
}?.let { symbolProvider.provideUniverseSymbol(it) }
else -> error("bad isStaticMember")
} as JKClassSymbol?
}
}
@@ -17,11 +17,16 @@ import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.types.*
import org.jetbrains.kotlin.psi.KtClass
class TypeMappingConversion(context: NewJ2kConverterContext) : RecursiveApplicableConversionBase(context) {
class TypeMappingConversion(
context: NewJ2kConverterContext,
inline val filter: (typeElement: JKTypeElement) -> Boolean = { true }
) : RecursiveApplicableConversionBase(context) {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
when (element) {
is JKTypeElement -> {
element.type = element.type.mapType(element)
if (filter(element)) {
element.type = element.type.mapType(element)
}
}
is JKNewExpression -> {
val newClassSymbol = element.classSymbol.mapClassSymbol()
@@ -10,6 +10,7 @@ import com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.nj2k.conversions.RecursiveApplicableConversionBase
import org.jetbrains.kotlin.nj2k.symbols.JKMethodSymbol
import org.jetbrains.kotlin.nj2k.symbols.JKSymbol
import org.jetbrains.kotlin.nj2k.symbols.JKUnresolvedMethod
import org.jetbrains.kotlin.nj2k.tree.*
@@ -339,3 +340,16 @@ fun JKClass.isLocalClass(): Boolean =
val JKClass.declarationList: List<JKDeclaration>
get() = classBody.declarations
val JKTreeElement.identifier: JKSymbol?
get() = when (this) {
is JKFieldAccessExpression -> identifier
is JKCallExpression -> identifier
is JKClassAccessExpression -> identifier
is JKPackageAccessExpression -> identifier
is JKNewExpression -> classSymbol
else -> null
}
val JKClass.isObjectOrCompanionObject
get() = classKind == JKClass.ClassKind.OBJECT || classKind == JKClass.ClassKind.COMPANION
@@ -17,7 +17,10 @@ sealed class JKClassSymbol : JKSymbol
class JKUniverseClassSymbol(override val typeFactory: JKTypeFactory) : JKClassSymbol(), JKUniverseSymbol<JKClass> {
override lateinit var target: JKClass
override val name: String
get() = target.name.value
get() = when {
target.classKind == JKClass.ClassKind.COMPANION -> "Companion"
else -> target.name.value
}
}
class JKMultiverseClassSymbol(
@@ -6,15 +6,25 @@
package org.jetbrains.kotlin.nj2k.symbols
import com.intellij.psi.PsiPackage
import org.jetbrains.kotlin.nj2k.JKSymbolProvider
import org.jetbrains.kotlin.nj2k.tree.JKPackageDeclaration
import org.jetbrains.kotlin.nj2k.types.JKTypeFactory
sealed class JKPackageSymbol : JKSymbol {
abstract override val target: PsiPackage
}
sealed class JKPackageSymbol : JKSymbol
class JKMultiversePackageSymbol(
override val target: PsiPackage,
override val typeFactory: JKTypeFactory
) : JKPackageSymbol(), JKMultiverseSymbol<PsiPackage>
) : JKPackageSymbol(), JKMultiverseSymbol<PsiPackage> {
override val declaredIn: JKSymbol?
get() = null
}
class JKUniversePackageSymbol(
override val typeFactory: JKTypeFactory
) : JKPackageSymbol(), JKUniverseSymbol<JKPackageDeclaration> {
override lateinit var target: JKPackageDeclaration
override val declaredIn: JKSymbol?
get() = null
}
@@ -48,7 +48,10 @@ interface JKUniverseSymbol<T : JKDeclaration> : JKSymbol {
get() = target.name.value
override val declaredIn: JKSymbol?
get() = target.parentOfType<JKDeclaration>()?.let { symbolProvider.symbolsByJK[it] }
get() = target.parentOfType<JKDeclaration>()?.let { symbolProvider.provideUniverseSymbol(it) }
?: target.parentOfType<JKFile>()?.packageDeclaration
?.takeIf { it.name.value.isNotEmpty() }
?.let { symbolProvider.provideUniverseSymbol(it) }
}
interface JKMultiverseSymbol<T> : JKSymbol where T : PsiNamedElement, T : PsiElement {
@@ -5,18 +5,18 @@
package org.jetbrains.kotlin.nj2k.symbols
import com.intellij.lang.jvm.JvmModifier
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiEnumConstant
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiModifierListOwner
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsNoWrapping
import org.jetbrains.kotlin.nj2k.isObjectOrCompanionObject
import org.jetbrains.kotlin.nj2k.psi
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.types.JKType
import org.jetbrains.kotlin.nj2k.types.arrayInnerType
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -24,9 +24,10 @@ val JKSymbol.isUnresolved
get() = this is JKUnresolvedSymbol
fun JKSymbol.getDisplayName(): String {
fun JKSymbol.isDisplayable() = this is JKClassSymbol || this is JKPackageSymbol
if (this !is JKUniverseSymbol<*>) return fqName
return generateSequence(declaredIn as? JKUniverseClassSymbol) { symbol ->
symbol.declaredIn.safeAs<JKUniverseClassSymbol>()?.takeIf { !it.target.hasOtherModifier(OtherModifier.INNER) }
return generateSequence(declaredIn?.takeIf { it.isDisplayable() }) { symbol ->
symbol.declaredIn?.takeIf { it.isDisplayable() }
}.fold(name) { acc, symbol -> "${symbol.name}.$acc" }
}
@@ -45,3 +46,23 @@ fun JKSymbol.deepestFqName(): String? {
}
return target.deepestFqNameForTarget() ?: fqName
}
val JKSymbol.isStaticMember
get() = when (val target = target) {
is PsiModifierListOwner -> target.hasModifier(JvmModifier.STATIC)
is KtElement -> target.getStrictParentOfType<KtClassOrObject>()
?.safeAs<KtObjectDeclaration>()
?.isCompanion() == true
is JKTreeElement ->
target.safeAs<JKOtherModifiersOwner>()?.hasOtherModifier(OtherModifier.STATIC) == true
|| target.parentOfType<JKClass>()?.isObjectOrCompanionObject == true
else -> false
}
val JKSymbol.isEnumConstant
get() = when (target) {
is JKEnumConstant -> true
is PsiEnumConstant -> true
is KtEnumEntry -> true
else -> false
}
@@ -71,8 +71,8 @@ class JKInheritanceInfo(
override fun accept(visitor: JKVisitor) = visitor.visitInheritanceInfo(this)
}
class JKPackageDeclaration(name: JKNameIdentifier) : JKTreeElement() {
var name: JKNameIdentifier by child(name)
class JKPackageDeclaration(name: JKNameIdentifier) : JKDeclaration() {
override var name: JKNameIdentifier by child(name)
override fun accept(visitor: JKVisitor) = visitor.visitPackageDeclaration(this)
}
@@ -0,0 +1,4 @@
@file:JvmName("Bar")
package p
fun bar() {}
fun bar(x:Int) {}
+7
View File
@@ -0,0 +1,7 @@
import static p.Bar.bar;
class Impl {
void foo() {
bar();
}
}
+7
View File
@@ -0,0 +1,7 @@
import p.bar
internal class Impl {
fun foo() {
bar()
}
}
@@ -0,0 +1,12 @@
public class Rectangle {
public static int x = 0;
public int y;
}
public class Main {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.x = 1;
rectangle.y = 2;
}
}
@@ -0,0 +1,16 @@
class Rectangle {
var y = 0
companion object {
var x = 0
}
}
object Main {
@JvmStatic
fun main(args: Array<String>) {
val rectangle = Rectangle()
Rectangle.x = 1
rectangle.y = 2
}
}
@@ -0,0 +1,13 @@
public class Base {
public static void foo() {
}
}
public class Derived extends Base {
}
public class User {
public static void test() {
Derived.foo();
}
}
@@ -0,0 +1,12 @@
open class Base {
companion object {
fun foo() {}
}
}
class Derived : Base()
object User {
fun test() {
Base.foo()
}
}
@@ -4475,6 +4475,21 @@ public class NewJavaToKotlinConverterSingleFileTestGenerated extends AbstractNew
public void testPrivateStaticMethods4() throws Exception {
runTest("nj2k/testData/newJ2k/staticMembers/PrivateStaticMethods4.java");
}
@TestMetadata("StaticImport.java")
public void testStaticImport() throws Exception {
runTest("nj2k/testData/newJ2k/staticMembers/StaticImport.java");
}
@TestMetadata("StaticMemmberAccessedViaInstance.java")
public void testStaticMemmberAccessedViaInstance() throws Exception {
runTest("nj2k/testData/newJ2k/staticMembers/StaticMemmberAccessedViaInstance.java");
}
@TestMetadata("StaticQualifiedByInheritor.java")
public void testStaticQualifiedByInheritor() throws Exception {
runTest("nj2k/testData/newJ2k/staticMembers/StaticQualifiedByInheritor.java");
}
}
@TestMetadata("nj2k/testData/newJ2k/strings")