[JS IR BE] Reduce JsScope usage in codegen

This commit is contained in:
Svyatoslav Kuzmich
2019-06-27 18:32:34 +03:00
parent a9c8fd903b
commit 5dcb0ee016
10 changed files with 42 additions and 49 deletions
@@ -24,12 +24,7 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatemen
override fun visitClass(declaration: IrClass, context: JsGenerationContext): JsStatement {
return JsClassGenerator(
declaration,
context.newDeclaration(
JsDeclarationScope(
context.currentScope,
"scope for class ${declaration.name.asString()}"
)
)
context.newDeclaration()
).generate()
}
@@ -277,7 +277,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
if (jsDispatchReceiver != null) {
// TODO: Do not create IIFE when receiver expression is simple or has no side effects
// TODO: Do not create IIFE at all? (Currently there is no reliable way to create temporary variable in current scope)
val receiverName = context.currentScope.declareFreshName("\$externalVarargReceiverTmp")
val receiverName = JsName("\$externalVarargReceiverTmp")
val receiverRef = receiverName.makeRef()
JsInvocation(
// Create scope for temporary variable holding dispatch receiver
@@ -285,7 +285,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
JsNameRef(
"call",
JsFunction(
context.currentScope,
emptyScope,
JsBlock(
JsVars(JsVars.JsVar(receiverName, jsDispatchReceiver)),
JsReturn(
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.ir.isElseBranch
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.COROUTINE_SWITCH
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
@@ -83,7 +84,7 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
val jsCatch = aTry.catches.singleOrNull()?.let {
val name = context.getNameForValueDeclaration(it.catchParameter)
val jsCatchBlock = it.result.accept(this, context)
JsCatch(context.currentScope, name.ident, jsCatchBlock)
JsCatch(emptyScope, name.ident, jsCatchBlock)
}
val jsFinallyBlock = aTry.finallyExpression?.accept(this, context)?.asBlock()
@@ -7,14 +7,12 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.path
import org.jetbrains.kotlin.js.backend.ast.JsDeclarationScope
import org.jetbrains.kotlin.js.backend.ast.JsStatement
import org.jetbrains.kotlin.js.backend.ast.JsBlock
class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
override fun visitFile(declaration: IrFile, data: JsGenerationContext): JsStatement {
val fileContext = data.newDeclaration(JsDeclarationScope(data.currentScope, "scope for file ${declaration.path}"))
val block = fileContext.currentBlock
class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsBlock, JsGenerationContext> {
override fun visitFile(declaration: IrFile, data: JsGenerationContext): JsBlock {
val fileContext = data.newDeclaration()
val block = JsBlock()
declaration.declarations.forEach {
block.statements.add(it.accept(IrDeclarationToJsTransformer(), fileContext))
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.*
@@ -50,7 +49,7 @@ class IrModuleToJsTransformer(
module.files.forEach {
statements.add(JsDocComment(mapOf("file" to it.path)).makeStmt())
statements.add(it.accept(IrFileToJsTransformer(), context))
statements.addAll(it.accept(IrFileToJsTransformer(), context).statements)
}
// sort member forwarding code
@@ -145,20 +144,17 @@ class IrModuleToJsTransformer(
irNamer = nameGenerator
)
val rootContext = JsGenerationContext(
parent = null,
currentBlock = program.globalBlock,
currentFunction = null,
currentScope = program.rootScope,
staticContext = staticContext
)
val rootFunction = JsFunction(program.rootScope, JsBlock(), "root function")
val internalModuleName = rootFunction.scope.declareName("_")
val internalModuleName = JsName("_")
val (importStatements, importedJsModules) =
generateImportStatements(
getNameForExternalDeclaration = { rootContext.getNameForStaticDeclaration(it) },
declareFreshGlobal = { rootFunction.scope.declareFreshName(sanitizeName(it)) }
declareFreshGlobal = { JsName(sanitizeName(it)) } // TODO: Declare fresh name
)
val moduleBody = generateModuleBody(module, rootContext)
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
@@ -126,7 +127,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
val getterName = context.getNameForMemberFunction(delegate)
val returnStatement = JsReturn(JsInvocation(JsNameRef(getterName, JsThisRef())))
return JsFunction(JsFunctionScope(context.currentScope, ""), JsBlock(returnStatement), "")
return JsFunction(emptyScope, JsBlock(returnStatement), "")
}
private fun generateMemberFunction(declaration: IrSimpleFunction): JsStatement? {
@@ -182,7 +183,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
private fun maybeGenerateObjectInstance(): List<JsStatement> {
val instanceVarName = className.objectInstanceName()
val getInstanceFunName = "${className.ident}_getInstance"
val jsVarNode = context.currentScope.declareName(instanceVarName)
val jsVarNode = JsName(instanceVarName) // TODO: Use namer?
val varStmt = JsVars(JsVars.JsVar(jsVarNode))
val function = generateGetInstanceFunction(jsVarNode, getInstanceFunName)
return listOf(varStmt, function.makeStmt())
@@ -190,8 +191,8 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
private fun generateGetInstanceFunction(instanceVar: JsName, instanceFunName: String): JsFunction {
val functionBody = JsBlock()
val func = JsFunction(JsFunctionScope(context.currentScope, "getInstance for ${irClass.name} object"), functionBody, "getInstance")
func.name = context.currentScope.declareName(instanceFunName)
val func = JsFunction(emptyScope, functionBody, "getInstance")
func.name = JsName(instanceFunName) // TODO: Use namer?
functionBody.statements += JsIf(
JsBinaryOperation(JsBinaryOperator.REF_EQ, instanceVar.makeRef(), JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(0))),
@@ -204,7 +205,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
private fun maybeGeneratePrimaryConstructor() {
if (!irClass.declarations.any { it is IrConstructor }) {
val func = JsFunction(JsFunctionScope(context.currentScope, "Ctor for ${irClass.name}"), JsBlock(), "Ctor for ${irClass.name}")
val func = JsFunction(emptyScope, JsBlock(), "Ctor for ${irClass.name}")
func.name = className
classBlock.statements += func.makeStmt()
classModel.preDeclarationBlock.statements += generateInheritanceCode()
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
@@ -121,12 +122,12 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
}
addIfNotNull(intrinsics.jsCode) { call, context ->
val jsCode = translateJsCode(call as IrCall, context.currentScope)
val jsCode = translateJsCode(call as IrCall)
when (jsCode) {
is JsExpression -> jsCode
// TODO don't generate function for this case
else -> JsInvocation(JsFunction(context.currentScope, jsCode as? JsBlock ?: JsBlock(jsCode as JsStatement), ""))
else -> JsInvocation(JsFunction(emptyScope, jsCode as? JsBlock ?: JsBlock(jsCode as JsStatement), ""))
}
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
@@ -42,18 +43,17 @@ fun jsAssignment(left: JsExpression, right: JsExpression) = JsBinaryOperation(Js
fun prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, classNameRef)
fun translateFunction(declaration: IrFunction, name: JsName?, isObjectConstructor: Boolean, context: JsGenerationContext): JsFunction {
val functionScope = JsFunctionScope(context.currentScope, "scope for ${name ?: "annon"}")
val functionContext = context.newDeclaration(functionScope, declaration)
val functionContext = context.newDeclaration(declaration)
val functionParams = declaration.valueParameters.map { functionContext.getNameForValueDeclaration(it) }
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock()
val functionBody = if (isObjectConstructor) {
val instanceName = context.currentScope.declareName(name!!.objectInstanceName())
val instanceName = JsName(name!!.objectInstanceName())
val assignObject = jsAssignment(JsNameRef(instanceName), JsThisRef())
JsBlock(assignObject.makeStmt(), body)
} else body
val function = JsFunction(functionScope, functionBody, "member function ${name ?: "annon"}")
val function = JsFunction(emptyScope, functionBody, "member function ${name ?: "annon"}")
function.name = name
@@ -64,7 +64,7 @@ fun translateFunction(declaration: IrFunction, name: JsName?, isObjectConstructo
declaration.extensionReceiverParameter?.let { function.addParameter(functionContext.getNameForValueDeclaration(it)) }
functionParams.forEach { function.addParameter(it) }
if (declaration.descriptor.isSuspend) {
function.addParameter(context.currentScope.declareName(Namer.CONTINUATION))
function.addParameter(JsName(Namer.CONTINUATION)) // TODO: Use namer?
}
return function
@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.parser.parse
fun translateJsCode(call: IrCall, scope: JsScope): JsNode {
fun translateJsCode(call: IrCall): JsNode {
//TODO check non simple compile time constants (expressions)
fun foldString(expression: IrExpression): String {
@@ -38,7 +38,7 @@ fun translateJsCode(call: IrCall, scope: JsScope): JsNode {
}
val code = call.getValueArgument(0)!!
val statements = parseJsCode(code.run(::foldString), JsFunctionScope(scope, "<js-code>")).orEmpty()
val statements = parseJsCode(foldString(code)).orEmpty()
val size = statements.size
return when (size) {
@@ -48,15 +48,15 @@ fun translateJsCode(call: IrCall, scope: JsScope): JsNode {
}
}
private fun parseJsCode(jsCode: String, currentScope: JsScope): List<JsStatement>? {
private fun parseJsCode(jsCode: String): List<JsStatement>? {
// Parser can change local or global scope.
// In case of js we want to keep new local names,
// but no new global ones.
assert(currentScope is JsFunctionScope) { "Usage of js outside of function is unexpected" }
val temporaryRootScope = JsRootScope(JsProgram())
val scope = DelegatingJsFunctionScopeWithTemporaryParent(currentScope as JsFunctionScope, temporaryRootScope)
val currentScope = JsFunctionScope(temporaryRootScope, "js")
// TODO write debug info, see how it's done in CallExpressionTranslator.parseJsCode
return parse(jsCode, ThrowExceptionOnErrorReporter, scope, "<js-code>")
return parse(jsCode, ThrowExceptionOnErrorReporter, currentScope, "<js-code>")
}
@@ -9,18 +9,19 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.js.backend.ast.*
val emptyScope: JsScope
get() = object : JsScope("nil") {
override fun doCreateName(ident: String): JsName {
error("Trying to create name in empty scope")
}
}
class JsGenerationContext(
val parent: JsGenerationContext?,
val currentBlock: JsBlock,
val currentScope: JsScope,
val currentFunction: IrFunction?,
val staticContext: JsStaticContext
): IrNamer by staticContext {
fun newDeclaration(scope: JsScope, func: IrFunction? = null): JsGenerationContext {
fun newDeclaration(func: IrFunction? = null): JsGenerationContext {
return JsGenerationContext(
parent = this,
currentBlock = if (func != null) JsBlock() else JsGlobalBlock(),
currentScope = scope,
currentFunction = func,
staticContext = staticContext
)
@@ -31,7 +32,7 @@ class JsGenerationContext(
JsThisRef()
} else {
if (currentFunction!!.descriptor.isSuspend) {
JsNameRef(currentScope.declareName(Namer.CONTINUATION))
JsNameRef(Namer.CONTINUATION)
} else {
getNameForValueDeclaration(currentFunction.valueParameters.last()).makeRef()
}