Object freezing API (#1390)

This commit is contained in:
Nikolay Igotti
2018-03-14 11:51:48 +03:00
committed by GitHub
parent 610c9b5278
commit a6fa19a7fd
12 changed files with 413 additions and 50 deletions
@@ -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") }
@@ -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()
}
@@ -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,
@@ -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
)
@@ -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 {
+16
View File
@@ -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")
}
}