Merge branch 'master' into inline
This commit is contained in:
-27
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.SharedVariablesManager
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
|
||||
interface BackendContext {
|
||||
val builtIns: KotlinBuiltIns
|
||||
val irBuiltIns: IrBuiltIns
|
||||
val sharedVariablesManager: SharedVariablesManager
|
||||
}
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
|
||||
interface FileLoweringPass {
|
||||
fun lower(irFile: IrFile)
|
||||
}
|
||||
|
||||
interface ClassLoweringPass {
|
||||
fun lower(irClass: IrClass)
|
||||
}
|
||||
|
||||
interface DeclarationContainerLoweringPass {
|
||||
fun lower(irDeclarationContainer: IrDeclarationContainer)
|
||||
}
|
||||
|
||||
interface FunctionLoweringPass {
|
||||
fun lower(irFunction: IrFunction)
|
||||
}
|
||||
|
||||
interface BodyLoweringPass {
|
||||
fun lower(irBody: IrBody)
|
||||
}
|
||||
|
||||
fun ClassLoweringPass.runOnFilePostfix(irFile: IrFile) {
|
||||
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
declaration.acceptChildrenVoid(this)
|
||||
lower(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun DeclarationContainerLoweringPass.asClassLoweringPass() = object : ClassLoweringPass {
|
||||
override fun lower(irClass: IrClass) {
|
||||
this@asClassLoweringPass.lower(irClass)
|
||||
}
|
||||
}
|
||||
|
||||
fun DeclarationContainerLoweringPass.runOnFilePostfix(irFile: IrFile) {
|
||||
this.asClassLoweringPass().runOnFilePostfix(irFile)
|
||||
this.lower(irFile)
|
||||
}
|
||||
|
||||
fun BodyLoweringPass.runOnFilePostfix(irFile: IrFile) {
|
||||
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitBody(body: IrBody) {
|
||||
body.acceptChildrenVoid(this)
|
||||
lower(body)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun FunctionLoweringPass.runOnFilePostfix(irFile: IrFile) {
|
||||
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
declaration.acceptChildrenVoid(this)
|
||||
lower(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrSetVariable
|
||||
|
||||
interface SharedVariablesManager {
|
||||
fun createSharedVariableDescriptor(variableDescriptor: VariableDescriptor): VariableDescriptor
|
||||
fun defineSharedValue(sharedVariableDescriptor: VariableDescriptor, originalDeclaration: IrVariable): IrStatement
|
||||
fun getSharedValue(sharedVariableDescriptor: VariableDescriptor, originalGet: IrGetValue): IrExpression
|
||||
fun setSharedValue(sharedVariableDescriptor: VariableDescriptor, originalSet: IrSetVariable): IrExpression
|
||||
}
|
||||
-371
@@ -1,371 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parents
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
class LocalFunctionsLowering(val context: BackendContext): DeclarationContainerLoweringPass {
|
||||
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
||||
irDeclarationContainer.declarations.transformFlat { memberDeclaration ->
|
||||
if (memberDeclaration is IrFunction)
|
||||
LocalFunctionsTransformer(memberDeclaration).lowerLocalFunctions()
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private class LocalFunctionContext(val declaration: IrFunction) {
|
||||
lateinit var closure: Closure
|
||||
|
||||
val closureParametersCount: Int get() = closure.capturedValues.size
|
||||
|
||||
lateinit var transformedDescriptor: FunctionDescriptor
|
||||
|
||||
val old2new: MutableMap<ValueDescriptor, ValueDescriptor> = HashMap()
|
||||
|
||||
var index: Int = -1
|
||||
|
||||
override fun toString(): String =
|
||||
"LocalFunctionContext for ${declaration.descriptor}"
|
||||
}
|
||||
|
||||
private inner class LocalFunctionsTransformer(val memberFunction: IrFunction) {
|
||||
val localFunctions: MutableMap<FunctionDescriptor, LocalFunctionContext> = LinkedHashMap()
|
||||
val new2old: MutableMap<ValueDescriptor, ValueDescriptor> = HashMap()
|
||||
|
||||
fun lowerLocalFunctions(): List<IrDeclaration>? {
|
||||
collectLocalFunctions()
|
||||
if (localFunctions.isEmpty()) return null
|
||||
|
||||
collectClosures()
|
||||
|
||||
transformDescriptors()
|
||||
|
||||
rewriteBodies()
|
||||
|
||||
return collectRewrittenDeclarations()
|
||||
}
|
||||
|
||||
private fun collectRewrittenDeclarations(): ArrayList<IrDeclaration> =
|
||||
ArrayList<IrDeclaration>(localFunctions.size + 1).apply {
|
||||
add(memberFunction)
|
||||
|
||||
localFunctions.values.mapTo(this) {
|
||||
val original = it.declaration
|
||||
IrFunctionImpl(
|
||||
original.startOffset, original.endOffset, original.origin,
|
||||
it.transformedDescriptor,
|
||||
original.body
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class FunctionBodiesRewriter(val localFunctionContext: LocalFunctionContext?) : IrElementTransformerVoid() {
|
||||
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
// ignore local classes for now
|
||||
return declaration
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||
// replace local function definition with an empty composite
|
||||
return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.builtIns.unitType)
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
val remapped = localFunctionContext?.let { it.old2new[expression.descriptor] }
|
||||
return if (remapped == null)
|
||||
expression
|
||||
else
|
||||
IrGetValueImpl(expression.startOffset, expression.endOffset, remapped, expression.origin)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val oldCallee = expression.descriptor.original
|
||||
val localFunctionData = localFunctions[oldCallee] ?: return expression
|
||||
|
||||
val newCallee = localFunctionData.transformedDescriptor
|
||||
|
||||
val newCall = createNewCall(expression, newCallee).fillArguments(localFunctionData, expression)
|
||||
|
||||
return newCall
|
||||
}
|
||||
|
||||
private fun <T : IrMemberAccessExpression> T.fillArguments(calleeContext: LocalFunctionContext, oldExpression: IrMemberAccessExpression): T {
|
||||
val closureParametersCount = calleeContext.closureParametersCount
|
||||
|
||||
mapValueParametersIndexed { index, newValueParameterDescriptor ->
|
||||
val capturedValueDescriptor = new2old[newValueParameterDescriptor] ?:
|
||||
throw AssertionError("Non-mapped parameter $newValueParameterDescriptor")
|
||||
if (index >= closureParametersCount)
|
||||
oldExpression.getValueArgument(capturedValueDescriptor as ValueParameterDescriptor)
|
||||
else {
|
||||
val remappedValueDescriptor = localFunctionContext?.let { it.old2new[capturedValueDescriptor] }
|
||||
IrGetValueImpl(oldExpression.startOffset, oldExpression.endOffset,
|
||||
remappedValueDescriptor ?: capturedValueDescriptor)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispatchReceiver = oldExpression.dispatchReceiver
|
||||
extensionReceiver = oldExpression.extensionReceiver
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
override fun visitCallableReference(expression: IrCallableReference): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val oldCallee = expression.descriptor.original
|
||||
val localFunctionData = localFunctions[oldCallee] ?: return expression
|
||||
val newCallee = localFunctionData.transformedDescriptor
|
||||
|
||||
val newCallableReference = IrCallableReferenceImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
expression.type, // TODO functional type for transformed descriptor
|
||||
newCallee,
|
||||
remapTypeArguments(expression, newCallee),
|
||||
expression.origin
|
||||
).fillArguments(localFunctionData, expression)
|
||||
|
||||
return newCallableReference
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val oldReturnTarget = expression.returnTarget
|
||||
val localFunctionData = localFunctions[oldReturnTarget] ?: return expression
|
||||
val newReturnTarget = localFunctionData.transformedDescriptor
|
||||
|
||||
return IrReturnImpl(expression.startOffset, expression.endOffset, newReturnTarget, expression.value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewriteFunctionDeclaration(irFunction: IrFunction, localFunctionContext: LocalFunctionContext?) {
|
||||
irFunction.transformChildrenVoid(FunctionBodiesRewriter(localFunctionContext))
|
||||
}
|
||||
|
||||
private fun rewriteBodies() {
|
||||
localFunctions.values.forEach {
|
||||
rewriteFunctionDeclaration(it.declaration, it)
|
||||
}
|
||||
|
||||
rewriteFunctionDeclaration(memberFunction, null)
|
||||
}
|
||||
|
||||
private fun createNewCall(oldCall: IrCall, newCallee: FunctionDescriptor) =
|
||||
if (oldCall is IrCallWithShallowCopy)
|
||||
oldCall.shallowCopy(oldCall.origin, newCallee, oldCall.superQualifier)
|
||||
else
|
||||
IrCallImpl(
|
||||
oldCall.startOffset, oldCall.endOffset,
|
||||
newCallee,
|
||||
remapTypeArguments(oldCall, newCallee),
|
||||
oldCall.origin, oldCall.superQualifier
|
||||
)
|
||||
|
||||
private fun remapTypeArguments(oldExpression: IrMemberAccessExpression, newCallee: FunctionDescriptor): Map<TypeParameterDescriptor, KotlinType>? {
|
||||
val oldCallee = oldExpression.descriptor
|
||||
|
||||
return if (oldCallee.typeParameters.isEmpty())
|
||||
null
|
||||
else oldCallee.typeParameters.associateBy(
|
||||
{ newCallee.typeParameters[it.index] },
|
||||
{ oldExpression.getTypeArgument(it)!! }
|
||||
)
|
||||
}
|
||||
|
||||
private fun transformDescriptors() {
|
||||
localFunctions.values.forEach {
|
||||
it.transformedDescriptor = createTransformedDescriptor(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun suggestLocalName(descriptor: DeclarationDescriptor): String {
|
||||
val localFunctionContext = localFunctions[descriptor]
|
||||
return if (localFunctionContext != null && localFunctionContext.index >= 0)
|
||||
"lambda-${localFunctionContext.index}"
|
||||
else
|
||||
descriptor.name.asString()
|
||||
}
|
||||
|
||||
private fun generateNameForLiftedFunction(functionDescriptor: FunctionDescriptor): Name =
|
||||
Name.identifier(
|
||||
functionDescriptor.parentsWithSelf
|
||||
.takeWhile { it is FunctionDescriptor }
|
||||
.toList().reversed()
|
||||
.map { suggestLocalName(it) }
|
||||
.joinToString(separator = "$")
|
||||
)
|
||||
|
||||
private fun createTransformedDescriptor(localFunctionContext: LocalFunctionContext): FunctionDescriptor {
|
||||
val oldDescriptor = localFunctionContext.declaration.descriptor
|
||||
|
||||
val memberOwner = memberFunction.descriptor.containingDeclaration
|
||||
val newDescriptor = SimpleFunctionDescriptorImpl.create(
|
||||
memberOwner,
|
||||
oldDescriptor.annotations,
|
||||
generateNameForLiftedFunction(oldDescriptor),
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
oldDescriptor.source
|
||||
)
|
||||
|
||||
val closureParametersCount = localFunctionContext.closureParametersCount
|
||||
val newValueParametersCount = closureParametersCount + oldDescriptor.valueParameters.size
|
||||
|
||||
val newDispatchReceiverParameter =
|
||||
if (memberOwner is ClassDescriptor && oldDescriptor.dispatchReceiverParameter != null)
|
||||
memberOwner.thisAsReceiverParameter
|
||||
else
|
||||
null
|
||||
|
||||
// Do not substitute type parameters for now.
|
||||
val newTypeParameters = oldDescriptor.typeParameters
|
||||
|
||||
val newValueParameters = ArrayList<ValueParameterDescriptor>(newValueParametersCount).apply {
|
||||
localFunctionContext.closure.capturedValues.mapIndexedTo(this) { i, capturedValueDescriptor ->
|
||||
createUnsubstitutedCapturedValueParameter(newDescriptor, capturedValueDescriptor, i).apply {
|
||||
localFunctionContext.recordRemapped(capturedValueDescriptor, this)
|
||||
}
|
||||
}
|
||||
|
||||
oldDescriptor.valueParameters.mapIndexedTo(this) { i, oldValueParameterDescriptor ->
|
||||
createUnsubstitutedParameter(newDescriptor, oldValueParameterDescriptor, closureParametersCount + i).apply {
|
||||
localFunctionContext.recordRemapped(oldValueParameterDescriptor, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newDescriptor.initialize(
|
||||
oldDescriptor.extensionReceiverParameter?.type,
|
||||
newDispatchReceiverParameter,
|
||||
newTypeParameters,
|
||||
newValueParameters,
|
||||
oldDescriptor.returnType,
|
||||
Modality.FINAL,
|
||||
Visibilities.PRIVATE
|
||||
)
|
||||
|
||||
oldDescriptor.extensionReceiverParameter?.let {
|
||||
localFunctionContext.recordRemapped(it, newDescriptor.extensionReceiverParameter!!)
|
||||
}
|
||||
|
||||
return newDescriptor
|
||||
}
|
||||
|
||||
private fun LocalFunctionContext.recordRemapped(oldDescriptor: ValueDescriptor, newDescriptor: ValueDescriptor): ValueDescriptor {
|
||||
old2new[oldDescriptor] = newDescriptor
|
||||
new2old[newDescriptor] = oldDescriptor
|
||||
return newDescriptor
|
||||
}
|
||||
|
||||
private fun suggestNameForCapturedValueParameter(valueDescriptor: ValueDescriptor): Name =
|
||||
if (valueDescriptor.name.isSpecial) {
|
||||
val oldNameStr = valueDescriptor.name.asString()
|
||||
Name.identifier("$" + oldNameStr.substring(1, oldNameStr.length - 1))
|
||||
}
|
||||
else
|
||||
valueDescriptor.name
|
||||
|
||||
private fun createUnsubstitutedCapturedValueParameter(
|
||||
newParameterOwner: CallableMemberDescriptor,
|
||||
valueDescriptor: ValueDescriptor,
|
||||
index: Int
|
||||
): ValueParameterDescriptor =
|
||||
ValueParameterDescriptorImpl(
|
||||
newParameterOwner, null, index,
|
||||
valueDescriptor.annotations,
|
||||
suggestNameForCapturedValueParameter(valueDescriptor),
|
||||
valueDescriptor.type,
|
||||
false, false, false, null, valueDescriptor.source
|
||||
)
|
||||
|
||||
private fun createUnsubstitutedParameter(
|
||||
newParameterOwner: CallableMemberDescriptor,
|
||||
valueParameterDescriptor: ValueParameterDescriptor,
|
||||
newIndex: Int
|
||||
): ValueParameterDescriptor =
|
||||
valueParameterDescriptor.copy(newParameterOwner, valueParameterDescriptor.name, newIndex)
|
||||
|
||||
|
||||
private fun collectClosures() {
|
||||
memberFunction.acceptChildrenVoid(object : AbstractClosureAnnotator() {
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
// ignore local classes for now
|
||||
return
|
||||
}
|
||||
|
||||
override fun recordFunctionClosure(functionDescriptor: FunctionDescriptor, closure: Closure) {
|
||||
localFunctions[functionDescriptor]?.closure = closure
|
||||
}
|
||||
|
||||
override fun recordClassClosure(classDescriptor: ClassDescriptor, closure: Closure) {
|
||||
// ignore local classes for now
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun collectLocalFunctions() {
|
||||
memberFunction.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
var lambdasCount = 0
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
declaration.acceptChildrenVoid(this)
|
||||
val localFunctionContext = LocalFunctionContext(declaration)
|
||||
localFunctions[declaration.descriptor] = localFunctionContext
|
||||
if (declaration.descriptor.name.isSpecial) {
|
||||
localFunctionContext.index = lambdasCount++
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
// ignore local classes for now
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-5
@@ -5,16 +5,13 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
class IrLoweringContext(backendContext: BackendContext) : GeneratorContext {
|
||||
override val irBuiltIns: IrBuiltIns = backendContext.irBuiltIns
|
||||
}
|
||||
class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns)
|
||||
|
||||
class FunctionIrGenerator(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : GeneratorWithScope {
|
||||
class FunctionIrGenerator(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : IrGeneratorWithScope {
|
||||
override val context = IrLoweringContext(backendContext)
|
||||
override val scope = Scope(functionDescriptor)
|
||||
}
|
||||
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrSetVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import java.util.*
|
||||
|
||||
class SharedVariablesLowering(val context: BackendContext) : FunctionLoweringPass {
|
||||
override fun lower(irFunction: IrFunction) {
|
||||
SharedVariablesTransformer(irFunction).lowerSharedVariables()
|
||||
}
|
||||
|
||||
private inner class SharedVariablesTransformer(val irFunction: IrFunction) {
|
||||
val sharedVariables = HashSet<ValueDescriptor>()
|
||||
|
||||
fun lowerSharedVariables() {
|
||||
collectSharedVariables()
|
||||
if (sharedVariables.isEmpty()) return
|
||||
|
||||
rewriteSharedVariables()
|
||||
}
|
||||
|
||||
private fun collectSharedVariables() {
|
||||
irFunction.acceptVoid(object : IrElementVisitorVoid {
|
||||
val declarationsStack = ArrayDeque<IrDeclaration>()
|
||||
val currentDeclaration: DeclarationDescriptor
|
||||
get() = declarationsStack.peek().descriptor
|
||||
|
||||
val relevantVars = HashSet<VariableDescriptor>()
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
declarationsStack.push(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
declarationsStack.pop()
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable) {
|
||||
declaration.acceptChildrenVoid(this)
|
||||
|
||||
val variableDescriptor = declaration.descriptor
|
||||
if (variableDescriptor.isVar) {
|
||||
relevantVars.add(variableDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitVariableAccess(expression: IrValueAccessExpression) {
|
||||
expression.acceptChildrenVoid(this)
|
||||
|
||||
val descriptor = expression.descriptor
|
||||
if (descriptor in relevantVars && descriptor.containingDeclaration != currentDeclaration) {
|
||||
sharedVariables.add(descriptor)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun rewriteSharedVariables() {
|
||||
val transformedDescriptors = HashMap<ValueDescriptor, VariableDescriptor>()
|
||||
|
||||
irFunction.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
val oldDescriptor = declaration.descriptor
|
||||
if (oldDescriptor !in sharedVariables) return declaration
|
||||
|
||||
val newDescriptor = context.sharedVariablesManager.createSharedVariableDescriptor(oldDescriptor)
|
||||
transformedDescriptors[oldDescriptor] = newDescriptor
|
||||
|
||||
return context.sharedVariablesManager.defineSharedValue(newDescriptor, declaration)
|
||||
}
|
||||
})
|
||||
|
||||
irFunction.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val newDescriptor = getTransformedDescriptor(expression.descriptor) ?: return expression
|
||||
|
||||
return context.sharedVariablesManager.getSharedValue(newDescriptor, expression)
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val newDescriptor = getTransformedDescriptor(expression.descriptor) ?: return expression
|
||||
|
||||
return context.sharedVariablesManager.setSharedValue(newDescriptor, expression)
|
||||
}
|
||||
|
||||
private fun getTransformedDescriptor(oldDescriptor: ValueDescriptor): VariableDescriptor? =
|
||||
transformedDescriptors.getOrElse(oldDescriptor) {
|
||||
assert(oldDescriptor !in sharedVariables) {
|
||||
"Shared variable is not transformed: $oldDescriptor"
|
||||
}
|
||||
null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -17,6 +17,10 @@ internal class KonanLower(val context: Context) {
|
||||
fun lower(irFile: IrFile) {
|
||||
val phaser = PhaseManager(context)
|
||||
|
||||
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
|
||||
DefaultParameterStubGenerator(context).runOnFilePostfix(irFile)
|
||||
DefaultParameterInjector(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) {
|
||||
BuiltinOperatorLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
|
||||
+1
@@ -11,6 +11,7 @@ enum class KonanPhase(val description: String,
|
||||
/* */ BACKEND("All backend"),
|
||||
/* ... */ LOWER("IR Lowering"),
|
||||
/* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering"),
|
||||
/* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering"),
|
||||
/* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering"),
|
||||
/* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering"),
|
||||
/* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering"),
|
||||
|
||||
+2
-2
@@ -18,8 +18,8 @@ package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.KonanPlatformConfigurator
|
||||
import org.jetbrains.kotlin.descriptors.PlatformKind
|
||||
import org.jetbrains.kotlin.resolve.ImportPath
|
||||
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.PlatformConfigurator
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
@@ -35,7 +35,7 @@ class KonanBuiltIns: KotlinBuiltIns {
|
||||
}
|
||||
|
||||
object KonanPlatform : TargetPlatform("Konan") {
|
||||
override val kind = PlatformKind.DEFAULT /* TODO: native */
|
||||
override val multiTargetPlatform = MultiTargetPlatform.Specific(platformName)
|
||||
override val defaultImports: List<ImportPath> = Default.defaultImports + listOf(
|
||||
ImportPath("kotlin.*"),
|
||||
ImportPath("kotlin.collections.*"),
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ internal class MacOSPlatform(distrib: Distribution,
|
||||
|
||||
override val linkerOptimizationFlags =
|
||||
properties.propertyList("linkerOptimizationFlags.osx")
|
||||
override val linkerKonanFlags = listOf(distrib.libCppAbi)
|
||||
override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.osx")
|
||||
override val linker = "${distrib.sysRoot}/usr/bin/ld"
|
||||
|
||||
override fun linkCommand(objectFiles: List<String>, executable: String, optimize: Boolean): List<String> {
|
||||
|
||||
+2
-1
@@ -31,7 +31,8 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProvid
|
||||
|
||||
object TopDownAnalyzerFacadeForKonan {
|
||||
fun analyzeFiles(files: Collection<KtFile>, config: KonanConfig): AnalysisResult {
|
||||
val context = ContextForNewModule(ProjectContext(config.project), Name.special("<${config.moduleId}>"), KonanPlatform.builtIns)
|
||||
val context = ContextForNewModule(ProjectContext(config.project), Name.special("<${config.moduleId}>"),
|
||||
KonanPlatform.builtIns, null)
|
||||
|
||||
val builtinsForCompilerModule = KonanBuiltIns(context.storageManager, true)
|
||||
val compilerBuiltInsModule = builtinsForCompilerModule.builtInsModule
|
||||
|
||||
+61
-1
@@ -1,6 +1,8 @@
|
||||
package org.jetbrains.kotlin.backend.konan.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
@@ -103,7 +105,7 @@ internal fun KonanBuiltIns.getKonanInternalClassOrNull(name: String): ClassDescr
|
||||
* @return built-in class `konan.internal.$name`
|
||||
*/
|
||||
internal fun KonanBuiltIns.getKonanInternalClass(name: String): ClassDescriptor =
|
||||
getKonanInternalClassOrNull(name)!!
|
||||
getKonanInternalClassOrNull(name) ?: TODO(name)
|
||||
|
||||
internal fun KonanBuiltIns.getKonanInternalFunctions(name: String): List<FunctionDescriptor> {
|
||||
return konanInternal.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).toList()
|
||||
@@ -149,3 +151,61 @@ internal val FunctionDescriptor.isFunctionInvoke: Boolean
|
||||
}
|
||||
|
||||
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
|
||||
|
||||
internal val ClassDescriptor.сontributedMethods: List<FunctionDescriptor>
|
||||
get() {
|
||||
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
|
||||
// (includes declarations from supers)
|
||||
|
||||
val functions = contributedDescriptors.filterIsInstance<FunctionDescriptor>()
|
||||
|
||||
val properties = contributedDescriptors.filterIsInstance<PropertyDescriptor>()
|
||||
val getters = properties.mapNotNull { it.getter }
|
||||
val setters = properties.mapNotNull { it.setter }
|
||||
|
||||
val allMethods = (functions + getters + setters).sortedBy {
|
||||
// TODO: use local hash instead, but it needs major refactoring.
|
||||
it.functionName.hashCode()
|
||||
}
|
||||
return allMethods
|
||||
}
|
||||
|
||||
fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
|
||||
|
||||
// TODO: optimize
|
||||
val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
|
||||
get() {
|
||||
assert(!this.isInterface)
|
||||
|
||||
val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(this)) {
|
||||
emptyList()
|
||||
} else {
|
||||
this.getSuperClassOrAny().vtableEntries
|
||||
}
|
||||
|
||||
val methods = this.сontributedMethods
|
||||
|
||||
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
|
||||
methods.single { OverridingUtil.overrides(it, superMethod) }
|
||||
}
|
||||
|
||||
return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable }
|
||||
}
|
||||
|
||||
val ClassDescriptor.vtableSize: Int
|
||||
get() = if (this.isAbstract()) 0 else this.vtableEntries.size
|
||||
|
||||
fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
|
||||
this.vtableEntries.forEachIndexed { index, functionDescriptor ->
|
||||
if (functionDescriptor == function.original) return index
|
||||
}
|
||||
throw Error(function.toString() + " not in vtable of " + this.toString())
|
||||
}
|
||||
|
||||
val ClassDescriptor.methodTableEntries: List<FunctionDescriptor>
|
||||
get() {
|
||||
assert (!this.isAbstract())
|
||||
|
||||
return this.сontributedMethods.filter { it.isOverridableOrOverrides }
|
||||
// TODO: probably method table should contain all accessible methods to improve binary compatibility
|
||||
}
|
||||
+14
-11
@@ -7,6 +7,7 @@ import org.jetbrains.kotlin.backend.konan.Distribution
|
||||
import org.jetbrains.kotlin.backend.konan.hash.GlobalHash
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.backingField
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.vtableSize
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
@@ -114,22 +115,24 @@ internal interface ContextUtils {
|
||||
return constValue(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pointer to struct { TypeInfo, vtable }.
|
||||
*/
|
||||
val ClassDescriptor.typeInfoWithVtable: ConstPointer
|
||||
get() {
|
||||
val type = structType(runtime.typeInfoType, LLVMArrayType(int8TypePtr, this.vtableSize)!!)
|
||||
return constPointer(externalGlobal(this.typeInfoSymbolName, type))
|
||||
}
|
||||
|
||||
val ClassDescriptor.typeInfoPtr: ConstPointer
|
||||
get() = typeInfoWithVtable.getElementPtr(0)
|
||||
|
||||
/**
|
||||
* Pointer to type info for given class.
|
||||
* It may be declared as pointer to external variable.
|
||||
*/
|
||||
val ClassDescriptor.llvmTypeInfoPtr: LLVMValueRef
|
||||
get() {
|
||||
val module = context.llvmModule
|
||||
val globalName = this.typeInfoSymbolName
|
||||
val globalPtr = LLVMGetNamedGlobal(module, globalName) ?:
|
||||
LLVMAddGlobal(module, runtime.typeInfoType, globalName)!!
|
||||
|
||||
return globalPtr
|
||||
}
|
||||
|
||||
val ClassDescriptor.typeInfoPtr: ConstPointer
|
||||
get() = constPointer(this.llvmTypeInfoPtr)
|
||||
get() = typeInfoPtr.llvm
|
||||
|
||||
/**
|
||||
* Pointer to type info for this type, or `null` if the type doesn't have corresponding type info.
|
||||
|
||||
+28
-22
@@ -1337,11 +1337,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
private fun evaluateGetField(value: IrGetField): LLVMValueRef {
|
||||
context.log("evaluateGetField : ${ir2string(value)}")
|
||||
if (value.descriptor.dispatchReceiverParameter != null) {
|
||||
val thisPtr = instanceFieldAccessReceiver(value)
|
||||
val thisPtr = evaluateExpression(value.receiver!!)
|
||||
return codegen.loadSlot(
|
||||
fieldPtrOfClass(thisPtr, value.descriptor), value.descriptor.isVar())
|
||||
}
|
||||
else {
|
||||
assert (value.receiver == null)
|
||||
val ptr = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)!!
|
||||
return codegen.loadSlot(ptr, value.descriptor.isVar())
|
||||
}
|
||||
@@ -1362,26 +1363,15 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun instanceFieldAccessReceiver(expression: IrFieldAccessExpression): LLVMValueRef {
|
||||
val receiverExpression = expression.receiver
|
||||
if (receiverExpression != null) {
|
||||
return evaluateExpression(receiverExpression)
|
||||
} else {
|
||||
val classDescriptor = expression.descriptor.containingDeclaration as ClassDescriptor
|
||||
return currentCodeContext.genGetValue(classDescriptor.thisAsReceiverParameter)
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateSetField(value: IrSetField): LLVMValueRef {
|
||||
context.log("evaluateSetField : ${ir2string(value)}")
|
||||
val valueToAssign = evaluateExpression(value.value)
|
||||
if (value.descriptor.dispatchReceiverParameter != null) {
|
||||
val thisPtr = instanceFieldAccessReceiver(value)
|
||||
val thisPtr = evaluateExpression(value.receiver!!)
|
||||
codegen.storeAnyGlobal(valueToAssign, fieldPtrOfClass(thisPtr, value.descriptor))
|
||||
}
|
||||
else {
|
||||
assert (value.receiver == null)
|
||||
val globalValue = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)
|
||||
codegen.storeAnyGlobal(valueToAssign, globalValue!!)
|
||||
}
|
||||
@@ -1741,7 +1731,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val typeInfo = codegen.typeInfoValue(containingClass)
|
||||
val allocHint = Int32(1).llvm
|
||||
val thisValue = if (containingClass.isArray) {
|
||||
assert(args.size == 1 && args[0].type == int32Type)
|
||||
assert(args.size >= 1 && args[0].type == int32Type)
|
||||
val allocArrayInstanceArgs = listOf(typeInfo, allocHint, args[0])
|
||||
call(context.llvm.allocArrayFunction, allocArrayInstanceArgs)
|
||||
} else {
|
||||
@@ -1854,14 +1844,30 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
val thisI8PtrPtr = codegen.bitcast(kInt8PtrPtr, args[0]) // Cast "this (i8*)" to i8**.
|
||||
val typeInfoI8Ptr = codegen.load(thisI8PtrPtr) // Load TypeInfo address.
|
||||
val typeInfoPtr = codegen.bitcast(codegen.kTypeInfoPtr, typeInfoI8Ptr) // Cast TypeInfo (i8*) to TypeInfo*.
|
||||
val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked
|
||||
val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup
|
||||
val llvmMethod = call(context.llvm.lookupOpenMethodFunction,
|
||||
lookupArgs) // Get method ptr to be invoked
|
||||
val typeInfoPtrPtr = LLVMBuildStructGEP(codegen.builder, args[0], 0 /* type_info */, "")!!
|
||||
val typeInfoPtr = codegen.load(typeInfoPtrPtr)
|
||||
assert (typeInfoPtr.type == codegen.kTypeInfoPtr)
|
||||
|
||||
val owner = descriptor.containingDeclaration as ClassDescriptor
|
||||
val llvmMethod = if (!owner.isInterface) {
|
||||
// If this is a virtual method of the class - we can call via vtable.
|
||||
val index = owner.vtableIndex(descriptor)
|
||||
|
||||
val vtablePlace = codegen.gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1
|
||||
val vtable = codegen.bitcast(kInt8PtrPtr, vtablePlace)
|
||||
|
||||
val slot = codegen.gep(vtable, Int32(index).llvm)
|
||||
codegen.load(slot)
|
||||
} else {
|
||||
// Otherwise, call via hashtable.
|
||||
// TODO: optimize by storing interface number in lower bits of 'this' pointer
|
||||
// when passing object as an interface. This way we can use those bits as index
|
||||
// for an additional per-interface vtable.
|
||||
val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked
|
||||
val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup
|
||||
call(context.llvm.lookupOpenMethodFunction,
|
||||
lookupArgs)
|
||||
}
|
||||
val functionPtrType = pointerType(codegen.getLlvmFunctionType(descriptor)) // Construct type of the method to be invoked
|
||||
val function = codegen.bitcast(functionPtrType, llvmMethod) // Cast method address to the type
|
||||
return call(descriptor, function, args) // Invoke the method
|
||||
|
||||
+3
-2
@@ -150,8 +150,9 @@ internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef = memScoped {
|
||||
}
|
||||
|
||||
internal fun ContextUtils.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef {
|
||||
val returnType = if (function is ConstructorDescriptor) voidType else getLLVMReturnType(function.returnType!!)
|
||||
val paramTypes = ArrayList(function.allValueParameters.map { getLLVMType(it.type) })
|
||||
val original = function.original
|
||||
val returnType = if (original is ConstructorDescriptor) voidType else getLLVMReturnType(original.returnType!!)
|
||||
val paramTypes = ArrayList(original.allValueParameters.map { getLLVMType(it.type) })
|
||||
if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr)
|
||||
|
||||
memScoped {
|
||||
|
||||
+7
-5
@@ -1,13 +1,15 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind.*
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
@@ -21,7 +23,7 @@ private val exportForCppRuntimeAnnotation = FqName("konan.internal.ExportForCppR
|
||||
fun typeToHashString(type: KotlinType): String {
|
||||
if (TypeUtils.isTypeParameter(type)) return "GENERIC"
|
||||
|
||||
var hashString = type.constructor.toString()
|
||||
var hashString = TypeUtils.getClassDescriptor(type)!!.fqNameSafe.asString()
|
||||
if (!type.arguments.isEmpty()) {
|
||||
hashString += "<${type.arguments.map {
|
||||
typeToHashString(it.type)
|
||||
@@ -94,4 +96,4 @@ internal val ClassDescriptor.typeInfoSymbolName: String
|
||||
get() = "ktype:" + this.fqNameSafe.toString()
|
||||
|
||||
internal val PropertyDescriptor.symbolName:String
|
||||
get() = "kvar:${this.containingDeclaration.name.asString()}.${this.name.asString()}"
|
||||
get() = "kvar:${this.containingDeclaration.name.asString()}.${this.name.asString()}"
|
||||
+11
-58
@@ -4,16 +4,12 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.implementation
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.implementedInterfaces
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
|
||||
|
||||
@@ -31,7 +27,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
val objOffsetsCount: Int,
|
||||
val interfaces: ConstValue,
|
||||
val interfacesCount: Int,
|
||||
val vtable: ConstValue,
|
||||
val methods: ConstValue,
|
||||
val methodsCount: Int,
|
||||
val fields: ConstValue,
|
||||
@@ -50,8 +45,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
interfaces,
|
||||
Int32(interfacesCount),
|
||||
|
||||
vtable,
|
||||
|
||||
methods,
|
||||
Int32(methodsCount),
|
||||
|
||||
@@ -71,46 +64,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
return classType
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
private fun getVtableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
|
||||
assert (!classDesc.isInterface)
|
||||
|
||||
val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDesc)) {
|
||||
emptyList()
|
||||
} else {
|
||||
getVtableEntries(classDesc.getSuperClassOrAny())
|
||||
}
|
||||
|
||||
val methods = classDesc.getContributedMethods() // TODO: ensure order is well-defined
|
||||
|
||||
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
|
||||
methods.single { OverridingUtil.overrides(it, superMethod) }
|
||||
}
|
||||
|
||||
return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable }
|
||||
}
|
||||
|
||||
private fun getMethodTableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
|
||||
assert (classDesc.modality != Modality.ABSTRACT)
|
||||
|
||||
return classDesc.getContributedMethods().filter { it.isOverridableOrOverrides }
|
||||
// TODO: probably method table should contain all accessible methods to improve binary compatibility
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.getContributedMethods(): List<FunctionDescriptor> {
|
||||
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
|
||||
// (includes declarations from supers)
|
||||
|
||||
val functions = contributedDescriptors.filterIsInstance<FunctionDescriptor>()
|
||||
|
||||
val properties = contributedDescriptors.filterIsInstance<PropertyDescriptor>()
|
||||
val getters = properties.mapNotNull { it.getter }
|
||||
val setters = properties.mapNotNull { it.setter }
|
||||
|
||||
val allMethods = functions + getters + setters
|
||||
return allMethods
|
||||
}
|
||||
|
||||
private fun exportTypeInfoIfRequired(classDesc: ClassDescriptor, typeInfoGlobal: LLVMValueRef?) {
|
||||
val annot = classDesc.annotations.findAnnotation(FqName("konan.ExportTypeInfo"))
|
||||
if (annot != null) {
|
||||
@@ -181,26 +134,27 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
|
||||
runtime.fieldTableRecordType, fields)
|
||||
|
||||
val vtable: List<ConstValue>
|
||||
val vtableEntries: List<ConstValue>
|
||||
val methods: List<ConstValue>
|
||||
|
||||
if (classDesc.modality != Modality.ABSTRACT) {
|
||||
if (!classDesc.isAbstract()) {
|
||||
// TODO: compile-time resolution limits binary compatibility
|
||||
vtable = getVtableEntries(classDesc).map { it.implementation.entryPointAddress }
|
||||
vtableEntries = classDesc.vtableEntries.map { it.implementation.entryPointAddress }
|
||||
|
||||
methods = getMethodTableEntries(classDesc).map {
|
||||
methods = classDesc.methodTableEntries.map {
|
||||
val nameSignature = it.functionName.localHash
|
||||
// TODO: compile-time resolution limits binary compatibility
|
||||
val methodEntryPoint = it.implementation.entryPointAddress
|
||||
MethodTableRecord(nameSignature, methodEntryPoint)
|
||||
}.sortedBy { it.nameSignature.value }
|
||||
} else {
|
||||
vtable = emptyList()
|
||||
vtableEntries = emptyList()
|
||||
methods = emptyList()
|
||||
}
|
||||
|
||||
assert (vtableEntries.size == classDesc.vtableSize)
|
||||
|
||||
val vtablePtr = staticData.placeGlobalConstArray("kvtable:$className", pointerType(int8Type), vtable)
|
||||
val vtable = ConstArray(int8TypePtr, vtableEntries)
|
||||
|
||||
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
|
||||
runtime.methodTableRecordType, methods)
|
||||
@@ -209,15 +163,14 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
superType,
|
||||
objOffsetsPtr, objOffsets.size,
|
||||
interfacesPtr, interfaces.size,
|
||||
vtablePtr,
|
||||
methodsPtr, methods.size,
|
||||
fieldsPtr, if (classDesc.isInterface) -1 else fields.size)
|
||||
|
||||
val typeInfoGlobal = classDesc.llvmTypeInfoPtr // TODO: it is a hack
|
||||
LLVMSetInitializer(typeInfoGlobal, typeInfo.llvm)
|
||||
val typeInfoGlobal = classDesc.typeInfoWithVtable.llvm // TODO: it is a hack
|
||||
LLVMSetInitializer(typeInfoGlobal, Struct(typeInfo, vtable).llvm)
|
||||
LLVMSetGlobalConstant(typeInfoGlobal, 1)
|
||||
|
||||
exportTypeInfoIfRequired(classDesc, typeInfoGlobal)
|
||||
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+25
-12
@@ -9,9 +9,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.unboundCallableReferenceTy
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
@@ -120,27 +118,42 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
||||
}
|
||||
|
||||
override fun IrExpression.useAs(type: KotlinType): IrExpression {
|
||||
return this.adaptIfNecessary(type)
|
||||
|
||||
val actualType = when (this) {
|
||||
is IrCall -> this.descriptor.original.returnType ?: this.type
|
||||
is IrGetField -> this.descriptor.original.type
|
||||
else -> this.type
|
||||
}
|
||||
|
||||
return this.adaptIfNecessary(actualType, type)
|
||||
}
|
||||
|
||||
private fun IrExpression.adaptIfNecessary(expectedType: KotlinType): IrExpression {
|
||||
val thisRepresentation = getCustomRepresentation(this.type)
|
||||
override fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression {
|
||||
return this.useAsValue(parameter.original)
|
||||
}
|
||||
|
||||
override fun IrExpression.useForField(field: PropertyDescriptor): IrExpression {
|
||||
return this.useForVariable(field.original)
|
||||
}
|
||||
|
||||
private fun IrExpression.adaptIfNecessary(actualType: KotlinType, expectedType: KotlinType): IrExpression {
|
||||
val actualRepresentation = getCustomRepresentation(actualType)
|
||||
val expectedRepresentation = getCustomRepresentation(expectedType)
|
||||
|
||||
return when {
|
||||
thisRepresentation == expectedRepresentation -> this
|
||||
actualRepresentation == expectedRepresentation -> this
|
||||
|
||||
thisRepresentation == null && expectedRepresentation != null -> {
|
||||
actualRepresentation == null && expectedRepresentation != null -> {
|
||||
// This may happen in the following cases:
|
||||
// 1. `this.type` is `Nothing`;
|
||||
// 2. `this` has the incompatible type.
|
||||
// 1. `actualType` is `Nothing`;
|
||||
// 2. `actualType` is incompatible.
|
||||
|
||||
this.unbox(expectedRepresentation)
|
||||
}
|
||||
|
||||
thisRepresentation != null && expectedRepresentation == null -> this.box(thisRepresentation)
|
||||
actualRepresentation != null && expectedRepresentation == null -> this.box(actualRepresentation)
|
||||
|
||||
else -> throw IllegalArgumentException("this is ${this.type}, expected $expectedType")
|
||||
else -> throw IllegalArgumentException("actual type is $actualType, expected $expectedType")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-13
@@ -33,19 +33,6 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
|
||||
private val builtIns = context.builtIns
|
||||
private val irBuiltins = context.irModule!!.irBuiltins
|
||||
|
||||
override fun visitTry(aTry: IrTry): IrExpression {
|
||||
// Workaround for the bug in IrTryImpl.transformChildren: transform `finallyExpression` too.
|
||||
// TODO: fix the bug and remove it.
|
||||
val transformer = this
|
||||
return (aTry as IrTryImpl).apply {
|
||||
tryResult = tryResult.transform(transformer, null)
|
||||
catches.forEachIndexed { i, irCatch ->
|
||||
catches[i] = irCatch.transform(transformer, null)
|
||||
}
|
||||
finallyExpression = finallyExpression?.transform(transformer, null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
|
||||
+5
-3
@@ -201,10 +201,10 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering,
|
||||
index = index + 1,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = param.name,
|
||||
outType = param.type,
|
||||
outType = builtIns.nullableAnyType, // Use erased type.
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false, isNoinline = false,
|
||||
varargElementType = (param as? ValueParameterDescriptor)?.varargElementType,
|
||||
varargElementType = null,
|
||||
source = SourceElement.NO_SOURCE)
|
||||
}
|
||||
|
||||
@@ -223,8 +223,10 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering,
|
||||
|
||||
val newValueParameters = listOf(functionParameter) + newUnboundParams
|
||||
|
||||
val newReturnType = builtIns.nullableAnyType // Use erased type.
|
||||
|
||||
newDescriptor.initialize(null, null, descriptor.typeParameters, newValueParameters,
|
||||
descriptor.returnType, Modality.FINAL, Visibilities.PRIVATE)
|
||||
newReturnType, Modality.FINAL, Visibilities.PRIVATE)
|
||||
|
||||
return newDescriptor
|
||||
}
|
||||
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.KonanPlatform
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||
|
||||
class DefaultParameterStubGenerator internal constructor(val context: Context): DeclarationContainerLoweringPass {
|
||||
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
||||
irDeclarationContainer.declarations.transformFlat { memberDeclaration ->
|
||||
if (memberDeclaration is IrFunction)
|
||||
lower(memberDeclaration)
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER :
|
||||
IrDeclarationOriginImpl("DEFAULT_PARAMETER_EXTENT")
|
||||
|
||||
private fun lower(irFunction: IrFunction): List<IrFunction> {
|
||||
val bodies = mutableListOf<IrExpressionBody>()
|
||||
irFunction.acceptChildrenVoid(object:IrElementVisitorVoid{
|
||||
override fun visitExpressionBody(body: IrExpressionBody) {
|
||||
bodies.add(body)
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
})
|
||||
|
||||
val functionDescriptor = irFunction.descriptor
|
||||
if (bodies.isNotEmpty()) {
|
||||
val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor()
|
||||
val generator = context.createFunctionIrGenerator(descriptor)
|
||||
val builder = generator.createIrBuilder()
|
||||
builder.apply {
|
||||
startOffset = irFunction.startOffset
|
||||
endOffset = irFunction.endOffset
|
||||
}
|
||||
val body = generator.irBlockBody {
|
||||
val params = mutableListOf<VariableDescriptor>()
|
||||
val variables = mutableMapOf<VariableDescriptor, VariableDescriptor>()
|
||||
|
||||
for (valueParameter in functionDescriptor.valueParameters) {
|
||||
val parameterDescriptor = descriptor.valueParameters[valueParameter.index]
|
||||
if (valueParameter.hasDefaultValue()) {
|
||||
val variable = scope.createTemporaryVariable(
|
||||
irExpression = nullConst(valueParameter.type)!!,
|
||||
isMutable = true)
|
||||
val variableDescriptor = variable.descriptor
|
||||
params.add(variableDescriptor)
|
||||
+variable
|
||||
val condition = irNotEquals(irCall(intAnd).apply {
|
||||
dispatchReceiver = irGet(mask)
|
||||
putValueArgument(0, irInt(1 shl valueParameter.index))
|
||||
}, irInt(0))
|
||||
val exprBody = getDefaultParameterExpressionBody(irFunction, valueParameter)
|
||||
/* Use previously calculated values in next expression. */
|
||||
exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() {
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
if (!variables.containsKey(expression.descriptor))
|
||||
return expression
|
||||
return irGet(variables[expression.descriptor] as VariableDescriptor)
|
||||
}
|
||||
})
|
||||
/* Mapping calculated values with its origin variables. */
|
||||
variables.put(valueParameter, variableDescriptor)
|
||||
+irIfThenElse(
|
||||
type = KonanPlatform.builtIns.unitType,
|
||||
condition = condition,
|
||||
thenPart = irSetVar(variableDescriptor, exprBody.expression),
|
||||
elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor)))
|
||||
|
||||
} else {
|
||||
params.add(parameterDescriptor)
|
||||
}
|
||||
}
|
||||
+ irReturn(irCall(functionDescriptor).apply {
|
||||
if (functionDescriptor.dispatchReceiverParameter != null) {
|
||||
dispatchReceiver = irGet(dispatch!!)
|
||||
}
|
||||
if (functionDescriptor.extensionReceiverParameter != null) {
|
||||
extensionReceiver = irGet(extension!!)
|
||||
}
|
||||
params.forEachIndexed { i, variable ->
|
||||
putValueArgument(i, irGet(variable))
|
||||
}
|
||||
})
|
||||
}
|
||||
// TODO: replace irFunction with new one without expression bodies.
|
||||
return listOf(irFunction, IrFunctionImpl(
|
||||
irFunction.startOffset ,
|
||||
irFunction.endOffset,
|
||||
DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
|
||||
descriptor, body))
|
||||
}
|
||||
return irFunction.singletonList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultParameterExpressionBody(irFunction: IrFunction, valueParameter: ValueParameterDescriptor):IrExpressionBody {
|
||||
return irFunction.getDefault(valueParameter) as? IrExpressionBody ?: TODO("FIXME!!!")
|
||||
}
|
||||
|
||||
private fun nullConst(type: KotlinType): IrExpression? {
|
||||
when {
|
||||
KotlinBuiltIns.isFloat(type) -> return IrConstImpl<Float> (0, 0, type, IrConstKind.Float, 0.0F)
|
||||
KotlinBuiltIns.isDouble(type) -> return IrConstImpl<Double> (0, 0, type, IrConstKind.Double, 0.0)
|
||||
KotlinBuiltIns.isBoolean(type) -> return IrConstImpl<Boolean> (0, 0, type, IrConstKind.Boolean, false)
|
||||
KotlinBuiltIns.isByte(type) -> return IrConstImpl<Byte> (0, 0, type, IrConstKind.Byte, 0)
|
||||
KotlinBuiltIns.isShort(type) -> return IrConstImpl<Short> (0, 0, type, IrConstKind.Short, 0)
|
||||
KotlinBuiltIns.isInt(type) -> return IrConstImpl<Int> (0, 0, type, IrConstKind.Int, 0)
|
||||
KotlinBuiltIns.isLong(type) -> return IrConstImpl<Long> (0, 0, type, IrConstKind.Long, 0)
|
||||
else -> return IrConstImpl<Nothing?>(0, 0, type, IrConstKind.Null, null)
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultParameterInjector internal constructor(val context: Context): BodyLoweringPass {
|
||||
override fun lower(irBody: IrBody) {
|
||||
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
super.visitCall(expression)
|
||||
val descriptor = expression.descriptor
|
||||
if (descriptor.valueParameters.none{it.hasDefaultValue()})
|
||||
return expression
|
||||
var argumentsCount = 0
|
||||
expression.acceptChildrenVoid(object:IrElementVisitorVoid{
|
||||
override fun visitElement(element: IrElement) {
|
||||
argumentsCount++
|
||||
}
|
||||
})
|
||||
if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)
|
||||
argumentsCount--
|
||||
if (argumentsCount == descriptor.valueParameters.size)
|
||||
return expression
|
||||
var maskValue = 0
|
||||
val functionDescriptor = descriptor as FunctionDescriptor
|
||||
val (defaultFunctiondescriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor()
|
||||
val params = descriptor.valueParameters.mapIndexed { i, it ->
|
||||
if (expression.getValueArgument(i) == null) maskValue = maskValue or (1 shl i)
|
||||
val valueParameterDescriptor = defaultFunctiondescriptor.valueParameters[i]
|
||||
return@mapIndexed valueParameterDescriptor to (expression.getValueArgument(i) ?: nullConst(valueParameterDescriptor.type))
|
||||
} + (mask to IrConstImpl<Int>(
|
||||
startOffset = irBody.startOffset,
|
||||
endOffset = irBody.endOffset,
|
||||
type = KonanPlatform.builtIns.intType,
|
||||
kind = IrConstKind.Int,
|
||||
value = maskValue))
|
||||
return IrCallImpl(
|
||||
startOffset = irBody.startOffset,
|
||||
endOffset = irBody.endOffset,
|
||||
type = descriptor.returnType!!,
|
||||
descriptor = defaultFunctiondescriptor,
|
||||
typeArguments = null)
|
||||
.apply {
|
||||
params.forEach {
|
||||
putValueArgument(it.first.index, it.second)
|
||||
}
|
||||
extension?.apply {
|
||||
putValueArgument(extension.index, expression.extensionReceiver)
|
||||
}
|
||||
dispatch?.apply {
|
||||
putValueArgument(dispatch.index, expression.dispatchReceiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val intDesctiptor = DescriptorUtils.getClassDescriptorForType(KonanPlatform.builtIns.intType)
|
||||
val intAnd = DescriptorUtils.getFunctionByName(intDesctiptor.unsubstitutedMemberScope, Name.identifier("and"))
|
||||
|
||||
data class DefaultParameterDescriptor(val function: FunctionDescriptor, val mask:ValueParameterDescriptor,
|
||||
val extensionReceiver:ValueParameterDescriptor?, val dispatchReceiver: ValueParameterDescriptor?)
|
||||
|
||||
fun FunctionDescriptor.generateDefaultsDescriptor():DefaultParameterDescriptor {
|
||||
val name = Name.identifier("${this.name.asString()}\$default")
|
||||
val descriptor = SimpleFunctionDescriptorImpl.create(this.containingDeclaration, Annotations.EMPTY,
|
||||
name, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE)
|
||||
val maskVariable = valueParameter(descriptor, valueParameters.size, "__\$mask\$__", KonanPlatform.builtIns.intType)
|
||||
|
||||
var extensionReceiver:ValueParameterDescriptor? = null
|
||||
var index = valueParameters.size + 1
|
||||
extensionReceiverParameter?.let {
|
||||
extensionReceiver = valueParameter(descriptor, index++, "__\$ext_receiver\$__", extensionReceiverParameter!!.type)
|
||||
}
|
||||
var dispatchReceiver:ValueParameterDescriptor? = null
|
||||
dispatchReceiverParameter?.let {
|
||||
dispatchReceiver = valueParameter(descriptor, index++, "__\$dispatch_receiver\$__", dispatchReceiverParameter!!.type)
|
||||
}
|
||||
|
||||
val parameterList = mutableListOf(*valueParameters.map{
|
||||
if (it.hasDefaultValue()) ValueParameterDescriptorImpl(
|
||||
containingDeclaration = it.containingDeclaration,
|
||||
original = it.original,
|
||||
index = it.index,
|
||||
annotations = it.annotations,
|
||||
name = it.name,
|
||||
outType = it.type,
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = it.isCrossinline,
|
||||
isNoinline = it.isNoinline,
|
||||
varargElementType = it.varargElementType,
|
||||
source = it.source)
|
||||
else it
|
||||
}.toTypedArray())
|
||||
|
||||
parameterList.add(maskVariable)
|
||||
if (extensionReceiver != null) parameterList.add(extensionReceiver)
|
||||
if (dispatchReceiver != null) parameterList.add(dispatchReceiver)
|
||||
descriptor.initialize(
|
||||
/* receiverParameterType = */ null,
|
||||
/* dispatchReceiverParameterType = */ null,
|
||||
/* typeParameters = */ typeParameters,
|
||||
/* unsubstitutedValueParameters = */ parameterList,
|
||||
/* unsubstitutedReturnType = */ returnType,
|
||||
/* modality = */ this.modality,
|
||||
/* visibility = */ this.visibility)
|
||||
return DefaultParameterDescriptor(descriptor, maskVariable, extensionReceiver, dispatchReceiver)
|
||||
}
|
||||
|
||||
private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: String, type: KotlinType):ValueParameterDescriptor {
|
||||
return ValueParameterDescriptorImpl(
|
||||
containingDeclaration = descriptor,
|
||||
original = null,
|
||||
index = index,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = Name.identifier(name),
|
||||
outType = type,
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
varargElementType = null,
|
||||
source = SourceElement.NO_SOURCE
|
||||
)
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
|
||||
inline fun IrBuilderWithScope.irLet(
|
||||
value: IrExpression,
|
||||
origin: IrStatementOrigin? = null,
|
||||
nameHint: String? = null,
|
||||
body: (VariableDescriptor) -> IrExpression
|
||||
): IrExpression {
|
||||
val irTemporary = scope.createTemporaryVariable(value, nameHint)
|
||||
val irResult = body(irTemporary.descriptor)
|
||||
val irBlock = IrBlockImpl(startOffset, endOffset, irResult.type, origin)
|
||||
irBlock.statements.add(irTemporary)
|
||||
irBlock.statements.add(irResult)
|
||||
return irBlock
|
||||
}
|
||||
|
||||
fun <T : IrElement> IrStatementsBuilder<T>.defineTemporary(value: IrExpression, nameHint: String? = null): VariableDescriptor {
|
||||
val temporary = scope.createTemporaryVariable(value, nameHint)
|
||||
+temporary
|
||||
return temporary.descriptor
|
||||
}
|
||||
|
||||
fun <T : IrElement> IrStatementsBuilder<T>.defineTemporaryVar(value: IrExpression, nameHint: String? = null): VariableDescriptor {
|
||||
val temporary = scope.createTemporaryVariable(value, nameHint, isMutable = true)
|
||||
+temporary
|
||||
return temporary.descriptor
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irReturn(value: IrExpression) =
|
||||
IrReturnImpl(startOffset, endOffset, context.builtIns.nothingType, scope.assertCastOwner(), value)
|
||||
|
||||
fun IrBuilderWithScope.irReturnTrue() =
|
||||
irReturn(IrConstImpl(startOffset, endOffset, context.builtIns.booleanType, IrConstKind.Boolean, true))
|
||||
|
||||
fun IrBuilderWithScope.irReturnFalse() =
|
||||
irReturn(IrConstImpl(startOffset, endOffset, context.builtIns.booleanType, IrConstKind.Boolean, false))
|
||||
|
||||
fun IrBuilderWithScope.irIfThenElse(type: KotlinType, condition: IrExpression, thenPart: IrExpression, elsePart: IrExpression) =
|
||||
IrIfThenElseImpl(startOffset, endOffset, type, condition, thenPart, elsePart)
|
||||
|
||||
fun IrBuilderWithScope.irIfNull(type: KotlinType, subject: IrExpression, thenPart: IrExpression, elsePart: IrExpression) =
|
||||
irIfThenElse(type, irEqualsNull(subject), thenPart, elsePart)
|
||||
|
||||
fun IrBuilderWithScope.irThrowNpe(origin: IrStatementOrigin) =
|
||||
IrNullaryPrimitiveImpl(startOffset, endOffset, origin, context.irBuiltIns.throwNpe)
|
||||
|
||||
fun IrBuilderWithScope.irIfThenReturnTrue(condition: IrExpression) =
|
||||
IrIfThenElseImpl(startOffset, endOffset, context.builtIns.unitType, condition, irReturnTrue())
|
||||
|
||||
fun IrBuilderWithScope.irIfThenReturnFalse(condition: IrExpression) =
|
||||
IrIfThenElseImpl(startOffset, endOffset, context.builtIns.unitType, condition, irReturnFalse())
|
||||
|
||||
fun IrBuilderWithScope.irThis() =
|
||||
scope.classOwner().let { classOwner ->
|
||||
IrGetValueImpl(startOffset, endOffset, classOwner.thisAsReceiverParameter)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irGet(variable: VariableDescriptor) =
|
||||
IrGetValueImpl(startOffset, endOffset, variable)
|
||||
|
||||
fun IrBuilderWithScope.irSetVar(variable: VariableDescriptor, value: IrExpression) =
|
||||
IrSetVariableImpl(startOffset, endOffset, variable, value, IrStatementOrigin.EQ)
|
||||
|
||||
fun IrBuilderWithScope.irOther() =
|
||||
irGet(scope.functionOwner().valueParameters.single())
|
||||
|
||||
fun IrBuilderWithScope.irEqeqeq(arg1: IrExpression, arg2: IrExpression) =
|
||||
context.eqeqeq(startOffset, endOffset, arg1, arg2)
|
||||
|
||||
fun IrBuilderWithScope.irNull() =
|
||||
IrConstImpl.constNull(startOffset, endOffset, context.builtIns.nullableNothingType)
|
||||
|
||||
fun IrBuilderWithScope.irEqualsNull(argument: IrExpression) =
|
||||
primitiveOp2(startOffset, endOffset, context.irBuiltIns.eqeq, IrStatementOrigin.EQEQ,
|
||||
argument, irNull())
|
||||
|
||||
fun IrBuilderWithScope.irNotEquals(arg1: IrExpression, arg2: IrExpression) =
|
||||
primitiveOp1(startOffset, endOffset, context.irBuiltIns.booleanNot, IrStatementOrigin.EXCLEQ,
|
||||
primitiveOp2(startOffset, endOffset, context.irBuiltIns.eqeq, IrStatementOrigin.EXCLEQ,
|
||||
arg1, arg2))
|
||||
|
||||
fun IrBuilderWithScope.irGet(receiver: IrExpression, property: PropertyDescriptor): IrExpression =
|
||||
IrGetterCallImpl(startOffset, endOffset, property.getter!!, null, receiver, null, IrStatementOrigin.GET_PROPERTY)
|
||||
|
||||
fun IrBuilderWithScope.irCall(callee: CallableDescriptor) =
|
||||
IrCallImpl(startOffset, endOffset, callee.returnType!!, callee, null)
|
||||
|
||||
fun IrBuilderWithScope.irCallOp(callee: CallableDescriptor, dispatchReceiver: IrExpression, argument: IrExpression) =
|
||||
IrCallImpl(startOffset, endOffset, callee.returnType!!, callee, null).apply {
|
||||
this.dispatchReceiver = dispatchReceiver
|
||||
putValueArgument(0, argument)
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irIs(argument: IrExpression, type: KotlinType) =
|
||||
IrTypeOperatorCallImpl(startOffset, endOffset, context.builtIns.booleanType, IrTypeOperator.INSTANCEOF, type, argument)
|
||||
|
||||
fun IrBuilderWithScope.irNotIs(argument: IrExpression, type: KotlinType) =
|
||||
IrTypeOperatorCallImpl(startOffset, endOffset, context.builtIns.booleanType, IrTypeOperator.NOT_INSTANCEOF, type, argument)
|
||||
|
||||
fun IrBuilderWithScope.irAs(argument: IrExpression, type: KotlinType) =
|
||||
IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.CAST, type, argument)
|
||||
|
||||
fun IrBuilderWithScope.irImplicitCast(argument: IrExpression, type: KotlinType) =
|
||||
IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.IMPLICIT_CAST, type, argument)
|
||||
|
||||
fun IrBuilderWithScope.irInt(value: Int) =
|
||||
IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, value)
|
||||
|
||||
fun IrBuilderWithScope.irString(value: String) =
|
||||
IrConstImpl.string(startOffset, endOffset, context.builtIns.stringType, value)
|
||||
|
||||
fun IrBuilderWithScope.irConcat() =
|
||||
IrStringConcatenationImpl(startOffset, endOffset, context.builtIns.stringType)
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
|
||||
// TODO: rename Generator* to IrGenerator* when contributing back to Kotlin.
|
||||
|
||||
interface Generator {
|
||||
val context: GeneratorContext
|
||||
}
|
||||
|
||||
interface GeneratorWithScope : Generator {
|
||||
val scope: Scope
|
||||
}
|
||||
|
||||
interface GeneratorContext {
|
||||
val irBuiltIns: IrBuiltIns
|
||||
val builtIns: KotlinBuiltIns get() = irBuiltIns.builtIns
|
||||
}
|
||||
-127
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
abstract class IrBuilder(
|
||||
override val context: GeneratorContext,
|
||||
var startOffset: Int,
|
||||
var endOffset: Int
|
||||
) : Generator
|
||||
|
||||
abstract class IrBuilderWithScope(
|
||||
context: GeneratorContext,
|
||||
override val scope: Scope,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
) : IrBuilder(context, startOffset, endOffset), GeneratorWithScope
|
||||
|
||||
abstract class IrStatementsBuilder<out T : IrElement>(
|
||||
context: GeneratorContext,
|
||||
scope: Scope,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
) : IrBuilderWithScope(context, scope, startOffset, endOffset) {
|
||||
operator fun IrStatement.unaryPlus() {
|
||||
addStatement(this)
|
||||
}
|
||||
|
||||
protected abstract fun addStatement(irStatement: IrStatement)
|
||||
protected abstract fun doBuild(): T
|
||||
}
|
||||
|
||||
open class IrBlockBodyBuilder(
|
||||
context: GeneratorContext,
|
||||
scope: Scope,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
) : IrStatementsBuilder<IrBlockBody>(context, scope, startOffset, endOffset) {
|
||||
private val irBlockBody = IrBlockBodyImpl(startOffset, endOffset)
|
||||
|
||||
inline fun blockBody(body: IrBlockBodyBuilder.() -> Unit): IrBlockBody {
|
||||
body()
|
||||
return doBuild()
|
||||
}
|
||||
|
||||
override fun addStatement(irStatement: IrStatement) {
|
||||
irBlockBody.statements.add(irStatement)
|
||||
}
|
||||
|
||||
override fun doBuild(): IrBlockBody {
|
||||
return irBlockBody
|
||||
}
|
||||
}
|
||||
|
||||
class IrBlockBuilder(
|
||||
context: GeneratorContext, scope: Scope,
|
||||
startOffset: Int, endOffset: Int,
|
||||
val origin: IrStatementOrigin? = null,
|
||||
var resultType: KotlinType? = null
|
||||
) : IrStatementsBuilder<IrBlock>(context, scope, startOffset, endOffset) {
|
||||
private val statements = ArrayList<IrStatement>()
|
||||
|
||||
inline fun block(body: IrBlockBuilder.() -> Unit): IrBlock {
|
||||
body()
|
||||
return doBuild()
|
||||
}
|
||||
|
||||
override fun addStatement(irStatement: IrStatement) {
|
||||
statements.add(irStatement)
|
||||
}
|
||||
|
||||
override fun doBuild(): IrBlock {
|
||||
val resultType = this.resultType ?:
|
||||
(statements.lastOrNull() as? IrExpression)?.type ?:
|
||||
context.builtIns.unitType
|
||||
val irBlock = IrBlockImpl(startOffset, endOffset, resultType, origin)
|
||||
irBlock.statements.addAll(statements)
|
||||
return irBlock
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : IrBuilder> T.at(startOffset: Int, endOffset: Int): T {
|
||||
this.startOffset = startOffset
|
||||
this.endOffset = endOffset
|
||||
return this
|
||||
}
|
||||
|
||||
inline fun GeneratorWithScope.irBlock(startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET,
|
||||
origin: IrStatementOrigin? = null,
|
||||
resultType: KotlinType? = null,
|
||||
body: IrBlockBuilder.() -> Unit
|
||||
): IrExpression =
|
||||
IrBlockBuilder(context, scope,
|
||||
startOffset,
|
||||
endOffset,
|
||||
origin, resultType
|
||||
).block(body)
|
||||
|
||||
inline fun GeneratorWithScope.irBlockBody(startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET,
|
||||
body: IrBlockBodyBuilder.() -> Unit
|
||||
) : IrBlockBody =
|
||||
IrBlockBodyBuilder(context, scope,
|
||||
startOffset,
|
||||
endOffset
|
||||
).blockBody(body)
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
|
||||
class IrMemberFunctionBuilder(
|
||||
context: GeneratorContext,
|
||||
val irClass: IrClassImpl,
|
||||
val function: FunctionDescriptor,
|
||||
val origin: IrDeclarationOrigin,
|
||||
startOffset: Int = UNDEFINED_OFFSET,
|
||||
endOffset: Int = UNDEFINED_OFFSET
|
||||
) : IrBlockBodyBuilder(context, Scope(function), startOffset, endOffset) {
|
||||
inline fun addToClass(body: IrMemberFunctionBuilder.() -> Unit) {
|
||||
val irFunction = IrFunctionImpl(startOffset, endOffset, origin, function)
|
||||
body()
|
||||
irFunction.body = doBuild()
|
||||
irClass.addMember(irFunction)
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
|
||||
fun primitiveOp1(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, origin: IrStatementOrigin,
|
||||
argument: IrExpression): IrExpression =
|
||||
IrUnaryPrimitiveImpl(startOffset, endOffset, origin, primitiveOpDescriptor, argument)
|
||||
|
||||
fun primitiveOp2(startOffset: Int, endOffset: Int, primitiveOpDescriptor: CallableDescriptor, origin: IrStatementOrigin,
|
||||
argument1: IrExpression, argument2: IrExpression): IrExpression =
|
||||
IrBinaryPrimitiveImpl(startOffset, endOffset, origin, primitiveOpDescriptor, argument1, argument2)
|
||||
|
||||
fun GeneratorContext.constNull(startOffset: Int, endOffset: Int): IrExpression =
|
||||
IrConstImpl.constNull(startOffset, endOffset, builtIns.nullableNothingType)
|
||||
|
||||
fun GeneratorContext.equalsNull(startOffset: Int, endOffset: Int, argument: IrExpression): IrExpression =
|
||||
primitiveOp2(startOffset, endOffset, irBuiltIns.eqeq, IrStatementOrigin.EQEQ,
|
||||
argument, constNull(startOffset, endOffset))
|
||||
|
||||
fun GeneratorContext.eqeqeq(startOffset: Int, endOffset: Int, argument1: IrExpression, argument2: IrExpression): IrExpression =
|
||||
primitiveOp2(startOffset, endOffset, irBuiltIns.eqeqeq, IrStatementOrigin.EQEQEQ, argument1, argument2)
|
||||
|
||||
fun GeneratorContext.throwNpe(startOffset: Int, endOffset: Int, origin: IrStatementOrigin): IrExpression =
|
||||
IrNullaryPrimitiveImpl(startOffset, endOffset, origin, irBuiltIns.throwNpe)
|
||||
|
||||
// a || b == if (a) true else b
|
||||
fun GeneratorContext.oror(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.OROR): IrWhen =
|
||||
IrIfThenElseImpl(startOffset, endOffset, builtIns.booleanType,
|
||||
a, IrConstImpl.constTrue(b.startOffset, b.endOffset, b.type), b,
|
||||
origin)
|
||||
|
||||
fun GeneratorContext.oror(a: IrExpression, b: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.OROR): IrWhen =
|
||||
oror(b.startOffset, b.endOffset, a, b, origin)
|
||||
|
||||
fun GeneratorContext.whenComma(a: IrExpression, b: IrExpression): IrWhen =
|
||||
oror(a, b, IrStatementOrigin.WHEN_COMMA)
|
||||
|
||||
// a && b == if (a) b else false
|
||||
fun GeneratorContext.andand(startOffset: Int, endOffset: Int, a: IrExpression, b: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.ANDAND): IrWhen =
|
||||
IrIfThenElseImpl(startOffset, endOffset, builtIns.booleanType,
|
||||
a, b, IrConstImpl.constFalse(b.startOffset, b.endOffset, b.type),
|
||||
origin)
|
||||
|
||||
fun GeneratorContext.andand(a: IrExpression, b: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.ANDAND): IrWhen =
|
||||
andand(b.startOffset, b.endOffset, a, b, origin)
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptor
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
class Scope(val scopeOwner: DeclarationDescriptor) {
|
||||
private var lastTemporaryIndex: Int = 0
|
||||
private fun nextTemporaryIndex(): Int = lastTemporaryIndex++
|
||||
|
||||
private fun createDescriptorForTemporaryVariable(type: KotlinType, nameHint: String? = null, isMutable: Boolean = false): IrTemporaryVariableDescriptor =
|
||||
IrTemporaryVariableDescriptorImpl(scopeOwner, Name.identifier(getNameForTemporary(nameHint)), type, isMutable)
|
||||
|
||||
private fun getNameForTemporary(nameHint: String?): String {
|
||||
val index = nextTemporaryIndex()
|
||||
return if (nameHint != null) "tmp${index}_$nameHint" else "tmp$index"
|
||||
}
|
||||
|
||||
fun createTemporaryVariable(irExpression: IrExpression, nameHint: String? = null, isMutable: Boolean = false): IrVariable =
|
||||
IrVariableImpl(
|
||||
irExpression.startOffset, irExpression.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
|
||||
createDescriptorForTemporaryVariable(irExpression.type, nameHint, isMutable),
|
||||
irExpression
|
||||
)
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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 org.jetbrains.kotlin.ir.builders
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
|
||||
inline fun <reified T> Scope.assertCastOwner() =
|
||||
scopeOwner as? T ?:
|
||||
throw AssertionError("Unexpected scopeOwner: $scopeOwner")
|
||||
|
||||
fun Scope.functionOwner(): FunctionDescriptor =
|
||||
assertCastOwner()
|
||||
|
||||
fun Scope.classOwner(): ClassDescriptor =
|
||||
when (scopeOwner) {
|
||||
is ClassDescriptor -> scopeOwner
|
||||
is MemberDescriptor -> scopeOwner.containingDeclaration as ClassDescriptor
|
||||
else -> throw AssertionError("Unexpected scopeOwner: $scopeOwner")
|
||||
}
|
||||
@@ -10,12 +10,13 @@ libGcc.linux = target-gcc-toolchain-3-linux-x86-64/lib/gcc/x86_64-unknown-linux-
|
||||
|
||||
llvmLtoFlags.osx = -O3 -function-sections -exported-symbol=_main
|
||||
llvmLlcFlags.osx = -mtriple=x86_64-apple-macosx10.10.0
|
||||
linkerKonanFlags.osx = -lc++
|
||||
linkerOptimizationFlags.osx = -dead_strip
|
||||
macosVersionMin.osx = 10.10.0
|
||||
|
||||
llvmLtoFlags.linux = -O3 -function-sections -exported-symbol=main
|
||||
llvmLlcFlags.linux = -march=x86-64
|
||||
linkerKonanFlags.linux = -Bstatic -lc++abi -Bdynamic -ldl -lm -lpthread
|
||||
linkerKonanFlags.linux = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread
|
||||
linkerOptimizationFlags.linux = --gc-sections
|
||||
pluginOptimizationFlags.linux = -plugin-opt=mcpu=x86-64 -plugin-opt=O3
|
||||
|
||||
|
||||
@@ -41,11 +41,11 @@ abstract class KonanTest extends DefaultTask {
|
||||
project.javaexec {
|
||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
||||
classpath = project.configurations.cli_bc
|
||||
jvmArgs "-ea",
|
||||
jvmArgs "-ea",
|
||||
"-Dkonan.home=${dist.canonicalPath}",
|
||||
"-Djava.library.path=${dist.canonicalPath}/konan/nativelib"
|
||||
args("-output", output,
|
||||
source,
|
||||
source,
|
||||
*moreArgs,
|
||||
*project.globalArgs)
|
||||
}
|
||||
@@ -88,7 +88,7 @@ abstract class KonanTest extends DefaultTask {
|
||||
|
||||
class RunKonanTest extends KonanTest {
|
||||
void compileTest(String source, String exe) {
|
||||
runCompiler(source, exe, [])
|
||||
runCompiler(source, exe, [])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ class LinkKonanTest extends KonanTest {
|
||||
void compileTest(String source, String exe) {
|
||||
def libDir = project.file(lib).absolutePath
|
||||
def libBc = "${libDir}.bc"
|
||||
|
||||
|
||||
runCompiler(lib, libBc, ['-nolink', '-nostdlib'])
|
||||
runCompiler(source, exe, ['-library', libBc])
|
||||
}
|
||||
@@ -149,6 +149,12 @@ task objectInitialization(type: RunKonanTest) {
|
||||
source = "codegen/object/initialization.kt"
|
||||
}
|
||||
|
||||
task objectInitialization1(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "init\nfield\nconstructor1\ninit\nfield\nconstructor1\nconstructor2\n"
|
||||
source = "codegen/object/initialization1.kt"
|
||||
}
|
||||
|
||||
task check_type(type: RunKonanTest) {
|
||||
goldValue = "true\nfalse\ntrue\ntrue\ntrue\ntrue\n"
|
||||
source = "codegen/basics/check_type.kt"
|
||||
@@ -173,6 +179,41 @@ task sum2(type: RunKonanTest) {
|
||||
source = "codegen/function/sum_imm.kt"
|
||||
}
|
||||
|
||||
task defaults(type: RunKonanTest) {
|
||||
source = "codegen/function/defaults.kt"
|
||||
}
|
||||
|
||||
task defaults1(type: RunKonanTest) {
|
||||
source = "codegen/function/defaults1.kt"
|
||||
}
|
||||
|
||||
task defaults2(type: RunKonanTest) {
|
||||
source = "codegen/function/defaults2.kt"
|
||||
}
|
||||
|
||||
task defaults3(type: RunKonanTest) {
|
||||
source = "codegen/function/defaults3.kt"
|
||||
}
|
||||
|
||||
task defaults4(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "43\n"
|
||||
source = "codegen/function/defaults4.kt"
|
||||
}
|
||||
|
||||
task defaults5(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "5\n6\n"
|
||||
source = "codegen/function/defaults5.kt"
|
||||
}
|
||||
|
||||
task defaults6(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "42\n"
|
||||
source = "codegen/function/defaults6.kt"
|
||||
}
|
||||
|
||||
|
||||
task sum_3const(type: RunKonanTest) {
|
||||
source = "codegen/function/sum_3const.kt"
|
||||
}
|
||||
@@ -195,7 +236,6 @@ task null_check(type: RunKonanTest) {
|
||||
}
|
||||
|
||||
task array_to_any(type: RunKonanTest) {
|
||||
disabled = true
|
||||
source = "codegen/basics/array_to_any.kt"
|
||||
}
|
||||
|
||||
@@ -247,8 +287,8 @@ task tostring3(type: RunKonanTest) {
|
||||
"1.17549E-38\n3.40282E+38\n-INF\nINF\n" +
|
||||
// Linux version prints -NAN.
|
||||
// "NAN\n" +
|
||||
"4.94066E-324\n1.79769E+308\n-INF\nINF\n"
|
||||
// "NAN\n"
|
||||
"4.94066E-324\n1.79769E+308\n-INF\nINF\n"
|
||||
// "NAN\n"
|
||||
source = "runtime/basic/tostring3.kt"
|
||||
}
|
||||
|
||||
@@ -267,6 +307,11 @@ task array1(type: RunKonanTest) {
|
||||
source = "runtime/collections/array1.kt"
|
||||
}
|
||||
|
||||
task array2(type: RunKonanTest) {
|
||||
goldValue = "0\n2\n4\n6\n8\n40\n"
|
||||
source = "runtime/collections/array2.kt"
|
||||
}
|
||||
|
||||
task if_else(type: RunKonanTest) {
|
||||
source = "codegen/branching/if_else.kt"
|
||||
}
|
||||
@@ -308,6 +353,11 @@ task bool_yes(type: RunKonanTest) {
|
||||
source = "codegen/function/boolean.kt"
|
||||
}
|
||||
|
||||
task named(type: RunKonanTest) {
|
||||
source = "codegen/function/named.kt"
|
||||
}
|
||||
|
||||
|
||||
task plus_eq(type: RunKonanTest) {
|
||||
source = "codegen/function/plus_eq.kt"
|
||||
}
|
||||
@@ -360,7 +410,7 @@ task intrinsic(type: RunKonanTest) {
|
||||
}
|
||||
|
||||
/*
|
||||
Disabled until we extract the classes that should be
|
||||
Disabled until we extract the classes that should be
|
||||
always present from stdlib.kt.bc into a separate binary.
|
||||
|
||||
task link(type: LinkKonanTest) {
|
||||
@@ -386,7 +436,6 @@ task statements0(type: RunKonanTest) {
|
||||
}
|
||||
|
||||
task boxing0(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "17\n"
|
||||
source = "codegen/boxing/boxing0.kt"
|
||||
}
|
||||
@@ -498,6 +547,10 @@ task moderately_large_array(type: RunKonanTest) {
|
||||
source = "runtime/collections/moderately_large_array.kt"
|
||||
}
|
||||
|
||||
task moderately_large_array1(type: RunKonanTest) {
|
||||
goldValue = "-45392\n"
|
||||
source = "runtime/collections/moderately_large_array1.kt"
|
||||
}
|
||||
|
||||
task string_builder0(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
@@ -807,6 +860,11 @@ task memory_throw_cleanup(type: RunKonanTest) {
|
||||
source = "runtime/memory/throw_cleanup.kt"
|
||||
}
|
||||
|
||||
task memory_collect_cycles(type: RunKonanTest) {
|
||||
goldValue = "42\n"
|
||||
source = "runtime/memory/cycles0.kt"
|
||||
}
|
||||
|
||||
task unit1(type: RunKonanTest) {
|
||||
goldValue = "First\nkotlin.Unit\n"
|
||||
source = "codegen/basics/unit1.kt"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
fun main(args: Array<String>) {
|
||||
println(foo().toString())
|
||||
foo().hashCode()
|
||||
}
|
||||
|
||||
fun foo(): Any {
|
||||
return Array<Any?>(0)
|
||||
return Array<Any?>(0, { i -> null })
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Created by minamoto on 12/26/16.
|
||||
*/
|
||||
//package defaults
|
||||
|
||||
open class A(val a:Int) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other == null || other as? A == null) return false
|
||||
return (other as A).a == a // Where is smart casting?
|
||||
}
|
||||
|
||||
companion object {
|
||||
val zero = A(0)
|
||||
val one = A(1)
|
||||
val magic = A(42)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// FUN public fun foo(a: defaults.A = ...): kotlin.Int
|
||||
// a: EXPRESSION_BODY
|
||||
// CALL '<get-magic>(): A' type=defaults.A origin=GET_PROPERTY
|
||||
// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion
|
||||
// BLOCK_BODY
|
||||
// RETURN type=kotlin.Nothing from='foo(A = ...): Int'
|
||||
// CALL '<get-a>(): Int' type=kotlin.Int origin=GET_PROPERTY
|
||||
// $this: GET_VAR 'value-parameter a: A = ...' type=defaults.A origin=null
|
||||
fun foo(a: A = A.magic, b:Int = 0xdeadbeef.toInt()) = a.a
|
||||
|
||||
// FUN public fun bar(a: defaults.A, inc: kotlin.Int = ...): defaults.A
|
||||
// inc: EXPRESSION_BODY
|
||||
// CONST Int type=kotlin.Int value='0'
|
||||
// BLOCK_BODY
|
||||
// RETURN type=kotlin.Nothing from='bar(A, Int = ...): A'
|
||||
// CALL 'constructor A(Int)' type=defaults.A origin=null
|
||||
// a: CALL 'plus(Int): Int' type=kotlin.Int origin=PLUS
|
||||
// $this: CALL '<get-a>(): Int' type=kotlin.Int origin=GET_PROPERTY
|
||||
// $this: GET_VAR 'value-parameter a: A' type=defaults.A origin=null
|
||||
// other: GET_VAR 'value-parameter inc: Int = ...' type=kotlin.Int origin=null
|
||||
fun bar(a:A, inc:Int = 0) = A(a.a + inc)
|
||||
|
||||
|
||||
fun main(args:Array<String>) {
|
||||
|
||||
// if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ
|
||||
// arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ
|
||||
// arg0: CALL 'foo(A = ...): Int' type=kotlin.Int origin=null
|
||||
// arg1: CALL '<get-a>(): Int' type=kotlin.Int origin=GET_PROPERTY
|
||||
// $this: CALL '<get-magic>(): A' type=defaults.A origin=GET_PROPERTY
|
||||
// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion
|
||||
if (foo() != A.magic.a) {
|
||||
println("magic failed")
|
||||
throw Error()
|
||||
}
|
||||
|
||||
// if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ
|
||||
// arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ
|
||||
// arg0: CALL 'foo(A = ...): Int' type=kotlin.Int origin=null
|
||||
// a: CALL 'constructor A(Int)' type=defaults.A origin=null
|
||||
// a: CONST Int type=kotlin.Int value='1'
|
||||
// arg1: CONST Int type=kotlin.Int value='1'
|
||||
if (foo(A(1)) != 1) {
|
||||
println("one failed: foo(A(1))")
|
||||
throw Error()
|
||||
}
|
||||
|
||||
// if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ
|
||||
// arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ
|
||||
// arg0: CALL 'bar(A, Int = ...): A' type=defaults.A origin=null
|
||||
// a: CALL '<get-one>(): A' type=defaults.A origin=GET_PROPERTY <---
|
||||
// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion
|
||||
// arg1: CALL '<get-one>(): A' type=defaults.A origin=GET_PROPERTY
|
||||
// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion
|
||||
|
||||
if (bar(A.one) != A.one) {
|
||||
println("A one failed")
|
||||
throw Error()
|
||||
}
|
||||
|
||||
|
||||
// if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ
|
||||
// arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ
|
||||
// arg0: CALL '<get-a>(): Int' type=kotlin.Int origin=GET_PROPERTY
|
||||
// $this: CALL 'bar(A, Int = ...): A' type=defaults.A origin=null
|
||||
// a: CALL '<get-one>(): A' type=defaults.A origin=GET_PROPERTY
|
||||
// $this: GET_OBJECT 'companion object of A' type=defaults.A.Companion
|
||||
// inc: CONST Int type=kotlin.Int value='1'
|
||||
// arg1: CONST Int type=kotlin.Int value='2'
|
||||
if (bar(A.one, 1).a != 2) {
|
||||
println("A one + 1 failed")
|
||||
throw Error()
|
||||
}
|
||||
println("all tests passed")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fun foo(x:Int = 0, y:Int = x + 1, z:Int = x + y + 1) = x + y + z
|
||||
|
||||
fun main(arg:Array<String>) {
|
||||
val v = foo()
|
||||
if (v != 3) {
|
||||
println("test failed $v expected 3")
|
||||
throw Error()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
fun Int.foo(inc0:Int, inc:Int = 0) = this + inc0 + inc
|
||||
|
||||
fun main(arg:Array<String>) {
|
||||
val v = 42.foo(0)
|
||||
if (v != 42) {
|
||||
println("test failed v:$v expected:42")
|
||||
throw Error()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fun foo(a:Int = 2, b:String = "Hello", c:Int = 4):String = "$b-$c$a"
|
||||
fun foo(a:Int = 3, b:Int = a + 1, c:Int = a + b) = a + b + c
|
||||
|
||||
fun main(arg:Array<String>){
|
||||
val a = foo(b="Universe")
|
||||
if (a != "Universe-42")
|
||||
throw Error()
|
||||
|
||||
val b = foo(b = 5)
|
||||
if (b != (/* a = */ 3 + /* b = */ 5 + /* c = */ (3 + 5)))
|
||||
throw Error()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
open class A {
|
||||
open fun foo(x: Int = 42) = println(x)
|
||||
}
|
||||
|
||||
class B : A() {
|
||||
override fun foo(x: Int) = println(x + 1)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
B().foo()
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
class Test(val x: Int) {
|
||||
fun foo(y: Int = x) {
|
||||
println(y)
|
||||
}
|
||||
}
|
||||
|
||||
fun Test.bar(y: Int = x) {
|
||||
println(y)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Test(5).foo()
|
||||
Test(6).bar()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
open class Foo(val x: Int = 42)
|
||||
class Bar : Foo()
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(Bar().x)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(a:Int, b:Int) = a - b
|
||||
fun main(args:Array<String>) {
|
||||
if (foo(b = 24, a = 42) != 18)
|
||||
throw Error()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
class Test {
|
||||
constructor() {
|
||||
println("constructor1")
|
||||
}
|
||||
|
||||
constructor(x: Int) : this() {
|
||||
println("constructor2")
|
||||
}
|
||||
|
||||
init {
|
||||
println("init")
|
||||
}
|
||||
|
||||
val f = println("field")
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Test()
|
||||
Test(1)
|
||||
}
|
||||
@@ -24,6 +24,6 @@ fun main(args : Array<String>) {
|
||||
val booleanArray = BooleanArray(12)
|
||||
println(booleanArray.size.toString())
|
||||
|
||||
val stringArray = Array<String>(13)
|
||||
val stringArray = Array<String>(13, { i -> ""})
|
||||
println(stringArray.size.toString())
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun main(args : Array<String>) {
|
||||
val byteArray = Array<Byte>(5, { i -> (i * 2).toByte() })
|
||||
byteArray.map { println(it) }
|
||||
|
||||
val intArray = Array<Int>(5, { i -> i * 4 })
|
||||
println(intArray.sum())
|
||||
}
|
||||
@@ -181,7 +181,6 @@ fun testPutEntry() {
|
||||
assertTrue(expected == m)
|
||||
}
|
||||
|
||||
/* Fails due to variance.
|
||||
fun testRemoveAllEntries() {
|
||||
val expected = mapOf("a" to "1", "b" to "2", "c" to "3")
|
||||
val m = HashMap(expected)
|
||||
@@ -199,7 +198,7 @@ fun testRetainAllEntries() {
|
||||
assertEquals(expected, m)
|
||||
assertTrue(m.entries.retainAll(mapOf("b" to "22", "c" to "3", "d" to "4").entries))
|
||||
assertEquals(mapOf("c" to "3"), m)
|
||||
} */
|
||||
}
|
||||
|
||||
fun testContainsAllValues() {
|
||||
val m = HashMap(mapOf("a" to "1", "b" to "2", "c" to "3"))
|
||||
@@ -256,8 +255,8 @@ fun main(args : Array<String>) {
|
||||
testHashCode()
|
||||
testToString()
|
||||
testPutEntry()
|
||||
//testRemoveAllEntries()
|
||||
//testRetainAllEntries()
|
||||
testRemoveAllEntries()
|
||||
testRetainAllEntries()
|
||||
testContainsAllValues()
|
||||
testRemoveValue()
|
||||
testRemoveAllValues()
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fun main(args: Array<String>) {
|
||||
val a = Array<Byte>(100000, { i -> i.toByte()})
|
||||
|
||||
var sum = 0
|
||||
for (b in a) {
|
||||
sum += b
|
||||
}
|
||||
|
||||
println(sum)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
data class Node(val data: Int, var next: Node?, var prev: Node?, val outer: Node?)
|
||||
|
||||
fun makeCycle(len: Int, outer: Node?): Node {
|
||||
val start = Node(0, null, null, outer)
|
||||
var prev = start
|
||||
for (i in 1 .. len - 1) {
|
||||
prev = Node(i, prev, null, outer)
|
||||
}
|
||||
start.next = prev
|
||||
return start
|
||||
}
|
||||
|
||||
fun makeDoubleCycle(len: Int): Node {
|
||||
val start = makeCycle(len, null)
|
||||
var prev = start
|
||||
var cur = prev.next
|
||||
while (cur != start) {
|
||||
cur!!.prev = prev
|
||||
prev = cur
|
||||
cur = cur.next
|
||||
}
|
||||
start.prev = prev
|
||||
return start
|
||||
}
|
||||
|
||||
fun createCycles(junk: Node) {
|
||||
val cycle1 = makeCycle(1, junk)
|
||||
val cycle2 = makeCycle(2, junk)
|
||||
val cycle10 = makeCycle(10, junk)
|
||||
val cycle100 = makeCycle(100, junk)
|
||||
val dcycle1 = makeDoubleCycle(1)
|
||||
val dcycle2 = makeDoubleCycle(2)
|
||||
val dcycle10 = makeDoubleCycle(10)
|
||||
val dcycle100 = makeDoubleCycle(100)
|
||||
|
||||
}
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
// Create outer link from cyclic garbage.
|
||||
val outer = Node(42, null, null, null)
|
||||
createCycles(outer)
|
||||
konan.internal.GC.collect()
|
||||
// Ensure outer is not collected.
|
||||
println(outer.data)
|
||||
}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
kotlin_version=1.1-M03
|
||||
#kotlinCompilerModule=org.jetbrains.kotlin:kotlin-compiler:1.1-SNAPSHOT
|
||||
kotlinCompilerModule=org.jetbrains.kotlin:kotlin-compiler:1.1-20161229.093306-344
|
||||
kotlinCompilerModule=org.jetbrains.kotlin:kotlin-compiler:1.1-20170116.100209-359
|
||||
@@ -2,5 +2,7 @@
|
||||
#define RUNTIME_COMMON_H
|
||||
|
||||
#define RUNTIME_NOTHROW __attribute__((nothrow))
|
||||
#define RUNTIME_CONST __attribute__((const))
|
||||
#define RUNTIME_PURE __attribute__((pure))
|
||||
|
||||
#endif // RUNTIME_COMMON_H
|
||||
|
||||
+375
-48
@@ -3,7 +3,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cstddef> // for offsetof
|
||||
#include <set> // only for memory tracing.
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "Assert.h"
|
||||
@@ -11,44 +11,193 @@
|
||||
#include "Memory.h"
|
||||
#include "Natives.h"
|
||||
|
||||
// Define to 1 to see all memory operations.
|
||||
#define TRACE_MEMORY 0
|
||||
// Define to 1 to use in multithreaded environment.
|
||||
// Define to 1 to use in the multithreaded environment.
|
||||
#define CONCURRENT 0
|
||||
// If garbage collection algorithm for cyclic garbage to be used.
|
||||
#define USE_GC 1
|
||||
// Define to 1 to print all memory operations.
|
||||
#define TRACE_MEMORY 0
|
||||
// Trace garbage collection phases.
|
||||
#define TRACE_GC_PHASES 0
|
||||
|
||||
ContainerHeader ObjHeader::theStaticObjectsContainer = { CONTAINER_TAG_NOCOUNT };
|
||||
ContainerHeader ObjHeader::theStaticObjectsContainer = {
|
||||
CONTAINER_TAG_NOCOUNT | CONTAINER_TAG_INCREMENT
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
// Current number of allocated containers.
|
||||
int allocCount = 0;
|
||||
#if TRACE_MEMORY
|
||||
// List of all global objects addresses.
|
||||
std::vector<KRef*>* globalObjects = nullptr;
|
||||
// Set of all containers.
|
||||
std::set<ContainerHeader*>* containers = nullptr;
|
||||
#if USE_GC
|
||||
// Collection threshold default (collect after having so many elements in the
|
||||
// release candidates set).
|
||||
constexpr size_t kGcThreshold = 10000;
|
||||
#endif
|
||||
|
||||
#if TRACE_MEMORY || USE_GC
|
||||
typedef std::unordered_set<ContainerHeader*> ContainerHeaderSet;
|
||||
typedef std::vector<ContainerHeader*> ContainerHeaderList;
|
||||
typedef std::vector<KRef*> KRefPtrList;
|
||||
#endif
|
||||
|
||||
struct MemoryState {
|
||||
// Current number of allocated containers.
|
||||
int allocCount = 0;
|
||||
|
||||
#if TRACE_MEMORY
|
||||
// List of all global objects addresses.
|
||||
KRefPtrList* globalObjects;
|
||||
// Set of all containers.
|
||||
ContainerHeaderSet* containers;
|
||||
#endif
|
||||
|
||||
#if USE_GC
|
||||
// Set of references to release.
|
||||
ContainerHeaderSet* toFree;
|
||||
// How many GC suspend requests happened.
|
||||
int gcSuspendCount;
|
||||
// How many candidate elements in toFree shall trigger collection.
|
||||
size_t gcThreshold;
|
||||
// If collection is in progress.
|
||||
bool gcInProgress;
|
||||
#endif
|
||||
};
|
||||
|
||||
MemoryState* memoryState = nullptr;
|
||||
|
||||
#if USE_GC
|
||||
bool isPermanent(const ContainerHeader* header) {
|
||||
return (header->ref_count_ & CONTAINER_TAG_MASK) == CONTAINER_TAG_NOCOUNT;
|
||||
}
|
||||
|
||||
// Must be vector or map 'container -> number', to keep reference counters correct.
|
||||
ContainerHeaderList collectMutableReferred(ContainerHeader* header) {
|
||||
ContainerHeaderList result;
|
||||
ObjHeader* obj = reinterpret_cast<ObjHeader*>(header + 1);
|
||||
const TypeInfo* typeInfo = obj->type_info();
|
||||
// TODO: generalize iteration over all references.
|
||||
// TODO: this code relies on single object per container assumption.
|
||||
for (int index = 0; index < typeInfo->objOffsetsCount_; index++) {
|
||||
ObjHeader** location = reinterpret_cast<ObjHeader**>(
|
||||
reinterpret_cast<uintptr_t>(obj + 1) + typeInfo->objOffsets_[index]);
|
||||
ObjHeader* obj = *location;
|
||||
if (obj != nullptr && !isPermanent(obj->container())) {
|
||||
result.push_back(obj->container());
|
||||
}
|
||||
}
|
||||
if (typeInfo == theArrayTypeInfo) {
|
||||
ArrayHeader* array = obj->array();
|
||||
for (int index = 0; index < array->count_; index++) {
|
||||
ObjHeader* obj = *ArrayAddressOfElementAt(array, index);
|
||||
if (obj != nullptr && !isPermanent(obj->container())) {
|
||||
result.push_back(obj->container());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void dumpWorker(const char* prefix, ContainerHeader* header, ContainerHeaderSet* seen) {
|
||||
fprintf(stderr, "%s: %p (%08x): %d refs %s\n",
|
||||
prefix,
|
||||
header, header->ref_count_, header->ref_count_ >> CONTAINER_TAG_SHIFT,
|
||||
(header->ref_count_ & CONTAINER_TAG_SEEN) != 0 ? "X" : "-");
|
||||
seen->insert(header);
|
||||
auto children = collectMutableReferred(header);
|
||||
for (auto child : children) {
|
||||
if (seen->count(child) == 0) {
|
||||
dumpWorker(prefix, child, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dumpReachable(const char* prefix, const ContainerHeaderSet* roots) {
|
||||
ContainerHeaderSet seen;
|
||||
for (auto container : *roots) {
|
||||
dumpWorker(prefix, container, &seen);
|
||||
}
|
||||
}
|
||||
|
||||
void phase1(ContainerHeader* header) {
|
||||
if ((header->ref_count_ & CONTAINER_TAG_SEEN) != 0)
|
||||
return;
|
||||
header->ref_count_ |= CONTAINER_TAG_SEEN;
|
||||
auto containers = collectMutableReferred(header);
|
||||
for (auto container : containers) {
|
||||
container->ref_count_ -= CONTAINER_TAG_INCREMENT;
|
||||
phase1(container);
|
||||
}
|
||||
}
|
||||
|
||||
void phase2(ContainerHeader* header, ContainerHeaderSet* rootset) {
|
||||
if ((header->ref_count_ & CONTAINER_TAG_SEEN) == 0)
|
||||
return;
|
||||
if ((header->ref_count_ >> CONTAINER_TAG_SHIFT) != 0)
|
||||
rootset->insert(header);
|
||||
header->ref_count_ &= ~CONTAINER_TAG_SEEN;
|
||||
auto containers = collectMutableReferred(header);
|
||||
for (auto container : containers) {
|
||||
phase2(container, rootset);
|
||||
}
|
||||
}
|
||||
|
||||
void phase3(ContainerHeader* header) {
|
||||
if ((header->ref_count_ & CONTAINER_TAG_SEEN) != 0) {
|
||||
return;
|
||||
}
|
||||
header->ref_count_ |= CONTAINER_TAG_SEEN;
|
||||
auto containers = collectMutableReferred(header);
|
||||
for (auto container : containers) {
|
||||
container->ref_count_ += CONTAINER_TAG_INCREMENT;
|
||||
phase3(container);
|
||||
}
|
||||
}
|
||||
|
||||
void phase4(ContainerHeader* header, ContainerHeaderSet* toRemove) {
|
||||
auto ref_count = header->ref_count_ >> CONTAINER_TAG_SHIFT;
|
||||
bool seen = (ref_count > 0 && (header->ref_count_ & CONTAINER_TAG_SEEN) == 0) ||
|
||||
(ref_count == 0 && (header->ref_count_ & CONTAINER_TAG_SEEN) != 0);
|
||||
if (seen) return;
|
||||
|
||||
// Add to toRemove set.
|
||||
if (ref_count == 0)
|
||||
toRemove->insert(header);
|
||||
|
||||
// Update seen bit.
|
||||
if (ref_count == 0)
|
||||
header->ref_count_ |= CONTAINER_TAG_SEEN;
|
||||
else
|
||||
header->ref_count_ &= ~CONTAINER_TAG_SEEN;
|
||||
auto containers = collectMutableReferred(header);
|
||||
for (auto container : containers) {
|
||||
phase4(container, toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_GC
|
||||
|
||||
} // namespace
|
||||
|
||||
ContainerHeader* AllocContainer(size_t size) {
|
||||
ContainerHeader* result = reinterpret_cast<ContainerHeader*>(calloc(1, size));
|
||||
#if TRACE_MEMORY
|
||||
printf(">>> alloc %d -> %p\n", (int)size, result);
|
||||
containers->insert(result);
|
||||
fprintf(stderr, ">>> alloc %d -> %p\n", (int)size, result);
|
||||
memoryState->containers->insert(result);
|
||||
#endif
|
||||
// TODO: atomic increment in concurrent case.
|
||||
allocCount++;
|
||||
memoryState->allocCount++;
|
||||
return result;
|
||||
}
|
||||
|
||||
void FreeContainer(ContainerHeader* header) {
|
||||
RuntimeAssert(!isPermanent(header), "this kind of container shalln't be freed");
|
||||
#if TRACE_MEMORY
|
||||
printf("<<< free %p\n", header);
|
||||
containers->erase(header);
|
||||
fprintf(stderr, "<<< free %p\n", header);
|
||||
memoryState->containers->erase(header);
|
||||
#endif
|
||||
header->ref_count_ = CONTAINER_TAG_INVALID;
|
||||
|
||||
#if USE_GC
|
||||
if (memoryState->toFree)
|
||||
memoryState->toFree->erase(header);
|
||||
#endif
|
||||
// Now let's clean all object's fields in this container.
|
||||
// TODO: this is gross hack, relying on the fact that we now only alloc
|
||||
// ArenaContainer and ObjectContainer, which both have single element.
|
||||
@@ -69,10 +218,30 @@ void FreeContainer(ContainerHeader* header) {
|
||||
|
||||
// And release underlying memory.
|
||||
// TODO: atomic decrement in concurrent case.
|
||||
allocCount--;
|
||||
#if CONCURRENT
|
||||
#error "Atomic update of allocCount"
|
||||
#endif
|
||||
memoryState->allocCount--;
|
||||
free(header);
|
||||
}
|
||||
|
||||
#if USE_GC
|
||||
void FreeContainerNoRef(ContainerHeader* header) {
|
||||
RuntimeAssert(!isPermanent(header), "this kind of container shalln't be freed");
|
||||
#if TRACE_MEMORY
|
||||
fprintf(stderr, "<<< free %p\n", header);
|
||||
memoryState->containers->erase(header);
|
||||
#endif
|
||||
header->ref_count_ = CONTAINER_TAG_INVALID;
|
||||
#if USE_GC
|
||||
if (memoryState->toFree)
|
||||
memoryState->toFree->erase(header);
|
||||
#endif
|
||||
memoryState->allocCount--;
|
||||
free(header);
|
||||
}
|
||||
#endif
|
||||
|
||||
ArenaContainer::ArenaContainer(uint32_t size) {
|
||||
ArenaContainerHeader* header =
|
||||
static_cast<ArenaContainerHeader*>(AllocContainer(size + sizeof(ArenaContainerHeader)));
|
||||
@@ -92,7 +261,7 @@ void ObjectContainer::Init(const TypeInfo* type_info) {
|
||||
// header->ref_count_ is zero initialized by AllocContainer().
|
||||
SetMeta(GetPlace(), type_info);
|
||||
#if TRACE_MEMORY
|
||||
printf("object at %p\n", GetPlace());
|
||||
fprintf(stderr, "object at %p\n", GetPlace());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -109,7 +278,7 @@ void ArrayContainer::Init(const TypeInfo* type_info, uint32_t elements) {
|
||||
GetPlace()->count_ = elements;
|
||||
SetMeta(GetPlace()->obj(), type_info);
|
||||
#if TRACE_MEMORY
|
||||
printf("array at %p\n", GetPlace());
|
||||
fprintf(stderr, "array at %p\n", GetPlace());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -139,16 +308,37 @@ ArrayHeader* ArenaContainer::PlaceArray(const TypeInfo* type_info, int count) {
|
||||
|
||||
inline void AddRef(const ObjHeader* object) {
|
||||
#if TRACE_MEMORY
|
||||
printf("AddRef on %p in %p\n", object, object->container());
|
||||
fprintf(stderr, "AddRef on %p in %p\n", object, object->container());
|
||||
#endif
|
||||
AddRef(object->container());
|
||||
#if USE_GC
|
||||
// TODO: one could remove from toFree set here, as now container is reachable
|
||||
// from the rootset, so cannot be cycle collection candidate.
|
||||
// memoryState->toFree->erase(object->container());
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void ReleaseRef(const ObjHeader* object) {
|
||||
#if TRACE_MEMORY
|
||||
printf("ReleaseRef on %p in %p\n", object, object->container());
|
||||
fprintf(stderr, "ReleaseRef on %p in %p\n", object, object->container());
|
||||
#endif
|
||||
#if USE_GC
|
||||
// If object is not a cycle candidate - just return.
|
||||
if (Release(object->container())) {
|
||||
return;
|
||||
}
|
||||
#if TRACE_MEMORY
|
||||
fprintf(stderr, "%p is release candidate\n", object->container());
|
||||
#endif
|
||||
if (memoryState->toFree != nullptr) {
|
||||
memoryState->toFree->insert(object->container());
|
||||
if (memoryState->gcSuspendCount == 0 &&
|
||||
memoryState->toFree->size() > memoryState->gcThreshold)
|
||||
GarbageCollect();
|
||||
}
|
||||
#else // !USE_GC
|
||||
Release(object->container());
|
||||
#endif // USE_GC
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
@@ -162,36 +352,51 @@ void InitMemory() {
|
||||
==
|
||||
offsetof(ObjHeader , container_offset_negative_),
|
||||
"Layout mismatch");
|
||||
RuntimeAssert(memoryState == nullptr, "memory state must be clear");
|
||||
memoryState = new MemoryState();
|
||||
// TODO: initialize heap here.
|
||||
allocCount = 0;
|
||||
memoryState->allocCount = 0;
|
||||
#if TRACE_MEMORY
|
||||
globalObjects = new std::vector<KRef*>();
|
||||
containers = new std::set<ContainerHeader*>();
|
||||
memoryState->globalObjects = new KRefPtrList();
|
||||
memoryState->containers = new ContainerHeaderSet();
|
||||
#endif
|
||||
#if USE_GC
|
||||
#if CONCURRENT
|
||||
#error "Concurrent GC is not yet implemented"
|
||||
#endif
|
||||
memoryState->toFree = new ContainerHeaderSet();
|
||||
memoryState->gcInProgress = false;
|
||||
memoryState->gcThreshold = kGcThreshold;
|
||||
memoryState->gcSuspendCount = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DeinitMemory() {
|
||||
#if TRACE_MEMORY
|
||||
// Free all global objects, to ensure no memory leaks happens.
|
||||
for (auto location: *globalObjects) {
|
||||
printf("Release global in *%p: %p\n", location, *location);
|
||||
for (auto location: *memoryState->globalObjects) {
|
||||
fprintf(stderr, "Release global in *%p: %p\n", location, *location);
|
||||
UpdateGlobalRef(location, nullptr);
|
||||
}
|
||||
delete globalObjects;
|
||||
globalObjects = nullptr;
|
||||
delete memoryState->globalObjects;
|
||||
memoryState->globalObjects = nullptr;
|
||||
#endif
|
||||
|
||||
if (allocCount > 0) {
|
||||
#if USE_GC
|
||||
GarbageCollect();
|
||||
#endif // USE_GC
|
||||
|
||||
if (memoryState->allocCount > 0) {
|
||||
#if TRACE_MEMORY
|
||||
// TODO: move out of TRACE_MEMORY, once exceptions free memory.
|
||||
printf("*** Memory leaks, leaked %d containers ***\n", allocCount);
|
||||
for (auto container: *containers) {
|
||||
printf("Unfreed container %p, count = %d\n", container, container->ref_count_);
|
||||
}
|
||||
delete containers;
|
||||
containers = nullptr;
|
||||
fprintf(stderr, "*** Memory leaks, leaked %d containers ***\n", memoryState->allocCount);
|
||||
dumpReachable("", memoryState->containers);
|
||||
delete memoryState->containers;
|
||||
memoryState->containers = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
delete memoryState;
|
||||
memoryState = nullptr;
|
||||
}
|
||||
|
||||
// Now we ignore all placement hints and always allocate heap space for new object.
|
||||
@@ -242,7 +447,7 @@ OBJ_GETTER(InitInstance,
|
||||
// TODO: locking or smth lock-free in MT case?
|
||||
#endif
|
||||
#if TRACE_MEMORY
|
||||
globalObjects->push_back(location);
|
||||
memoryState->globalObjects->push_back(location);
|
||||
#endif
|
||||
RETURN_OBJ_RESULT();
|
||||
} catch (...) {
|
||||
@@ -254,7 +459,7 @@ OBJ_GETTER(InitInstance,
|
||||
|
||||
void SetLocalRef(ObjHeader** location, const ObjHeader* object) {
|
||||
#if TRACE_MEMORY
|
||||
printf("SetLocalRef *%p: %p\n", location, object);
|
||||
fprintf(stderr, "SetLocalRef *%p: %p\n", location, object);
|
||||
#endif
|
||||
*const_cast<const ObjHeader**>(location) = object;
|
||||
if (object != nullptr) {
|
||||
@@ -264,7 +469,7 @@ void SetLocalRef(ObjHeader** location, const ObjHeader* object) {
|
||||
|
||||
void SetGlobalRef(ObjHeader** location, const ObjHeader* object) {
|
||||
#if TRACE_MEMORY
|
||||
printf("SetGlobalRef *%p: %p\n", location, object);
|
||||
fprintf(stderr, "SetGlobalRef *%p: %p\n", location, object);
|
||||
#endif
|
||||
*const_cast<const ObjHeader**>(location) = object;
|
||||
if (object != nullptr) {
|
||||
@@ -278,16 +483,16 @@ void SetGlobalRef(ObjHeader** location, const ObjHeader* object) {
|
||||
void UpdateLocalRef(ObjHeader** location, const ObjHeader* object) {
|
||||
ObjHeader* old = *location;
|
||||
#if TRACE_MEMORY
|
||||
printf("UpdateLocalRef *%p: %p -> %p\n", location, old, object);
|
||||
fprintf(stderr, "UpdateLocalRef *%p: %p -> %p\n", location, old, object);
|
||||
#endif
|
||||
if (old != object) {
|
||||
if (object != nullptr) {
|
||||
AddRef(object);
|
||||
}
|
||||
*const_cast<const ObjHeader**>(location) = object;
|
||||
if (old > reinterpret_cast<ObjHeader*>(1)) {
|
||||
ReleaseRef(old);
|
||||
}
|
||||
if (object != nullptr) {
|
||||
AddRef(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +500,7 @@ void UpdateGlobalRef(ObjHeader** location, const ObjHeader* object) {
|
||||
#if CONCURRENT
|
||||
ObjHeader* old = *location;
|
||||
#if TRACE_MEMORY
|
||||
printf("UpdateGlobalRef *%p: %p -> %p\n", location, old, object);
|
||||
fprintf(stderr, "UpdateGlobalRef *%p: %p -> %p\n", location, old, object);
|
||||
#endif
|
||||
if (old != object) {
|
||||
if (object != nullptr) {
|
||||
@@ -320,7 +525,7 @@ void UpdateGlobalRef(ObjHeader** location, const ObjHeader* object) {
|
||||
|
||||
void ReleaseLocalRefs(ObjHeader** start, int count) {
|
||||
#if TRACE_MEMORY
|
||||
printf("ReleaseLocalRefs %p .. %p\n", start, start + count);
|
||||
fprintf(stderr, "ReleaseLocalRefs %p .. %p\n", start, start + count);
|
||||
#endif
|
||||
ObjHeader** current = start;
|
||||
while (count-- > 0) {
|
||||
@@ -336,7 +541,7 @@ void ReleaseLocalRefs(ObjHeader** start, int count) {
|
||||
|
||||
void ReleaseGlobalRefs(ObjHeader** start, int count) {
|
||||
#if TRACE_MEMORY
|
||||
printf("ReleaseGlobalRefs %p .. %p\n", start, start + count);
|
||||
fprintf(stderr, "ReleaseGlobalRefs %p .. %p\n", start, start + count);
|
||||
#endif
|
||||
#if CONCURRENT
|
||||
ObjHeader** current = start;
|
||||
@@ -364,4 +569,126 @@ void ReleaseGlobalRefs(ObjHeader** start, int count) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_GC
|
||||
void GarbageCollect() {
|
||||
RuntimeAssert(memoryState->toFree != nullptr, "GC must not be stopped");
|
||||
RuntimeAssert(!memoryState->gcInProgress, "Recursive GC is disallowed");
|
||||
memoryState->gcInProgress = true;
|
||||
// Traverse inner pointers in the closure of release candidates, and
|
||||
// temporary decrement refs on them. Set CONTAINER_TAG_SEEN while traversing.
|
||||
#if TRACE_GC_PHASES
|
||||
dumpReachable("P0", memoryState->toFree);
|
||||
#endif
|
||||
for (auto container : *memoryState->toFree) {
|
||||
phase1(container);
|
||||
}
|
||||
#if TRACE_GC_PHASES
|
||||
dumpReachable("P1", memoryState->toFree);
|
||||
#endif
|
||||
|
||||
// Collect rootset from containers with non-zero reference counter. Those must
|
||||
// be referenced from outside of newly released object graph.
|
||||
// Clear CONTAINER_TAG_SEEN while traversing.
|
||||
ContainerHeaderSet rootset;
|
||||
for (auto container : *memoryState->toFree) {
|
||||
phase2(container, &rootset);
|
||||
}
|
||||
#if TRACE_GC_PHASES
|
||||
dumpReachable("P2", memoryState->toFree);
|
||||
#endif
|
||||
|
||||
// Increment references for all elements reachable from the rootset.
|
||||
// Set CONTAINER_TAG_SEEN while traversing.
|
||||
for (auto container : rootset) {
|
||||
#if TRACE_MEMORY
|
||||
fprintf(stderr, "rootset %p\n", container);
|
||||
#endif
|
||||
phase3(container);
|
||||
}
|
||||
#if TRACE_GC_PHASES
|
||||
dumpReachable("P3", memoryState->toFree);
|
||||
#endif
|
||||
|
||||
// Traverse all elements, and collect those not having CONTAINER_TAG_SEEN and zero RC.
|
||||
// Clear CONTAINER_TAG_SEEN while traversing on live elements, set in on dead elements.
|
||||
ContainerHeaderSet toRemove;
|
||||
for (auto container : *memoryState->toFree) {
|
||||
phase4(container, &toRemove);
|
||||
}
|
||||
#if TRACE_GC_PHASES
|
||||
dumpReachable("P4", memoryState->toFree);
|
||||
#endif
|
||||
|
||||
// Clear cycle candidates list.
|
||||
memoryState->toFree->clear();
|
||||
|
||||
for (auto header : toRemove) {
|
||||
RuntimeAssert((header->ref_count_ & CONTAINER_TAG_SEEN) != 0, "Must be not seen");
|
||||
FreeContainerNoRef(header);
|
||||
}
|
||||
|
||||
memoryState->gcInProgress = false;
|
||||
}
|
||||
|
||||
#endif // USE_GC
|
||||
|
||||
void Kotlin_konan_internal_GC_collect(KRef) {
|
||||
#if USE_GC
|
||||
GarbageCollect();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_suspend(KRef) {
|
||||
#if USE_GC
|
||||
memoryState->gcSuspendCount++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_resume(KRef) {
|
||||
#if USE_GC
|
||||
if (memoryState->gcSuspendCount > 0) {
|
||||
memoryState->gcSuspendCount--;
|
||||
if (memoryState->toFree != nullptr &&
|
||||
memoryState->toFree->size() >= memoryState->gcThreshold) {
|
||||
GarbageCollect();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_stop(KRef) {
|
||||
#if USE_GC
|
||||
if (memoryState->toFree != nullptr) {
|
||||
GarbageCollect();
|
||||
delete memoryState->toFree;
|
||||
memoryState->toFree = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_start(KRef) {
|
||||
#if USE_GC
|
||||
if (memoryState->toFree == nullptr) {
|
||||
memoryState->toFree = new ContainerHeaderSet();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kotlin_konan_internal_GC_setThreshold(KRef, KInt value) {
|
||||
#if USE_GC
|
||||
if (value > 0) {
|
||||
memoryState->gcThreshold = value;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
KInt Kotlin_konan_internal_GC_getThreshold(KRef) {
|
||||
#if USE_GC
|
||||
return memoryState->gcThreshold;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -26,10 +26,14 @@ typedef enum {
|
||||
CONTAINER_TAG_SHARED = 2,
|
||||
// Container is no longer valid.
|
||||
CONTAINER_TAG_INVALID = 3,
|
||||
// Container was seen during GC.
|
||||
CONTAINER_TAG_SEEN = 4,
|
||||
// Shift to get actual counter..
|
||||
CONTAINER_TAG_SHIFT = 3,
|
||||
// Actual value to increment/decrement conatiner by. Tag is in lower bits.
|
||||
CONTAINER_TAG_INCREMENT = 1 << 2,
|
||||
// Mask for container type.
|
||||
CONTAINER_TAG_MASK = (CONTAINER_TAG_INCREMENT - 1)
|
||||
CONTAINER_TAG_INCREMENT = 1 << CONTAINER_TAG_SHIFT,
|
||||
// Mask for container type, disregard seen bit.
|
||||
CONTAINER_TAG_MASK = ((CONTAINER_TAG_INCREMENT >> 1) - 1)
|
||||
} ContainerTag;
|
||||
|
||||
// Could be made 64-bit for large memory configs.
|
||||
@@ -127,8 +131,8 @@ inline uint32_t ArrayDataSizeBytes(const ArrayHeader* obj) {
|
||||
|
||||
void FreeContainer(ContainerHeader* header);
|
||||
|
||||
// Those two operations are implemented by translator when storing references
|
||||
// to objects.
|
||||
// TODO: those two operations can be implemented by translator when storing
|
||||
// reference to an object.
|
||||
inline void AddRef(ContainerHeader* header) {
|
||||
// Looking at container type we may want to skip AddRef() totally
|
||||
// (non-escaping stack objects, constant objects).
|
||||
@@ -144,18 +148,25 @@ inline void AddRef(ContainerHeader* header) {
|
||||
case CONTAINER_TAG_INVALID:
|
||||
RuntimeAssert(false, "trying to addref invalid container");
|
||||
break;
|
||||
default:
|
||||
RuntimeAssert(false, "unknown container type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void Release(ContainerHeader* header) {
|
||||
// Release returns 'true' iff container cannot be part of cycle (either NOCOUNT
|
||||
// object or container was fully released and will be collected).
|
||||
inline bool Release(ContainerHeader* header) {
|
||||
switch (header->ref_count_ & CONTAINER_TAG_MASK) {
|
||||
case CONTAINER_TAG_NORMAL:
|
||||
if ((header->ref_count_ -= CONTAINER_TAG_INCREMENT) == CONTAINER_TAG_NORMAL) {
|
||||
FreeContainer(header);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case CONTAINER_TAG_NOCOUNT:
|
||||
break;
|
||||
// NOCOUNT containers aren't loop candidate.
|
||||
return true;
|
||||
// Note that shared containers have potentially subtle race, if object holds a
|
||||
// reference to another object, stored in shorter living container. In this
|
||||
// case there's unlikely, but possible case, where one mutator takes reference,
|
||||
@@ -173,12 +184,18 @@ inline void Release(ContainerHeader* header) {
|
||||
if (__sync_sub_and_fetch(
|
||||
&header->ref_count_, CONTAINER_TAG_INCREMENT) == CONTAINER_TAG_SHARED) {
|
||||
FreeContainer(header);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case CONTAINER_TAG_INVALID:
|
||||
RuntimeAssert(false, "trying to release invalid container");
|
||||
break;
|
||||
default:
|
||||
RuntimeAssert(false, "unknown container type");
|
||||
break;
|
||||
}
|
||||
// Object with non-zero counter after release are loop candidates.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Class representing arbitrary placement container.
|
||||
@@ -346,6 +363,8 @@ void UpdateGlobalRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTH
|
||||
// Optimization: release all references in range.
|
||||
void ReleaseLocalRefs(ObjHeader** start, int count) RUNTIME_NOTHROW;
|
||||
void ReleaseGlobalRefs(ObjHeader** start, int count) RUNTIME_NOTHROW;
|
||||
// Collect garbage, which cannot be found by reference counting (cycles).
|
||||
void GarbageCollect() RUNTIME_NOTHROW;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ KInt Kotlin_Byte_unaryPlus (KByte a ) { return +a; }
|
||||
KInt Kotlin_Byte_unaryMinus (KByte a ) { return -a; }
|
||||
|
||||
KByte Kotlin_Byte_toByte (KByte a ) { return a; }
|
||||
KChar Kotlin_Byte_toChar (KByte a ) { return a; }
|
||||
KShort Kotlin_Byte_toShort (KByte a ) { return a; }
|
||||
KInt Kotlin_Byte_toInt (KByte a ) { return a; }
|
||||
KLong Kotlin_Byte_toLong (KByte a ) { return a; }
|
||||
@@ -136,6 +137,7 @@ KInt Kotlin_Short_unaryPlus (KShort a ) { return +a; }
|
||||
KInt Kotlin_Short_unaryMinus (KShort a ) { return -a; }
|
||||
|
||||
KByte Kotlin_Short_toByte (KShort a ) { return a; }
|
||||
KChar Kotlin_Short_toChar (KShort a ) { return a; }
|
||||
KShort Kotlin_Short_toShort (KShort a ) { return a; }
|
||||
KInt Kotlin_Short_toInt (KShort a ) { return a; }
|
||||
KLong Kotlin_Short_toLong (KShort a ) { return a; }
|
||||
@@ -268,6 +270,7 @@ KLong Kotlin_Long_ushr_Int (KLong a, KInt b) {
|
||||
}
|
||||
|
||||
KByte Kotlin_Long_toByte (KLong a ) { return a; }
|
||||
KChar Kotlin_Long_toChar (KLong a ) { return a; }
|
||||
KShort Kotlin_Long_toShort (KLong a ) { return a; }
|
||||
KInt Kotlin_Long_toInt (KLong a ) { return a; }
|
||||
KLong Kotlin_Long_toLong (KLong a ) { return a; }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
// If one shall use binary search when looking up methods and fields.
|
||||
// TODO: maybe select strategy basing on number of elements.
|
||||
#define USE_BINARY_SEARCH 0
|
||||
#define USE_BINARY_SEARCH 1
|
||||
|
||||
extern "C" {
|
||||
#if USE_BINARY_SEARCH
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Names.h"
|
||||
|
||||
// An element of sorted by hash in-place array representing methods.
|
||||
@@ -33,24 +34,30 @@ struct TypeInfo {
|
||||
const TypeInfo* const* implementedInterfaces_;
|
||||
int32_t implementedInterfacesCount_;
|
||||
// Null for abstract classes and interfaces.
|
||||
// TODO: place vtable at the end of TypeInfo to eliminate the indirection.
|
||||
void* const* vtable_;
|
||||
// Null for abstract classes and interfaces.
|
||||
const MethodTableRecord* openMethods_;
|
||||
uint32_t openMethodsCount_;
|
||||
const FieldTableRecord* fields_;
|
||||
// Is negative to mark an interface.
|
||||
int32_t fieldsCount_;
|
||||
|
||||
// vtable starts just after declared contents of the TypeInfo:
|
||||
// void* const vtable_[];
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// Find offset of given hash in table.
|
||||
int LookupFieldOffset(const TypeInfo* type_info, FieldNameHash hash);
|
||||
// Note, that we use attribute const, which assumes function doesn't
|
||||
// dereference global memory, while this function does. However, it seems
|
||||
// to be safe, as actual result of this computation depends only on 'type_info'
|
||||
// and 'hash' numeric values and doesn't really depends on global memory state
|
||||
// (as TypeInfo is compile time constant and type info pointers are stable).
|
||||
int LookupFieldOffset(const TypeInfo* type_info, FieldNameHash hash) RUNTIME_CONST;
|
||||
|
||||
// Find open method by its hash. Other methods are resolved in compile-time.
|
||||
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature);
|
||||
// See comment in LookupFieldOffset().
|
||||
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) RUNTIME_CONST;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef RUNTIME_TYPES_H
|
||||
#define RUNTIME_TYPES_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Memory.h"
|
||||
#include "TypeInfo.h"
|
||||
|
||||
@@ -36,9 +37,9 @@ extern const TypeInfo* theBooleanArrayTypeInfo;
|
||||
extern const TypeInfo* theStringTypeInfo;
|
||||
extern const TypeInfo* theThrowableTypeInfo;
|
||||
|
||||
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info);
|
||||
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE;
|
||||
void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
|
||||
KBoolean IsArray(KConstRef obj);
|
||||
KBoolean IsArray(KConstRef obj) RUNTIME_PURE;
|
||||
|
||||
typedef void (*Initializer)();
|
||||
struct InitNode {
|
||||
|
||||
@@ -26,21 +26,11 @@ public annotation class Used
|
||||
|
||||
// Following annotations can be used to mark functions that need to be fixed,
|
||||
// once certain language feature is implemented.
|
||||
/**
|
||||
* Need to be fixed because of boxing.
|
||||
*/
|
||||
public annotation class FixmeBoxing
|
||||
|
||||
/**
|
||||
* Need to be fixed because of inner classes.
|
||||
*/
|
||||
public annotation class FixmeInner
|
||||
|
||||
/**
|
||||
* Need to be fixed because of lambdas.
|
||||
*/
|
||||
public annotation class FixmeLambda
|
||||
|
||||
/**
|
||||
* Need to be fixed.
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,76 @@ class BooleanBox(val value: Boolean) : Comparable<Boolean> {
|
||||
|
||||
fun boxBoolean(value: Boolean) = BooleanBox(value)
|
||||
|
||||
class CharBox(val value: Char) : Comparable<Char> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is CharBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
|
||||
override fun compareTo(other: Char): Int = value.compareTo(other)
|
||||
}
|
||||
|
||||
fun boxChar(value: Char) = CharBox(value)
|
||||
|
||||
class ByteBox(val value: Byte) : Number(), Comparable<Byte> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is ByteBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
|
||||
override fun compareTo(other: Byte): Int = value.compareTo(other)
|
||||
|
||||
override fun toByte() = value.toByte()
|
||||
override fun toChar() = value.toChar()
|
||||
override fun toShort() = value.toShort()
|
||||
override fun toInt() = value.toInt()
|
||||
override fun toLong() = value.toLong()
|
||||
override fun toFloat() = value.toFloat()
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
fun boxByte(value: Byte) = ByteBox(value)
|
||||
|
||||
class ShortBox(val value: Short) : Number(), Comparable<Short> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is ShortBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
|
||||
override fun compareTo(other: Short): Int = value.compareTo(other)
|
||||
|
||||
override fun toByte() = value.toByte()
|
||||
override fun toChar() = value.toChar()
|
||||
override fun toShort() = value.toShort()
|
||||
override fun toInt() = value.toInt()
|
||||
override fun toLong() = value.toLong()
|
||||
override fun toFloat() = value.toFloat()
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
fun boxShort(value: Short) = ShortBox(value)
|
||||
|
||||
class IntBox(val value: Int) : Number(), Comparable<Int> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is IntBox) {
|
||||
@@ -46,4 +116,80 @@ class IntBox(val value: Int) : Number(), Comparable<Int> {
|
||||
|
||||
fun boxInt(value: Int) = IntBox(value)
|
||||
|
||||
// TODO: support other boxes.
|
||||
class LongBox(val value: Long) : Number(), Comparable<Long> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is LongBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
|
||||
override fun compareTo(other: Long): Int = value.compareTo(other)
|
||||
|
||||
override fun toByte() = value.toByte()
|
||||
override fun toChar() = value.toChar()
|
||||
override fun toShort() = value.toShort()
|
||||
override fun toInt() = value.toInt()
|
||||
override fun toLong() = value.toLong()
|
||||
override fun toFloat() = value.toFloat()
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
fun boxLong(value: Long) = LongBox(value)
|
||||
|
||||
class FloatBox(val value: Float) : Number(), Comparable<Float> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is FloatBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
|
||||
override fun compareTo(other: Float): Int = value.compareTo(other)
|
||||
|
||||
override fun toByte() = value.toByte()
|
||||
override fun toChar() = value.toChar()
|
||||
override fun toShort() = value.toShort()
|
||||
override fun toInt() = value.toInt()
|
||||
override fun toLong() = value.toLong()
|
||||
override fun toFloat() = value.toFloat()
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
fun boxFloat(value: Float) = FloatBox(value)
|
||||
|
||||
class DoubleBox(val value: Double) : Number(), Comparable<Double> {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is DoubleBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
|
||||
override fun compareTo(other: Double): Int = value.compareTo(other)
|
||||
|
||||
override fun toByte() = value.toByte()
|
||||
override fun toChar() = value.toChar()
|
||||
override fun toShort() = value.toShort()
|
||||
override fun toInt() = value.toInt()
|
||||
override fun toLong() = value.toLong()
|
||||
override fun toFloat() = value.toFloat()
|
||||
override fun toDouble() = value.toDouble()
|
||||
}
|
||||
|
||||
fun boxDouble(value: Double) = DoubleBox(value)
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package konan.internal
|
||||
|
||||
// Cycle garbage collector interface.
|
||||
//
|
||||
// Konan relies upon reference counting for object management, however it could
|
||||
// not collect cyclical garbage, so we perform periodic garbage collection.
|
||||
// This may slow down application, so this interface provides control over how
|
||||
// garbage collector activates and runs.
|
||||
// Garbage collector can be in one of the following states:
|
||||
// * running
|
||||
// * suspended (so cycle candidates are collected, but GC is not performed until resume)
|
||||
// * stopped (all cyclical garbage is hopelessly lost)
|
||||
// Immediately after startup GC is in running state.
|
||||
// Depending on application needs it may select to suspend GC for certain phases of
|
||||
// its lifetime, and resume it later on, or just completely turn it off, if GC pauses
|
||||
// are less desirable than cyclical garbage leaks.
|
||||
object GC {
|
||||
// To force garbage collection immediately, unless collector is stopped
|
||||
// with stop() operation. Even if GC is suspended, collect() still triggers collection.
|
||||
@SymbolName("Kotlin_konan_internal_GC_collect")
|
||||
external fun collect()
|
||||
|
||||
// Suspend garbage collection. Release candidates are still collected, but
|
||||
// GC algorithm is not executed.
|
||||
@SymbolName("Kotlin_konan_internal_GC_suspend")
|
||||
external fun suspend()
|
||||
|
||||
// Resume garbage collection. Can potentially lead to GC immediately.
|
||||
@SymbolName("Kotlin_konan_internal_GC_resume")
|
||||
external fun resume()
|
||||
|
||||
// Stop garbage collection. Cyclical garbage is no longer collected.
|
||||
@SymbolName("Kotlin_konan_internal_GC_stop")
|
||||
external fun stop()
|
||||
|
||||
// Start garbage collection. Cyclical garbage produced while GC was stopped
|
||||
// cannot be reclaimed, but all new garbage is collected.
|
||||
@SymbolName("Kotlin_konan_internal_GC_start")
|
||||
external fun start()
|
||||
|
||||
// GC threshold, controlling how frequenly GC is activated, and how much time GC
|
||||
// takes. Bigger values lead to longer GC pauses, but less GCs.
|
||||
var threshold: Int
|
||||
get() = getThreshold()
|
||||
set(value) = setThreshold(value)
|
||||
|
||||
@SymbolName("Kotlin_konan_internal_GC_getThreshold")
|
||||
private external fun getThreshold(): Int
|
||||
|
||||
@SymbolName("Kotlin_konan_internal_GC_setThreshold")
|
||||
private external fun setThreshold(value: Int)
|
||||
}
|
||||
@@ -3,9 +3,15 @@ package kotlin
|
||||
// TODO: remove that, as RTTI shall be per instantiation.
|
||||
@ExportTypeInfo("theArrayTypeInfo")
|
||||
public final class Array<T> : Cloneable {
|
||||
// TODO: actual constructor has initializer parameter, implement it once lambdas are implemented.
|
||||
// Constructors are handled with compiler magic.
|
||||
public constructor(@Suppress("UNUSED_PARAMETER") size: Int) {}
|
||||
public constructor(size: Int, init: (Int) -> T) {
|
||||
var index = 0
|
||||
while (index < size) {
|
||||
this[index] = init(index)
|
||||
index++
|
||||
}
|
||||
}
|
||||
internal constructor(@Suppress("UNUSED_PARAMETER") size: Int) {}
|
||||
|
||||
public val size: Int
|
||||
get() = getArrayLength()
|
||||
|
||||
@@ -507,3 +507,89 @@ public val <T> Array<out T>.indices: IntRange
|
||||
*/
|
||||
public val <T> Array<out T>.lastIndex: Int
|
||||
get() = size - 1
|
||||
|
||||
/**
|
||||
* Applies the given [transform] function to each element of the original array
|
||||
* and appends the results to the given [destination].
|
||||
*/
|
||||
public inline fun <T, R, C : MutableCollection<in R>> Array<out T>.mapTo(destination: C, transform: (T) -> R): C {
|
||||
for (item in this)
|
||||
destination.add(transform(item))
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing the results of applying the given [transform] function
|
||||
* to each element in the original array.
|
||||
*/
|
||||
public inline fun <T, R> Array<out T>.map(transform: (T) -> R): List<R> {
|
||||
return mapTo(ArrayList<R>(size), transform)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of all elements in the array.
|
||||
*/
|
||||
public fun Array<out Byte>.sum(): Int {
|
||||
var sum: Int = 0
|
||||
for (element in this) {
|
||||
sum += element
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of all elements in the array.
|
||||
*/
|
||||
public fun Array<out Short>.sum(): Int {
|
||||
var sum: Int = 0
|
||||
for (element in this) {
|
||||
sum += element
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of all elements in the array.
|
||||
*/
|
||||
public fun Array<out Int>.sum(): Int {
|
||||
var sum: Int = 0
|
||||
for (element in this) {
|
||||
sum += element
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of all elements in the array.
|
||||
*/
|
||||
public fun Array<out Long>.sum(): Long {
|
||||
var sum: Long = 0L
|
||||
for (element in this) {
|
||||
sum += element
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of all elements in the array.
|
||||
*/
|
||||
public fun Array<out Float>.sum(): Float {
|
||||
var sum: Float = 0.0f
|
||||
for (element in this) {
|
||||
sum += element
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of all elements in the array.
|
||||
*/
|
||||
public fun Array<out Double>.sum(): Double {
|
||||
var sum: Double = 0.0
|
||||
for (element in this) {
|
||||
sum += element
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1091,8 +1091,7 @@ public final class Float : Number(), Comparable<Float> {
|
||||
|
||||
@SymbolName("Kotlin_Float_toByte")
|
||||
external public override fun toByte(): Byte
|
||||
@SymbolName("Kotlin_Float_toChar")
|
||||
external public override fun toChar(): Char
|
||||
public override fun toChar(): Char = this.toInt().toChar()
|
||||
@SymbolName("Kotlin_Float_toShort")
|
||||
external public override fun toShort(): Short
|
||||
@SymbolName("Kotlin_Float_toInt")
|
||||
@@ -1302,8 +1301,7 @@ public final class Double : Number(), Comparable<Double> {
|
||||
|
||||
@SymbolName("Kotlin_Double_toByte")
|
||||
external public override fun toByte(): Byte
|
||||
@SymbolName("Kotlin_Double_toChar")
|
||||
external public override fun toChar(): Char
|
||||
public override fun toChar(): Char = this.toInt().toChar()
|
||||
@SymbolName("Kotlin_Double_toShort")
|
||||
external public override fun toShort(): Short
|
||||
@SymbolName("Kotlin_Double_toInt")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package kotlin.collections
|
||||
|
||||
/**
|
||||
* Returns an array of objects of the given type with the given [size], initialized with **lateinit** _uninitialized_ values.
|
||||
* Returns an array of objects of the given type with the given [size], initialized with _uninitialized_ values.
|
||||
* Attempts to read _uninitialized_ values from this array work in implementation-dependent manner,
|
||||
* either throwing exception or returning some kind of implementation-specific default value.
|
||||
*/
|
||||
fun <E> arrayOfUninitializedElements(size: Int): Array<E> {
|
||||
// return (if (size == 0) emptyArray else Array<Any>(size)) as Array<E>
|
||||
return Array<E>(size)
|
||||
internal fun <E> arrayOfUninitializedElements(size: Int): Array<E> {
|
||||
// TODO: special case for size == 0?
|
||||
return Array<E>(size)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ fun <E> arrayOfUninitializedElements(size: Int): Array<E> {
|
||||
* either throwing exception or returning some kind of implementation-specific default value.
|
||||
*/
|
||||
fun <E> Array<E>.copyOfUninitializedElements(newSize: Int): Array<E> {
|
||||
val result = Array<E>(newSize)
|
||||
val result = arrayOfUninitializedElements<E>(newSize)
|
||||
this.copyRangeTo(result, 0, if (newSize > this.size) this.size else newSize, 0)
|
||||
return result
|
||||
}
|
||||
@@ -33,7 +33,7 @@ fun IntArray.copyOfUninitializedElements(newSize: Int): IntArray {
|
||||
* Attempts to read _uninitialized_ value work in implementation-dependent manner,
|
||||
* either throwing exception or returning some kind of implementation-specific default value.
|
||||
*/
|
||||
fun <E> Array<E>.resetAt(index: Int) {
|
||||
internal fun <E> Array<E>.resetAt(index: Int) {
|
||||
(@Suppress("UNCHECKED_CAST")(this as Array<Any?>))[index] = null
|
||||
}
|
||||
|
||||
@@ -50,11 +50,11 @@ external private fun fillImpl(array: IntArray, fromIndex: Int, toIndex: Int, val
|
||||
* Attempts to read _uninitialized_ values work in implementation-dependent manner,
|
||||
* either throwing exception or returning some kind of implementation-specific default value.
|
||||
*/
|
||||
fun <E> Array<E>.resetRange(fromIndex: Int, toIndex: Int) {
|
||||
internal fun <E> Array<E>.resetRange(fromIndex: Int, toIndex: Int) {
|
||||
fillImpl(@Suppress("UNCHECKED_CAST") (this as Array<Any>), fromIndex, toIndex, null)
|
||||
}
|
||||
|
||||
fun IntArray.fill(fromIndex: Int, toIndex: Int, value: Int) {
|
||||
internal fun IntArray.fill(fromIndex: Int, toIndex: Int, value: Int) {
|
||||
fillImpl(this, fromIndex, toIndex, value)
|
||||
}
|
||||
|
||||
|
||||
@@ -59,9 +59,8 @@ public inline fun <K, V> mapOf(): Map<K, V> = emptyMap()
|
||||
* @sample samples.collections.Maps.Instantiation.mutableMapFromPairs
|
||||
* @sample samples.collections.Maps.Instantiation.emptyMutableMap
|
||||
*/
|
||||
@Fixme
|
||||
public fun <K, V> mutableMapOf(vararg pairs: Pair<K, V>): MutableMap<K, V> = hashMapOf(*pairs)
|
||||
// = HashMap<K, V>(mapCapacity(pairs.size)).apply { putAll(pairs) }
|
||||
public fun <K, V> mutableMapOf(vararg pairs: Pair<K, V>): MutableMap<K, V>
|
||||
= HashMap<K, V>(mapCapacity(pairs.size)).apply { putAll(pairs) }
|
||||
|
||||
/**
|
||||
* Returns a new [HashMap] with the specified contents, given as a list of pairs
|
||||
@@ -69,13 +68,8 @@ public fun <K, V> mutableMapOf(vararg pairs: Pair<K, V>): MutableMap<K, V> = has
|
||||
*
|
||||
* @sample samples.collections.Maps.Instantiation.hashMapFromPairs
|
||||
*/
|
||||
public fun <K, V> hashMapOf(vararg pairs: Pair<K, V>): HashMap<K, V> {
|
||||
// = HashMap<K, V>(mapCapacity(pairs.size)).apply { putAll(pairs) }
|
||||
val result = HashMap<K, V>(mapCapacity(pairs.size))
|
||||
for (pair in pairs)
|
||||
result.put(pair.first, pair.second)
|
||||
return result
|
||||
}
|
||||
public fun <K, V> hashMapOf(vararg pairs: Pair<K, V>): HashMap<K, V>
|
||||
= HashMap<K, V>(mapCapacity(pairs.size)).apply { putAll(pairs) }
|
||||
|
||||
/**
|
||||
* Returns a new [HashMap] with the specified contents, given as a list of pairs
|
||||
@@ -649,11 +643,9 @@ public inline fun <K, V, R : Any> Map<out K, V>.mapNotNull(transform: (Map.Entry
|
||||
* Applies the given [transform] function to each entry in the original map
|
||||
* and appends only the non-null results to the given [destination].
|
||||
*/
|
||||
@FixmeLambda
|
||||
public inline fun <K, V, R : Any, C : MutableCollection<in R>> Map<out K, V>.mapNotNullTo(destination: C, transform: (Map.Entry<K, V>) -> R?): C {
|
||||
TODO()
|
||||
//forEach { element -> transform(element)?.let { destination.add(it) } }
|
||||
//return destination
|
||||
forEach { element -> transform(element)?.let { destination.add(it) } }
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -149,20 +149,16 @@ public fun <T> MutableIterable<T>.removeAll(predicate: (T) -> Boolean): Boolean
|
||||
*/
|
||||
public fun <T> MutableIterable<T>.retainAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, false)
|
||||
|
||||
// TODO: due to boxing problems, cannot use Boolean type.
|
||||
@FixmeBoxing
|
||||
private fun <T> MutableIterable<T>.filterInPlace(predicate: (T) -> Boolean, predicateResultToRemove: Boolean): Boolean {
|
||||
TODO()
|
||||
/*
|
||||
var result = false
|
||||
with (iterator()) {
|
||||
while (hasNext())
|
||||
if (predicate(next()).toString() == predicateResultToRemove) {
|
||||
if (predicate(next()) == predicateResultToRemove) {
|
||||
remove()
|
||||
result = true
|
||||
}
|
||||
}
|
||||
return result */
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user