Get rid of RecompilationDecision enum

This commit is contained in:
Alexey Tsvetkov
2015-08-14 23:13:22 +03:00
parent 8cc2e0f68d
commit 43bcc4a6a6
3 changed files with 165 additions and 150 deletions
@@ -57,10 +57,6 @@ import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
import org.jetbrains.kotlin.jps.incremental.* import org.jetbrains.kotlin.jps.incremental.*
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.DO_NOTHING
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_ALL_IN_CHUNK_AND_DEPENDANTS
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_OTHER_IN_CHUNK_AND_DEPENDANTS
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_OTHER_KOTLIN_IN_CHUNK
import org.jetbrains.kotlin.load.kotlin.ModuleMapping import org.jetbrains.kotlin.load.kotlin.ModuleMapping
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils import org.jetbrains.kotlin.load.kotlin.PackageClassUtils
import org.jetbrains.kotlin.load.kotlin.header.isCompatiblePackageFacadeKind import org.jetbrains.kotlin.load.kotlin.header.isCompatiblePackageFacadeKind
@@ -198,27 +194,55 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
context.checkCanceled() context.checkCanceled()
val recompilationDecision: IncrementalCacheImpl.RecompilationDecision val isJsModule = JpsUtils.isJsKotlinModule(chunk.representativeTarget())
if (JpsUtils.isJsKotlinModule(chunk.representativeTarget())) { val changesInfo: ChangesInfo = when {
recompilationDecision = DO_NOTHING isJsModule -> ChangesInfo.NO_CHANGES
} else -> {
else { val generatedClasses = generatedFiles.filterIsInstance<GeneratedJvmClass>()
val generatedClasses = generatedFiles.filterIsInstance<GeneratedJvmClass>() val info = updateKotlinIncrementalCache(compilationErrors, incrementalCaches, generatedFiles)
recompilationDecision = updateKotlinIncrementalCache(compilationErrors, incrementalCaches, generatedFiles) updateJavaMappings(chunk, compilationErrors, context, dirtyFilesHolder, filesToCompile, generatedClasses)
updateJavaMappings(chunk, compilationErrors, context, dirtyFilesHolder, filesToCompile, generatedClasses) info
}
} }
if (compilationErrors) { if (compilationErrors) {
return ABORT return ABORT
} }
if (JpsUtils.isJsKotlinModule(chunk.representativeTarget())) { if (isJsModule) {
copyJsLibraryFilesIfNeeded(chunk, project) copyJsLibraryFilesIfNeeded(chunk, project)
} }
if (IncrementalCompilation.ENABLED) { if (!IncrementalCompilation.ENABLED) return OK
val caches = filesToCompile.keySet().map { incrementalCaches[it]!! }
val caches = filesToCompile.keySet().map { incrementalCaches[it]!! }
val marker = ChangesProcessor(context, chunk, allCompiledFiles, caches)
marker.processChanges(changesInfo)
return ADDITIONAL_PASS_REQUIRED
}
class ChangesProcessor(
val context: CompileContext,
val chunk: ModuleChunk,
val allCompiledFiles: MutableSet<File>,
val caches: List<IncrementalCacheImpl>
) {
fun processChanges(changesInfo: ChangesInfo) {
changesInfo.doProcessChanges()
}
private fun ChangesInfo.doProcessChanges() {
when {
constantsChanged -> recompileOtherAndDependents()
protoChanged -> recompileOtherKotlinInChunk()
}
if (inlineChanged) {
recompileInlined()
}
}
private fun recompileInlined() {
for (cache in caches) { for (cache in caches) {
val filesToReinline = cache.getFilesToReinline() val filesToReinline = cache.getFilesToReinline()
@@ -226,34 +250,29 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
FSOperations.markDirty(context, CompilationRound.NEXT, it) FSOperations.markDirty(context, CompilationRound.NEXT, it)
} }
} }
when (recompilationDecision) {
RECOMPILE_ALL_IN_CHUNK_AND_DEPENDANTS -> {
allCompiledFiles.clear()
FSOperations.markDirtyRecursively(context, chunk)
}
RECOMPILE_OTHER_IN_CHUNK_AND_DEPENDANTS -> {
// Workaround for IDEA 14.0-14.0.2: extended version of markDirtyRecursively is not available
try {
Class.forName("org.jetbrains.jps.incremental.fs.CompilationRound")
FSOperations.markDirtyRecursively(context, CompilationRound.NEXT, chunk, { file -> file !in allCompiledFiles })
}
catch (e: ClassNotFoundException) {
allCompiledFiles.clear()
FSOperations.markDirtyRecursively(context, chunk)
}
}
RECOMPILE_OTHER_KOTLIN_IN_CHUNK -> {
FSOperations.markDirty(context, chunk, { file ->
KotlinSourceFileCollector.isKotlinSourceFile(file) && file !in allCompiledFiles
})
}
}
return ADDITIONAL_PASS_REQUIRED
} }
return OK private fun recompileEverything() {
allCompiledFiles.clear()
FSOperations.markDirtyRecursively(context, chunk)
}
private fun recompileOtherAndDependents() {
// Workaround for IDEA 14.0-14.0.2: extended version of markDirtyRecursively is not available
try {
Class.forName("org.jetbrains.jps.incremental.fs.CompilationRound")
FSOperations.markDirtyRecursively(context, CompilationRound.NEXT, chunk, { file -> file !in allCompiledFiles })
} catch (e: ClassNotFoundException) {
recompileEverything()
}
}
private fun recompileOtherKotlinInChunk() {
FSOperations.markDirty(context, chunk, { file ->
KotlinSourceFileCollector.isKotlinSourceFile(file) && file !in allCompiledFiles
})
}
} }
private fun doCompileModuleChunk( private fun doCompileModuleChunk(
@@ -423,19 +442,19 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
compilationErrors: Boolean, compilationErrors: Boolean,
incrementalCaches: Map<ModuleBuildTarget, IncrementalCacheImpl>, incrementalCaches: Map<ModuleBuildTarget, IncrementalCacheImpl>,
generatedFiles: List<GeneratedFile> generatedFiles: List<GeneratedFile>
): IncrementalCacheImpl.RecompilationDecision { ): ChangesInfo {
incrementalCaches.values().forEach { it.saveCacheFormatVersion() } incrementalCaches.values().forEach { it.saveCacheFormatVersion() }
if (!IncrementalCompilation.ENABLED) { if (!IncrementalCompilation.ENABLED) {
return DO_NOTHING return ChangesInfo.NO_CHANGES
} }
var recompilationDecision = DO_NOTHING var changesInfo = ChangesInfo.NO_CHANGES
for (generatedFile in generatedFiles) { for (generatedFile in generatedFiles) {
val ic = incrementalCaches[generatedFile.target]!! val ic = incrementalCaches[generatedFile.target]!!
val newDecision = val newChangesInfo =
if (generatedFile is GeneratedJvmClass) { if (generatedFile is GeneratedJvmClass) {
ic.saveFileToCache(generatedFile.sourceFiles, generatedFile.outputClass) ic.saveFileToCache(generatedFile)
} }
else if (generatedFile.outputFile.isModuleMappingFile()) { else if (generatedFile.outputFile.isModuleMappingFile()) {
ic.saveModuleMappingToCache(generatedFile.sourceFiles, generatedFile.outputFile) ic.saveModuleMappingToCache(generatedFile.sourceFiles, generatedFile.outputFile)
@@ -444,17 +463,17 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
continue continue
} }
recompilationDecision = recompilationDecision.merge(newDecision) changesInfo += newChangesInfo
} }
if (!compilationErrors) { if (!compilationErrors) {
incrementalCaches.values().forEach { incrementalCaches.values().forEach {
val newDecision = it.clearCacheForRemovedClasses() val newChangesInfo = it.clearCacheForRemovedClasses()
recompilationDecision = recompilationDecision.merge(newDecision) changesInfo += newChangesInfo
} }
} }
return recompilationDecision return changesInfo
} }
private fun File.isModuleMappingFile() = extension == ModuleMapping.MAPPING_FILE_EXT && parentFile.name == "META-INF" private fun File.isModuleMappingFile() = extension == ModuleMapping.MAPPING_FILE_EXT && parentFile.name == "META-INF"
@@ -686,7 +705,7 @@ private open class GeneratedFile(
val outputFile: File val outputFile: File
) )
private class GeneratedJvmClass( class GeneratedJvmClass (
target: ModuleBuildTarget, target: ModuleBuildTarget,
sourceFiles: Collection<File>, sourceFiles: Collection<File>,
outputFile: File outputFile: File
@@ -33,10 +33,8 @@ import org.jetbrains.jps.incremental.storage.PathStringDescriptor
import org.jetbrains.jps.incremental.storage.StorageOwner import org.jetbrains.jps.incremental.storage.StorageOwner
import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.jps.build.GeneratedJvmClass
import org.jetbrains.kotlin.jps.build.KotlinBuilder import org.jetbrains.kotlin.jps.build.KotlinBuilder
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.DO_NOTHING
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_OTHER_IN_CHUNK_AND_DEPENDANTS
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_OTHER_KOTLIN_IN_CHUNK
import org.jetbrains.kotlin.jps.incremental.storage.BasicMap import org.jetbrains.kotlin.jps.incremental.storage.BasicMap
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.JvmAnnotationNames
@@ -213,103 +211,86 @@ public class IncrementalCacheImpl(
return File(outputDir, "$internalClassName.class").canonicalPath return File(outputDir, "$internalClassName.class").canonicalPath
} }
private fun getRecompilationDecision(protoChanged: Boolean, constantsChanged: Boolean) =
when {
constantsChanged -> RECOMPILE_OTHER_IN_CHUNK_AND_DEPENDANTS
protoChanged -> RECOMPILE_OTHER_KOTLIN_IN_CHUNK
else -> DO_NOTHING
}
public fun saveCacheFormatVersion() { public fun saveCacheFormatVersion() {
cacheFormatVersion.saveIfNeeded() cacheFormatVersion.saveIfNeeded()
} }
public fun saveModuleMappingToCache(sourceFiles: Collection<File>, file: File): RecompilationDecision { public fun saveModuleMappingToCache(sourceFiles: Collection<File>, file: File): ChangesInfo {
val jvmClassName = JvmClassName.byInternalName(MODULE_MAPPING_FILE_NAME) val jvmClassName = JvmClassName.byInternalName(MODULE_MAPPING_FILE_NAME)
protoMap.put(jvmClassName, file.readBytes(), isPackage = false, checkChangesIsOpenPart = false) protoMap.process(jvmClassName, file.readBytes(), isPackage = false, checkChangesIsOpenPart = false)
dirtyOutputClassesMap.notDirty(MODULE_MAPPING_FILE_NAME) dirtyOutputClassesMap.notDirty(MODULE_MAPPING_FILE_NAME)
sourceFiles.forEach { sourceToClassesMap.add(it, jvmClassName) } sourceFiles.forEach { sourceToClassesMap.add(it, jvmClassName) }
return DO_NOTHING return ChangesInfo.NO_CHANGES
} }
public fun saveFileToCache(sourceFiles: Collection<File>, kotlinClass: LocalFileKotlinClass): RecompilationDecision { public fun saveFileToCache(generatedClass: GeneratedJvmClass): ChangesInfo {
val fileBytes = kotlinClass.getFileContents() val sourceFiles: Collection<File> = generatedClass.sourceFiles
val className = JvmClassName.byClassId(kotlinClass.getClassId()) val kotlinClass: LocalFileKotlinClass = generatedClass.outputClass
val header = kotlinClass.getClassHeader() val className = JvmClassName.byClassId(kotlinClass.classId)
dirtyOutputClassesMap.notDirty(className.getInternalName()) dirtyOutputClassesMap.notDirty(className.internalName)
sourceFiles.forEach { sourceFiles.forEach {
sourceToClassesMap.add(it, className) sourceToClassesMap.add(it, className)
classToSourcesMap.add(className, it) classToSourcesMap.add(className, it)
} }
inlineFunctionsMap.process(className, fileBytes) val header = kotlinClass.classHeader
val changesInfo = when {
val decision = when {
header.isCompatiblePackageFacadeKind() -> header.isCompatiblePackageFacadeKind() ->
getRecompilationDecision( protoMap.process(kotlinClass, isPackage = true)
protoChanged = protoMap.put(className, BitEncoding.decodeBytes(header.annotationData!!), isPackage = true),
constantsChanged = false
)
header.isCompatibleFileFacadeKind() -> { header.isCompatibleFileFacadeKind() -> {
assert(sourceFiles.size() == 1) { "Package part from several source files: $sourceFiles" } assert(sourceFiles.size() == 1) { "Package part from several source files: $sourceFiles" }
packagePartMap.addPackagePart(className) packagePartMap.addPackagePart(className)
getRecompilationDecision(
protoChanged = protoMap.put(className, BitEncoding.decodeBytes(header.annotationData!!), isPackage = true),
constantsChanged = constantsMap.process(className, fileBytes)
)
}
header.isCompatibleClassKind() ->
when (header.classKind!!) {
JvmAnnotationNames.KotlinClass.Kind.CLASS -> getRecompilationDecision(
protoChanged = protoMap.put(className, BitEncoding.decodeBytes(header.annotationData!!), isPackage = false),
constantsChanged = constantsMap.process(className, fileBytes)
)
JvmAnnotationNames.KotlinClass.Kind.LOCAL_CLASS, JvmAnnotationNames.KotlinClass.Kind.ANONYMOUS_OBJECT -> DO_NOTHING protoMap.process(kotlinClass, isPackage = true) +
} constantsMap.process(kotlinClass)
}
header.isCompatibleClassKind() && JvmAnnotationNames.KotlinClass.Kind.CLASS == header.classKind ->
protoMap.process(kotlinClass, isPackage = false) +
constantsMap.process(kotlinClass) +
inlineFunctionsMap.process(kotlinClass)
header.syntheticClassKind == JvmAnnotationNames.KotlinSyntheticClass.Kind.PACKAGE_PART -> { header.syntheticClassKind == JvmAnnotationNames.KotlinSyntheticClass.Kind.PACKAGE_PART -> {
assert(sourceFiles.size() == 1) { "Package part from several source files: $sourceFiles" } assert(sourceFiles.size() == 1) { "Package part from several source files: $sourceFiles" }
packagePartMap.addPackagePart(className) packagePartMap.addPackagePart(className)
getRecompilationDecision( constantsMap.process(kotlinClass) +
protoChanged = false, inlineFunctionsMap.process(kotlinClass)
constantsChanged = constantsMap.process(className, fileBytes)
)
}
else -> {
DO_NOTHING
} }
else -> ChangesInfo.NO_CHANGES
} }
if (decision != DO_NOTHING) {
KotlinBuilder.LOG.debug("$decision because $className is changed") changesInfo.logIfSomethingChanged(className)
} return changesInfo
return decision
} }
public fun clearCacheForRemovedClasses(): RecompilationDecision { private fun ChangesInfo.logIfSomethingChanged(className: JvmClassName) {
var recompilationDecision = DO_NOTHING if (this == ChangesInfo.NO_CHANGES) return
for (internalClassName in dirtyOutputClassesMap.getDirtyOutputClasses()) {
val className = JvmClassName.byInternalName(internalClassName)
val newDecision = getRecompilationDecision( KotlinBuilder.LOG.debug("$className is changed: $this")
protoChanged = internalClassName in protoMap, }
constantsChanged = internalClassName in constantsMap
)
if (newDecision != DO_NOTHING) {
KotlinBuilder.LOG.debug("$newDecision because $internalClassName is removed")
}
recompilationDecision = recompilationDecision.merge(newDecision) public fun clearCacheForRemovedClasses(): ChangesInfo {
val dirtyClasses = dirtyOutputClassesMap
.getDirtyOutputClasses()
.map(JvmClassName::byInternalName)
.toList()
protoMap.remove(className) val changesInfo = dirtyClasses.fold(ChangesInfo.NO_CHANGES) { info, className ->
packagePartMap.remove(className) val internalName = className.internalName
constantsMap.remove(className) val newInfo = ChangesInfo(protoChanged = internalName in protoMap,
inlineFunctionsMap.remove(className) constantsChanged = internalName in constantsMap)
newInfo.logIfSomethingChanged(className)
info + newInfo
}
dirtyClasses.forEach {
protoMap.remove(it)
packagePartMap.remove(it)
constantsMap.remove(it)
inlineFunctionsMap.remove(it)
} }
dirtyOutputClassesMap.clean() dirtyOutputClassesMap.clean()
return recompilationDecision return changesInfo
} }
override fun getObsoletePackageParts(): Collection<String> { override fun getObsoletePackageParts(): Collection<String> {
@@ -342,19 +323,27 @@ public class IncrementalCacheImpl(
private inner class ProtoMap(storageFile: File) : BasicMap<ByteArray>(storageFile, ByteArrayExternalizer) { private inner class ProtoMap(storageFile: File) : BasicMap<ByteArray>(storageFile, ByteArrayExternalizer) {
public fun put(className: JvmClassName, data: ByteArray, isPackage: Boolean, checkChangesIsOpenPart: Boolean = true): Boolean { public fun process(kotlinClass: LocalFileKotlinClass, isPackage: Boolean, checkChangesIsOpenPart: Boolean = true): ChangesInfo {
val key = className.getInternalName() val header = kotlinClass.classHeader
val bytes = BitEncoding.decodeBytes(header.annotationData!!)
return put(kotlinClass.className, bytes, isPackage, checkChangesIsOpenPart)
}
public fun process(className: JvmClassName, data: ByteArray, isPackage: Boolean, checkChangesIsOpenPart: Boolean): ChangesInfo {
return put(className, data, isPackage, checkChangesIsOpenPart)
}
private fun put(className: JvmClassName, data: ByteArray, isPackage: Boolean, checkChangesIsOpenPart: Boolean): ChangesInfo {
val key = className.internalName
val oldData = storage[key] val oldData = storage[key]
if (Arrays.equals(data, oldData)) {
return false
}
storage.put(key, data)
if (oldData != null && checkChangesIsOpenPart && isOpenPartNotChanged(oldData, data, isPackage)) { if (!Arrays.equals(data, oldData)) {
return false storage.put(key, data)
} }
return true return ChangesInfo(protoChanged = oldData == null ||
!checkChangesIsOpenPart ||
!isOpenPartNotChanged(oldData, data, isPackage))
} }
public fun get(className: JvmClassName): ByteArray? { public fun get(className: JvmClassName): ByteArray? {
@@ -434,24 +423,24 @@ public class IncrementalCacheImpl(
return if (result.isEmpty()) null else result return if (result.isEmpty()) null else result
} }
public fun process(className: JvmClassName, bytes: ByteArray): Boolean { public fun process(kotlinClass: LocalFileKotlinClass): ChangesInfo {
return put(className, getConstantsMap(bytes)) return put(kotlinClass.className, getConstantsMap(kotlinClass.fileContents))
} }
private fun put(className: JvmClassName, constantsMap: Map<String, Any>?): Boolean { private fun put(className: JvmClassName, constantsMap: Map<String, Any>?): ChangesInfo {
val key = className.getInternalName() val key = className.getInternalName()
val oldMap = storage[key] val oldMap = storage[key]
if (oldMap == constantsMap) { if (oldMap == constantsMap) return ChangesInfo.NO_CHANGES
return false
}
if (constantsMap != null) { if (constantsMap != null) {
storage.put(key, constantsMap) storage.put(key, constantsMap)
} }
else { else {
storage.remove(key) storage.remove(key)
} }
return true
return ChangesInfo(constantsChanged = true)
} }
public fun remove(className: JvmClassName) { public fun remove(className: JvmClassName) {
@@ -553,11 +542,11 @@ public class IncrementalCacheImpl(
return result return result
} }
public fun process(className: JvmClassName, bytes: ByteArray): Boolean { public fun process(kotlinClass: LocalFileKotlinClass): ChangesInfo {
return put(className, getInlineFunctionsMap(bytes)) return put(kotlinClass.className, getInlineFunctionsMap(kotlinClass.fileContents))
} }
private fun put(className: JvmClassName, newMap: Map<String, Long>): Boolean { private fun put(className: JvmClassName, newMap: Map<String, Long>): ChangesInfo {
val internalName = className.internalName val internalName = className.internalName
val oldMap = storage[internalName] ?: emptyMap() val oldMap = storage[internalName] ?: emptyMap()
@@ -580,10 +569,9 @@ public class IncrementalCacheImpl(
if (changed.isNotEmpty()) { if (changed.isNotEmpty()) {
dirtyInlineFunctionsMap.put(className, changed.toList()) dirtyInlineFunctionsMap.put(className, changed.toList())
return true
} }
return false return ChangesInfo(inlineChanged = changed.isNotEmpty())
} }
public fun remove(className: JvmClassName) { public fun remove(className: JvmClassName) {
@@ -736,19 +724,24 @@ public class IncrementalCacheImpl(
return mutableMapping return mutableMapping
} }
} }
enum class RecompilationDecision {
DO_NOTHING,
RECOMPILE_OTHER_KOTLIN_IN_CHUNK,
RECOMPILE_OTHER_IN_CHUNK_AND_DEPENDANTS,
RECOMPILE_ALL_IN_CHUNK_AND_DEPENDANTS;
fun merge(other: RecompilationDecision): RecompilationDecision {
return if (other.ordinal() > this.ordinal()) other else this
}
}
} }
data class ChangesInfo(
public val protoChanged: Boolean = false,
public val constantsChanged: Boolean = false,
public val inlineChanged: Boolean = false
) {
companion object {
public val NO_CHANGES: ChangesInfo = ChangesInfo()
}
public fun plus(other: ChangesInfo): ChangesInfo =
ChangesInfo(protoChanged || other.protoChanged,
constantsChanged || other.constantsChanged,
inlineChanged || other.inlineChanged)
}
public fun BuildDataPaths.getKotlinCacheVersion(target: BuildTarget<*>): CacheFormatVersion = CacheFormatVersion(getTargetDataRoot(target)) public fun BuildDataPaths.getKotlinCacheVersion(target: BuildTarget<*>): CacheFormatVersion = CacheFormatVersion(getTargetDataRoot(target))
private data class KotlinIncrementalStorageProvider( private data class KotlinIncrementalStorageProvider(
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.jps.incremental
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import java.io.File import java.io.File
class LocalFileKotlinClass private constructor( class LocalFileKotlinClass private constructor(
@@ -39,6 +40,8 @@ class LocalFileKotlinClass private constructor(
} }
} }
public val className: JvmClassName by lazy { JvmClassName.byClassId(classId) }
override fun getLocation() = file.getAbsolutePath() override fun getLocation() = file.getAbsolutePath()
public override fun getFileContents(): ByteArray = fileContents public override fun getFileContents(): ByteArray = fileContents