Implement basic IR -> JS class translation

This commit is contained in:
Roman Artemev
2018-03-26 15:17:04 +03:00
parent e70162771d
commit 68e47a9d60
15 changed files with 529 additions and 114 deletions
@@ -1,8 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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
class JsIrBackendContext
@@ -5,37 +5,28 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.js.backend.ast.*
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> {
override fun visitProperty(declaration: IrProperty, data: Nothing?): JsStatement {
return jsVar(declaration.name, declaration.backingField?.initializer?.expression)
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
override fun visitProperty(declaration: IrProperty, context: JsGenerationContext): JsStatement {
return jsVar(declaration.name, declaration.backingField?.initializer?.expression, context)
}
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: Nothing?): JsStatement {
return JsExpressionStatement(transformIrFunctionToJsFunction(declaration))
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsStatement {
return declaration.accept(IrFunctionToJsTransformer(), context).makeStmt()
}
private fun transformIrFunctionToJsFunction(declaration: IrSimpleFunction): JsFunction {
val funName = declaration.name.asString()
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), null) as? JsBlock ?: JsBlock()
val function = JsFunction(JsFunctionScope(dummyScope, "scope for $funName"), body, "function $funName")
function.name = declaration.name.toJsName()
fun JsFunction.addParameter(parameterName: String) {
val parameter = function.scope.declareName(parameterName)
parameters.add(JsParameter(parameter))
}
declaration.extensionReceiverParameter?.let { function.addParameter("\$receiver") }
declaration.valueParameters.forEach {
function.addParameter(it.name.asString())
}
return function
override fun visitConstructor(declaration: IrConstructor, context: JsGenerationContext): JsStatement {
return declaration.accept(IrFunctionToJsTransformer(), context).makeStmt()
}
override fun visitClass(declaration: IrClass, context: JsGenerationContext): JsStatement {
return JsClassGenerator(declaration, context).generate()
}
}
@@ -5,21 +5,19 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary
import org.jetbrains.kotlin.ir.backend.js.utils.name
import org.jetbrains.kotlin.ir.backend.js.utils.parameterCount
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.js.backend.ast.*
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, Nothing?> {
override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): JsExpression {
return body.expression.accept(this, data)
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, JsGenerationContext> {
override fun visitExpressionBody(body: IrExpressionBody, context: JsGenerationContext): JsExpression {
return body.expression.accept(this, context)
}
override fun <T> visitConst(expression: IrConst<T>, data: Nothing?): JsExpression {
override fun <T> visitConst(expression: IrConst<T>, context: JsGenerationContext): JsExpression {
val kind = expression.kind
return when (kind) {
is IrConstKind.String -> JsStringLiteral(kind.valueOf(expression))
@@ -29,34 +27,57 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
is IrConstKind.Short -> JsIntLiteral(kind.valueOf(expression).toInt())
is IrConstKind.Int -> JsIntLiteral(kind.valueOf(expression))
is IrConstKind.Long,
is IrConstKind.Char -> super.visitConst(expression, data)
is IrConstKind.Char -> super.visitConst(expression, context)
is IrConstKind.Float -> JsDoubleLiteral(kind.valueOf(expression).toDouble())
is IrConstKind.Double -> JsDoubleLiteral(kind.valueOf(expression))
}
}
override fun visitStringConcatenation(expression: IrStringConcatenation, data: Nothing?): JsExpression {
override fun visitStringConcatenation(expression: IrStringConcatenation, context: JsGenerationContext): JsExpression {
// TODO revisit
return expression.arguments.fold<IrExpression, JsExpression>(JsStringLiteral("")) { jsExpr, irExpr ->
JsBinaryOperation(
JsBinaryOperator.ADD,
jsExpr,
irExpr.accept(this, data)
irExpr.accept(this, context)
)
}
}
override fun visitGetValue(expression: IrGetValue, data: Nothing?): JsExpression {
return JsNameRef(expression.symbol.name.toJsName())
override fun visitGetField(expression: IrGetField, context: JsGenerationContext): JsExpression {
return JsNameRef(expression.symbol.name.asString(), expression.receiver?.accept(this, context))
}
override fun visitSetVariable(expression: IrSetVariable, data: Nothing?): JsExpression {
override fun visitGetValue(expression: IrGetValue, context: JsGenerationContext): JsExpression {
return if (expression.symbol.isSpecial) {
context.getSpecialRefForName(expression.symbol.name)
} else {
JsNameRef(context.getNameForSymbol(expression.symbol))
}
}
override fun visitSetField(expression: IrSetField, context: JsGenerationContext): JsExpression {
val dest = JsNameRef(expression.symbol.name.asString(), expression.receiver?.accept(this, context))
val source = expression.value.accept(this, context)
return jsAssignment(dest, source)
}
override fun visitSetVariable(expression: IrSetVariable, context: JsGenerationContext): JsExpression {
val ref = JsNameRef(expression.symbol.name.toJsName())
val value = expression.value.accept(this, data)
val value = expression.value.accept(this, context)
return JsBinaryOperation(JsBinaryOperator.ASG, ref, value)
}
override fun visitCall(expression: IrCall, data: Nothing?): JsExpression {
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
val classNameRef = expression.symbol.owner.descriptor.constructedClass.name.toJsName().makeRef()
val callFuncRef = JsNameRef(Namer.CALL_FUNCTION, classNameRef)
val arguments = translateCallArguments(expression, expression.symbol.parameterCount, context)
return JsInvocation(callFuncRef, listOf(JsThisRef()) + arguments)
}
override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
// TODO rewrite more accurately, right now it just copy-pasted and adopted from old version
// TODO support:
// * ir intrinsics
@@ -67,34 +88,24 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
val symbol = expression.symbol
val dispatchReceiver = expression.dispatchReceiver?.accept(this, data)
val extensionReceiver = expression.extensionReceiver?.accept(this, data)
// TODO sanitize name
val symbolName = (symbol.owner as IrSimpleFunction).name.asString()
val ref = if (dispatchReceiver != null) JsNameRef(symbolName, dispatchReceiver) else JsNameRef(symbolName)
val arguments =
// TODO mapTo?
(0 until expression.symbol.parameterCount).map {
val argument = expression.getValueArgument(it)
if (argument != null) {
argument.accept(this, data)
} else {
JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
}
}
val dispatchReceiver = expression.dispatchReceiver?.accept(this, context)
val extensionReceiver = expression.extensionReceiver?.accept(this, context)
if (symbol is IrConstructorSymbol && symbol.isPrimary) {
return JsNew(JsNameRef((symbol.owner.parent as IrClass).name.asString()), arguments)
val arguments = translateCallArguments(expression, expression.symbol.parameterCount, context)
return if (symbol is IrConstructorSymbol && symbol.isPrimary) {
JsNew(JsNameRef((symbol.owner.parent as IrClass).name.asString()), arguments)
} else {
// TODO sanitize name
val symbolName = context.getNameForSymbol(symbol)
val ref = if (dispatchReceiver != null) JsNameRef(symbolName, dispatchReceiver) else JsNameRef(symbolName)
JsInvocation(ref, extensionReceiver?.let { listOf(extensionReceiver) + arguments } ?: arguments)
}
return JsInvocation(ref, extensionReceiver?.let { listOf(extensionReceiver) + arguments } ?: arguments)
}
override fun visitWhen(expression: IrWhen, data: Nothing?): JsExpression {
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsExpression {
// TODO check when w/o else branch and empty when
return expression.toJsNode(this, data, ::JsConditional)!!
return expression.toJsNode(this, context, ::JsConditional)!!
}
}
@@ -5,59 +5,74 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.js.backend.ast.*
class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> {
override fun visitBlockBody(body: IrBlockBody, data: Nothing?): JsStatement {
return JsBlock(body.statements.map { it.accept(this, data) })
class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
override fun visitBlockBody(body: IrBlockBody, context: JsGenerationContext): JsStatement {
return JsBlock(body.statements.map { it.accept(this, context) })
}
override fun visitBlock(expression: IrBlock, data: Nothing?): JsBlock {
return JsBlock(expression.statements.map { it.accept(this, data) })
override fun visitBlock(expression: IrBlock, context: JsGenerationContext): JsBlock {
return JsBlock(expression.statements.map { it.accept(this, context) })
}
override fun visitComposite(expression: IrComposite, data: Nothing?): JsStatement {
override fun visitComposite(expression: IrComposite, context: JsGenerationContext): JsStatement {
// TODO introduce JsCompositeBlock?
return JsBlock(expression.statements.map { it.accept(this, data) })
return JsBlock(expression.statements.map { it.accept(this, context) })
}
override fun visitExpression(expression: IrExpression, data: Nothing?): JsStatement {
return JsExpressionStatement(expression.accept(IrElementToJsExpressionTransformer(), data))
override fun visitExpression(expression: IrExpression, context: JsGenerationContext): JsStatement {
return JsExpressionStatement(expression.accept(IrElementToJsExpressionTransformer(), context))
}
override fun visitBreak(jump: IrBreak, data: Nothing?): JsStatement {
override fun visitBreak(jump: IrBreak, context: JsGenerationContext): JsStatement {
return JsBreak(jump.label?.let(::JsNameRef))
}
override fun visitContinue(jump: IrContinue, data: Nothing?): JsStatement {
override fun visitContinue(jump: IrContinue, context: JsGenerationContext): JsStatement {
return JsContinue(jump.label?.let(::JsNameRef))
}
override fun visitReturn(expression: IrReturn, data: Nothing?): JsStatement {
return JsReturn(expression.value.accept(IrElementToJsExpressionTransformer(), data))
override fun visitReturn(expression: IrReturn, context: JsGenerationContext): JsStatement {
return JsReturn(expression.value.accept(IrElementToJsExpressionTransformer(), context))
}
override fun visitThrow(expression: IrThrow, data: Nothing?): JsStatement {
return JsThrow(expression.value.accept(IrElementToJsExpressionTransformer(), data))
override fun visitThrow(expression: IrThrow, context: JsGenerationContext): JsStatement {
return JsThrow(expression.value.accept(IrElementToJsExpressionTransformer(), context))
}
override fun visitVariable(declaration: IrVariable, data: Nothing?): JsStatement {
return jsVar(declaration.name, declaration.initializer)
override fun visitVariable(declaration: IrVariable, context: JsGenerationContext): JsStatement {
return jsVar(declaration.name, declaration.initializer, context)
}
override fun visitWhen(expression: IrWhen, data: Nothing?): JsStatement {
return expression.toJsNode(this, data, ::JsIf) ?: JsEmpty
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsStatement {
if (KotlinBuiltIns.isAny(expression.symbol.constructedClass)) {
return JsEmpty
}
return expression.accept(IrElementToJsExpressionTransformer(), context).makeStmt()
}
override fun visitWhileLoop(loop: IrWhileLoop, data: Nothing?): JsStatement {
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, context: JsGenerationContext): JsStatement {
// TODO: implement
return JsEmpty
}
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsStatement {
return expression.toJsNode(this, context, ::JsIf) ?: JsEmpty
}
override fun visitWhileLoop(loop: IrWhileLoop, context: JsGenerationContext): JsStatement {
//TODO what if body null?
return JsWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), data), loop.body?.accept(this, data))
return JsWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
}
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Nothing?): JsStatement {
override fun visitDoWhileLoop(loop: IrDoWhileLoop, context: JsGenerationContext): JsStatement {
//TODO what if body null?
return JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), data), loop.body?.accept(this, data))
return JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
}
}
@@ -5,16 +5,19 @@
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.js.backend.ast.JsBlock
import org.jetbrains.kotlin.ir.declarations.name
import org.jetbrains.kotlin.js.backend.ast.JsDeclarationScope
import org.jetbrains.kotlin.js.backend.ast.JsStatement
class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, Nothing?> {
override fun visitFile(declaration: IrFile, data: Nothing?): JsStatement {
val block = JsBlock()
class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
override fun visitFile(declaration: IrFile, context: JsGenerationContext): JsStatement {
val fileContext = context.newDeclaration(JsDeclarationScope(context.currentScope, "scope for file ${declaration.name}"))
val block = fileContext.currentBlock
declaration.declarations.forEach {
block.statements.add(it.accept(IrDeclarationToJsTransformer(), null))
block.statements.add(it.accept(IrDeclarationToJsTransformer(), fileContext))
}
return block
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.constructedClassName
import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.js.backend.ast.JsFunction
import org.jetbrains.kotlin.js.backend.ast.JsStatement
open class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer<JsFunction, JsGenerationContext> {
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsFunction {
return translateFunction(declaration, declaration.name, context)
}
override fun visitConstructor(declaration: IrConstructor, context: JsGenerationContext): JsFunction {
assert(declaration.symbol.isPrimary)
return translateFunction(declaration, declaration.symbol.constructedClassName, context)
}
}
@@ -5,16 +5,19 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.js.backend.ast.JsNode
import org.jetbrains.kotlin.js.backend.ast.JsProgram
import org.jetbrains.kotlin.js.backend.ast.JsRootScope
class IrModuleToJsTransformer : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode {
val program = JsProgram()
val rootContext = JsGenerationContext(JsRootScope(program))
declaration.files.forEach {
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), null))
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext))
}
return program
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.transformers.irToJs
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.js.backend.ast.*
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
private val classNameRef = irClass.name.toJsName().makeRef()
private val classPrototypeRef = prototypeOf(classNameRef)
private val classBlock = JsBlock()
fun generate(): JsStatement {
maybeGeneratePrimaryConstructor()
val transformer = IrFunctionToJsTransformer()
for (declaration in irClass.declarations) {
if (declaration is IrConstructor) {
classBlock.statements += declaration.accept(transformer, context).makeStmt()
classBlock.statements += generateInheritanceCode()
} else if (declaration is IrFunction) {
if (declaration.symbol.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
declaration.symbol.modality != Modality.ABSTRACT
) {
classBlock.statements += declaration.accept(transformer, context).apply { name = null }.let {
val memberName = context.getNameForSymbol(declaration.symbol)
val memberRef = JsNameRef(memberName, classPrototypeRef)
jsAssignment(memberRef, it).makeStmt()
}
}
}
}
classBlock.statements += generateClassMetadata()
return classBlock
}
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}")
func.name = irClass.name.toJsName()
classBlock.statements += func.makeStmt()
classBlock.statements += generateInheritanceCode()
}
}
private fun generateInheritanceCode(): List<JsStatement> {
val baseClass = irClass.superClasses.first { it.kind != ClassKind.INTERFACE }
if (baseClass.isAny) {
return emptyList()
}
val createCall = jsAssignment(
classPrototypeRef, JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototypeOf(baseClass.owner.name.toJsName().makeRef()))
).makeStmt()
val ctorAssign = jsAssignment(JsNameRef(Namer.CONSTRUCTOR_NAME, classPrototypeRef), classNameRef).makeStmt()
return listOf(createCall, ctorAssign)
}
private fun generateClassMetadata(): JsStatement {
val metadataLiteral = JsObjectLiteral(true)
val simpleName = irClass.symbol.name
if (!simpleName.isSpecial) {
val simpleNameProp = JsPropertyInitializer(JsNameRef(Namer.METADATA_SIMPLE_NAME), JsStringLiteral(simpleName.identifier))
metadataLiteral.propertyInitializers += simpleNameProp
}
metadataLiteral.propertyInitializers += generateSuperClasses()
return jsAssignment(JsNameRef(Namer.METADATA, classNameRef), metadataLiteral).makeStmt()
}
private fun generateSuperClasses(): JsPropertyInitializer = JsPropertyInitializer(
JsNameRef(Namer.METADATA_INTERFACES),
JsArrayLiteral(irClass.superClasses.filter { it.kind == ClassKind.INTERFACE }.map { JsNameRef(it.owner.name.toJsName()) })
)
}
@@ -5,14 +5,11 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.expressions.IrBranch
import org.jetbrains.kotlin.ir.expressions.IrElseBranch
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrWhen
import org.jetbrains.kotlin.js.backend.ast.JsDynamicScope
import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsNode
import org.jetbrains.kotlin.js.backend.ast.JsVars
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.name.Name
// TODO don't use JsDynamicScope
@@ -22,12 +19,12 @@ fun Name.toJsName() =
// TODO sanitize
dummyScope.declareName(asString())
fun jsVar(name: Name, initializer: IrExpression?): JsVars {
val jsInitializer = initializer?.accept(IrElementToJsExpressionTransformer(), null)
fun jsVar(name: Name, initializer: IrExpression?, context: JsGenerationContext): JsVars {
val jsInitializer = initializer?.accept(IrElementToJsExpressionTransformer(), context)
return JsVars(JsVars.JsVar(name.toJsName(), jsInitializer))
}
fun <T : JsNode, D : Nothing?> IrWhen.toJsNode(tr: BaseIrElementToJsNodeTransformer<T, D>, data: D, node: (JsExpression, T, T?) -> T): T? =
fun <T : JsNode, D : JsGenerationContext> IrWhen.toJsNode(tr: BaseIrElementToJsNodeTransformer<T, D>, data: D, node: (JsExpression, T, T?) -> T): T? =
branches.foldRight<IrBranch, T?>(null) { br, n ->
val body = br.result.accept(tr, data)
if (br is IrElseBranch) body
@@ -35,4 +32,46 @@ fun <T : JsNode, D : Nothing?> IrWhen.toJsNode(tr: BaseIrElementToJsNodeTransfor
val condition = br.condition.accept(IrElementToJsExpressionTransformer(), data)
node(condition, body, n)
}
}
}
fun jsAssignment(left: JsExpression, right: JsExpression) = JsBinaryOperation(JsBinaryOperator.ASG, left, right)
fun prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, classNameRef)
fun translateFunction(declaration: IrFunction, name: Name?, context: JsGenerationContext): JsFunction {
val functionScope = JsFunctionScope(context.currentScope, "scope for ${name ?: "annon"}")
val functionContext = context.newDeclaration(functionScope)
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock()
val function = JsFunction(functionScope, body, "member function ${name ?: "annon"}")
function.name = name?.toJsName()
fun JsFunction.addParameter(parameterName: String) {
val parameter = function.scope.declareName(parameterName)
parameters.add(JsParameter(parameter))
}
declaration.extensionReceiverParameter?.let { function.addParameter("\$receiver") }
declaration.valueParameters.forEach {
if (it.name.isSpecial) {
function.addParameter(context.staticContext.getSpecialNameString(it.name.asString()))
} else {
function.addParameter(it.name.asString())
}
}
return function
}
fun translateCallArguments(
expression: IrMemberAccessExpression,
parameterCount: Int,
context: JsGenerationContext
): List<JsExpression> {
val transformer = IrElementToJsExpressionTransformer()
// TODO: map to?
return (0 until parameterCount).map {
val argument = expression.getValueArgument(it)
argument?.accept(transformer, context) ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrElementToJsExpressionTransformer
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrElementToJsStatementTransformer
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.toJsName
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.name.Name
class JsGenerationContext {
fun newDeclaration(scope: JsScope): JsGenerationContext {
return JsGenerationContext(this, JsBlock(), scope)
}
val currentBlock: JsBlock
val currentScope: JsScope
val parent: JsGenerationContext?
val staticContext: JsStaticContext
private val program: JsProgram
constructor(rootScope: JsRootScope) {
this.parent = null
this.program = rootScope.program
this.staticContext = JsStaticContext(rootScope, program.globalBlock, SimpleNameGenerator())
this.currentScope = rootScope
this.currentBlock = program.globalBlock
}
constructor(parent: JsGenerationContext, block: JsBlock, scope: JsScope) {
this.parent = parent
this.program = parent.program
this.staticContext = parent.staticContext
this.currentBlock = block
this.currentScope = scope
}
fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol)
fun getSpecialRefForName(name: Name): JsExpression = staticContext.getSpecialRefForName(name)
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.name.Name
class JsStaticContext(
private val rootScope: JsRootScope,
private val globalBlock: JsGlobalBlock,
private val nameGenerator: NameGenerator
) {
fun getNameForSymbol(irSymbol: IrSymbol) = nameGenerator.getNameForSymbol(irSymbol, rootScope)
fun getSpecialRefForName(name: Name): JsExpression = nameGenerator.getSpecialRefForName(name)
fun getSpecialNameString(specNameString: String): String = nameGenerator.getSpecialNameString(specNameString)
}
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsName
import org.jetbrains.kotlin.js.backend.ast.JsScope
import org.jetbrains.kotlin.name.Name
interface NameGenerator {
fun getNameForSymbol(symbol: IrSymbol, scope: JsScope): JsName
fun getSpecialRefForName(name: Name): JsExpression
fun getSpecialNameString(specNameString: String): String
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.idea.KotlinLanguage
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
object Namer {
val KOTLIN_NAME = KotlinLanguage.NAME
val KOTLIN_LOWER_NAME = KOTLIN_NAME.toLowerCase()
// val EQUALS_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getAny(), "equals")
// val COMPARE_TO_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getComparable(), "compareTo")
val LONG_FROM_NUMBER = "fromNumber"
val LONG_TO_NUMBER = "toNumber"
val LONG_FROM_INT = "fromInt"
val LONG_ZERO = "ZERO"
val LONG_ONE = "ONE"
val LONG_NEG_ONE = "NEG_ONE"
val LONG_MAX_VALUE = "MAX_VALUE"
val LONG_MIN_VALUE = "MIN_VALUE"
val PRIMITIVE_COMPARE_TO = "primitiveCompareTo"
val IS_CHAR = "isChar"
val IS_NUMBER = "isNumber"
val IS_CHAR_SEQUENCE = "isCharSequence"
val GET_KCLASS = "getKClass"
val GET_KCLASS_FROM_EXPRESSION = "getKClassFromExpression"
val CALLEE_NAME = "\$fun"
val CALL_FUNCTION = "call"
val APPLY_FUNCTION = "apply"
val OUTER_FIELD_NAME = "\$outer"
val DELEGATE = "\$delegate"
val ROOT_PACKAGE = "_"
val RECEIVER_PARAMETER_NAME = "\$receiver"
val ANOTHER_THIS_PARAMETER_NAME = "$this"
val THROW_CLASS_CAST_EXCEPTION_FUN_NAME = "throwCCE"
val THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME = "throwISE"
val THROW_UNINITIALIZED_PROPERTY_ACCESS_EXCEPTION = "throwUPAE"
val NULL_CHECK_INTRINSIC_NAME = "ensureNotNull"
val PROTOTYPE_NAME = "prototype"
val CONSTRUCTOR_NAME = "constructor"
val CAPTURED_VAR_FIELD = "v"
val IS_ARRAY_FUN_REF = JsNameRef("isArray", "Array")
val DEFINE_INLINE_FUNCTION = "defineInlineFunction"
val DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "\$default"
val JS_OBJECT = JsNameRef("Object")
val JS_OBJECT_CREATE_FUNCTION = JsNameRef("create", JS_OBJECT)
val LOCAL_MODULE_PREFIX = "\$module\$"
val METADATA = "\$metadata\$"
val METADATA_INTERFACES = "interfaces"
val METADATA_SIMPLE_NAME = "simpleName"
val METADATA_CLASS_KIND = "kind"
val CLASS_KIND_ENUM = "Kind"
val CLASS_KIND_CLASS = "CLASS"
val CLASS_KIND_INTERFACE = "INTERFACE"
val CLASS_KIND_OBJECT = "OBJECT"
val OBJECT_INSTANCE_VAR_SUFFIX = "_instance"
val OBJECT_INSTANCE_FUNCTION_SUFFIX = "_getInstance"
val ENUM_NAME_FIELD = "name\$"
val ENUM_ORDINAL_FIELD = "ordinal\$"
val IMPORTS_FOR_INLINE_PROPERTY = "\$\$importsForInline\$\$"
val GETTER_PREFIX = "get_"
val SETTER_PREFIX = "set_"
val SETTER_ARGUMENT = "\$setValue"
val THIS_SPECIAL_NAME = "<this>"
val SET_SPECIAL_NAME = "<set-?>"
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.descriptors.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.name.Name
class SimpleNameGenerator : NameGenerator {
private val nameCache = mutableMapOf<DeclarationDescriptor, JsName>()
override fun getNameForSymbol(symbol: IrSymbol, scope: JsScope): JsName = getNameForDescriptor(symbol.descriptor, scope)
override fun getSpecialRefForName(name: Name): JsExpression {
assert(name.isSpecial)
val nameString = name.asString()
return when (nameString) {
Namer.THIS_SPECIAL_NAME -> JsThisRef()
else -> JsNameRef(getSpecialNameString(nameString))
}
}
override fun getSpecialNameString(specNameString: String): String = when (specNameString) {
Namer.SET_SPECIAL_NAME -> Namer.SETTER_ARGUMENT
else -> TODO("for Name ${specNameString}")
}
private fun getNameForDescriptor(descriptor: DeclarationDescriptor, scope: JsScope): JsName = nameCache.getOrPut(descriptor, {
val nameBuilder = StringBuilder()
when (descriptor) {
is PropertyAccessorDescriptor -> {
when (descriptor) {
is PropertyGetterDescriptor -> nameBuilder.append(Namer.GETTER_PREFIX)
is PropertySetterDescriptor -> nameBuilder.append(Namer.SETTER_PREFIX)
}
nameBuilder.append(descriptor.correspondingProperty.name.asString())
}
is CallableDescriptor -> {
nameBuilder.append(descriptor.name.asString())
descriptor.typeParameters.forEach { nameBuilder.append("_${it.name.asString()}") }
descriptor.valueParameters.forEach { nameBuilder.append("_${it.type}") }
}
}
return@getOrPut scope.declareName(nameBuilder.toString())
})
}
@@ -5,12 +5,27 @@
package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.symbols.*
val IrFunctionSymbol.parameterCount get() = descriptor.valueParameters.size
val IrFunctionSymbol.kind get() = descriptor.kind
val IrFunctionSymbol.modality get() = descriptor.modality
val IrConstructorSymbol.isPrimary get() = descriptor.isPrimary
val IrValueSymbol.name get() = descriptor.name
val IrConstructorSymbol.constructedClassName get() = descriptor.constructedClass.name
val IrConstructorSymbol.constructedClass get() = descriptor.constructedClass
val IrValueSymbol.isSpecial get() = descriptor.name.isSpecial
val IrSymbol.name get() = descriptor.name
val IrClassSymbol.kind get() = descriptor.kind
val IrClassSymbol.modality get() = descriptor.modality
val IrClassSymbol.isAny get() = KotlinBuiltIns.isAny(descriptor)