This commit is contained in:
Dmitry Petrov
2016-08-25 15:45:06 +03:00
committed by Dmitry Petrov
parent d45811a5da
commit b17e3b0299
14 changed files with 255 additions and 12 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.psiUtil.endOffset
@@ -24,6 +25,7 @@ import org.jetbrains.kotlin.psi2ir.intermediate.*
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.scopes.receivers.*
import java.lang.AssertionError
fun StatementGenerator.generateReceiverOrNull(ktDefaultElement: KtElement, receiver: ReceiverValue?): IntermediateValue? =
receiver?.let { generateReceiver(ktDefaultElement, receiver) }
@@ -82,14 +84,47 @@ fun StatementGenerator.generateCallReceiver(
}
}
fun StatementGenerator.generateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? =
fun StatementGenerator.generateVarargExpression(varargArgument: VarargValueArgument, valueParameter: ValueParameterDescriptor) : IrExpression? {
if (varargArgument.arguments.isEmpty()) {
return null
}
val varargStartOffset = varargArgument.arguments.fold(Int.MAX_VALUE) { minStartOffset, argument ->
Math.min(minStartOffset, argument.asElement().startOffset)
}
val varargEndOffset = varargArgument.arguments.fold(Int.MIN_VALUE) { maxEndOffset, argument ->
Math.max(maxEndOffset, argument.asElement().endOffset)
}
val varargElementType = valueParameter.varargElementType ?:
throw AssertionError("Vararg argument for non-vararg parameter $valueParameter")
val irVararg = IrVarargImpl(varargStartOffset, varargEndOffset, valueParameter.type, varargElementType)
for (argument in varargArgument.arguments) {
val ktArgumentExpression = argument.getArgumentExpression() ?:
throw AssertionError("No argument expression for vararg element ${argument.asElement().text}")
val irVarargElement =
if (argument.getSpreadElement() != null)
IrSpreadElementImpl(ktArgumentExpression.startOffset, ktArgumentExpression.endOffset,
generateExpression(ktArgumentExpression))
else
generateExpression(ktArgumentExpression)
irVararg.addElement(irVarargElement)
}
return irVararg
}
fun StatementGenerator.generateValueArgument(valueArgument: ResolvedValueArgument, valueParameter: ValueParameterDescriptor): IrExpression? =
when (valueArgument) {
is DefaultValueArgument ->
null
is ExpressionValueArgument ->
generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!)
is VarargValueArgument ->
createDummyExpression(valueArgument.arguments[0].getArgumentExpression()!!, "vararg")
generateVarargExpression(valueArgument, valueParameter)
else ->
TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}")
}
@@ -98,7 +133,8 @@ fun StatementGenerator.pregenerateCall(resolvedCall: ResolvedCall<*>): CallBuild
val call = pregenerateCallReceivers(resolvedCall)
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument ->
call.irValueArgumentsByIndex[index] = generateValueArgument(valueArgument)
val valueParameter = call.descriptor.valueParameters[index]
call.irValueArgumentsByIndex[index] = generateValueArgument(valueArgument, valueParameter)
}
return call
@@ -115,6 +115,16 @@ class InsertImplicitCasts(val builtIns: KotlinBuiltIns): IrElementVisitor<Unit,
}
}
override fun visitVararg(expression: IrVararg, data: Nothing?) {
expression.acceptChildren(this, data)
for (element in expression.elements) {
when (element) {
is IrSpreadElement -> element.expression.replaceWithCast(expression.type)
is IrExpression -> element.replaceWithCast(expression.varargElementType)
}
}
}
private fun IrExpression.replaceWithCast(expectedType: KotlinType?) {
replaceWith { it.wrapWithImplicitCast(expectedType) }
}
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.ir.throwNoSuchSlot
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrExpression : IrStatement {
interface IrExpression : IrStatement, IrVarargElement {
val type: KotlinType
}
@@ -0,0 +1,109 @@
/*
* 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.expressions
import org.jetbrains.kotlin.ir.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.SmartList
interface IrVarargElement : IrElement
interface IrVararg : IrExpression {
val varargElementType : KotlinType
val elements: List<IrVarargElement>
}
interface IrSpreadElement : IrVarargElement {
var expression: IrExpression
}
class IrVarargImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
override val varargElementType: KotlinType
) : IrVararg, IrExpressionBase(startOffset, endOffset, type) {
override val elements: MutableList<IrVarargElement> = SmartList()
fun addElement(varargElement: IrVarargElement) {
varargElement.assertDetached()
varargElement.setTreeLocation(this, elements.size)
elements.add(varargElement)
}
override fun getChild(slot: Int): IrElement? =
elements.getOrNull(slot)
override fun replaceChild(slot: Int, newChild: IrElement) {
if (slot < 0 || slot >= elements.size) throwNoSuchSlot(slot)
newChild.assertDetached()
elements[slot].detach()
elements[slot] = newChild.assertCast()
newChild.setTreeLocation(this, slot)
}
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitVararg(this, data)
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
elements.forEach { it.accept(visitor, data) }
}
}
class IrSpreadElementImpl(
startOffset: Int,
endOffset: Int
) : IrElementBase(startOffset, endOffset), IrSpreadElement {
constructor(startOffset: Int, endOffset: Int, expression: IrExpression) : this(startOffset, endOffset) {
this.expression = expression
}
private var expressionImpl: IrExpression? = null
override var expression: IrExpression
get() = expressionImpl!!
set(value) {
value.assertDetached()
expressionImpl?.detach()
expressionImpl = value
value.setTreeLocation(this, CHILD_EXPRESSION_SLOT)
}
override fun getChild(slot: Int): IrElement? =
when (slot) {
CHILD_EXPRESSION_SLOT -> expression
else -> null
}
override fun replaceChild(slot: Int, newChild: IrElement) {
when (slot) {
CHILD_EXPRESSION_SLOT -> expression = newChild.assertCast()
}
}
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitSpreadElement(this, data)
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
expression.accept(visitor, data)
}
}
@@ -66,6 +66,12 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
override fun <T> visitConst(expression: IrConst<T>, data: Nothing?): String =
"CONST ${expression.kind} type=${expression.renderType()} value='${expression.value}'"
override fun visitVararg(expression: IrVararg, data: Nothing?): String =
"VARARG type=${expression.type} varargElementType=${expression.varargElementType}"
override fun visitSpreadElement(spread: IrSpreadElement, data: Nothing?): String =
"SPREAD_ELEMENT"
override fun visitBlock(expression: IrBlock, data: Nothing?): String =
"BLOCK type=${expression.renderType()} operator=${expression.operator}"
@@ -41,6 +41,8 @@ interface IrElementVisitor<out R, in D> {
fun visitExpression(expression: IrExpression, data: D): R = visitElement(expression, data)
fun <T> visitConst(expression: IrConst<T>, data: D): R = visitExpression(expression, data)
fun visitVararg(expression: IrVararg, data: D): R = visitExpression(expression, data)
fun visitSpreadElement(spread: IrSpreadElement, data: D): R = visitElement(spread, data)
fun visitBlock(expression: IrBlock, data: D): R = visitExpression(expression, data)
fun visitStringConcatenation(expression: IrStringConcatenation, data: D) = visitExpression(expression, data)
@@ -73,4 +75,5 @@ interface IrElementVisitor<out R, in D> {
// NB Use it only for testing purposes; will be removed as soon as all Kotlin expression types are covered
fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: D) = visitDeclaration(declaration, data)
fun visitDummyExpression(expression: IrDummyExpression, data: D) = visitExpression(expression, data)
}
+8 -4
View File
@@ -3,8 +3,10 @@ FILE /arrayAssignment.kt
BLOCK_BODY
VAR val x: kotlin.IntArray
CALL .intArrayOf type=kotlin.IntArray operator=null
elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
DUMMY vararg type=kotlin.Int
elements: VARARG type=IntArray varargElementType=Int
CONST Int type=kotlin.Int value='1'
CONST Int type=kotlin.Int value='2'
CONST Int type=kotlin.Int value='3'
CALL .set type=kotlin.Unit operator=EQ
$this: GET_VAR x type=kotlin.IntArray operator=null
index: CONST Int type=kotlin.Int value='1'
@@ -17,7 +19,9 @@ FILE /arrayAssignment.kt
BLOCK_BODY
CALL .set type=kotlin.Unit operator=EQ
$this: CALL .intArrayOf type=kotlin.IntArray operator=null
elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
DUMMY vararg type=kotlin.Int
elements: VARARG type=IntArray varargElementType=Int
CONST Int type=kotlin.Int value='1'
CONST Int type=kotlin.Int value='2'
CONST Int type=kotlin.Int value='3'
index: CALL .foo type=kotlin.Int operator=null
value: CONST Int type=kotlin.Int value='1'
+4 -2
View File
@@ -3,8 +3,10 @@ FILE /arrayAugmentedAssignment1.kt
BLOCK_BODY
RETURN type=kotlin.Nothing
CALL .intArrayOf type=kotlin.IntArray operator=null
elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
DUMMY vararg type=kotlin.Int
elements: VARARG type=IntArray varargElementType=Int
CONST Int type=kotlin.Int value='1'
CONST Int type=kotlin.Int value='2'
CONST Int type=kotlin.Int value='3'
FUN public fun bar(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing
+4 -2
View File
@@ -5,8 +5,10 @@ FILE /incrementDecrement.kt
PROPERTY public val arr: kotlin.IntArray getter=null setter=null
EXPRESSION_BODY
CALL .intArrayOf type=kotlin.IntArray operator=null
elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
DUMMY vararg type=kotlin.Int
elements: VARARG type=IntArray varargElementType=Int
CONST Int type=kotlin.Int value='1'
CONST Int type=kotlin.Int value='2'
CONST Int type=kotlin.Int value='3'
FUN public fun testVarPrefix(): kotlin.Unit
BLOCK_BODY
VAR var x: kotlin.Int
+3
View File
@@ -0,0 +1,3 @@
val test1 = arrayOf<String>()
val test2 = arrayOf("1", "2", "3")
val test3 = arrayOf("0", *test2, *test1, "4")
+21
View File
@@ -0,0 +1,21 @@
FILE /vararg.kt
PROPERTY public val test1: kotlin.Array<kotlin.String> getter=null setter=null
EXPRESSION_BODY
CALL .arrayOf type=kotlin.Array<kotlin.String> operator=null
PROPERTY public val test2: kotlin.Array<kotlin.String> getter=null setter=null
EXPRESSION_BODY
CALL .arrayOf type=kotlin.Array<kotlin.String> operator=null
elements: VARARG type=Array<out String> varargElementType=String
CONST String type=kotlin.String value='1'
CONST String type=kotlin.String value='2'
CONST String type=kotlin.String value='3'
PROPERTY public val test3: kotlin.Array<kotlin.String> getter=null setter=null
EXPRESSION_BODY
CALL .arrayOf type=kotlin.Array<kotlin.String> operator=null
elements: VARARG type=Array<out String> varargElementType=String
CONST String type=kotlin.String value='0'
SPREAD_ELEMENT
CALL .<get-test2> type=kotlin.Array<kotlin.String> operator=GET_PROPERTY
SPREAD_ELEMENT
CALL .<get-test1> type=kotlin.Array<kotlin.String> operator=GET_PROPERTY
CONST String type=kotlin.String value='4'
+9
View File
@@ -0,0 +1,9 @@
fun testScalar(a: Any): IntArray {
if (a !is Int) return intArrayOf()
return intArrayOf(a)
}
fun testSpread(a: Any): IntArray {
if (a !is IntArray) return intArrayOf()
return intArrayOf(*a)
}
+26
View File
@@ -0,0 +1,26 @@
FILE /varargWithImplicitCast.kt
FUN public fun testScalar(/*0*/ a: kotlin.Any): kotlin.IntArray
BLOCK_BODY
WHEN type=kotlin.Unit operator=IF
if: TYPE_OP operator=NOT_INSTANCEOF typeOperand=kotlin.Int
GET_VAR a type=kotlin.Any operator=null
then: RETURN type=kotlin.Nothing
CALL .intArrayOf type=kotlin.IntArray operator=null
RETURN type=kotlin.Nothing
CALL .intArrayOf type=kotlin.IntArray operator=null
elements: VARARG type=IntArray varargElementType=Int
TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.Int
GET_VAR a type=kotlin.Any operator=null
FUN public fun testSpread(/*0*/ a: kotlin.Any): kotlin.IntArray
BLOCK_BODY
WHEN type=kotlin.Unit operator=IF
if: TYPE_OP operator=NOT_INSTANCEOF typeOperand=kotlin.IntArray
GET_VAR a type=kotlin.Any operator=null
then: RETURN type=kotlin.Nothing
CALL .intArrayOf type=kotlin.IntArray operator=null
RETURN type=kotlin.Nothing
CALL .intArrayOf type=kotlin.IntArray operator=null
elements: VARARG type=IntArray varargElementType=Int
SPREAD_ELEMENT
TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray
GET_VAR a type=kotlin.Any operator=null
@@ -299,6 +299,18 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName);
}
@TestMetadata("vararg.kt")
public void testVararg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/vararg.kt");
doTest(fileName);
}
@TestMetadata("varargWithImplicitCast.kt")
public void testVarargWithImplicitCast() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/varargWithImplicitCast.kt");
doTest(fileName);
}
@TestMetadata("variableAsFunctionCall.kt")
public void testVariableAsFunctionCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/variableAsFunctionCall.kt");