KT-1829 NoClassDefFoundError when using java inner classes

#KT-1829 Fixed
This commit is contained in:
Stepan Koltsov
2012-05-11 01:08:30 +04:00
parent b0cea24faf
commit 9908791094
14 changed files with 131 additions and 29 deletions
@@ -64,7 +64,7 @@ public class ClassFileFactory {
FqName fqName = JetPsiUtil.getFQName(file); FqName fqName = JetPsiUtil.getFQName(file);
NamespaceCodegen codegen = ns2codegen.get(fqName); NamespaceCodegen codegen = ns2codegen.get(fqName);
if (codegen == null) { if (codegen == null) {
final ClassBuilder builder = newVisitor(NamespaceCodegen.getJVMClassName(fqName, true) + ".class"); final ClassBuilder builder = newVisitor(NamespaceCodegen.getJVMClassNameForKotlinNs(fqName) + ".class");
codegen = new NamespaceCodegen(builder, fqName, state, file.getContainingFile()); codegen = new NamespaceCodegen(builder, fqName, state, file.getContainingFile());
ns2codegen.put(fqName, codegen); ns2codegen.put(fqName, codegen);
} }
@@ -113,7 +113,7 @@ public abstract class CodegenContext {
while(!(descriptor instanceof NamespaceDescriptor)) { while(!(descriptor instanceof NamespaceDescriptor)) {
descriptor = descriptor.getContainingDeclaration(); descriptor = descriptor.getContainingDeclaration();
} }
return NamespaceCodegen.getJVMClassName(DescriptorUtils.getFQName(descriptor).toSafe(), true); return NamespaceCodegen.getJVMClassNameForKotlinNs(DescriptorUtils.getFQName(descriptor).toSafe());
} }
public OwnerKind getContextKind() { public OwnerKind getContextKind() {
@@ -333,7 +333,7 @@ public class FunctionCodegen {
String ownerInternalName; String ownerInternalName;
if (contextClass instanceof NamespaceDescriptor) { if (contextClass instanceof NamespaceDescriptor) {
ownerInternalName = NamespaceCodegen.getJVMClassName(DescriptorUtils.getFQName(contextClass).toSafe(), true); ownerInternalName = NamespaceCodegen.getJVMClassNameForKotlinNs(DescriptorUtils.getFQName(contextClass).toSafe());
} }
else { else {
ownerInternalName = state.getInjector().getJetTypeMapper().mapType(((ClassDescriptor) contextClass).getDefaultType(), MapTypeMode.IMPL).getInternalName(); ownerInternalName = state.getInjector().getJetTypeMapper().mapType(((ClassDescriptor) contextClass).getDefaultType(), MapTypeMode.IMPL).getInternalName();
@@ -16,6 +16,7 @@
package org.jetbrains.jet.codegen; package org.jetbrains.jet.codegen;
import com.google.common.collect.Lists;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -184,28 +185,64 @@ public class JetTypeMapper {
} }
} }
private String jvmClassNameForNamespace(NamespaceDescriptor namespace) { @NotNull
FqName fqName = DescriptorUtils.getFQName(namespace).toSafe(); private JavaNamespaceKind getNsKind(@NotNull NamespaceDescriptor ns) {
JavaNamespaceKind javaNamespaceKind = bindingContext.get(JavaBindingContext.JAVA_NAMESPACE_KIND, namespace); JavaNamespaceKind javaNamespaceKind = bindingContext.get(JavaBindingContext.JAVA_NAMESPACE_KIND, ns);
Boolean src = bindingContext.get(BindingContext.NAMESPACE_IS_SRC, namespace); Boolean src = bindingContext.get(BindingContext.NAMESPACE_IS_SRC, ns);
if (javaNamespaceKind == null && src == null) { if (javaNamespaceKind == null && src == null) {
throw new IllegalStateException("unknown namespace origin: " + fqName); throw new IllegalStateException("unknown namespace origin: " + ns);
} }
boolean classStatics;
if (javaNamespaceKind != null) { if (javaNamespaceKind != null) {
if (javaNamespaceKind == JavaNamespaceKind.CLASS_STATICS && src != null) { if (javaNamespaceKind == JavaNamespaceKind.CLASS_STATICS && src != null) {
throw new IllegalStateException( throw new IllegalStateException(
"conflicting namespace " + fqName + ": it is both java statics and from src"); "conflicting namespace " + ns + ": it is both java statics and from src");
} }
classStatics = javaNamespaceKind == JavaNamespaceKind.CLASS_STATICS; return javaNamespaceKind;
} }
else { else {
classStatics = false; return JavaNamespaceKind.PROPER;
}
}
@NotNull
private String jvmClassNameForNamespace(@NotNull NamespaceDescriptor namespace) {
StringBuilder r = new StringBuilder();
List<DeclarationDescriptor> path = DescriptorUtils.getPathWithoutRootNsAndModule(namespace);
for (DeclarationDescriptor pathElement : path) {
NamespaceDescriptor ns = (NamespaceDescriptor) pathElement;
if (r.length() > 0) {
JavaNamespaceKind nsKind = getNsKind((NamespaceDescriptor) ns.getContainingDeclaration());
if (nsKind == JavaNamespaceKind.PROPER) {
r.append("/");
}
else if (nsKind == JavaNamespaceKind.CLASS_STATICS) {
r.append("$");
}
}
if (ns.getName().length() == 0) {
throw new IllegalStateException(
"name must not be empty at this point when generating for " + namespace);
}
r.append(ns.getName());
} }
return NamespaceCodegen.getJVMClassName(fqName, !classStatics); if (getNsKind(namespace) == JavaNamespaceKind.PROPER) {
if (r.length() > 0) {
r.append("/");
}
r.append("namespace");
}
if (r.length() == 0) {
throw new IllegalStateException("internal error: failed to generate classname for " + namespace);
}
return r.toString();
} }
@NotNull public Type mapReturnType(@NotNull final JetType jetType) { @NotNull public Type mapReturnType(@NotNull final JetType jetType) {
@@ -44,7 +44,7 @@ public class NamespaceCodegen {
v.defineClass(sourceFile, V1_6, v.defineClass(sourceFile, V1_6,
ACC_PUBLIC/*|ACC_SUPER*/, ACC_PUBLIC/*|ACC_SUPER*/,
getJVMClassName(fqName, true), getJVMClassNameForKotlinNs(fqName),
null, null,
//"jet/lang/Namespace", //"jet/lang/Namespace",
"java/lang/Object", "java/lang/Object",
@@ -137,10 +137,7 @@ public class NamespaceCodegen {
v.done(); v.done();
} }
/** public static String getJVMClassNameForKotlinNs(@NotNull FqName fqName) {
* @param namespace true for "namespace" suffix
*/
public static String getJVMClassName(@NotNull FqName fqName, boolean namespace) {
if (fqName.isRoot()) { if (fqName.isRoot()) {
return JvmAbi.PACKAGE_CLASS; return JvmAbi.PACKAGE_CLASS;
} }
@@ -149,9 +146,7 @@ public class NamespaceCodegen {
if (name.startsWith(JavaDescriptorResolver.JAVA_ROOT)) { if (name.startsWith(JavaDescriptorResolver.JAVA_ROOT)) {
name = name.substring(JavaDescriptorResolver.JAVA_ROOT.length() + 1, name.length()); name = name.substring(JavaDescriptorResolver.JAVA_ROOT.length() + 1, name.length());
} }
if (namespace) { name += "/" + JvmAbi.PACKAGE_CLASS;
name += "/" + JvmAbi.PACKAGE_CLASS;
}
return name; return name;
} }
} }
@@ -314,6 +314,19 @@ public class DescriptorUtils {
return namespaceDescriptor.getContainingDeclaration() instanceof ModuleDescriptor; return namespaceDescriptor.getContainingDeclaration() instanceof ModuleDescriptor;
} }
@NotNull
public static List<DeclarationDescriptor> getPathWithoutRootNsAndModule(@NotNull DeclarationDescriptor descriptor) {
List<DeclarationDescriptor> path = Lists.newArrayList();
DeclarationDescriptor current = descriptor;
for (;;) {
if (current instanceof NamespaceDescriptor && isRootNamespace((NamespaceDescriptor) current)) {
return Lists.reverse(path);
}
path.add(current);
current = current.getContainingDeclaration();
}
}
public static boolean isClassObject(@NotNull DeclarationDescriptor descriptor) { public static boolean isClassObject(@NotNull DeclarationDescriptor descriptor) {
if(descriptor instanceof ClassDescriptor) { if(descriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
@@ -0,0 +1,5 @@
class R {
public static class id {
public static final int main = 17;
}
}
@@ -0,0 +1,2 @@
fun box() =
if (R.id.main == 17) "OK" else "fail"
@@ -0,0 +1,7 @@
class R {
public static class id {
public static class zzz {
public static final int main = 17;
}
}
}
@@ -0,0 +1,2 @@
fun box() =
if (R.id.zzz.main == 17) "OK" else "fail"
@@ -16,9 +16,9 @@
package org.jetbrains.jet.codegen; package org.jetbrains.jet.codegen;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.CompileCompilerDependenciesTest;
import org.jetbrains.jet.JetLiteFixture; import org.jetbrains.jet.JetLiteFixture;
import org.jetbrains.jet.analyzer.AnalyzeExhaust; import org.jetbrains.jet.analyzer.AnalyzeExhaust;
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory;
@@ -26,20 +26,31 @@ import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetPsiUtil; import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.AnalyzingUtils; import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM; import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode;
import org.jetbrains.jet.parsing.JetParsingTest; import org.jetbrains.jet.parsing.JetParsingTest;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections; import java.util.Collections;
import java.util.List;
/** /**
* @author yole * @author yole
*/ */
public abstract class CodegenTestCase extends JetLiteFixture { public abstract class CodegenTestCase extends JetLiteFixture {
// for environment and classloader
private List<File> extraClasspath = Lists.newArrayList();
protected void addToClasspath(@NotNull File file) {
myEnvironment.addToClasspath(file);
extraClasspath.add(file);
}
protected static void assertThrows(Method foo, Class<? extends Throwable> exceptionClass, Object instance, Object... args) throws IllegalAccessException { protected static void assertThrows(Method foo, Class<? extends Throwable> exceptionClass, Object instance, Object... args) throws IllegalAccessException {
boolean caught = false; boolean caught = false;
try { try {
@@ -108,7 +119,7 @@ public abstract class CodegenTestCase extends JetLiteFixture {
GeneratedClassLoader loader = createClassLoader(codegens); GeneratedClassLoader loader = createClassLoader(codegens);
try { try {
String fqName = NamespaceCodegen.getJVMClassName(JetPsiUtil.getFQName(myFile), true).replace("/", "."); String fqName = NamespaceCodegen.getJVMClassNameForKotlinNs(JetPsiUtil.getFQName(myFile)).replace("/", ".");
Class<?> namespaceClass = loader.loadClass(fqName); Class<?> namespaceClass = loader.loadClass(fqName);
Method method = namespaceClass.getMethod("box"); Method method = namespaceClass.getMethod("box");
return (String) method.invoke(null); return (String) method.invoke(null);
@@ -118,7 +129,12 @@ public abstract class CodegenTestCase extends JetLiteFixture {
} }
protected GeneratedClassLoader createClassLoader(ClassFileFactory codegens) throws MalformedURLException { protected GeneratedClassLoader createClassLoader(ClassFileFactory codegens) throws MalformedURLException {
return new GeneratedClassLoader(codegens); List<URL> urls = Lists.newArrayList();
for (File file : extraClasspath) {
urls.add(file.toURI().toURL());
}
ClassLoader parentClassLoader = new URLClassLoader(urls.toArray(new URL[0]), CodegenTestCase.class.getClassLoader());
return new GeneratedClassLoader(codegens, parentClassLoader);
} }
protected String generateToText() { protected String generateToText() {
@@ -147,7 +163,7 @@ public abstract class CodegenTestCase extends JetLiteFixture {
} }
protected Class loadRootNamespaceClass(@NotNull ClassFileFactory state) { protected Class loadRootNamespaceClass(@NotNull ClassFileFactory state) {
String fqName = NamespaceCodegen.getJVMClassName(JetPsiUtil.getFQName(myFile), true).replace("/", "."); String fqName = NamespaceCodegen.getJVMClassNameForKotlinNs(JetPsiUtil.getFQName(myFile)).replace("/", ".");
try { try {
return createClassLoader(state).loadClass(fqName); return createClassLoader(state).loadClass(fqName);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
@@ -16,8 +16,12 @@
package org.jetbrains.jet.codegen; package org.jetbrains.jet.codegen;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode;
import java.io.File;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -113,4 +117,25 @@ public class FunctionGenTest extends CodegenTestCase {
public void testInvoke() { public void testInvoke() {
blackBoxFile("functions/invoke.kt"); blackBoxFile("functions/invoke.kt");
} }
private void blackBoxFileWithJava(@NotNull String ktFile) throws Exception {
File javaClassesTempDirectory = new File(FileUtil.getTempDirectory(), "java-classes");
JetTestUtils.mkdirs(javaClassesTempDirectory);
JetTestUtils.compileJavaFile(
new File("compiler/testData/codegen/" + ktFile.replaceFirst("\\.kt$", ".java")),
javaClassesTempDirectory);
addToClasspath(javaClassesTempDirectory);
blackBoxFile(ktFile);
}
public void testReferencesStaticInnerClassMethod() throws Exception {
blackBoxFileWithJava("functions/referencesStaticInnerClassMethod.kt");
}
public void testReferencesStaticInnerClassMethodTwoLevels() throws Exception {
blackBoxFileWithJava("functions/referencesStaticInnerClassMethodL2.kt");
}
} }
@@ -146,7 +146,7 @@ public class StdlibTest extends CodegenTestCase {
GeneratedClassLoader loader = createClassLoader(codegens); GeneratedClassLoader loader = createClassLoader(codegens);
try { try {
String fqName = NamespaceCodegen.getJVMClassName(JetPsiUtil.getFQName(myFile), true).replace("/", "."); String fqName = NamespaceCodegen.getJVMClassNameForKotlinNs(JetPsiUtil.getFQName(myFile)).replace("/", ".");
Class<?> namespaceClass = loader.loadClass(fqName); Class<?> namespaceClass = loader.loadClass(fqName);
Method method = namespaceClass.getMethod("box", Method.class); Method method = namespaceClass.getMethod("box", Method.class);
method.setAccessible(true); method.setAccessible(true);
@@ -137,10 +137,10 @@ public class JetPositionManager implements PositionManager {
else { else {
JetFile namespace = PsiTreeUtil.getParentOfType(sourcePosition.getElementAt(), JetFile.class); JetFile namespace = PsiTreeUtil.getParentOfType(sourcePosition.getElementAt(), JetFile.class);
if (namespace != null) { if (namespace != null) {
names.add(NamespaceCodegen.getJVMClassName(JetPsiUtil.getFQName(namespace), true)); names.add(NamespaceCodegen.getJVMClassNameForKotlinNs(JetPsiUtil.getFQName(namespace)));
} }
else { else {
names.add(NamespaceCodegen.getJVMClassName(JetPsiUtil.getFQName(file), true)); names.add(NamespaceCodegen.getJVMClassNameForKotlinNs(JetPsiUtil.getFQName(file)));
} }
} }
} }