Initial support of descriptor loading at runtime

This commit is contained in:
Alexander Udalov
2014-08-21 14:23:01 +04:00
parent be954f2c27
commit 356f54b5df
34 changed files with 4142 additions and 49 deletions
+1
View File
@@ -17,6 +17,7 @@
<orderEntry type="module" module-name="serialization" />
<orderEntry type="module" module-name="serialization.jvm" />
<orderEntry type="module" module-name="builtins-serializer" />
<orderEntry type="module" module-name="descriptors.runtime" />
<orderEntry type="module" module-name="js.frontend" />
<orderEntry type="module" module-name="js.tests" />
<orderEntry type="module" module-name="preloader" />
@@ -0,0 +1,146 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.jvm.runtime
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAllTo
import org.jetbrains.kotlin.codegen.GeneratedClassLoader
import org.jetbrains.kotlin.codegen.GenerationUtils
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.jvm.compiler.ExpectedLoadErrorsUtil
import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass
import org.jetbrains.kotlin.load.kotlin.reflect.RuntimeModuleData
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererBuilder
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.scopes.RedeclarationHandler
import org.jetbrains.kotlin.resolve.scopes.WritableScope.LockLevel
import org.jetbrains.kotlin.resolve.scopes.WritableScopeImpl
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.JetTestUtils
import org.jetbrains.kotlin.test.TestCaseWithTmpdir
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.utils.sure
import java.io.File
public abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
class object {
private val renderer = DescriptorRendererBuilder()
.setWithDefinedIn(false)
.setExcludedAnnotationClasses(listOf(
ExpectedLoadErrorsUtil.ANNOTATION_CLASS_NAME,
"kotlin.deprecated",
"kotlin.data",
"org.jetbrains.annotations.NotNull",
"org.jetbrains.annotations.Nullable",
"org.jetbrains.annotations.Mutable",
"org.jetbrains.annotations.ReadOnly"
).map { FqName(it) })
.setOverrideRenderingPolicy(DescriptorRenderer.OverrideRenderingPolicy.RENDER_OPEN_OVERRIDE)
.setIncludeSynthesizedParameterNames(false)
.setIncludePropertyConstant(false)
.setVerbose(true)
.build()
}
protected fun doTest(ktFileName: String) {
val ktFile = File(ktFileName)
val environment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(myTestRootDisposable, ConfigurationKind.ALL)
val jetFile = JetTestUtils.createFile(ktFileName, FileUtil.loadFile(ktFile, true), environment.getProject())
val classFileFactory = GenerationUtils.compileFileGetClassFileFactoryForTest(jetFile)
val classLoader = GeneratedClassLoader(classFileFactory, null, ForTestCompileRuntime.runtimeJarForTests().toURI().toURL())
classFileFactory.writeAllTo(tmpdir)
val module = RuntimeModuleData.create(classLoader).module
// Since runtime package view descriptor doesn't support getAllDescriptors(), we construct a synthetic package view here.
// It has in its scope descriptors for all the classes and top level members generated by the compiler
val actual = object : PackageViewDescriptor {
val scope = WritableScopeImpl(JetScope.Empty, this, RedeclarationHandler.THROW_EXCEPTION, "runtime descriptor loader test")
override fun getFqName() = LoadDescriptorUtil.TEST_PACKAGE_FQNAME
override fun getMemberScope() = scope
override fun getModule() = module
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R =
visitor.visitPackageViewDescriptor(this, data)
override fun getContainingDeclaration() = throw UnsupportedOperationException()
override fun getOriginal() = throw UnsupportedOperationException()
override fun substitute(substitutor: TypeSubstitutor) = throw UnsupportedOperationException()
override fun acceptVoid(visitor: DeclarationDescriptorVisitor<Void, Void>?) = throw UnsupportedOperationException()
override fun getAnnotations() = throw UnsupportedOperationException()
override fun getName() = throw UnsupportedOperationException()
}
val scope = actual.getMemberScope()
scope.changeLockLevel(LockLevel.BOTH)
for (outputFile in classLoader.getAllGeneratedFiles()) {
val className = outputFile.relativePath.substringBeforeLast(".class").replace('/', '.').replace('\\', '.')
val klass = classLoader.loadClass(className).sure("Couldn't load class $className")
when (ReflectKotlinClass(klass).getClassHeader().kind) {
KotlinClassHeader.Kind.PACKAGE_FACADE -> {
val packageView = module.getPackage(actual.getFqName()) ?: error("Couldn't resolve package ${actual.getFqName()}")
for (descriptor in packageView.getMemberScope().getAllDescriptors()) {
when (descriptor) {
is FunctionDescriptor -> scope.addFunctionDescriptor(descriptor)
is PropertyDescriptor -> scope.addPropertyDescriptor(descriptor)
}
}
}
KotlinClassHeader.Kind.CLASS -> {
val classDescriptor =
resolveClassByFqNameInModule(module, FqNameUnsafe(className)).sure("Couldn't resolve class $className")
if (classDescriptor.getContainingDeclaration() is PackageFragmentDescriptor) {
scope.addClassifierDescriptor(classDescriptor)
}
}
}
}
val expected = LoadDescriptorUtil.loadTestPackageAndBindingContextFromJavaRoot(tmpdir, getTestRootDisposable(),
ConfigurationKind.ALL)
val configuration = RecursiveDescriptorComparator.DONT_INCLUDE_METHODS_OF_OBJECT
.checkPrimaryConstructors(true)
.checkPropertyAccessors(true)
.withRenderer(renderer)
RecursiveDescriptorComparator.validateAndCompareDescriptors(expected.first, actual, configuration, null)
}
private fun resolveClassByFqNameInModule(module: ModuleDescriptor, fqName: FqNameUnsafe): ClassDescriptor? {
if (fqName.isRoot()) return null
if (fqName.isSafe()) {
val topLevel = module.resolveTopLevelClass(fqName.toSafe())
if (topLevel != null) return topLevel
}
val parent = resolveClassByFqNameInModule(module, fqName.parent()) ?: return null
return parent.getUnsubstitutedInnerClassesScope().getClassifier(fqName.shortName()) as? ClassDescriptor
}
}
@@ -26,10 +26,7 @@ import org.jetbrains.kotlin.context.GlobalContext
import org.jetbrains.kotlin.di.InjectorForTopDownAnalyzerForJvm
import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.load.java.structure.reflect.classId
import org.jetbrains.kotlin.resolve.TopDownAnalysisParameters
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
@@ -83,15 +80,6 @@ public abstract class AbstractLocalClassProtoTest : TestCaseWithTmpdir() {
)
}
private val Class<*>.classId: ClassId
get() {
if (getEnclosingMethod() != null || getEnclosingConstructor() != null) {
return ClassId(FqName.ROOT, FqNameUnsafe(getName()), /* local = */ true)
}
return getDeclaringClass()?.classId?.createNestedClassId(Name.identifier(getSimpleName()))
?: ClassId.topLevel(FqName(getName()))
}
private fun assertHasAnnotationData(clazz: Class<*>) {
[suppress("UNCHECKED_CAST")]
val annotation = clazz.getAnnotation(