[JS IR] Fill source info in codegen

#KT-46551 In Progress
This commit is contained in:
Zalim Bashorov
2021-07-01 21:24:26 +03:00
committed by teamcityserver
parent 64c6d852de
commit 5a3efc1a98
7 changed files with 84 additions and 41 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -40,7 +40,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
override fun visitVararg(expression: IrVararg, context: JsGenerationContext): JsExpression {
assert(expression.elements.none { it is IrSpreadElement })
return JsArrayLiteral(expression.elements.map { it.accept(this, context) })
return JsArrayLiteral(expression.elements.map { it.accept(this, context) }).withSource(expression, context)
}
override fun visitExpressionBody(body: IrExpressionBody, context: JsGenerationContext): JsExpression =
@@ -64,7 +64,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
is IrConstKind.Char -> throw IllegalStateException("Char const should have been lowered at this point")
is IrConstKind.Float -> JsDoubleLiteral(toDoubleConst(kind.valueOf(expression)))
is IrConstKind.Double -> JsDoubleLiteral(kind.valueOf(expression))
}
}.withSource(expression, context)
}
private fun toDoubleConst(f: Float) = if (f.isInfinite() || f.isNaN()) f.toDouble() else f.toString().toDouble()
@@ -94,19 +94,19 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
return JsNameRef(
field.getJsNameOrKotlinName().identifier,
context.getNameForClass(fieldParent).makeRef()
)
).withSource(expression, context)
}
if (fieldParent is IrClass && fieldParent.isInline) {
return expression.receiver!!.accept(this, context)
return expression.receiver!!.accept(this, context).withSource(expression, context)
}
val fieldName = context.getNameForField(field)
return JsNameRef(fieldName, expression.receiver?.accept(this, context))
return JsNameRef(fieldName, expression.receiver?.accept(this, context)).withSource(expression, context)
}
override fun visitGetValue(expression: IrGetValue, context: JsGenerationContext): JsExpression {
if (expression.symbol.owner.isThisReceiver()) return JsThisRef()
return context.getNameForValueDeclaration(expression.symbol.owner).makeRef()
if (expression.symbol.owner.isThisReceiver()) return JsThisRef().withSource(expression, context)
return context.getNameForValueDeclaration(expression.symbol.owner).makeRef().withSource(expression, context)
}
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext): JsExpression {
@@ -114,20 +114,20 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
assert(obj.kind == ClassKind.OBJECT)
assert(obj.isEffectivelyExternal()) { "Non external IrGetObjectValue must be lowered" }
return context.getRefForExternalClass(obj)
return context.getRefForExternalClass(obj).withSource(expression, context)
}
override fun visitSetField(expression: IrSetField, context: JsGenerationContext): JsExpression {
val fieldName = context.getNameForField(expression.symbol.owner)
val dest = JsNameRef(fieldName, expression.receiver?.accept(this, context))
val source = expression.value.accept(this, context)
return jsAssignment(dest, source)
return jsAssignment(dest, source).withSource(expression, context)
}
override fun visitSetValue(expression: IrSetValue, context: JsGenerationContext): JsExpression {
val ref = JsNameRef(context.getNameForValueDeclaration(expression.symbol.owner))
val value = expression.value.accept(this, context)
return JsBinaryOperation(JsBinaryOperator.ASG, ref, value)
return JsBinaryOperation(JsBinaryOperator.ASG, ref, value).withSource(expression, context)
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
@@ -143,14 +143,14 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
assert(constructor.isPrimary) {
"Delegation to secondary inline constructors must be lowered into simple function calls"
}
return JsBinaryOperation(JsBinaryOperator.ASG, thisRef, arguments.single())
return JsBinaryOperation(JsBinaryOperator.ASG, thisRef, arguments.single()).withSource(expression, context)
}
return if (context.staticContext.backendContext.es6mode) {
JsInvocation(JsNameRef("super"), arguments)
} else {
JsInvocation(callFuncRef, listOf(thisRef) + arguments)
}
}.withSource(expression, context)
}
override fun visitConstructorCall(expression: IrConstructorCall, context: JsGenerationContext): JsExpression {
@@ -190,7 +190,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
val ref = context.getNameForClass(klass).makeRef()
JsNew(ref, arguments)
}
}
}.withSource(expression, context)
}
override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
@@ -200,7 +200,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
val lastStatement = statements.last()
if (statements.size == 1) {
if (lastStatement is JsExpressionStatement) return lastStatement.expression
if (lastStatement is JsExpressionStatement) return lastStatement.expression.withSource(expression, context)
}
val newStatements = statements.toMutableList()
@@ -216,10 +216,10 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
}
val syntheticFunction = JsFunction(emptyScope, JsBlock(newStatements), "")
return JsInvocation(syntheticFunction)
return JsInvocation(syntheticFunction).withSource(expression, context)
}
return translateCall(expression, context, this)
return translateCall(expression, context, this).withSource(expression, context)
}
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsExpression {
@@ -239,11 +239,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
return when (expression.operator) {
IrTypeOperator.REINTERPRET_CAST -> expression.argument.accept(this, data)
else -> error("All type operator calls except REINTERPRET_CAST should be lowered at this point: ${expression.operator}")
}
}.withSource(expression, data)
}
override fun visitDynamicMemberExpression(expression: IrDynamicMemberExpression, data: JsGenerationContext): JsExpression =
JsNameRef(expression.memberName, expression.receiver.accept(this, data))
JsNameRef(expression.memberName, expression.receiver.accept(this, data)).withSource(expression, data)
override fun visitDynamicOperatorExpression(expression: IrDynamicOperatorExpression, data: JsGenerationContext): JsExpression =
when (expression.operator) {
@@ -294,7 +294,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
)
else -> error("Unexpected operator ${expression.operator}: ${expression.render()}")
}
}.withSource(expression, data)
override fun visitRawFunctionReference(expression: IrRawFunctionReference, data: JsGenerationContext): JsExpression {
val name = when (val function = expression.symbol.owner) {
@@ -302,7 +302,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
is IrSimpleFunction -> data.getNameForStaticFunction(function)
else -> error("Unexpected function kind")
}
return JsNameRef(name)
return JsNameRef(name).withSource(expression, data)
}
private fun prefixOperation(operator: JsUnaryOperator, expression: IrDynamicOperatorExpression, data: JsGenerationContext) =
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -38,15 +38,15 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
}
override fun visitExpression(expression: IrExpression, context: JsGenerationContext): JsStatement {
return JsExpressionStatement(expression.accept(IrElementToJsExpressionTransformer(), context))
return expression.accept(IrElementToJsExpressionTransformer(), context).makeStmt()
}
override fun visitBreak(jump: IrBreak, context: JsGenerationContext): JsStatement {
return JsBreak(context.getNameForLoop(jump.loop)?.let { JsNameRef(it) })
return JsBreak(context.getNameForLoop(jump.loop)?.let { JsNameRef(it) }).withSource(jump, context)
}
override fun visitContinue(jump: IrContinue, context: JsGenerationContext): JsStatement {
return JsContinue(context.getNameForLoop(jump.loop)?.let { JsNameRef(it) })
return JsContinue(context.getNameForLoop(jump.loop)?.let { JsNameRef(it) }).withSource(jump, context)
}
private fun IrExpression.maybeOptimizeIntoSwitch(context: JsGenerationContext, transformer: (JsExpression) -> JsStatement): JsStatement {
@@ -62,20 +62,21 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
val fieldName = context.getNameForField(expression.symbol.owner)
val expressionTransformer = IrElementToJsExpressionTransformer()
val dest = JsNameRef(fieldName, expression.receiver?.accept(expressionTransformer, context))
return expression.value.maybeOptimizeIntoSwitch(context) { jsAssignment(dest, it).makeStmt() }
return expression.value.maybeOptimizeIntoSwitch(context) { jsAssignment(dest, it).withSource(expression, context).makeStmt() }
}
override fun visitSetValue(expression: IrSetValue, context: JsGenerationContext): JsStatement {
val ref = JsNameRef(context.getNameForValueDeclaration(expression.symbol.owner))
return expression.value.maybeOptimizeIntoSwitch(context) { JsBinaryOperation(JsBinaryOperator.ASG, ref, it).makeStmt() }
return expression.value
.maybeOptimizeIntoSwitch(context) { jsAssignment(ref, it).withSource(expression, context).makeStmt() }
}
override fun visitReturn(expression: IrReturn, context: JsGenerationContext): JsStatement {
return expression.value.maybeOptimizeIntoSwitch(context) { JsReturn(it) }
return expression.value.maybeOptimizeIntoSwitch(context) { JsReturn(it) }.withSource(expression, context)
}
override fun visitThrow(expression: IrThrow, context: JsGenerationContext): JsStatement {
return expression.value.maybeOptimizeIntoSwitch(context) { JsThrow(it) }
return expression.value.maybeOptimizeIntoSwitch(context) { JsThrow(it) }.withSource(expression, context)
}
override fun visitVariable(declaration: IrVariable, context: JsGenerationContext): JsStatement {
@@ -90,11 +91,11 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
}
SwitchOptimizer(context, transformer).tryOptimize(value)?.let {
return JsBlock(JsVars(JsVars.JsVar(varName)), it)
return JsBlock(JsVars(JsVars.JsVar(varName)), it).withSource(declaration, context)
}
}
return jsVar(varName, value, context)
return jsVar(varName, value, context).withSource(declaration, context)
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsStatement {
@@ -109,12 +110,12 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
val statements = translateJsCodeIntoStatementList(expression.getValueArgument(0) ?: error("JsCode is expected"))!!
return when (statements.size) {
0 -> JsEmpty
1 -> statements.single()
1 -> statements.single().withSource(expression, data)
// TODO: use transparent block (e.g. JsCompositeBlock)
else -> JsBlock(statements)
}
}
return translateCall(expression, data, IrElementToJsExpressionTransformer()).makeStmt()
return translateCall(expression, data, IrElementToJsExpressionTransformer()).withSource(expression, data).makeStmt()
}
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, context: JsGenerationContext): JsStatement {
@@ -157,3 +158,4 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
return label?.let { JsLabel(it, loopStatement) } ?: loopStatement
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsBlock, JsGenerationContext> {
override fun visitFile(declaration: IrFile, data: JsGenerationContext): JsBlock {
val fileContext = data.newDeclaration()
val fileContext = data.newFile(declaration)
val block = JsGlobalBlock()
declaration.declarations.forEach {
@@ -21,4 +21,4 @@ class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsBlock, JsGenera
return block
}
}
}
@@ -136,6 +136,7 @@ class IrModuleToJsTransformer(
globalNameScope = namer.globalNames
)
val rootContext = JsGenerationContext(
currentFile = null,
currentFunction = null,
staticContext = staticContext,
localNames = LocalNameGenerator(NameScope.EmptyScope)
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrFunction
@@ -480,3 +482,25 @@ object JsAstUtils {
return JsVars(JsVars.JsVar(name, expr))
}
}
internal fun <T : JsNode> T.withSource(node: IrElement, context: JsGenerationContext): T {
addSourceInfoIfNeed(node, context)
return this
}
@Suppress("NOTHING_TO_INLINE")
private inline fun <T : JsNode> T.addSourceInfoIfNeed(node: IrElement, context: JsGenerationContext) {
if (!context.staticContext.genSourcemaps) return
if (node.startOffset == UNDEFINED_OFFSET || node.endOffset == UNDEFINED_OFFSET) return
val fileEntry = context.currentFile?.fileEntry ?: return
val path = fileEntry.name
val startLine = fileEntry.getLineNumber(node.startOffset)
val startColumn = fileEntry.getColumnNumber(node.startOffset)
// TODO maybe it's better to fix in JsExpressionStatement
val locationTarget = if (this is JsExpressionStatement) this.expression else this
locationTarget.source = JsLocation(path, startLine, startColumn)
}
@@ -1,14 +1,16 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.expressions.IrReturnableBlock
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.js.backend.ast.JsName
@@ -24,12 +26,23 @@ val emptyScope: JsScope
}
class JsGenerationContext(
val currentFile: IrFile?,
val currentFunction: IrFunction?,
val staticContext: JsStaticContext,
val localNames: LocalNameGenerator? = null
): IrNamer by staticContext {
fun newFile(file: IrFile? = null, func: IrFunction? = null, localNames: LocalNameGenerator? = null): JsGenerationContext {
return JsGenerationContext(
currentFile = file,
currentFunction = func,
staticContext = staticContext,
localNames = localNames,
)
}
fun newDeclaration(func: IrFunction? = null, localNames: LocalNameGenerator? = null): JsGenerationContext {
return JsGenerationContext(
currentFile = currentFile,
currentFunction = func,
staticContext = staticContext,
localNames = localNames,
@@ -66,4 +79,4 @@ class JsGenerationContext(
}
fun checkIfJsCode(symbol: IrFunctionSymbol): Boolean = symbol == staticContext.backendContext.intrinsics.jsCode
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransfo
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
class JsStaticContext(
@@ -22,4 +23,6 @@ class JsStaticContext(
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
val initializerBlock = JsGlobalBlock()
}
val genSourcemaps = backendContext.configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)
}