Object freezing API (#1390)
This commit is contained in:
+1
-2
@@ -395,8 +395,6 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
|
||||
private fun importRtFunction(name: String) = importFunction(name, runtime.llvmModule)
|
||||
|
||||
var globalInitIndex:Int = 0
|
||||
|
||||
val allocInstanceFunction = importRtFunction("AllocInstance")
|
||||
val allocArrayFunction = importRtFunction("AllocArrayInstance")
|
||||
val initInstanceFunction = importRtFunction("InitInstance")
|
||||
@@ -412,6 +410,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
val throwExceptionFunction = importRtFunction("ThrowException")
|
||||
val appendToInitalizersTail = importRtFunction("AppendToInitializersTail")
|
||||
val initRuntimeIfNeeded = importRtFunction("Kotlin_initRuntimeIfNeeded")
|
||||
val mutationCheck = importRtFunction("MutationCheck")
|
||||
|
||||
val createKotlinObjCClass by lazy { importRtFunction("CreateKotlinObjCClass") }
|
||||
val getObjCKotlinTypeInfo by lazy { importRtFunction("GetObjCKotlinTypeInfo") }
|
||||
|
||||
+10
-4
@@ -477,8 +477,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
|
||||
override fun visitConstructor(declaration: IrConstructor) {
|
||||
context.log{"visitConstructor : ${ir2string(declaration)}"}
|
||||
if (declaration.constructedClass.isIntrinsic) {
|
||||
// Do not generate any ctors for intrinsic classes.
|
||||
if (declaration.descriptor.containingDeclaration.defaultType.isValueType()) {
|
||||
// Do not generate any ctors for value types.
|
||||
return
|
||||
}
|
||||
|
||||
@@ -692,6 +692,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
override fun visitProperty(declaration: IrProperty) {
|
||||
val container = declaration.descriptor.containingDeclaration
|
||||
// For value types with real backing field there's no point to generate an accessor.
|
||||
if (container is ClassDescriptor && container.defaultType.isValueType() && declaration.backingField != null)
|
||||
return
|
||||
declaration.getter?.acceptVoid(this)
|
||||
declaration.setter?.acceptVoid(this)
|
||||
declaration.backingField?.acceptVoid(this)
|
||||
@@ -1330,9 +1334,11 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
private fun evaluateSetField(value: IrSetField): LLVMValueRef {
|
||||
context.log{"evaluateSetField : ${ir2string(value)}"}
|
||||
val valueToAssign = evaluateExpression(value.value)
|
||||
|
||||
if (value.descriptor.dispatchReceiverParameter != null) {
|
||||
val thisPtr = evaluateExpression(value.receiver!!)
|
||||
functionGenerationContext.call(context.llvm.mutationCheck,
|
||||
listOf(functionGenerationContext.bitcast(codegen.kObjHeaderPtr, thisPtr)),
|
||||
Lifetime.IRRELEVANT, ExceptionHandler.Caller)
|
||||
functionGenerationContext.storeAny(valueToAssign, fieldPtrOfClass(thisPtr, value.symbol.owner))
|
||||
}
|
||||
else {
|
||||
@@ -2424,7 +2430,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
private fun call(descriptor: FunctionDescriptor, function: LLVMValueRef, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime): LLVMValueRef {
|
||||
val result = call(function, args, resultLifetime)
|
||||
if (descriptor.returnType?.isNothing() == true) {
|
||||
if (descriptor.returnType.isNothing()) {
|
||||
functionGenerationContext.unreachable()
|
||||
}
|
||||
|
||||
|
||||
-3
@@ -111,9 +111,6 @@ private fun StaticData.getArrayListClass(): ClassDescriptor {
|
||||
internal fun StaticData.createArrayList(elementType: TypeProjection, array: ConstPointer, length: Int): ConstPointer {
|
||||
val arrayListClass = context.ir.symbols.arrayList.owner
|
||||
|
||||
// type is ArrayList<elementType>:
|
||||
val type = arrayListClass.defaultType.replace(listOf(elementType))
|
||||
|
||||
val arrayListFqName = arrayListClass.fqNameSafe
|
||||
val arrayListFields = mapOf(
|
||||
"$arrayListFqName.array" to array,
|
||||
|
||||
+2
-2
@@ -597,7 +597,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
||||
symbolTable.mapClass(owner),
|
||||
vtableIndex,
|
||||
arguments,
|
||||
symbolTable.mapType(callee.returnType!!),
|
||||
symbolTable.mapType(callee.returnType),
|
||||
value
|
||||
)
|
||||
}
|
||||
@@ -606,7 +606,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
||||
DataFlowIR.Node.StaticCall(
|
||||
symbolTable.mapFunction(actualCallee),
|
||||
arguments,
|
||||
symbolTable.mapType(actualCallee.returnType!!),
|
||||
symbolTable.mapType(actualCallee.returnType),
|
||||
actualCallee.dispatchReceiverParameter?.let { symbolTable.mapType(it.type) },
|
||||
value
|
||||
)
|
||||
|
||||
+2
-1
@@ -277,7 +277,8 @@ private fun IrElement.innerEndOffset(descriptor: DeclarationDescriptorWithSource
|
||||
descriptor.endOffset ?: this.endOffset
|
||||
|
||||
inline fun <reified T> stub(name: String): T {
|
||||
return Proxy.newProxyInstance(T::class.java.classLoader, arrayOf(T::class.java)) { proxy, method, methodArgs ->
|
||||
return Proxy.newProxyInstance(T::class.java.classLoader, arrayOf(T::class.java)) {
|
||||
_ /* proxy */, method, _ /* methodArgs */ ->
|
||||
if (method.name == "toString" && method.parameterCount == 0) {
|
||||
"${T::class.simpleName} stub for $name"
|
||||
} else {
|
||||
|
||||
@@ -610,6 +610,22 @@ task worker8(type: RunKonanTest) {
|
||||
source = "runtime/workers/worker8.kt"
|
||||
}
|
||||
|
||||
task freeze0(type: RunKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No workers on WASM.
|
||||
goldValue = "frozen bit is true\n" +
|
||||
"Worker: SharedData(string=Hello, int=10, member=SharedDataMember(double=0.1))\n" +
|
||||
"Main: SharedData(string=Hello, int=10, member=SharedDataMember(double=0.1))\n" +
|
||||
"OK\n"
|
||||
source = "runtime/workers/freeze0.kt"
|
||||
}
|
||||
|
||||
task freeze1(type: RunKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // No exceptions on WASM.
|
||||
goldValue = "OK, cannot freeze cyclic\n"+
|
||||
"OK, cannot mutate frozen\n"
|
||||
source = "runtime/workers/freeze1.kt"
|
||||
}
|
||||
|
||||
task superFunCall(type: RunKonanTest) {
|
||||
goldValue = "<fun:C><fun:C1>\n<fun:C><fun:C3>\n"
|
||||
source = "codegen/basics/superFunCall.kt"
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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 runtime.workers.freeze0
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import konan.worker.*
|
||||
|
||||
data class SharedDataMember(val double: Double)
|
||||
|
||||
data class SharedData(val string: String, val int: Int, val member: SharedDataMember)
|
||||
|
||||
@Test fun runTest() {
|
||||
val worker = startWorker()
|
||||
// Create immutable shared data.
|
||||
val immutable = SharedData("Hello", 10, SharedDataMember(0.1)).freeze()
|
||||
println("frozen bit is ${immutable.isFrozen}")
|
||||
|
||||
val future = worker.schedule(TransferMode.CHECKED, { immutable } ) {
|
||||
input ->
|
||||
println("Worker: $input")
|
||||
input
|
||||
}
|
||||
future.consume {
|
||||
result -> println("Main: $result")
|
||||
}
|
||||
worker.requestTermination().consume { _ -> }
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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 runtime.workers.freeze1
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import konan.worker.*
|
||||
|
||||
data class Node(var previous: Node?, var data: Int)
|
||||
|
||||
fun makeCycle(count: Int): Node {
|
||||
val first = Node(null, 0)
|
||||
var current = first
|
||||
for (index in 1 .. count - 1) {
|
||||
current = Node(current, index)
|
||||
}
|
||||
first.previous = current
|
||||
return first
|
||||
}
|
||||
|
||||
data class Node2(var leaf1: Node2?, var leaf2: Node2?)
|
||||
|
||||
fun makeDiamond(): Node2 {
|
||||
val bottom = Node2(null, null)
|
||||
val mid1prime = Node2(bottom, null)
|
||||
val mid1 = Node2(mid1prime, null)
|
||||
val mid2 = Node2(bottom, null)
|
||||
return Node2(mid1, mid2)
|
||||
}
|
||||
|
||||
@Test fun runTest() {
|
||||
try {
|
||||
makeCycle(10).freeze()
|
||||
} catch (e: FreezingException) {
|
||||
println("OK, cannot freeze cyclic")
|
||||
}
|
||||
|
||||
// Must be able to freeze diamond shaped graph.
|
||||
val diamond = makeDiamond().freeze()
|
||||
|
||||
val immutable = Node(null, 4).freeze()
|
||||
try {
|
||||
immutable.data = 42
|
||||
} catch (e: InvalidMutabilityException) {
|
||||
println("OK, cannot mutate frozen")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user