Skip to content

Commit 8c412ad

Browse files
tihonovcoreromanart
authored andcommitted
[JS BE] implement es6-classes lowering
1 parent be85b36 commit 8c412ad

File tree

7 files changed

+819
-6
lines changed

7 files changed

+819
-6
lines changed

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,14 @@ private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeDe
228228
)
229229

230230
private val throwableSuccessorsLoweringPhase = makeBodyLoweringPhase(
231-
::ThrowableLowering,
231+
{ context ->
232+
context.run {
233+
val extendThrowableSymbol =
234+
if (es6mode) setPropertiesToThrowableInstanceSymbol else extendThrowableSymbol
235+
236+
ThrowableLowering(this, extendThrowableSymbol)
237+
}
238+
},
232239
name = "ThrowableLowering",
233240
description = "Link kotlin.Throwable and JavaScript Error together to provide proper interop between language and platform exceptions"
234241
)
@@ -517,6 +524,19 @@ private val typeOperatorLoweringPhase = makeBodyLoweringPhase(
517524
)
518525
)
519526

527+
private val es6AddInternalParametersToConstructorPhase = makeBodyLoweringPhase(
528+
::ES6AddInternalParametersToConstructorPhase,
529+
name = "ES6CreateInitFunctionPhase",
530+
description = "Add `box` and `resultType` params, create init functions for constructors"
531+
)
532+
533+
private val es6ConstructorLowering = makeBodyLoweringPhase(
534+
::ES6ConstructorLowering,
535+
name = "ES6ConstructorLoweringPhase",
536+
description = "Lower constructors",
537+
prerequisite = setOf(es6AddInternalParametersToConstructorPhase)
538+
)
539+
520540
private val secondaryConstructorLoweringPhase = makeDeclarationTransformerPhase(
521541
::SecondaryConstructorLowering,
522542
name = "SecondaryConstructorLoweringPhase",

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class JsMapping : DefaultMapping() {
2121
val classToSyntheticPrimaryConstructor = newMapping<IrClass, IrConstructor>()
2222
val privateMemberToCorrespondingStatic = newMapping<IrFunction, IrSimpleFunction>()
2323

24+
val constructorToInitFunction = newMapping<IrConstructor, IrSimpleFunction>()
25+
2426
val enumEntryToGetInstanceFun = newMapping<IrEnumEntry, IrSimpleFunction>()
2527
val enumEntryToInstanceField = newMapping<IrEnumEntry, IrField>()
2628
val enumConstructorToNewConstructor = newMapping<IrConstructor, IrConstructor>()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
4+
*/
5+
6+
package org.jetbrains.kotlin.ir.backend.js.lower
7+
8+
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
9+
import org.jetbrains.kotlin.descriptors.Modality
10+
import org.jetbrains.kotlin.descriptors.Visibilities
11+
import org.jetbrains.kotlin.ir.IrStatement
12+
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
13+
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
14+
import org.jetbrains.kotlin.ir.backend.js.lower.PrimaryConstructorLowering.*
15+
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
16+
import org.jetbrains.kotlin.ir.declarations.*
17+
import org.jetbrains.kotlin.ir.expressions.*
18+
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
19+
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
20+
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
21+
import org.jetbrains.kotlin.ir.types.isAny
22+
import org.jetbrains.kotlin.ir.util.defaultType
23+
import org.jetbrains.kotlin.ir.util.parentAsClass
24+
import org.jetbrains.kotlin.ir.util.statements
25+
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
26+
27+
/**
28+
* Add ES6_INIT_BOX_PARAMETER for each constructor (except constructors
29+
* of inline, external and primitive (string, arrays) classes).
30+
* Uses: Inline class's constructor initialize field of outer class before
31+
* call `super`, but its impossible in ES6. ES6_INIT_BOX_PARAMETER - object,
32+
* that collect these values and pass to super constructor. In the last
33+
* constructor of delegation chain ES6_INIT_BOX_PARAMETER will be open
34+
* with `Object.assign(this, box)`
35+
*
36+
* Add ES6_RESULT_TYPE PARAMETER for each secondary ctor (except
37+
* constructors of external and primitive (string, arrays) classes).
38+
* Uses: Pass information about type of new object to `Reflect.construct()`
39+
*
40+
* Transform SYNTHETIC_PRIMARY_CONSTRUCTOR:
41+
* constructor() {
42+
* //statements
43+
* }
44+
* ==>
45+
* constructor() {
46+
* init(this)
47+
* }
48+
* init($this$) {
49+
* //statements from ctor
50+
* }
51+
*/
52+
class ES6AddInternalParametersToConstructorPhase(val context: JsIrBackendContext) : BodyLoweringPass {
53+
object ES6_SYNTHETIC_PRIMARY_INIT_FUNCTION : IrDeclarationOriginImpl("ES6_SYNTHETIC_PRIMARY_INIT_FUNCTION")
54+
object ES6_INIT_BOX_PARAMETER : IrDeclarationOriginImpl("ES6_INIT_BOX_PARAMETER")
55+
object ES6_RESULT_TYPE_PARAMETER : IrDeclarationOriginImpl("ES6_RESULT_TYPE_PARAMETER")
56+
57+
override fun lower(irBody: IrBody, container: IrDeclaration) {
58+
assert(context.es6mode)
59+
60+
container.transform(CallSiteTransformer(), null)
61+
62+
if (container !is IrConstructor) return
63+
64+
if (!container.hasStrictSignature()) container.addInternalValueParameters()
65+
66+
if (container.origin === SYNTHETIC_PRIMARY_CONSTRUCTOR) {
67+
createInitFunction(container)
68+
openInitializerBox(container)
69+
}
70+
}
71+
72+
private fun IrConstructor.addInternalValueParameters() {
73+
addValueParameter("box", context.dynamicType, ES6_INIT_BOX_PARAMETER)
74+
75+
if (!isPrimary) {
76+
addValueParameter("resultType", context.dynamicType, ES6_RESULT_TYPE_PARAMETER)
77+
}
78+
}
79+
80+
private fun createInitFunction(constructor: IrConstructor) {
81+
val irClass = constructor.parentAsClass
82+
val initFunction = buildInitFunction(constructor, irClass)
83+
irClass.declarations += initFunction
84+
85+
context.mapping.constructorToInitFunction[constructor] = initFunction
86+
87+
initFunction.transformChildren(object : IrElementTransformerVoid() {
88+
override fun visitDeclaration(declaration: IrDeclaration): IrStatement {
89+
declaration.parent = initFunction
90+
return declaration
91+
}
92+
}, null)
93+
}
94+
95+
private fun openInitializerBox(constructor: IrConstructor) = with(constructor.body as IrBlockBody) {
96+
statements.clear()
97+
statements += JsIrBuilder.buildCall(context.intrinsics.jsOpenInitializerBox).also {
98+
it.putValueArgument(0, JsIrBuilder.buildGetValue(constructor.parentAsClass.thisReceiver!!.symbol))
99+
it.putValueArgument(1, JsIrBuilder.buildGetValue(constructor.valueParameters.last().symbol))
100+
}
101+
}
102+
103+
private fun buildInitFunction(constructor: IrConstructor, irClass: IrClass): IrSimpleFunction {
104+
val functionName = "${irClass.name}_init"
105+
106+
return JsIrBuilder.buildFunction(
107+
functionName,
108+
context.irBuiltIns.unitType,
109+
irClass,
110+
Visibilities.PROTECTED,
111+
Modality.FINAL,
112+
constructor.isInline,
113+
constructor.isExternal,
114+
origin = ES6_SYNTHETIC_PRIMARY_INIT_FUNCTION
115+
).apply {
116+
addValueParameter("\$this\$", context.dynamicType)
117+
118+
body = JsIrBuilder.buildBlockBody(constructor.body?.statements ?: emptyList())
119+
120+
transformChildren(object : IrElementTransformerVoid() {
121+
override fun visitGetValue(expression: IrGetValue): IrExpression {
122+
return if (expression.symbol.owner === constructor.parentAsClass.thisReceiver!!) {
123+
with(expression) { IrGetValueImpl(startOffset, endOffset, type, valueParameters.single().symbol) }
124+
} else {
125+
expression
126+
}
127+
}
128+
}, null)
129+
}
130+
}
131+
132+
/**
133+
* Pass `null` for ES6_INIT_BOX_PARAMETER and ES6_RESULT_TYPE_PARAMETER
134+
*/
135+
inner class CallSiteTransformer : IrElementTransformerVoid() {
136+
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
137+
val constructor = expression.symbol.owner
138+
val parent = constructor.parentAsClass
139+
140+
if (constructor.hasStrictSignature() || parent.defaultType.isAny()) return expression
141+
142+
val newArgsCount = if (constructor.isPrimary) 1 else 2
143+
return expression.run {
144+
IrConstructorCallImpl(
145+
startOffset,
146+
endOffset,
147+
type,
148+
symbol,
149+
typeArgumentsCount,
150+
constructorTypeArgumentsCount,
151+
valueArgumentsCount + newArgsCount
152+
).also {
153+
for (i in 0 until valueArgumentsCount) {
154+
it.putValueArgument(i, getValueArgument(i))
155+
}
156+
157+
it.putValueArgument(
158+
valueArgumentsCount,
159+
JsIrBuilder.buildNull(context.dynamicType)
160+
)
161+
162+
if (!constructor.isPrimary) {
163+
it.putValueArgument(
164+
valueArgumentsCount + 1,
165+
JsIrBuilder.buildNull(context.dynamicType)
166+
)
167+
}
168+
}
169+
}
170+
}
171+
172+
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
173+
val constructor = expression.symbol.owner
174+
val parent = constructor.parentAsClass
175+
176+
if (constructor.hasStrictSignature() || parent.defaultType.isAny()) return expression
177+
178+
val newArgsCount = if (constructor.isPrimary) 1 else 2
179+
180+
return expression.run {
181+
IrDelegatingConstructorCallImpl(
182+
startOffset,
183+
endOffset,
184+
type,
185+
symbol,
186+
typeArgumentsCount,
187+
valueArgumentsCount + newArgsCount
188+
).also {
189+
for (i in 0 until valueArgumentsCount) {
190+
it.putValueArgument(i, getValueArgument(i))
191+
}
192+
193+
it.putValueArgument(
194+
valueArgumentsCount,
195+
JsIrBuilder.buildNull(context.dynamicType)
196+
)
197+
198+
if (!constructor.isPrimary) {
199+
it.putValueArgument(
200+
valueArgumentsCount + 1,
201+
JsIrBuilder.buildNull(context.dynamicType)
202+
)
203+
}
204+
}
205+
}
206+
}
207+
}
208+
209+
private fun IrConstructor.hasStrictSignature(): Boolean {
210+
val primitives = with(context.irBuiltIns) { primitiveArrays + stringClass }
211+
return with(parentAsClass) { isExternal || isInline || symbol in primitives }
212+
}
213+
}

0 commit comments

Comments
 (0)