Freeze objects by default, stdlib improvements. (#1743)
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
# Immutability in Kotlin/Native
|
||||
|
||||
Kotlin/Native implements strict mutability checks, ensuring
|
||||
important invariant that object is either immutable or
|
||||
accessible from the single thread at the moment (`mutable XOR global`).
|
||||
|
||||
Immutability is the runtime property in Kotlin/Native, and can be applied
|
||||
to an arbitrary object subgraph using `konan.worker.freeze` function.
|
||||
It makes all objects reachable from the given one immutable, and
|
||||
such a transition is a one way operation (object cannot be unfrozen later).
|
||||
Some naturally immutable objects, such as `kotlin.String`, `kotlin.Int` and
|
||||
other primitive types, along with `AtomicInt` and `AtomicReference` are frozen
|
||||
by default. If mutating operation is applied to a frozen object,
|
||||
an `InvalidMutabilityException` is thrown.
|
||||
|
||||
To achieve `mutable XOR global` invariant all globally visible state (currently,
|
||||
`object` singletons and enums) are automatically frozen. If an object freezing
|
||||
is not desirable, `konan.ThreadLocal` annotation could be used, which will make
|
||||
object state thread local, and thus, mutable (but changed state not visible to
|
||||
other threads).
|
||||
|
||||
Class `AtomicReference` could be used to publish changed frozen state to
|
||||
other threads, and thus build patterns like shared caches.
|
||||
|
||||
+1
-1
@@ -686,7 +686,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
|
||||
positionAtEnd(bbInit)
|
||||
val typeInfo = codegen.typeInfoForAllocation(descriptor)
|
||||
val defaultConstructor = descriptor.constructors.first { it.valueParameters.size == 0 }
|
||||
val defaultConstructor = descriptor.constructors.single { it.valueParameters.size == 0 }
|
||||
val ctor = codegen.llvmFunction(defaultConstructor)
|
||||
val (initFunction, args) =
|
||||
if (shared) {
|
||||
|
||||
+8
-2
@@ -38,13 +38,19 @@ import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.getArguments
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
|
||||
val IrClassSymbol.objectIsShared get() = owner.origin == DECLARATION_ORIGIN_ENUM
|
||||
private val threadLocalAnnotationFqName = FqName("konan.ThreadLocal")
|
||||
|
||||
val IrClassSymbol.objectIsShared get() =
|
||||
!owner.hasAnnotation(threadLocalAnnotationFqName)
|
||||
|
||||
internal fun emitLLVM(context: Context) {
|
||||
val irModule = context.irModule!!
|
||||
@@ -2662,4 +2668,4 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
|
||||
internal data class LocationInfo(val scope:DIScopeOpaqueRef,
|
||||
val line:Int,
|
||||
val column:Int)
|
||||
val column:Int)
|
||||
|
||||
-1
@@ -562,7 +562,6 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
||||
result.putValueArgument(1,
|
||||
IrGetValueImpl(startOffset, endOffset, ordinalParameter.type, ordinalParameter.symbol, origin)
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -155,7 +155,8 @@ internal class InitializersLowering(val context: CommonBackendContext) : ClassLo
|
||||
}
|
||||
|
||||
override fun visitConstructor(declaration: IrConstructor): IrStatement {
|
||||
val blockBody = declaration.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${declaration.body}")
|
||||
val blockBody = declaration.body as? IrBlockBody
|
||||
?: throw AssertionError("Unexpected constructor body: ${declaration.body}")
|
||||
|
||||
blockBody.statements.transformFlat {
|
||||
when {
|
||||
|
||||
@@ -671,6 +671,12 @@ task freeze2(type: RunKonanTest) {
|
||||
source = "runtime/workers/freeze2.kt"
|
||||
}
|
||||
|
||||
task freeze3(type: RunKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No exceptions on WASM.
|
||||
goldValue = "OK\n"
|
||||
source = "runtime/workers/freeze3.kt"
|
||||
}
|
||||
|
||||
task atomic0(type: RunKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // Workers need pthreads.
|
||||
goldValue = "35\n" + "20\n" + "OK\n"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package runtime.workers.freeze3
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import konan.worker.*
|
||||
|
||||
object Immutable {
|
||||
var x = 1
|
||||
}
|
||||
|
||||
@konan.ThreadLocal
|
||||
object Mutable {
|
||||
var x = 2
|
||||
}
|
||||
|
||||
@Test fun runTest() {
|
||||
assertEquals(1, Immutable.x)
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
Immutable.x++
|
||||
}
|
||||
assertEquals(1, Immutable.x)
|
||||
Mutable.x++
|
||||
assertEquals(3, Mutable.x)
|
||||
println("OK")
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace {
|
||||
ALWAYS_INLINE inline void mutabilityCheck(KConstRef thiz) {
|
||||
// TODO: optimize it!
|
||||
if (thiz->container()->frozen()) {
|
||||
ThrowInvalidMutabilityException();
|
||||
ThrowInvalidMutabilityException(thiz);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "Atomic.h"
|
||||
#include "Common.h"
|
||||
#include "Exceptions.h"
|
||||
#include "Memory.h"
|
||||
#include "Types.h"
|
||||
|
||||
@@ -44,8 +45,6 @@ inline AtomicReferenceLayout* asAtomicReference(KRef thiz) {
|
||||
|
||||
extern "C" {
|
||||
|
||||
RUNTIME_NORETURN void ThrowInvalidMutabilityException();
|
||||
|
||||
KInt Kotlin_AtomicInt_addAndGet(KRef thiz, KInt delta) {
|
||||
return addAndGetImpl(thiz, delta);
|
||||
}
|
||||
@@ -68,7 +67,7 @@ KNativePtr Kotlin_AtomicNativePtr_compareAndSwap(KRef thiz, KNativePtr expectedV
|
||||
|
||||
void Kotlin_AtomicReference_checkIfFrozen(KRef value) {
|
||||
if (value != nullptr && !value->container()->permanentOrFrozen()) {
|
||||
ThrowInvalidMutabilityException();
|
||||
ThrowInvalidMutabilityException(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ void RUNTIME_NORETURN ThrowNotImplementedError();
|
||||
// Throws illegal character conversion exception (used in UTF8/UTF16 conversions).
|
||||
void RUNTIME_NORETURN ThrowIllegalCharacterConversionException();
|
||||
void RUNTIME_NORETURN ThrowIllegalArgumentException();
|
||||
void RUNTIME_NORETURN ThrowInvalidMutabilityException();
|
||||
void RUNTIME_NORETURN ThrowInvalidMutabilityException(KConstRef where);
|
||||
// Prints out mesage of Throwable.
|
||||
void PrintThrowable(KRef);
|
||||
|
||||
|
||||
+122
-86
@@ -420,7 +420,6 @@ RUNTIME_USED ContainerHeader theStaticObjectsContainer = {
|
||||
void objc_release(void* ptr);
|
||||
void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
|
||||
RUNTIME_NORETURN void ThrowFreezingException();
|
||||
RUNTIME_NORETURN void ThrowInvalidMutabilityException();
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -1175,7 +1174,28 @@ OBJ_GETTER(InitInstance,
|
||||
OBJ_GETTER(InitSharedInstance,
|
||||
ObjHeader** location, ObjHeader** localLocation, const TypeInfo* type_info, void (*ctor)(ObjHeader*)) {
|
||||
#if KONAN_NO_THREADS
|
||||
RETURN_RESULT_OF(InitInstance, location, type_info, ctor);
|
||||
ObjHeader* value = *location;
|
||||
if (value != nullptr) {
|
||||
// OK'ish, inited by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
ObjHeader* object = AllocInstance(type_info, OBJ_RESULT);
|
||||
UpdateRef(location, object);
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
FreezeSubgraph(object);
|
||||
return object;
|
||||
#else
|
||||
try {
|
||||
ctor(object);
|
||||
FreezeSubgraph(object);
|
||||
return object;
|
||||
} catch (...) {
|
||||
UpdateRef(OBJ_RESULT, nullptr);
|
||||
UpdateRef(location, nullptr);
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ObjHeader* value = *localLocation;
|
||||
if (value != nullptr) RETURN_OBJ(value);
|
||||
@@ -1188,28 +1208,27 @@ OBJ_GETTER(InitSharedInstance,
|
||||
// OK'ish, inited by someone else.
|
||||
RETURN_OBJ(value);
|
||||
}
|
||||
|
||||
ObjHeader* object = AllocInstance(type_info, OBJ_RESULT);
|
||||
MEMORY_LOG("Calling UpdateRef from InitInstance\n")
|
||||
RuntimeAssert(object->container()->normal() , "Shared object cannot be co-allocated");
|
||||
MEMORY_LOG("Calling UpdateRef from InitSharedInstance\n")
|
||||
UpdateRef(localLocation, object);
|
||||
#if KONAN_NO_EXCEPTIONS
|
||||
ctor(object);
|
||||
if (!object->container()->frozen())
|
||||
ThrowFreezingException();
|
||||
FreezeSubgraph(object);
|
||||
UpdateRef(location, object);
|
||||
__sync_synchronize();
|
||||
return object;
|
||||
#else
|
||||
try {
|
||||
ctor(object);
|
||||
if (!object->container()->frozen())
|
||||
ThrowFreezingException();
|
||||
FreezeSubgraph(object);
|
||||
UpdateRef(location, object);
|
||||
__sync_synchronize();
|
||||
return object;
|
||||
} catch (...) {
|
||||
UpdateRef(OBJ_RESULT, nullptr);
|
||||
UpdateRef(location, nullptr);
|
||||
UpdateRef(localLocation, nullptr);
|
||||
__sync_synchronize();
|
||||
throw;
|
||||
}
|
||||
@@ -1550,6 +1569,91 @@ void traverseStronglyConnectedComponent(ContainerHeader* container,
|
||||
}
|
||||
}
|
||||
|
||||
void freezeAcyclic(ContainerHeader* rootContainer) {
|
||||
KStdDeque<ContainerHeader*> queue;
|
||||
queue.push_back(rootContainer);
|
||||
while (!queue.empty()) {
|
||||
ContainerHeader* current = queue.front();
|
||||
queue.pop_front();
|
||||
current->unMark();
|
||||
current->resetBuffered();
|
||||
current->setColor(CONTAINER_TAG_GC_BLACK);
|
||||
// Note, that once object is frozen, it could be concurrently accessed, so
|
||||
// color and similar attributes shall not be used.
|
||||
current->freeze();
|
||||
traverseContainerReferredObjects(current, [current, &queue](ObjHeader* obj) {
|
||||
ContainerHeader* objContainer = obj->container();
|
||||
if (!objContainer->permanentOrFrozen()) {
|
||||
if (objContainer->marked())
|
||||
queue.push_back(objContainer);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void freezeCyclic(ContainerHeader* rootContainer, const KStdVector<ContainerHeader*>& order) {
|
||||
KStdUnorderedMap<ContainerHeader*, KStdVector<ContainerHeader*>> reversedEdges;
|
||||
KStdDeque<ContainerHeader*> queue;
|
||||
queue.push_back(rootContainer);
|
||||
while (!queue.empty()) {
|
||||
ContainerHeader* current = queue.front();
|
||||
queue.pop_front();
|
||||
current->unMark();
|
||||
reversedEdges.emplace(current, KStdVector<ContainerHeader*>(0));
|
||||
traverseContainerReferredObjects(current, [current, &queue, &reversedEdges](ObjHeader* obj) {
|
||||
ContainerHeader* objContainer = obj->container();
|
||||
if (!objContainer->permanentOrFrozen()) {
|
||||
if (objContainer->marked())
|
||||
queue.push_back(objContainer);
|
||||
reversedEdges.emplace(objContainer, KStdVector<ContainerHeader*>(0)).first->second.push_back(current);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
KStdVector<KStdVector<ContainerHeader*>> components;
|
||||
MEMORY_LOG("Condensation:\n");
|
||||
// Enumerate in the topological order.
|
||||
for (auto it = order.rbegin(); it != order.rend(); ++it) {
|
||||
auto* container = *it;
|
||||
if (container->marked()) continue;
|
||||
KStdVector<ContainerHeader*> component;
|
||||
traverseStronglyConnectedComponent(container, reversedEdges, component);
|
||||
MEMORY_LOG("SCC:\n");
|
||||
#if TRACE_MEMORY
|
||||
for (auto c : component)
|
||||
konan::consolePrintf(" %p\n", c);
|
||||
#endif
|
||||
components.push_back(std::move(component));
|
||||
}
|
||||
|
||||
// Enumerate strongly connected components in reversed topological order.
|
||||
for (auto it = components.rbegin(); it != components.rend(); ++it) {
|
||||
auto& component = *it;
|
||||
int internalRefsCount = 0;
|
||||
int totalCount = 0;
|
||||
for (auto* container : component) {
|
||||
totalCount += container->refCount();
|
||||
traverseContainerReferredObjects(container, [&internalRefsCount](ObjHeader* obj) {
|
||||
if (!obj->container()->permanentOrFrozen())
|
||||
++internalRefsCount;
|
||||
});
|
||||
}
|
||||
// Create fictitious container for the whole component.
|
||||
auto superContainer = component.size() == 1 ? component[0] : AllocAggregatingFrozenContainer(component);
|
||||
// Don't count internal references.
|
||||
superContainer->setRefCount(totalCount - internalRefsCount);
|
||||
|
||||
// Freeze component.
|
||||
for (auto* container : component) {
|
||||
container->resetBuffered();
|
||||
container->setColor(CONTAINER_TAG_GC_BLACK);
|
||||
// Note, that once object is frozen, it could be concurrently accessed, so
|
||||
// color and similar attributes shall not be used.
|
||||
container->freeze();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Theory of operations.
|
||||
*
|
||||
@@ -1570,12 +1674,12 @@ void traverseStronglyConnectedComponent(ContainerHeader* container,
|
||||
* incoming references from the same strongly connected component are not counted)
|
||||
* - mark all object's headers as frozen
|
||||
*
|
||||
* Further reference counting on frozen objects is performed with the atomic operations, and so frozen
|
||||
* references could be passed accross multiple threads.
|
||||
* Further reference counting on frozen objects is performed with atomic operations, and so frozen
|
||||
* references could be passed across multiple threads.
|
||||
*/
|
||||
void FreezeSubgraph(ObjHeader* root) {
|
||||
// TODO: for now, we just check that passed object graph has no cycles, and throw an exception,
|
||||
// if it does. Next version will run Kosoraju-Sharir if cycles are found.
|
||||
// First check that passed object graph has no cycles.
|
||||
// If there are cycles - run graph condensation on cyclic graphs using Kosoraju-Sharir.
|
||||
ContainerHeader* rootContainer = root->container();
|
||||
if (rootContainer->permanentOrFrozen()) return;
|
||||
|
||||
@@ -1583,79 +1687,11 @@ void FreezeSubgraph(ObjHeader* root) {
|
||||
bool hasCycles = false;
|
||||
KStdVector<ContainerHeader*> order;
|
||||
depthFirstTraversal(rootContainer, &hasCycles, order);
|
||||
|
||||
KStdUnorderedMap<ContainerHeader*, KStdVector<ContainerHeader*>> reversedEdges;
|
||||
// Now unmark all marked objects, and freeze them, if no cycles detected.
|
||||
KStdDeque<ContainerHeader*> queue;
|
||||
queue.push_back(rootContainer);
|
||||
while (!queue.empty()) {
|
||||
ContainerHeader* current = queue.front();
|
||||
queue.pop_front();
|
||||
current->unMark();
|
||||
|
||||
if (hasCycles) {
|
||||
reversedEdges.emplace(current, KStdVector<ContainerHeader*>(0));
|
||||
} else {
|
||||
current->resetBuffered();
|
||||
current->setColor(CONTAINER_TAG_GC_BLACK);
|
||||
// Note, that once object is frozen, it could be concurrently accessed, so
|
||||
// color and similar attributes shall not be used.
|
||||
current->freeze();
|
||||
}
|
||||
traverseContainerReferredObjects(current, [hasCycles, current, &queue, &reversedEdges](ObjHeader* obj) {
|
||||
ContainerHeader* objContainer = obj->container();
|
||||
if (!objContainer->permanentOrFrozen()) {
|
||||
if (objContainer->marked())
|
||||
queue.push_back(objContainer);
|
||||
if (hasCycles)
|
||||
reversedEdges.emplace(objContainer, KStdVector<ContainerHeader*>(0)).first->second.push_back(current);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (hasCycles) {
|
||||
KStdVector<KStdVector<ContainerHeader*>> components;
|
||||
MEMORY_LOG("Condensation:\n");
|
||||
// Enumerate in topological order.
|
||||
for (auto it = order.rbegin(); it != order.rend(); ++it) {
|
||||
auto* container = *it;
|
||||
if (container->marked()) continue;
|
||||
KStdVector<ContainerHeader*> component;
|
||||
traverseStronglyConnectedComponent(container, reversedEdges, component);
|
||||
MEMORY_LOG("SCC:\n");
|
||||
#if TRACE_MEMORY
|
||||
for (auto c : component)
|
||||
konan::consolePrintf(" %p\n", c);
|
||||
#endif
|
||||
components.push_back(std::move(component));
|
||||
}
|
||||
// Enumerate strongly connected components in reversed topological order.
|
||||
for (auto it = components.rbegin(); it != components.rend(); ++it) {
|
||||
auto& component = *it;
|
||||
int internalRefsCount = 0;
|
||||
int totalCount = 0;
|
||||
for (auto* container : component) {
|
||||
totalCount += container->refCount();
|
||||
traverseContainerReferredObjects(container, [&internalRefsCount](ObjHeader* obj) {
|
||||
if (!obj->container()->permanentOrFrozen())
|
||||
++internalRefsCount;
|
||||
});
|
||||
}
|
||||
auto superContainer = component.size() == 1
|
||||
? component[0]
|
||||
: AllocAggregatingFrozenContainer(component); // Create fictitious container for the whole component.
|
||||
// Don't count internal references.
|
||||
superContainer->setRefCount(totalCount - internalRefsCount);
|
||||
|
||||
// Freeze component.
|
||||
for (auto* container : component) {
|
||||
container->resetBuffered();
|
||||
container->setColor(CONTAINER_TAG_GC_BLACK);
|
||||
// Note, that once object is frozen, it could be concurrently accessed, so
|
||||
// color and similar attributes shall not be used.
|
||||
container->freeze();
|
||||
}
|
||||
}
|
||||
freezeCyclic(rootContainer, order);
|
||||
} else {
|
||||
freezeAcyclic(rootContainer );
|
||||
}
|
||||
|
||||
// Now remove frozen objects from the toFree list.
|
||||
@@ -1671,14 +1707,14 @@ void FreezeSubgraph(ObjHeader* root) {
|
||||
// This function is called from field mutators to check if object's header is frozen.
|
||||
// If object is frozen, an exception is thrown.
|
||||
void MutationCheck(ObjHeader* obj) {
|
||||
if (obj->container()->frozen()) ThrowInvalidMutabilityException();
|
||||
if (obj->container()->frozen()) ThrowInvalidMutabilityException(obj);
|
||||
}
|
||||
|
||||
OBJ_GETTER(SwapRefLocked,
|
||||
ObjHeader** location, ObjHeader* expectedValue, ObjHeader* newValue, int32_t* spinlock) {
|
||||
lock(spinlock);
|
||||
ObjHeader* oldValue = *location;
|
||||
// We do not use UpdateRef() here to avoid having ReleaseRef() on return slot under lock.
|
||||
// We do not use UpdateRef() here to avoid having ReleaseRef() on return slot under the lock.
|
||||
if (oldValue == expectedValue) {
|
||||
SetRef(location, newValue);
|
||||
} else {
|
||||
@@ -1697,7 +1733,7 @@ OBJ_GETTER(SwapRefLocked,
|
||||
OBJ_GETTER(ReadRefLocked, ObjHeader** location, int32_t* spinlock) {
|
||||
lock(spinlock);
|
||||
ObjHeader* value = *location;
|
||||
// We do not use UpdateRef() here to avoid having ReleaseRef() on return slot under lock.
|
||||
// We do not use UpdateRef() here to avoid having ReleaseRef() on return slot under the lock.
|
||||
if (value != nullptr)
|
||||
AddRef(value);
|
||||
unlock(spinlock);
|
||||
|
||||
@@ -67,6 +67,10 @@ struct ContainerHeader {
|
||||
// Number of objects in the container.
|
||||
uint32_t objectCount_;
|
||||
|
||||
inline bool normal() const {
|
||||
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_NORMAL;
|
||||
}
|
||||
|
||||
inline bool permanent() const {
|
||||
return (refCount_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_PERMANENT;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ annotation class SymbolName(val name: String)
|
||||
annotation class ExportTypeInfo(val name: String)
|
||||
|
||||
/**
|
||||
* * If lambda shall be carefully lowered by the compiler.
|
||||
* If a lambda shall be carefully lowered by the compiler.
|
||||
*/
|
||||
annotation class VolatileLambda
|
||||
|
||||
@@ -61,36 +61,6 @@ public annotation class Used
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
public annotation class Throws(vararg val exceptionClasses: KClass<out Throwable>)
|
||||
|
||||
/**
|
||||
* Need to be fixed because of reification support.
|
||||
*/
|
||||
public annotation class FixmeReified
|
||||
|
||||
/**
|
||||
* Need to be fixed because of sorting support.
|
||||
*/
|
||||
public annotation class FixmeSorting
|
||||
|
||||
/**
|
||||
* Need to be fixed because of specialization support.
|
||||
*/
|
||||
public annotation class FixmeSpecialization
|
||||
|
||||
/**
|
||||
* Need to be fixed because of sequences support.
|
||||
*/
|
||||
public annotation class FixmeSequences
|
||||
|
||||
/**
|
||||
* Need to be fixed because of variance support.
|
||||
*/
|
||||
public annotation class FixmeVariance
|
||||
|
||||
/**
|
||||
* Need to be fixed because of regular expressions.
|
||||
*/
|
||||
public annotation class FixmeRegex
|
||||
|
||||
/**
|
||||
* Need to be fixed because of reflection.
|
||||
*/
|
||||
@@ -101,18 +71,29 @@ public annotation class FixmeReflection
|
||||
*/
|
||||
public annotation class FixmeConcurrency
|
||||
|
||||
public annotation class FixmeInline
|
||||
/**
|
||||
* Need to be fixed because of header/impl notation
|
||||
*/
|
||||
public annotation class FixmeMultiplatform
|
||||
|
||||
/**
|
||||
* Need to be fixed.
|
||||
* Need to be fixed because of random support.
|
||||
*/
|
||||
public annotation class Fixme
|
||||
public annotation class FixmeRandom
|
||||
|
||||
/**
|
||||
* Escape analysis annotations.
|
||||
*/
|
||||
public annotation class Escapes(val who: Int)
|
||||
|
||||
public annotation class PointsTo(vararg val onWhom: Int)
|
||||
|
||||
/**
|
||||
* Need to be fixed because of header/impl notation
|
||||
* Top level variable or object is thread local, and so could be mutable.
|
||||
* One may use this annotation as the stopgap measure for singleton
|
||||
* object immutability.
|
||||
* PLEASE NOTE THAT THIS ANNOTATION MAY GO AWAY IN UPCOMING RELEASES.
|
||||
*/
|
||||
public annotation class FixmeMultiplatform
|
||||
@Target(AnnotationTarget.FIELD, AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ThreadLocal
|
||||
@@ -26,7 +26,8 @@ public class FreezingException() : RuntimeException()
|
||||
/**
|
||||
* Exception thrown whenever we attempt to mutate frozen objects.
|
||||
*/
|
||||
public class InvalidMutabilityException() : RuntimeException()
|
||||
public class InvalidMutabilityException(where: Any) :
|
||||
RuntimeException("mutation attempt of frozen $where (hash is 0x${where.hashCode().toString(16)})")
|
||||
|
||||
/**
|
||||
* Freezes object subgraph reachable from this object. Frozen objects can be freely
|
||||
@@ -50,4 +51,4 @@ internal external fun isFrozenInternal(it: Any?): Boolean
|
||||
internal fun ThrowFreezingException(): Nothing = throw FreezingException()
|
||||
|
||||
@ExportForCppRuntime
|
||||
internal fun ThrowInvalidMutabilityException(): Nothing = throw InvalidMutabilityException()
|
||||
internal fun ThrowInvalidMutabilityException(where: Any): Nothing = throw InvalidMutabilityException(where)
|
||||
|
||||
@@ -18,10 +18,7 @@ package kotlin.collections
|
||||
|
||||
import kotlin.comparisons.*
|
||||
|
||||
|
||||
// copies typed varargs array to array of objects
|
||||
// TODO: generally wrong, wrt specialization.
|
||||
@FixmeSpecialization
|
||||
// Copies typed varargs array to an array of objects
|
||||
internal actual fun <T> Array<out T>.copyToArrayOfAny(isVarargs: Boolean): Array<out Any?> =
|
||||
if (isVarargs)
|
||||
// if the array came from varargs and already is array of Any, copying isn't required.
|
||||
@@ -232,6 +229,7 @@ public actual fun <T> MutableList<T>.fill(value: T): Unit {
|
||||
*
|
||||
* See: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
|
||||
*/
|
||||
@FixmeRandom
|
||||
@SinceKotlin("1.2")
|
||||
public actual fun <T> MutableList<T>.shuffle(): Unit {
|
||||
for (i in lastIndex downTo 1) {
|
||||
|
||||
@@ -64,11 +64,9 @@ public actual fun <R, T> (suspend R.() -> T).createCoroutineUnchecked(
|
||||
(this.create(receiver, completion) as CoroutineImpl).facade
|
||||
|
||||
// INTERNAL DEFINITIONS
|
||||
// TODO: uncomment as soon as inlining works for stdlib.
|
||||
@FixmeInline
|
||||
private /*inline*/ fun <T> buildContinuationByInvokeCall(
|
||||
private inline fun <T> buildContinuationByInvokeCall(
|
||||
completion: Continuation<T>,
|
||||
/*crossinline*/ block: () -> Any?
|
||||
crossinline block: () -> Any?
|
||||
): Continuation<Unit> {
|
||||
val continuation =
|
||||
object : Continuation<Unit> {
|
||||
|
||||
Reference in New Issue
Block a user