diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt deleted file mode 100644 index 275d6998939..00000000000 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ /dev/null @@ -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 \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt index 7e1ebadb524..773bd9aa2d4 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt @@ -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 { - override fun visitProperty(declaration: IrProperty, data: Nothing?): JsStatement { - return jsVar(declaration.name, declaration.backingField?.initializer?.expression) +class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer { + 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() + } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index f1aa5315aaf..a61467f3a78 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -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 { - override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): JsExpression { - return body.expression.accept(this, data) +class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer { + override fun visitExpressionBody(body: IrExpressionBody, context: JsGenerationContext): JsExpression { + return body.expression.accept(this, context) } - override fun visitConst(expression: IrConst, data: Nothing?): JsExpression { + override fun visitConst(expression: IrConst, context: JsGenerationContext): JsExpression { val kind = expression.kind return when (kind) { is IrConstKind.String -> JsStringLiteral(kind.valueOf(expression)) @@ -29,34 +27,57 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer 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(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 { - override fun visitBlockBody(body: IrBlockBody, data: Nothing?): JsStatement { - return JsBlock(body.statements.map { it.accept(this, data) }) +class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer { + 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)) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFileToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFileToJsTransformer.kt index 3f0309d7423..9b9a4656253 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFileToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFileToJsTransformer.kt @@ -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 { - override fun visitFile(declaration: IrFile, data: Nothing?): JsStatement { - val block = JsBlock() +class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer { + 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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt new file mode 100644 index 00000000000..70ac06c5e38 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt @@ -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 { + 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) + } + + +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index 9b995272b96..c133655852f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -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 { 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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt new file mode 100644 index 00000000000..38e00c90549 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt @@ -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 { + 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()) }) + ) + +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index af3ea215530..59a2b60d263 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -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 IrWhen.toJsNode(tr: BaseIrElementToJsNodeTransformer, data: D, node: (JsExpression, T, T?) -> T): T? = +fun IrWhen.toJsNode(tr: BaseIrElementToJsNodeTransformer, data: D, node: (JsExpression, T, T?) -> T): T? = branches.foldRight(null) { br, n -> val body = br.result.accept(tr, data) if (br is IrElseBranch) body @@ -35,4 +32,46 @@ fun IrWhen.toJsNode(tr: BaseIrElementToJsNodeTransfor val condition = br.condition.accept(IrElementToJsExpressionTransformer(), data) node(condition, body, n) } - } \ No newline at end of file + } + +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 { + 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)) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt new file mode 100644 index 00000000000..88397d0d33b --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt @@ -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) +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt new file mode 100644 index 00000000000..9486063b2f5 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt @@ -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) +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameGenerator.kt new file mode 100644 index 00000000000..0ce97aac742 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameGenerator.kt @@ -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 +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt new file mode 100644 index 00000000000..4c59bcb055a --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt @@ -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 = "" + val SET_SPECIAL_NAME = "" +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt new file mode 100644 index 00000000000..4c3074b2732 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt @@ -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() + + 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()) + }) +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt index 4923f13d60c..a300ee998fd 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/descriptorBasedUtils.kt @@ -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 \ No newline at end of file +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) \ No newline at end of file