diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/foreignKClasses.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/foreignKClasses.kt new file mode 100644 index 00000000000..d13dd573029 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/foreignKClasses.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2014 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 kotlin.reflect.jvm.internal + +import java.lang.ref.WeakReference +import kotlin.reflect.jvm.internal.pcollections.HashPMap + +// TODO: collect nulls periodically +// Key of the map is Class.getName(), each value is either a WeakReference> or an Array>>. +// Arrays are needed because the same class can be loaded by different class loaders, which results in different Class instances. +// This variable is not volatile intentionally: we don't care if there's a data race on it and some KClass instances will be lost. +// We do care however about general performance on read access to it, thus no synchronization is done here whatsoever +private var FOREIGN_K_CLASSES = HashPMap.empty()!! + +// This function is invoked on each reflection access to Java classes, properties, etc. Performance is critical here. +fun foreignKotlinClass(jClass: Class): KClassImpl { + val name = jClass.getName() + val cached = FOREIGN_K_CLASSES[name] + if (cached is WeakReference<*>) { + val kClass = cached.get() as KClassImpl? + if (kClass?.jClass == jClass) { + return kClass!! + } + } + else if (cached != null) { + // If the cached value is not a weak reference, it's an array of weak references + cached as Array>> + for (ref in cached) { + val kClass = ref.get() + if (kClass?.jClass == jClass) { + return kClass!! + } + } + + // This is the most unlikely case: we found a cached array of references of length at least 2 (can't be 1 because + // the single element would be cached instead), and none of those classes is the one we're looking for + val size = cached.size + // Don't use Array constructor because it creates a lambda + val newArray = arrayOfNulls>>(size + 1) + // Don't use Arrays.copyOf because it works reflectively + System.arraycopy(cached, 0, newArray, 0, size) + val newKClass = KClassImpl(jClass) + newArray[size] = WeakReference(newKClass) + FOREIGN_K_CLASSES = FOREIGN_K_CLASSES.plus(name, newArray)!! + return newKClass + } + + val newKClass = KClassImpl(jClass) + FOREIGN_K_CLASSES = FOREIGN_K_CLASSES.plus(name, WeakReference(newKClass))!! + return newKClass +} diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/util.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/util.kt index 420555dafb1..1ea227e338a 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -16,10 +16,7 @@ package kotlin.reflect.jvm.internal -import java.lang.ref.WeakReference import java.lang.reflect.Method -import java.util.Arrays -import kotlin.reflect.jvm.internal.pcollections.HashPMap // TODO: use stdlib? suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") @@ -48,36 +45,6 @@ private fun Class<*>.getMaybeDeclaredMethod(name: String, vararg parameterTypes: } -// TODO: collect nulls periodically -private var FOREIGN_K_CLASSES = HashPMap.empty>>>()!! - -fun foreignKotlinClass(jClass: Class): KClassImpl { - val name = jClass.getName() - val cached = FOREIGN_K_CLASSES[name] - if (cached != null) { - var i = cached.size - 1 - while (i >= 0) { - val kClass = cached[i].get() - if (kClass?.jClass == jClass) { - return kClass as KClassImpl - } - i-- - } - - val newArray = Arrays.copyOf(cached, cached.size + 1) as Array>> - val newKClass = KClassImpl(jClass) - newArray[cached.size] = WeakReference(newKClass) - FOREIGN_K_CLASSES = FOREIGN_K_CLASSES.plus(name, newArray)!! - return newKClass - } - - val newKClass = KClassImpl(jClass) - val newArray = arrayOfNulls>>(1) - newArray[0] = WeakReference(newKClass) - FOREIGN_K_CLASSES = FOREIGN_K_CLASSES.plus(name, newArray as Array>>)!! - return newKClass -} - private val K_OBJECT_CLASS = Class.forName("kotlin.jvm.internal.KObject") fun kotlinClass(jClass: Class): KClassImpl {