JS: introduce new flat JS declaration structure

This commit is contained in:
Alexey Andreev
2016-10-03 17:37:58 +03:00
parent f1ae9949fe
commit f9a392fdec
42 changed files with 1428 additions and 933 deletions
@@ -132,8 +132,7 @@ public abstract class JsScope {
return names.containsKey(name); return names.containsKey(name);
} }
@Nullable private boolean hasName(@NotNull String name) {
public boolean hasName(@NotNull String name) {
return hasOwnName(name) || (parent != null && parent.hasName(name)); return hasOwnName(name) || (parent != null && parent.hasName(name));
} }
@@ -20,7 +20,9 @@ import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.check import org.jetbrains.kotlin.utils.addToStdlib.check
@@ -3269,6 +3269,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/extensionProperty"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.ANY, true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/extensionProperty"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.ANY, true);
} }
@TestMetadata("inClass.kt")
public void testInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/extensionProperty/inClass.kt");
doTest(fileName);
}
@TestMetadata("propertyWithGetterAndSetter.kt") @TestMetadata("propertyWithGetterAndSetter.kt")
public void testPropertyWithGetterAndSetter() throws Exception { public void testPropertyWithGetterAndSetter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/extensionProperty/propertyWithGetterAndSetter.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/extensionProperty/propertyWithGetterAndSetter.kt");
@@ -3385,6 +3391,39 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inheritance/withInitializeMethod.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inheritance/withInitializeMethod.kt");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("js/js.translator/testData/box/inheritance/interfaces")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Interfaces extends AbstractBoxJsTest {
@TestMetadata("abstractClassInheritingDefaultMethod.kt")
public void testAbstractClassInheritingDefaultMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inheritance/interfaces/abstractClassInheritingDefaultMethod.kt");
doTest(fileName);
}
public void testAllFilesPresentInInterfaces() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/inheritance/interfaces"), Pattern.compile("^([^_](.+))\\.kt$"), true);
}
@TestMetadata("withDefaultMethod.kt")
public void testWithDefaultMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inheritance/interfaces/withDefaultMethod.kt");
doTest(fileName);
}
@TestMetadata("withDefaultMethodFromSuperInterface.kt")
public void testWithDefaultMethodFromSuperInterface() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inheritance/interfaces/withDefaultMethodFromSuperInterface.kt");
doTest(fileName);
}
@TestMetadata("withDefaultProperty.kt")
public void testWithDefaultProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inheritance/interfaces/withDefaultProperty.kt");
doTest(fileName);
}
}
} }
@TestMetadata("js/js.translator/testData/box/initialize") @TestMetadata("js/js.translator/testData/box/initialize")
@@ -21,10 +21,7 @@ import com.google.dart.compiler.backend.js.ast.JsName
import com.google.dart.compiler.backend.js.ast.metadata.HasMetadata import com.google.dart.compiler.backend.js.ast.metadata.HasMetadata
import com.google.dart.compiler.backend.js.ast.metadata.SideEffectKind import com.google.dart.compiler.backend.js.ast.metadata.SideEffectKind
import com.google.dart.compiler.backend.js.ast.metadata.sideEffects import com.google.dart.compiler.backend.js.ast.metadata.sideEffects
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
@@ -42,8 +39,6 @@ val CallInfo.callableDescriptor: CallableDescriptor
return if (result is TypeAliasConstructorDescriptor) result.underlyingConstructorDescriptor else result return if (result is TypeAliasConstructorDescriptor) result.underlyingConstructorDescriptor else result
} }
fun CallInfo.isExtension(): Boolean = extensionReceiver != null
fun CallInfo.isNative(): Boolean = AnnotationsUtils.isNativeObject(callableDescriptor) fun CallInfo.isNative(): Boolean = AnnotationsUtils.isNativeObject(callableDescriptor)
fun CallInfo.isSuperInvocation(): Boolean { fun CallInfo.isSuperInvocation(): Boolean {
@@ -65,11 +60,16 @@ val VariableAccessInfo.variableName: JsName
fun VariableAccessInfo.isGetAccess(): Boolean = value == null fun VariableAccessInfo.isGetAccess(): Boolean = value == null
fun VariableAccessInfo.getAccessDescriptor(): DeclarationDescriptor { fun VariableAccessInfo.getAccessDescriptor(): PropertyAccessorDescriptor {
val descriptor = variableDescriptor val property = variableDescriptor as PropertyDescriptor
if (descriptor is PropertyDescriptor && (descriptor.isExtension || TranslationUtils.shouldGenerateAccessors(descriptor))) { return if (isGetAccess()) property.getter!! else property.setter!!
val propertyAccessorDescriptor = if (isGetAccess()) descriptor.getter else descriptor.setter }
return propertyAccessorDescriptor!!
fun VariableAccessInfo.getAccessDescriptorIfNeeded(): CallableDescriptor {
if (variableDescriptor is PropertyDescriptor &&
(variableDescriptor.isExtension || TranslationUtils.shouldAccessViaFunctions(variableDescriptor))
) {
return getAccessDescriptor()
} }
else { else {
return variableDescriptor return variableDescriptor
@@ -70,9 +70,11 @@ object CallTranslator {
val isNative = AnnotationsUtils.isNativeObject(functionDescriptor) val isNative = AnnotationsUtils.isNativeObject(functionDescriptor)
val hasSpreadOperator = false val hasSpreadOperator = false
if (dispatchReceiver != null) { if (dispatchReceiver != null) {
return DefaultFunctionCallCase.buildDefaultCallWithDispatchReceiver(argumentsInfo, dispatchReceiver, functionName, isNative, hasSpreadOperator) return DefaultFunctionCallCase.buildDefaultCallWithDispatchReceiver(argumentsInfo, dispatchReceiver, functionName, isNative,
hasSpreadOperator)
} else { } else {
return DefaultFunctionCallCase.buildDefaultCallWithoutReceiver(context, argumentsInfo, functionDescriptor, isNative, hasSpreadOperator) return DefaultFunctionCallCase.buildDefaultCallWithoutReceiver(context, argumentsInfo, functionDescriptor, isNative,
hasSpreadOperator)
} }
} }
} }
@@ -19,15 +19,13 @@ package org.jetbrains.kotlin.js.translate.callTranslator
import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.js.PredefinedAnnotation import org.jetbrains.kotlin.js.PredefinedAnnotation
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.operation.OperatorTable import org.jetbrains.kotlin.js.translate.operation.OperatorTable
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
@@ -83,7 +81,7 @@ object DefaultFunctionCallCase : FunctionCallCase() {
return JsInvocation(JsNameRef(context.getNameForDescriptor(callableDescriptor)), argumentsInfo.translateArguments) return JsInvocation(JsNameRef(context.getNameForDescriptor(callableDescriptor)), argumentsInfo.translateArguments)
} }
val functionRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(it) } val functionRef = ReferenceTranslator.translateAsFQReference(callableDescriptor, context)
return JsInvocation(functionRef, argumentsInfo.translateArguments) return JsInvocation(functionRef, argumentsInfo.translateArguments)
} }
@@ -103,7 +101,7 @@ object DefaultFunctionCallCase : FunctionCallCase() {
return JsInvocation(JsNameRef(functionName, extensionReceiver), argumentsInfo.translateArguments) return JsInvocation(JsNameRef(functionName, extensionReceiver), argumentsInfo.translateArguments)
} }
val functionRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(it) } val functionRef = ReferenceTranslator.translateAsFQReference(callableDescriptor, context)
val referenceToCall = val referenceToCall =
if (callableDescriptor.visibility == Visibilities.LOCAL) { if (callableDescriptor.visibility == Visibilities.LOCAL) {
@@ -214,9 +212,7 @@ object ConstructorCallCase : FunctionCallCase() {
private inline fun FunctionCallInfo.doTranslate( private inline fun FunctionCallInfo.doTranslate(
getArguments: CallArgumentTranslator.ArgumentsInfo.() -> List<JsExpression> getArguments: CallArgumentTranslator.ArgumentsInfo.() -> List<JsExpression>
): JsExpression { ): JsExpression {
val fqName = context.getQualifiedReference(callableDescriptor) val functionRef = ReferenceTranslator.translateAsFQReference(callableDescriptor, context)
val functionRef = if (isNative()) fqName else context.aliasOrValue(callableDescriptor) { fqName }
val invocationArguments = mutableListOf<JsExpression>() val invocationArguments = mutableListOf<JsExpression>()
val constructorDescriptor = callableDescriptor as ClassConstructorDescriptor val constructorDescriptor = callableDescriptor as ClassConstructorDescriptor
@@ -245,7 +241,7 @@ object SuperCallCase : FunctionCallCase() {
override fun FunctionCallInfo.dispatchReceiver(): JsExpression { override fun FunctionCallInfo.dispatchReceiver(): JsExpression {
// TODO: spread operator // TODO: spread operator
val prototypeClass = pureFqn(Namer.getPrototypeName(), calleeOwner) val prototypeClass = JsAstUtils.prototypeOf(calleeOwner)
val functionRef = Namer.getFunctionCallRef(JsNameRef(functionName, prototypeClass)) val functionRef = Namer.getFunctionCallRef(JsNameRef(functionName, prototypeClass))
return JsInvocation(functionRef, argumentsInfo.argsWithReceiver(dispatchReceiver!!)) return JsInvocation(functionRef, argumentsInfo.argsWithReceiver(dispatchReceiver!!))
} }
@@ -26,13 +26,14 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor import org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure import org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import java.util.* import java.util.*
object NativeVariableAccessCase : VariableAccessCase() { object NativeVariableAccessCase : VariableAccessCase() {
override fun VariableAccessInfo.extensionReceiver(): JsExpression { override fun VariableAccessInfo.extensionReceiver(): JsExpression {
return constructAccessExpression(JsNameRef(variableName, extensionReceiver!!)) return constructAccessExpression(JsNameRef(variableName, extensionReceiver!!))
@@ -40,8 +41,8 @@ object NativeVariableAccessCase : VariableAccessCase() {
override fun VariableAccessInfo.dispatchReceiver(): JsExpression { override fun VariableAccessInfo.dispatchReceiver(): JsExpression {
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { return if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) {
val methodRef = context.getNameForDescriptor(getAccessDescriptor()) val methodRef = context.getNameForDescriptor(getAccessDescriptorIfNeeded())
JsInvocation(pureFqn(methodRef, dispatchReceiver!!), *additionalArguments.toTypedArray()) JsInvocation(pureFqn(methodRef, dispatchReceiver!!), *additionalArguments.toTypedArray())
} }
else { else {
@@ -51,8 +52,8 @@ object NativeVariableAccessCase : VariableAccessCase() {
override fun VariableAccessInfo.noReceivers(): JsExpression { override fun VariableAccessInfo.noReceivers(): JsExpression {
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { return if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) {
val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptor()) } val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptorIfNeeded()) }
JsInvocation(methodRef, *additionalArguments.toTypedArray()) JsInvocation(methodRef, *additionalArguments.toTypedArray())
} }
else { else {
@@ -63,32 +64,49 @@ object NativeVariableAccessCase : VariableAccessCase() {
object DefaultVariableAccessCase : VariableAccessCase() { object DefaultVariableAccessCase : VariableAccessCase() {
override fun VariableAccessInfo.noReceivers(): JsExpression { override fun VariableAccessInfo.noReceivers(): JsExpression {
val descriptor = resolvedCall.resultingDescriptor val variableDescriptor = this.variableDescriptor
if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) {
val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptor()) } if (variableDescriptor is PropertyDescriptor &&
!JsDescriptorUtils.isSimpleFinalProperty(variableDescriptor) &&
context.isFromCurrentModule(variableDescriptor)
) {
val methodRef = context.getInnerReference(getAccessDescriptor())
return JsInvocation(methodRef, *additionalArguments.toTypedArray()) return JsInvocation(methodRef, *additionalArguments.toTypedArray())
} }
val functionRef = context.aliasOrValue(callableDescriptor) { val descriptor = resolvedCall.resultingDescriptor
context.getQualifiedReference(variableDescriptor) if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) {
val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptorIfNeeded()) }
return JsInvocation(methodRef, *additionalArguments.toTypedArray())
} }
val ref = if (descriptor is FakeCallableDescriptorForObject) {
if (isVarCapturedInClosure(context.bindingContext(), callableDescriptor)) { return JsInvocation(pureFqn(context.getNameForObjectInstance (descriptor.getReferencedObject()), null))
getCapturedVarAccessor(functionRef) }
} else {
functionRef val functionRef = context.aliasOrValue(callableDescriptor) {
} if (context.isFromCurrentModule(variableDescriptor)) {
context.getInnerReference(variableDescriptor)
}
else {
val qualifier = context.getInnerReference(variableDescriptor.containingDeclaration)
val name = context.getNameForDescriptor(variableDescriptor)
JsNameRef(name, qualifier)
}
}
val ref = if (isVarCapturedInClosure(context.bindingContext(), callableDescriptor)) {
getCapturedVarAccessor(functionRef)
}
else {
functionRef
}
val localVariableDescriptor = resolvedCall.resultingDescriptor as? LocalVariableDescriptor val localVariableDescriptor = resolvedCall.resultingDescriptor as? LocalVariableDescriptor
val accessorDescriptor = if (isGetAccess()) localVariableDescriptor?.getter else localVariableDescriptor?.setter val accessorDescriptor = if (isGetAccess()) localVariableDescriptor?.getter else localVariableDescriptor?.setter
if (accessorDescriptor != null) { if (accessorDescriptor != null) {
val funRef = JsNameRef(TranslationUtils.getAccessorFunctionName(accessorDescriptor), ref) val funRef = JsNameRef(TranslationUtils.getAccessorFunctionName(accessorDescriptor), ref)
return if (isGetAccess()) { return JsInvocation(funRef, *additionalArguments.toTypedArray())
JsInvocation(funRef)
} else {
JsInvocation(funRef, value!!)
}
} }
return constructAccessExpression(ref) return constructAccessExpression(ref)
@@ -96,8 +114,8 @@ object DefaultVariableAccessCase : VariableAccessCase() {
override fun VariableAccessInfo.dispatchReceiver(): JsExpression { override fun VariableAccessInfo.dispatchReceiver(): JsExpression {
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { return if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) {
val callExpr = pureFqn(context.getNameForDescriptor(getAccessDescriptor()), dispatchReceiver!!) val callExpr = pureFqn(context.getNameForDescriptor(getAccessDescriptorIfNeeded()), dispatchReceiver!!)
JsInvocation(callExpr, *additionalArguments.toTypedArray()) JsInvocation(callExpr, *additionalArguments.toTypedArray())
} }
else { else {
@@ -110,12 +128,12 @@ object DefaultVariableAccessCase : VariableAccessCase() {
} }
override fun VariableAccessInfo.extensionReceiver(): JsExpression { override fun VariableAccessInfo.extensionReceiver(): JsExpression {
val functionRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptor()) } val functionRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptorIfNeeded()) }
return JsInvocation(functionRef, extensionReceiver!!, *additionalArguments.toTypedArray()) return JsInvocation(functionRef, extensionReceiver!!, *additionalArguments.toTypedArray())
} }
override fun VariableAccessInfo.bothReceivers(): JsExpression { override fun VariableAccessInfo.bothReceivers(): JsExpression {
val funRef = JsNameRef(context.getNameForDescriptor(getAccessDescriptor()), dispatchReceiver!!) val funRef = JsNameRef(context.getNameForDescriptor(getAccessDescriptorIfNeeded()), dispatchReceiver!!)
return JsInvocation(funRef, extensionReceiver!!, *additionalArguments.toTypedArray()) return JsInvocation(funRef, extensionReceiver!!, *additionalArguments.toTypedArray())
} }
} }
@@ -150,16 +168,16 @@ object SuperPropertyAccessCase : VariableAccessCase() {
val variableName = context.program().getStringLiteral(this.variableName.ident) val variableName = context.program().getStringLiteral(this.variableName.ident)
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { return if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) {
val accessor = getAccessDescriptor() val accessor = getAccessDescriptorIfNeeded()
val prototype = pureFqn(Namer.getPrototypeName(), context.getQualifiedReference(descriptor.containingDeclaration)) val prototype = pureFqn(Namer.getPrototypeName(), context.getInnerReference(descriptor.containingDeclaration))
val funRef = Namer.getFunctionCallRef(pureFqn(context.getNameForDescriptor(accessor), prototype)) val funRef = Namer.getFunctionCallRef(pureFqn(context.getNameForDescriptor(accessor), prototype))
val arguments = listOf(dispatchReceiver!!) + additionalArguments val arguments = listOf(dispatchReceiver!!) + additionalArguments
JsInvocation(funRef, *arguments.toTypedArray()) JsInvocation(funRef, *arguments.toTypedArray())
} }
else { else {
val callExpr = if (isGetAccess()) context.namer().callGetProperty else context.namer().callSetProperty val callExpr = if (isGetAccess()) context.namer().callGetProperty else context.namer().callSetProperty
val arguments = listOf(dispatchReceiver!!, calleeOwner, variableName) + additionalArguments val arguments = listOf(dispatchReceiver!!, JsAstUtils.prototypeOf(calleeOwner), variableName) + additionalArguments
JsInvocation(callExpr, *arguments.toTypedArray()) JsInvocation(callExpr, *arguments.toTypedArray())
} }
} }
@@ -1,39 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.translate.context
import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
class DefinitionPlace(
private val scope: JsObjectScope,
private val fqName: JsExpression,
val properties: MutableList<JsPropertyInitializer>
) {
fun define(suggestedName: String, expression : JsExpression) = define(scope.declareFreshName(suggestedName), expression)
fun define(name: JsName, expression: JsExpression): JsNameRef {
if (expression is JsFunction) {
/** JsInliner should be able
* to find function by name */
name.staticRef = expression
}
properties.add(JsPropertyInitializer(name.makeRef(), expression))
return JsNameRef(name, fqName)
}
}
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.naming.NameSuggestion;
import org.jetbrains.kotlin.js.naming.SuggestedName; import org.jetbrains.kotlin.js.naming.SuggestedName;
import org.jetbrains.kotlin.js.resolve.JsPlatform; import org.jetbrains.kotlin.js.resolve.JsPlatform;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.FqNameUnsafe; import org.jetbrains.kotlin.name.FqNameUnsafe;
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.Name;
@@ -77,10 +78,6 @@ public final class Namer {
public static final String OUTER_FIELD_NAME = "$outer"; public static final String OUTER_FIELD_NAME = "$outer";
private static final String CLASS_OBJECT_NAME = "createClass";
private static final String ENUM_CLASS_OBJECT_NAME = "createEnumClass";
private static final String TRAIT_OBJECT_NAME = "createTrait";
private static final String OBJECT_OBJECT_NAME = "createObject";
private static final String CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME = "getCallableRefForMemberFunction"; private static final String CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME = "getCallableRefForMemberFunction";
private static final String CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME = "getCallableRefForExtensionFunction"; private static final String CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME = "getCallableRefForExtensionFunction";
private static final String CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME = "getCallableRefForLocalExtensionFunction"; private static final String CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME = "getCallableRefForLocalExtensionFunction";
@@ -89,13 +86,8 @@ public final class Namer {
private static final String CALLABLE_REF_FOR_MEMBER_PROPERTY = "getCallableRefForMemberProperty"; private static final String CALLABLE_REF_FOR_MEMBER_PROPERTY = "getCallableRefForMemberProperty";
private static final String CALLABLE_REF_FOR_EXTENSION_PROPERTY = "getCallableRefForExtensionProperty"; private static final String CALLABLE_REF_FOR_EXTENSION_PROPERTY = "getCallableRefForExtensionProperty";
private static final String SETTER_PREFIX = "set_";
private static final String GETTER_PREFIX = "get_";
private static final String BACKING_FIELD_PREFIX = "$";
private static final String DELEGATE = "$delegate"; private static final String DELEGATE = "$delegate";
private static final String SUPER_METHOD_NAME = "baseInitializer";
private static final String ROOT_PACKAGE = "_"; private static final String ROOT_PACKAGE = "_";
private static final String RECEIVER_PARAMETER_NAME = "$receiver"; private static final String RECEIVER_PARAMETER_NAME = "$receiver";
@@ -113,6 +105,14 @@ public final class Namer {
private static final JsNameRef JS_OBJECT_CREATE_FUNCTION = new JsNameRef("create", JS_OBJECT); private static final JsNameRef JS_OBJECT_CREATE_FUNCTION = new JsNameRef("create", JS_OBJECT);
public static final String LOCAL_MODULE_PREFIX = "$module$"; public static final String LOCAL_MODULE_PREFIX = "$module$";
public static final String METADATA = "$metadata$";
public static final String METADATA_SUPERTYPES = "baseClasses";
public static final String OBJECT_INSTANCE_VAR_SUFFIX = "_instance";
public static final String OBJECT_INSTANCE_FUNCTION_SUFFIX = "_getInstance";
public static final String ENUM_NAME_FIELD = "name$";
public static final String ENUM_ORDINAL_FIELD = "ordinal$";
public static boolean isUndefined(@NotNull JsExpression expr) { public static boolean isUndefined(@NotNull JsExpression expr) {
if (expr instanceof JsPrefixOperation) { if (expr instanceof JsPrefixOperation) {
@@ -148,50 +148,11 @@ public final class Namer {
return ROOT_PACKAGE; return ROOT_PACKAGE;
} }
@NotNull
public static JsNameRef superMethodNameRef(@NotNull JsName superClassJsName) {
return pureFqn(SUPER_METHOD_NAME, superClassJsName.makeRef());
}
@NotNull
public static String getNameForAccessor(@NotNull String propertyName, boolean isGetter, boolean useNativeAccessor) {
if (useNativeAccessor) {
return propertyName;
}
if (isGetter) {
return getNameForGetter(propertyName);
}
else {
return getNameForSetter(propertyName);
}
}
@NotNull
public static String getKotlinBackingFieldName(@NotNull String propertyName) {
return getNameWithPrefix(propertyName, BACKING_FIELD_PREFIX);
}
@NotNull
private static String getNameForGetter(@NotNull String propertyName) {
return getNameWithPrefix(propertyName, GETTER_PREFIX);
}
@NotNull
private static String getNameForSetter(@NotNull String propertyName) {
return getNameWithPrefix(propertyName, SETTER_PREFIX);
}
@NotNull @NotNull
public static String getPrototypeName() { public static String getPrototypeName() {
return PROTOTYPE_NAME; return PROTOTYPE_NAME;
} }
@NotNull
private static JsNameRef getRefToPrototype(@NotNull JsExpression classOrTraitExpression) {
return pureFqn(getPrototypeName(), classOrTraitExpression);
}
@NotNull @NotNull
public static String getDelegatePrefix() { public static String getDelegatePrefix() {
return DELEGATE; return DELEGATE;
@@ -207,11 +168,6 @@ public final class Namer {
return new JsNameRef(getDelegateName(propertyName), JsLiteral.THIS); return new JsNameRef(getDelegateName(propertyName), JsLiteral.THIS);
} }
@NotNull
private static String getNameWithPrefix(@NotNull String name, @NotNull String prefix) {
return prefix + name;
}
@NotNull @NotNull
public static JsNameRef getFunctionCallRef(@NotNull JsExpression functionExpression) { public static JsNameRef getFunctionCallRef(@NotNull JsExpression functionExpression) {
return pureFqn(CALL_FUNCTION, functionExpression); return pureFqn(CALL_FUNCTION, functionExpression);
@@ -224,7 +180,7 @@ public final class Namer {
@NotNull @NotNull
public static JsInvocation createObjectWithPrototypeFrom(JsNameRef referenceToClass) { public static JsInvocation createObjectWithPrototypeFrom(JsNameRef referenceToClass) {
return new JsInvocation(JS_OBJECT_CREATE_FUNCTION, getRefToPrototype(referenceToClass)); return new JsInvocation(JS_OBJECT_CREATE_FUNCTION, JsAstUtils.prototypeOf(referenceToClass));
} }
@NotNull @NotNull
@@ -245,18 +201,6 @@ public final class Namer {
@NotNull @NotNull
private final JsObjectScope kotlinScope; private final JsObjectScope kotlinScope;
@NotNull @NotNull
private final JsName classCreationMethodReference;
@NotNull
private final JsName enumClassCreationMethodName;
@NotNull
private final JsName interfaceCreationMethodName;
@NotNull
private final JsExpression definePackage;
@NotNull
private final JsExpression defineRootPackage;
@NotNull
private final JsName objectCreationMethodName;
@NotNull
private final JsName callableRefForMemberFunctionName; private final JsName callableRefForMemberFunctionName;
@NotNull @NotNull
private final JsName callableRefForExtensionFunctionName; private final JsName callableRefForExtensionFunctionName;
@@ -280,17 +224,10 @@ public final class Namer {
private Namer(@NotNull JsScope rootScope) { private Namer(@NotNull JsScope rootScope) {
kotlinScope = JsObjectScope(rootScope, "Kotlin standard object"); kotlinScope = JsObjectScope(rootScope, "Kotlin standard object");
interfaceCreationMethodName = kotlinScope.declareName(TRAIT_OBJECT_NAME);
definePackage = kotlin("definePackage");
defineRootPackage = kotlin("defineRootPackage");
callGetProperty = kotlin("callGetter"); callGetProperty = kotlin("callGetter");
callSetProperty = kotlin("callSetter"); callSetProperty = kotlin("callSetter");
classCreationMethodReference = kotlinScope.declareName(CLASS_OBJECT_NAME);
enumClassCreationMethodName = kotlinScope.declareName(ENUM_CLASS_OBJECT_NAME);
objectCreationMethodName = kotlinScope.declareName(OBJECT_OBJECT_NAME);
callableRefForMemberFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME); callableRefForMemberFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME);
callableRefForExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME); callableRefForExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME);
callableRefForLocalExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME); callableRefForLocalExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME);
@@ -313,16 +250,6 @@ public final class Namer {
return suggested.getNames().get(0); return suggested.getNames().get(0);
} }
@NotNull
public JsExpression packageDefinitionMethodReference() {
return definePackage;
}
@NotNull
public JsExpression rootPackageDefinitionMethodReference() {
return defineRootPackage;
}
@NotNull @NotNull
public JsExpression callableRefForMemberFunctionReference() { public JsExpression callableRefForMemberFunctionReference() {
return kotlin(callableRefForMemberFunctionName); return kotlin(callableRefForMemberFunctionName);
@@ -463,24 +390,6 @@ public final class Namer {
return packageFqName.isRoot() ? getRootPackageName() : packageFqName.shortName().asString(); return packageFqName.isRoot() ? getRootPackageName() : packageFqName.shortName().asString();
} }
@NotNull
public JsExpression classCreateInvocation(@NotNull ClassDescriptor descriptor) {
switch (descriptor.getKind()) {
case INTERFACE:
return kotlin(interfaceCreationMethodName);
case ENUM_CLASS:
return kotlin(enumClassCreationMethodName);
case ENUM_ENTRY:
case OBJECT:
return kotlin(objectCreationMethodName);
case ANNOTATION_CLASS:
case CLASS:
return kotlin(classCreationMethodReference);
default:
throw new UnsupportedOperationException("Unsupported class kind: " + descriptor);
}
}
@NotNull @NotNull
public static JsExpression getUndefinedExpression() { public static JsExpression getUndefinedExpression() {
return new JsPrefixOperation(JsUnaryOperator.VOID, JsNumberLiteral.ZERO); return new JsPrefixOperation(JsUnaryOperator.VOID, JsNumberLiteral.ZERO);
@@ -488,12 +397,12 @@ public final class Namer {
@NotNull @NotNull
public JsExpression getCallGetProperty() { public JsExpression getCallGetProperty() {
return callGetProperty; return callGetProperty.deepCopy();
} }
@NotNull @NotNull
public JsExpression getCallSetProperty() { public JsExpression getCallSetProperty() {
return callSetProperty; return callSetProperty.deepCopy();
} }
public static JsNameRef kotlinLong() { public static JsNameRef kotlinLong() {
@@ -113,6 +113,7 @@ public final class StandardClasses {
standardClasses.declare().forFQ("kotlin.Comparable").kotlinClass("Comparable"); standardClasses.declare().forFQ("kotlin.Comparable").kotlinClass("Comparable");
standardClasses.declare().forFQ("kotlin.Throwable").kotlinClass("Throwable"); standardClasses.declare().forFQ("kotlin.Throwable").kotlinClass("Throwable");
standardClasses.declare().forFQ("kotlin.Annotation").kotlinClass("Annotation");
} }
@@ -27,7 +27,9 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.LinkedHashMap; import com.intellij.util.containers.hash.LinkedHashMap;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.naming.NameSuggestion;
import org.jetbrains.kotlin.js.naming.SuggestedName; import org.jetbrains.kotlin.js.naming.SuggestedName;
@@ -36,10 +38,12 @@ import org.jetbrains.kotlin.js.translate.context.generator.Rule;
import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics; import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt; import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import java.util.*; import java.util.*;
@@ -63,7 +67,8 @@ public final class StaticContext {
Namer namer = Namer.newInstance(program.getRootScope()); Namer namer = Namer.newInstance(program.getRootScope());
Intrinsics intrinsics = new Intrinsics(); Intrinsics intrinsics = new Intrinsics();
StandardClasses standardClasses = StandardClasses.bindImplementations(namer.getKotlinScope()); StandardClasses standardClasses = StandardClasses.bindImplementations(namer.getKotlinScope());
return new StaticContext(program, bindingTrace, namer, intrinsics, standardClasses, program.getRootScope(), config, JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope());
return new StaticContext(program, rootFunction, bindingTrace, namer, intrinsics, standardClasses, program.getRootScope(), config,
moduleDescriptor); moduleDescriptor);
} }
@@ -84,10 +89,14 @@ public final class StaticContext {
@NotNull @NotNull
private final JsScope rootScope; private final JsScope rootScope;
@NotNull
private final Generator<JsName> innerNames = new InnerNameGenerator();
@NotNull @NotNull
private final Map<FqName, JsName> packageNames = Maps.newHashMap(); private final Map<FqName, JsName> packageNames = Maps.newHashMap();
@NotNull @NotNull
private final Generator<JsScope> scopes = new ScopeGenerator(); private final Generator<JsScope> scopes = new ScopeGenerator();
@NotNull
private final Generator<JsName> objectInstanceNames = new ObjectInstanceNameGenerator();
@NotNull @NotNull
private final Map<JsScope, JsFunction> scopeToFunction = Maps.newHashMap(); private final Map<JsScope, JsFunction> scopeToFunction = Maps.newHashMap();
@@ -124,9 +133,31 @@ public final class StaticContext {
@NotNull @NotNull
private final JsScope rootPackageScope; private final JsScope rootPackageScope;
@NotNull
private JsFunction rootFunction;
@NotNull
private final List<JsStatement> declarationStatements = new ArrayList<JsStatement>();
@NotNull
private final List<JsStatement> importStatements = new ArrayList<JsStatement>();
@NotNull
private final List<JsStatement> exportStatements = new ArrayList<JsStatement>();
@NotNull
private final Set<ClassDescriptor> classes = new HashSet<ClassDescriptor>();
@NotNull
private final ExportedPackage rootPackage = new ExportedPackage("");
@NotNull
private final JsObjectLiteral exportObject = rootPackage.objectLiteral;
//TODO: too many parameters in constructor //TODO: too many parameters in constructor
private StaticContext( private StaticContext(
@NotNull JsProgram program, @NotNull JsProgram program,
@NotNull JsFunction rootFunction,
@NotNull BindingTrace bindingTrace, @NotNull BindingTrace bindingTrace,
@NotNull Namer namer, @NotNull Namer namer,
@NotNull Intrinsics intrinsics, @NotNull Intrinsics intrinsics,
@@ -136,13 +167,15 @@ public final class StaticContext {
@NotNull ModuleDescriptor moduleDescriptor @NotNull ModuleDescriptor moduleDescriptor
) { ) {
this.program = program; this.program = program;
this.rootFunction = rootFunction;
this.bindingTrace = bindingTrace; this.bindingTrace = bindingTrace;
this.namer = namer; this.namer = namer;
this.intrinsics = intrinsics; this.intrinsics = intrinsics;
this.rootScope = rootScope; this.rootScope = rootScope;
this.standardClasses = standardClasses; this.standardClasses = standardClasses;
this.config = config; this.config = config;
currentModule = moduleDescriptor; this.currentModule = moduleDescriptor;
this.rootFunction = rootFunction;
rootPackageScope = new JsObjectScope(rootScope, "<root package>", "root-package"); rootPackageScope = new JsObjectScope(rootScope, "<root package>", "root-package");
} }
@@ -171,11 +204,6 @@ public final class StaticContext {
return namer; return namer;
} }
@NotNull
private JsScope getRootScope() {
return rootScope;
}
@NotNull @NotNull
public Map<String, JsName> getImportedModules() { public Map<String, JsName> getImportedModules() {
if (readOnlyImportedModules == null) { if (readOnlyImportedModules == null) {
@@ -219,6 +247,10 @@ public final class StaticContext {
@NotNull @NotNull
private JsExpression buildQualifiedExpression(@NotNull DeclarationDescriptor descriptor) { private JsExpression buildQualifiedExpression(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isAny((ClassDescriptor) descriptor)) {
return new JsNameRef("Object");
}
SuggestedName suggested = nameSuggestion.suggest(descriptor); SuggestedName suggested = nameSuggestion.suggest(descriptor);
if (suggested == null) { if (suggested == null) {
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor); ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
@@ -288,6 +320,20 @@ public final class StaticContext {
return name; return name;
} }
@NotNull
public JsName getInnerNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
JsName name = innerNames.get(descriptor.getOriginal());
assert name != null : "Must have inner name for descriptor";
return name;
}
@NotNull
public JsName getNameForObjectInstance(@NotNull ClassDescriptor descriptor) {
JsName name = objectInstanceNames.get(descriptor.getOriginal());
assert name != null : "Must have inner name for object instance";
return name;
}
@NotNull @NotNull
private List<JsName> getActualNameFromSuggested(@NotNull SuggestedName suggested) { private List<JsName> getActualNameFromSuggested(@NotNull SuggestedName suggested) {
JsScope scope = getScopeForDescriptor(suggested.getScope()); JsScope scope = getScopeForDescriptor(suggested.getScope());
@@ -362,6 +408,100 @@ public final class StaticContext {
return config; return config;
} }
private final class InnerNameGenerator extends Generator<JsName> {
public InnerNameGenerator() {
addRule(new Rule<JsName>() {
@Nullable
@Override
public JsName apply(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof ModuleDescriptor) {
return getModuleInnerName(descriptor);
}
if (descriptor instanceof LocalVariableDescriptor || descriptor instanceof ParameterDescriptor) {
return getNameForDescriptor(descriptor);
}
if (descriptor instanceof ConstructorDescriptor) {
if (((ConstructorDescriptor) descriptor).isPrimary()) {
return getInnerNameForDescriptor(((ConstructorDescriptor) descriptor).getConstructedClass());
}
}
JsName result = rootFunction.getScope().declareFreshName(getSuggestedName(descriptor));
ModuleDescriptor module = DescriptorUtilsKt.getModule(descriptor);
if (module != currentModule) {
importStatements.add(JsAstUtils.newVar(result, getQualifiedReference(descriptor)));
}
return result;
}
});
}
}
private final class ObjectInstanceNameGenerator extends Generator<JsName> {
public ObjectInstanceNameGenerator() {
addRule(new Rule<JsName>() {
@Nullable
@Override
public JsName apply(@NotNull DeclarationDescriptor descriptor) {
String suggested = getSuggestedName(descriptor) + Namer.OBJECT_INSTANCE_FUNCTION_SUFFIX;
JsName result = rootFunction.getScope().declareFreshName(suggested);
ModuleDescriptor module = DescriptorUtilsKt.getModule(descriptor);
if (module != currentModule) {
importStatements.add(JsAstUtils.newVar(result, getQualifiedReference(descriptor)));
}
return result;
}
});
}
}
@NotNull
public static String getSuggestedName(@NotNull DeclarationDescriptor descriptor) {
String suggestedName;
if (descriptor instanceof PropertyGetterDescriptor) {
PropertyGetterDescriptor getter = (PropertyGetterDescriptor) descriptor;
suggestedName = "get_" + getSuggestedName(getter.getCorrespondingProperty());
}
else if (descriptor instanceof PropertySetterDescriptor) {
PropertySetterDescriptor setter = (PropertySetterDescriptor) descriptor;
suggestedName = "set_" + getSuggestedName(setter.getCorrespondingProperty());
}
else if (descriptor instanceof ConstructorDescriptor) {
ConstructorDescriptor constructor = (ConstructorDescriptor) descriptor;
suggestedName = getSuggestedName(constructor.getContainingDeclaration()) + "_init";
descriptor = descriptor.getContainingDeclaration();
assert descriptor != null : "ConstructorDescriptor should have containing declaration: " + constructor;
}
else {
if (descriptor.getName().isSpecial()) {
if (descriptor instanceof ClassDescriptor) {
if (DescriptorUtils.isAnonymousObject(descriptor)) {
suggestedName = "ObjectLiteral";
}
else {
suggestedName = "Anonymous";
}
}
else if (descriptor instanceof FunctionDescriptor) {
suggestedName = "lambda";
}
else {
suggestedName = "anonymous";
}
}
else {
suggestedName = descriptor.getName().asString();
}
}
if (!(descriptor instanceof PackageFragmentDescriptor) && !DescriptorUtils.isTopLevelDeclaration(descriptor)) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
assert container != null : "We just figured out that descriptor is not for a top-level declaration: " + descriptor;
suggestedName = getSuggestedName(container) + "$" + suggestedName;
}
return suggestedName;
}
@NotNull @NotNull
public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) { public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
JsScope scope = getEnclosingScope(descriptor); JsScope scope = getEnclosingScope(descriptor);
@@ -384,7 +524,7 @@ public final class StaticContext {
return null; return null;
} }
if (getSuperclass((ClassDescriptor) descriptor) == null) { if (getSuperclass((ClassDescriptor) descriptor) == null) {
return getRootScope().innerObjectScope("Scope for class " + descriptor.getName()); return rootFunction.getScope();
} }
return null; return null;
} }
@@ -405,10 +545,7 @@ public final class StaticContext {
Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() { Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() {
@Override @Override
public JsScope apply(@NotNull DeclarationDescriptor descriptor) { public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
if (!(descriptor instanceof PackageFragmentDescriptor)) { return rootFunction.getScope();
return null;
}
return getRootScope().innerObjectScope("Package " + descriptor.getName());
} }
}; };
//TODO: never get there //TODO: never get there
@@ -426,7 +563,7 @@ public final class StaticContext {
return null; return null;
} }
JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(getRootScope()); JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(rootFunction.getScope());
assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor; assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction); scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
return correspondingFunction.getScope(); return correspondingFunction.getScope();
@@ -442,9 +579,15 @@ public final class StaticContext {
@Nullable @Nullable
public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) { public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) {
JsName name = getModuleInnerName(descriptor);
return name != null ? JsAstUtils.pureFqn(name, null) : null;
}
@Nullable
private JsName getModuleInnerName(@NotNull DeclarationDescriptor descriptor) {
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor); ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
if (currentModule == module) { if (currentModule == module) {
return pureFqn(Namer.getRootPackageName(), null); return rootScope.declareName(Namer.getRootPackageName());
} }
String moduleName; String moduleName;
if (module == module.getBuiltIns().getBuiltInsModule()) { if (module == module.getBuiltIns().getBuiltInsModule()) {
@@ -464,7 +607,7 @@ public final class StaticContext {
importedModules.put(moduleName, moduleId); importedModules.put(moduleName, moduleId);
} }
return JsAstUtils.pureFqn(moduleId, null); return moduleId;
} }
private static JsExpression applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) { private static JsExpression applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) {
@@ -493,4 +636,115 @@ public final class StaticContext {
public Map<ClassDescriptor, List<DeferredCallSite>> getDeferredCallSites() { public Map<ClassDescriptor, List<DeferredCallSite>> getDeferredCallSites() {
return deferredCallSites; return deferredCallSites;
} }
@NotNull
public JsFunction getRootFunction() {
return rootFunction;
}
@NotNull
public List<JsStatement> getDeclarationStatements() {
return declarationStatements;
}
public void addRootStatement(@NotNull JsStatement statement) {
declarationStatements.add(statement);
}
public void addClass(@NotNull ClassDescriptor classDescriptor) {
classes.add(classDescriptor);
}
public void export(@NotNull DeclarationDescriptor descriptor) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
if (descriptor instanceof ConstructorDescriptor) {
ConstructorDescriptor constructor = (ConstructorDescriptor) descriptor;
container = constructor.getConstructedClass().getContainingDeclaration();
}
if (!(container instanceof PackageFragmentDescriptor)) {
throw new IllegalArgumentException("Declaration " + descriptor + " is not a top-level declaration");
}
PackageFragmentDescriptor packageDescriptor = (PackageFragmentDescriptor) container;
ExportedPackage exportedPackage = rootPackage;
for (Name packageName : packageDescriptor.getFqName().pathSegments()) {
exportedPackage = exportedPackage.getSubpackage(packageName.asString());
}
JsExpression initializerExpr = DeclarationExporter.exportDeclaration(this, descriptor, exportStatements);
if (initializerExpr != null) {
JsPropertyInitializer initializer = new JsPropertyInitializer(
getNameForDescriptor(descriptor).makeRef(), initializerExpr);
exportedPackage.objectLiteral.getPropertyInitializers().add(initializer);
}
}
@NotNull
public NameSuggestion getNameSuggestion() {
return nameSuggestion;
}
@NotNull
public ModuleDescriptor getCurrentModule() {
return currentModule;
}
public void postProcess() {
rootFunction.getBody().getStatements().addAll(importStatements);
addClassPrototypes();
rootFunction.getBody().getStatements().addAll(declarationStatements);
JsName rootPackageName = rootFunction.getScope().declareName(Namer.getRootPackageName());
rootFunction.getBody().getStatements().add(JsAstUtils.newVar(rootPackageName, exportObject));
rootFunction.getBody().getStatements().addAll(exportStatements);
}
private void addClassPrototypes() {
Set<ClassDescriptor> visited = new HashSet<ClassDescriptor>();
for (ClassDescriptor cls : classes) {
addClassPrototypes(cls, visited);
}
}
private void addClassPrototypes(@NotNull ClassDescriptor cls, @NotNull Set<ClassDescriptor> visited) {
if (DescriptorUtilsKt.getModule(cls) != currentModule) return;
if (!visited.add(cls)) return;
ClassDescriptor superclass = DescriptorUtilsKt.getSuperClassNotAny(cls);
if (superclass != null) {
addClassPrototypes(superclass, visited);
List<JsStatement> statements = rootFunction.getBody().getStatements();
JsExpression superPrototype = JsAstUtils.prototypeOf(new JsNameRef(getInnerNameForDescriptor(superclass)));
JsExpression superPrototypeInstance = new JsInvocation(new JsNameRef("create", "Object"), superPrototype);
JsExpression classRef = new JsNameRef(getInnerNameForDescriptor(cls));
JsExpression prototype = JsAstUtils.prototypeOf(classRef);
statements.add(JsAstUtils.assignment(prototype, superPrototypeInstance).makeStmt());
JsExpression constructorRef = new JsNameRef("constructor", prototype.deepCopy());
statements.add(JsAstUtils.assignment(constructorRef, classRef.deepCopy()).makeStmt());
}
}
private static class ExportedPackage {
@NotNull final String name;
@NotNull final Map<String, ExportedPackage> subpackages = new HashMap<String, ExportedPackage>();
@NotNull final JsObjectLiteral objectLiteral = new JsObjectLiteral(true);
public ExportedPackage(@NotNull String name) {
this.name = name;
}
@NotNull
public ExportedPackage getSubpackage(@NotNull String name) {
ExportedPackage subpackage = subpackages.get(name);
if (subpackage == null) {
subpackage = new ExportedPackage(name);
subpackages.put(name, subpackage);
objectLiteral.getPropertyInitializers().add(new JsPropertyInitializer(new JsNameRef(name), subpackage.objectLiteral));
}
return subpackage;
}
}
} }
@@ -24,12 +24,14 @@ import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor; import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor;
import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics; import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
import java.util.*; import java.util.*;
@@ -52,17 +54,19 @@ public class TranslationContext {
@Nullable @Nullable
private final TranslationContext parent; private final TranslationContext parent;
@Nullable @Nullable
private final DefinitionPlace definitionPlace;
@Nullable
private final DeclarationDescriptor declarationDescriptor; private final DeclarationDescriptor declarationDescriptor;
@Nullable @Nullable
private final ClassDescriptor classDescriptor; private final ClassDescriptor classDescriptor;
@NotNull @NotNull
public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) { public static TranslationContext rootContext(
DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), rootFunction.getBody()); @NotNull StaticContext staticContext,
@NotNull JsFunction rootFunction
) {
JsBlock block = new JsBlock(staticContext.getDeclarationStatements());
DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), block);
AliasingContext rootAliasingContext = AliasingContext.getCleanContext(); AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null, null); return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null);
} }
private final Map<JsExpression, TemporaryConstVariable> expressionToTempConstVariableCache = new HashMap<JsExpression, TemporaryConstVariable>(); private final Map<JsExpression, TemporaryConstVariable> expressionToTempConstVariableCache = new HashMap<JsExpression, TemporaryConstVariable>();
@@ -73,7 +77,6 @@ public class TranslationContext {
@NotNull DynamicContext dynamicContext, @NotNull DynamicContext dynamicContext,
@NotNull AliasingContext aliasingContext, @NotNull AliasingContext aliasingContext,
@Nullable UsageTracker usageTracker, @Nullable UsageTracker usageTracker,
@Nullable DefinitionPlace definitionPlace,
@Nullable DeclarationDescriptor declarationDescriptor @Nullable DeclarationDescriptor declarationDescriptor
) { ) {
this.parent = parent; this.parent = parent;
@@ -81,7 +84,6 @@ public class TranslationContext {
this.staticContext = staticContext; this.staticContext = staticContext;
this.aliasingContext = aliasingContext; this.aliasingContext = aliasingContext;
this.usageTracker = usageTracker; this.usageTracker = usageTracker;
this.definitionPlace = definitionPlace;
this.declarationDescriptor = declarationDescriptor; this.declarationDescriptor = declarationDescriptor;
if (declarationDescriptor instanceof ClassDescriptor) { if (declarationDescriptor instanceof ClassDescriptor) {
this.classDescriptor = (ClassDescriptor) declarationDescriptor; this.classDescriptor = (ClassDescriptor) declarationDescriptor;
@@ -119,27 +121,25 @@ public class TranslationContext {
aliasingContext = this.aliasingContext.inner(); aliasingContext = this.aliasingContext.inner();
} }
return new TranslationContext(this, this.staticContext, dynamicContext, aliasingContext, this.usageTracker, null, descriptor); return new TranslationContext(this, this.staticContext, dynamicContext, aliasingContext, this.usageTracker, descriptor);
} }
@NotNull @NotNull
public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) { public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) {
DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody()); DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, fun.getScope()); UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, fun.getScope());
return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, descriptor);
this.definitionPlace, descriptor);
} }
@NotNull @NotNull
public TranslationContext innerWithUsageTracker(@NotNull JsScope scope, @NotNull MemberDescriptor descriptor) { public TranslationContext innerWithUsageTracker(@NotNull JsScope scope, @NotNull MemberDescriptor descriptor) {
UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, scope); UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, scope);
return new TranslationContext(this, staticContext, dynamicContext, aliasingContext.inner(), usageTracker, definitionPlace, return new TranslationContext(this, staticContext, dynamicContext, aliasingContext.inner(), usageTracker, descriptor);
descriptor);
} }
@NotNull @NotNull
public TranslationContext innerBlock(@NotNull JsBlock block) { public TranslationContext innerBlock(@NotNull JsBlock block) {
return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker, null, return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker,
this.declarationDescriptor); this.declarationDescriptor);
} }
@@ -149,14 +149,18 @@ public class TranslationContext {
} }
@NotNull @NotNull
public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor, @Nullable DefinitionPlace place) { public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor) {
DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor)); JsBlock innerBlock = getBlockForDescriptor(descriptor);
return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place, descriptor); if (innerBlock == null) {
innerBlock = dynamicContext.jsBlock();
}
DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), innerBlock);
return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, descriptor);
} }
@NotNull @NotNull
private TranslationContext innerWithAliasingContext(AliasingContext aliasingContext) { private TranslationContext innerWithAliasingContext(AliasingContext aliasingContext) {
return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, null, declarationDescriptor); return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, declarationDescriptor);
} }
@NotNull @NotNull
@@ -174,13 +178,13 @@ public class TranslationContext {
return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases)); return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases));
} }
@NotNull @Nullable
private JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) { private JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof CallableDescriptor) { if (descriptor instanceof CallableDescriptor) {
return getFunctionObject((CallableDescriptor) descriptor).getBody(); return getFunctionObject((CallableDescriptor) descriptor).getBody();
} }
else { else {
return new JsBlock(); return null;
} }
} }
@@ -211,8 +215,13 @@ public class TranslationContext {
} }
@NotNull @NotNull
public JsName getNameForPackage(@NotNull FqName fqName) { public JsName getInnerNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
return staticContext.getNameForPackage(fqName); return staticContext.getInnerNameForDescriptor(descriptor);
}
@NotNull
public JsName getNameForObjectInstance(@NotNull ClassDescriptor descriptor) {
return staticContext.getNameForObjectInstance(descriptor);
} }
@NotNull @NotNull
@@ -225,6 +234,11 @@ public class TranslationContext {
return staticContext.getQualifiedReference(descriptor); return staticContext.getQualifiedReference(descriptor);
} }
@NotNull
public JsNameRef getInnerReference(@NotNull DeclarationDescriptor descriptor) {
return JsAstUtils.pureFqn(getInnerNameForDescriptor(descriptor), null);
}
@NotNull @NotNull
public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) { public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
return staticContext.getQualifiedReference(packageFqName); return staticContext.getQualifiedReference(packageFqName);
@@ -358,7 +372,9 @@ public class TranslationContext {
return JsLiteral.THIS; return JsLiteral.THIS;
} }
else { else {
return getQualifiedReference(descriptor.getContainingDeclaration()); ClassDescriptor objectDescriptor = (ClassDescriptor) descriptor.getContainingDeclaration();
JsExpression instanceFunctionRef = JsAstUtils.pureFqn(getNameForObjectInstance(objectDescriptor), null);
return new JsInvocation(instanceFunctionRef);
} }
} }
@@ -381,7 +397,7 @@ public class TranslationContext {
} }
private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) { private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) {
if (declarationDescriptor instanceof ClassDescriptor && !DescriptorUtils.isCompanionObject(declarationDescriptor)) { if (declarationDescriptor instanceof ClassDescriptor) {
return descriptor == declarationDescriptor; return descriptor == declarationDescriptor;
} }
else { else {
@@ -402,8 +418,7 @@ public class TranslationContext {
return thisExpression; return thisExpression;
} }
ClassDescriptor parentDescriptor = parent.classDescriptor; if (classDescriptor != parent.classDescriptor) {
if (classDescriptor != parentDescriptor) {
return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression)); return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression));
} }
else { else {
@@ -411,20 +426,6 @@ public class TranslationContext {
} }
} }
@NotNull
public DefinitionPlace getDefinitionPlace() {
if (definitionPlace != null) return definitionPlace;
if (parent != null) return parent.getDefinitionPlace();
throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)");
}
@NotNull
public JsNameRef define(DeclarationDescriptor descriptor, JsExpression expression) {
String suggestedName = TranslationUtils.getSuggestedNameForInnerDeclaration(staticContext, descriptor);
return getDefinitionPlace().define(suggestedName, expression);
}
@Nullable @Nullable
private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) { private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) {
if (usageTracker != null) { if (usageTracker != null) {
@@ -560,4 +561,49 @@ public class TranslationContext {
public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) { public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) {
return staticContext.getModuleExpressionFor(descriptor); return staticContext.getModuleExpressionFor(descriptor);
} }
@NotNull
public JsFunction getRootFunction() {
return staticContext.getRootFunction();
}
public void addRootStatement(@NotNull JsStatement statement) {
staticContext.addRootStatement(statement);
}
@NotNull
public JsFunction defineTopLevelFunction(@NotNull DeclarationDescriptor descriptor) {
JsFunction function = createTopLevelFunction(descriptor);
addRootStatement(function.makeStmt());
return function;
}
@NotNull
public JsName createGlobalName(@NotNull String suggestedName) {
return staticContext.getRootFunction().getScope().declareFreshName(suggestedName);
}
@NotNull
public JsFunction createTopLevelFunction(@NotNull DeclarationDescriptor descriptor) {
JsFunction function = createTopLevelAnonymousFunction(descriptor);
function.setName(staticContext.getInnerNameForDescriptor(descriptor));
return function;
}
@NotNull
public JsFunction createTopLevelAnonymousFunction(@NotNull DeclarationDescriptor descriptor) {
return new JsFunction(getRootFunction().getScope(), new JsBlock(), descriptor.toString());
}
public void addClass(@NotNull ClassDescriptor classDescriptor) {
staticContext.addClass(classDescriptor);
}
public void export(@NotNull DeclarationDescriptor descriptor) {
staticContext.export(descriptor);
}
public boolean isFromCurrentModule(@NotNull DeclarationDescriptor descriptor) {
return staticContext.getCurrentModule() == DescriptorUtilsKt.getModule(descriptor);
}
} }
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:JvmName("DeclarationExporter")
package org.jetbrains.kotlin.js.translate.context
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
fun StaticContext.exportDeclaration(declaration: DeclarationDescriptor, additionalStatements: MutableList<in JsStatement>): JsExpression? {
return when {
declaration is ClassDescriptor && declaration.kind == ClassKind.OBJECT -> {
exportObject(declaration, additionalStatements)
null
}
declaration is PropertyDescriptor -> {
exportProperty(declaration, additionalStatements)
null
}
else -> {
getInnerNameForDescriptor(declaration).makeRef()
}
}
}
private fun StaticContext.exportObject(declaration: ClassDescriptor, additionalStatements: MutableList<in JsStatement>) {
val suggestedName = nameSuggestion.suggest(declaration)!!
val qualifier = getQualifiedReference(suggestedName.scope)
val name = getNameForDescriptor(declaration)
additionalStatements += JsAstUtils.defineGetter(program, qualifier, name.ident, getNameForObjectInstance(declaration).makeRef())
}
private fun StaticContext.exportProperty(declaration: PropertyDescriptor, additionalStatements: MutableList<in JsStatement>) {
val propertyLiteral = JsObjectLiteral(true)
val suggestedName = nameSuggestion.suggest(declaration)!!
val qualifier = getQualifiedReference(suggestedName.scope)
val name = getNameForDescriptor(declaration).ident
val getterBody: JsExpression = if (JsDescriptorUtils.isSimpleFinalProperty(declaration)) {
val accessToField = JsReturn(getInnerNameForDescriptor(declaration).makeRef())
JsFunction(rootFunction.scope, JsBlock(accessToField), "$declaration getter")
}
else {
getInnerNameForDescriptor(declaration.getter!!).makeRef()
}
propertyLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef("get"), getterBody)
if (declaration.isVar) {
val setterBody: JsExpression = if (JsDescriptorUtils.isSimpleFinalProperty(declaration)) {
val statements = mutableListOf<JsStatement>()
val function = JsFunction(rootFunction.scope, JsBlock(statements), "$declaration setter")
val valueName = function.scope.declareFreshName("value")
function.parameters += JsParameter(valueName)
statements += JsAstUtils.assignment(getInnerNameForDescriptor(declaration).makeRef(), valueName.makeRef()).makeStmt()
function
}
else {
getInnerNameForDescriptor(declaration.setter!!).makeRef()
}
propertyLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef("set"), setterBody)
}
additionalStatements += JsAstUtils.defineProperty(qualifier, name, propertyLiteral, program).makeStmt()
}
@@ -0,0 +1,116 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.translate.declaration
import com.google.dart.compiler.backend.js.ast.JsExpression
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
abstract class AbstractDeclarationVisitor : TranslatorVisitor<Unit>() {
override fun emptyResult(context: TranslationContext) { }
override fun visitClassOrObject(classOrObject: KtClassOrObject, context: TranslationContext) {
ClassTranslator.translate(classOrObject, context)
val descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), classOrObject)
addClass(descriptor)
}
override fun visitProperty(expression: KtProperty, context: TranslationContext) {
val descriptor = BindingUtils.getPropertyDescriptor(context.bindingContext(), expression)
if (descriptor.modality === Modality.ABSTRACT) return
val propertyContext = context.newDeclaration(descriptor)
val defaultTranslator = DefaultPropertyTranslator(descriptor, context, getBackingFieldReference(descriptor))
val getter = descriptor.getter!!
val getterExpr = if (expression.hasCustomGetter()) {
translateFunction(getter, expression.getter!!, propertyContext)
}
else {
val function = context.getFunctionObject(getter)
defaultTranslator.generateDefaultGetterFunction(getter, function)
function
}
val setterExpr = if (descriptor.isVar) {
val setter = descriptor.setter!!
if (expression.hasCustomSetter()) {
translateFunction(setter, expression.setter!!, propertyContext)
}
else {
val function = context.getFunctionObject(setter)
defaultTranslator.generateDefaultSetterFunction(setter, function)
function
}
}
else {
null
}
if (TranslationUtils.shouldAccessViaFunctions(descriptor) || descriptor.isExtensionProperty) {
addFunction(descriptor.getter!!, getterExpr)
descriptor.setter?.let { addFunction(it, setterExpr!!) }
}
else {
addProperty(descriptor, getterExpr, setterExpr)
}
}
override fun visitNamedFunction(expression: KtNamedFunction, context: TranslationContext) {
val descriptor = BindingUtils.getFunctionDescriptor(context.bindingContext(), expression)
if (descriptor.modality === Modality.ABSTRACT) return
addFunction(descriptor, translateFunction(descriptor, expression, context))
}
override fun visitTypeAlias(typeAlias: KtTypeAlias, data: TranslationContext?) {}
private fun translateFunction(
descriptor: FunctionDescriptor,
expression: KtDeclarationWithBody,
context: TranslationContext
): JsExpression {
val innerContext = context.newDeclaration(descriptor)
innerContext.getInnerNameForDescriptor(descriptor)
val function = context.getFunctionObject(descriptor)
return Translation.functionTranslator(expression, innerContext, function).translateAsMethod()
}
protected abstract fun addClass(descriptor: ClassDescriptor)
protected abstract fun addFunction(
descriptor: FunctionDescriptor,
expression: JsExpression
)
protected abstract fun addProperty(
descriptor: PropertyDescriptor,
getter: JsExpression,
setter: JsExpression?
)
protected abstract fun getBackingFieldReference(descriptor: PropertyDescriptor): JsExpression
}
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.js.translate.declaration package org.jetbrains.kotlin.js.translate.declaration
import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.*
import com.intellij.util.SmartList
import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.backend.common.bridges.Bridge import org.jetbrains.kotlin.backend.common.bridges.Bridge
import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor
@@ -30,21 +29,19 @@ import org.jetbrains.kotlin.js.translate.context.*
import org.jetbrains.kotlin.js.translate.expression.FunctionTranslator import org.jetbrains.kotlin.js.translate.expression.FunctionTranslator
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.initializer.ClassInitializerTranslator import org.jetbrains.kotlin.js.translate.initializer.ClassInitializerTranslator
import org.jetbrains.kotlin.js.translate.utils.BindingUtils import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getClassDescriptor import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getClassDescriptor
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter
import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getPrimaryConstructorParameters import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getPrimaryConstructorParameters
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
import org.jetbrains.kotlin.js.translate.utils.generateDelegateCall
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.toInvocationWith import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.toInvocationWith
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtSecondaryConstructor import org.jetbrains.kotlin.psi.KtSecondaryConstructor
import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.* import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForType
import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForTypeConstructor
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.types.CommonSupertypes.topologicallySortSuperclassesAndRecordAllInstances import org.jetbrains.kotlin.types.CommonSupertypes.topologicallySortSuperclassesAndRecordAllInstances
import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.types.TypeConstructor
@@ -60,20 +57,13 @@ class ClassTranslator private constructor(
) : AbstractTranslator(context) { ) : AbstractTranslator(context) {
private val descriptor = getClassDescriptor(context.bindingContext(), classDeclaration) private val descriptor = getClassDescriptor(context.bindingContext(), classDeclaration)
private val invocationArguments = mutableListOf<JsExpression>()
private val secondaryConstructors = mutableListOf<ConstructorInfo>() private val secondaryConstructors = mutableListOf<ConstructorInfo>()
private val secondaryConstructorProperties = mutableListOf<JsPropertyInitializer>()
private var primaryConstructor: ConstructorInfo? = null private var primaryConstructor: ConstructorInfo? = null
private lateinit var definitionPlace: DefinitionPlace private lateinit var cachedInstanceName: JsName
private val metadataLiteral = JsObjectLiteral(true)
private fun translate(): TranslationResult { private fun translate() {
generateClassCreateInvocationArguments() generateClassCreateInvocationArguments()
val classNameRef = context().getNameForDescriptor(descriptor).makeRef()
val classCreation = JsInvocation(context().namer().classCreateInvocation(descriptor), invocationArguments)
val properties = listOf(JsPropertyInitializer(classNameRef, classCreation)) + secondaryConstructorProperties
return TranslationResult(properties, definitionPlace)
} }
private fun isTrait(): Boolean = descriptor.kind == ClassKind.INTERFACE private fun isTrait(): Boolean = descriptor.kind == ClassKind.INTERFACE
@@ -81,35 +71,35 @@ class ClassTranslator private constructor(
private fun isAnnotation(): Boolean = descriptor.kind == ClassKind.ANNOTATION_CLASS private fun isAnnotation(): Boolean = descriptor.kind == ClassKind.ANNOTATION_CLASS
private fun generateClassCreateInvocationArguments() { private fun generateClassCreateInvocationArguments() {
val properties = SmartList<JsPropertyInitializer>()
val staticProperties = SmartList<JsPropertyInitializer>()
val qualifiedReference = context().getQualifiedReference(descriptor)
val scope = context().getScopeForDescriptor(descriptor) val scope = context().getScopeForDescriptor(descriptor)
val definitionPlace = DefinitionPlace(scope as JsObjectScope, qualifiedReference, staticProperties) val context = context().newDeclaration(descriptor)
val context = context().newDeclaration(descriptor, definitionPlace)
invocationArguments += getSuperclassReferences(context) val constructorFunction = context.defineTopLevelFunction(descriptor)
val nonConstructorContext = context.innerWithUsageTracker(scope, descriptor) val nonConstructorContext = context.innerWithUsageTracker(scope, descriptor)
nonConstructorContext.startDeclaration() nonConstructorContext.startDeclaration()
val delegationTranslator = DelegationTranslator(classDeclaration, nonConstructorContext) val delegationTranslator = DelegationTranslator(classDeclaration, nonConstructorContext)
translatePropertiesAsConstructorParameters(nonConstructorContext, properties) translatePropertiesAsConstructorParameters(nonConstructorContext)
val bodyVisitor = DeclarationBodyVisitor(properties, staticProperties, scope) val bodyVisitor = DeclarationBodyVisitor(descriptor, nonConstructorContext)
bodyVisitor.traverseContainer(classDeclaration, nonConstructorContext) bodyVisitor.traverseContainer(classDeclaration, nonConstructorContext)
delegationTranslator.generateDelegated(properties) constructorFunction.body.statements += bodyVisitor.initializerStatements
delegationTranslator.generateDelegated()
translatePrimaryConstructor(context, delegationTranslator) translatePrimaryConstructor(constructorFunction, context, delegationTranslator)
addMetadataObject()
addMetadataType()
context.addClass(descriptor)
addSuperclassReferences()
classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) } classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) }
generatedBridgeMethods(properties)
if (descriptor.isData) { if (descriptor.kind != ClassKind.INTERFACE) {
JsDataClassGenerator(classDeclaration, context, properties).generate() addInterfaceDefaultMembers()
} }
if (isEnumClass(descriptor)) { generatedBridgeMethods()
val enumEntries = JsObjectLiteral(bodyVisitor.enumEntryList, true)
invocationArguments += simpleReturnFunction(nonConstructorContext.getScopeForDescriptor(descriptor), enumEntries) if (descriptor.isData) {
JsDataClassGenerator(classDeclaration, context).generate()
} }
emitConstructors(nonConstructorContext, nonConstructorContext.endDeclaration()) emitConstructors(nonConstructorContext, nonConstructorContext.endDeclaration())
@@ -117,40 +107,63 @@ class ClassTranslator private constructor(
addClosureParameters(constructor, nonConstructorContext) addClosureParameters(constructor, nonConstructorContext)
} }
// ExpressionVisitor.visitObjectLiteralExpression uses DefinitionPlace of the translated class to generate call to if (isObjectLike()) {
// super constructor. Sometimes, when generating super call, we may translate another anonymous class passed as an addObjectMethods()
// argument. This class will be declared in the DefinitionPlace and put it static properties. See full explanation
// in ExpressionVisitor.visitObjectLiteralExpression
// TODO: It's a hack, we should think how to generate staticProperties lazily, whenever somebody tries to put
// definition into DefinitionPlace.
val hasStaticProperties = !staticProperties.isEmpty() || DescriptorUtils.isAnonymousObject(descriptor)
if (!properties.isEmpty() || hasStaticProperties) {
if (properties.isEmpty()) {
invocationArguments += JsLiteral.NULL
}
else {
// about "prototype" - see http://code.google.com/p/jsdoc-toolkit/wiki/TagLends
invocationArguments += JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, JsNameRef("prototype", qualifiedReference))
invocationArguments += JsObjectLiteral(properties, true)
}
} }
if (hasStaticProperties) {
invocationArguments += JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, qualifiedReference)
invocationArguments += JsObjectLiteral(staticProperties, true)
}
this.definitionPlace = definitionPlace
} }
private fun translatePrimaryConstructor(classContext: TranslationContext, delegationTranslator: DelegationTranslator) { private fun translatePrimaryConstructor(
if (isTrait()) return constructorFunction: JsFunction,
classContext: TranslationContext,
delegationTranslator: DelegationTranslator
) {
if (!isTrait()) {
val constructorContext = classContext.innerWithUsageTracker(constructorFunction.scope, descriptor)
if (isObjectLike()) {
addObjectCache(constructorFunction.body.statements)
}
ClassInitializerTranslator(classDeclaration, constructorContext, constructorFunction)
.generateInitializeMethod(delegationTranslator)
primaryConstructor = ConstructorInfo(constructorFunction, constructorContext, descriptor)
val scope = JsFunctionScope(classContext.scope(), "$descriptor: primary constructor") if (descriptor.kind == ClassKind.ENUM_CLASS) {
val constructorContext = classContext.innerWithUsageTracker(scope, descriptor) addEnumClassParameters(constructorFunction)
val initializer = ClassInitializerTranslator(classDeclaration, constructorContext).generateInitializeMethod(delegationTranslator) }
invocationArguments += initializer }
}
this.primaryConstructor = ConstructorInfo(initializer, constructorContext, descriptor) private fun addEnumClassParameters(constructorFunction: JsFunction) {
val nameParamName = constructorFunction.scope.declareFreshName("name")
val ordinalParamName = constructorFunction.scope.declareFreshName("ordinal")
constructorFunction.parameters += listOf(JsParameter(nameParamName), JsParameter(ordinalParamName))
constructorFunction.body.statements += JsAstUtils.assignmentToThisField(Namer.ENUM_NAME_FIELD, nameParamName.makeRef())
constructorFunction.body.statements += JsAstUtils.assignmentToThisField(Namer.ENUM_ORDINAL_FIELD, ordinalParamName.makeRef())
}
private fun isObjectLike() = when (descriptor.kind) {
ClassKind.OBJECT,
ClassKind.ENUM_ENTRY -> true
else -> false
}
private fun addMetadataObject() {
context().addRootStatement(JsAstUtils.assignment(createMetadataRef(), metadataLiteral).makeStmt())
}
private fun createMetadataRef() = JsNameRef(Namer.METADATA, context().getInnerReference(descriptor))
private fun addMetadataType() {
val kotlinType = JsNameRef("TYPE", Namer.KOTLIN_NAME)
val typeRef = when {
DescriptorUtils.isInterface(descriptor) -> JsNameRef("TRAIT", kotlinType)
DescriptorUtils.isObject(descriptor) -> JsNameRef("OBJECT", kotlinType)
else -> JsNameRef("CLASS", kotlinType)
}
val typeIndex = JsInvocation(JsNameRef("newClassIndex", Namer.KOTLIN_NAME))
metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef("type"), typeRef)
metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef("classIndex"), typeIndex)
} }
private fun generateSecondaryConstructor(classContext: TranslationContext, constructor: KtSecondaryConstructor) { private fun generateSecondaryConstructor(classContext: TranslationContext, constructor: KtSecondaryConstructor) {
@@ -177,12 +190,12 @@ class ClassTranslator private constructor(
} }
// Translate constructor body // Translate constructor body
val constructorInitializer = FunctionTranslator.newInstance(constructor, context).translateAsMethod() val constructorInitializer = context.defineTopLevelFunction(constructorDescriptor)
val constructorFunction = constructorInitializer.valueExpr as JsFunction FunctionTranslator.newInstance(constructor, context, constructorInitializer).translateAsMethodWithoutMetadata()
// Translate super/this call // Translate super/this call
val superCallGenerators = mutableListOf<(MutableList<JsStatement>) -> Unit>() val superCallGenerators = mutableListOf<(MutableList<JsStatement>) -> Unit>()
val referenceToClass = context.getQualifiedReference(classDescriptor) val referenceToClass = context.getInnerReference(classDescriptor)
superCallGenerators += { it += FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, context) } superCallGenerators += { it += FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, context) }
@@ -194,11 +207,11 @@ class ClassTranslator private constructor(
val leadingArgs = mutableListOf<JsExpression>() val leadingArgs = mutableListOf<JsExpression>()
if (outerClassName != null) { if (outerClassName != null) {
constructorFunction.parameters.add(0, JsParameter(outerClassName)) constructorInitializer.parameters.add(0, JsParameter(outerClassName))
leadingArgs += outerClassName.makeRef() leadingArgs += outerClassName.makeRef()
} }
constructorFunction.parameters += JsParameter(thisName) constructorInitializer.parameters += JsParameter(thisName)
// Generate super/this call to insert to beginning of the function // Generate super/this call to insert to beginning of the function
val resolvedCall = BindingContextUtils.getDelegationConstructorCall(context.bindingContext(), constructorDescriptor) val resolvedCall = BindingContextUtils.getDelegationConstructorCall(context.bindingContext(), constructorDescriptor)
@@ -223,18 +236,17 @@ class ClassTranslator private constructor(
} }
} }
constructorFunction.body.statements += JsReturn(thisNameRef) constructorInitializer.body.statements += JsReturn(thisNameRef)
val compositeSuperCallGenerator: () -> Unit = { val compositeSuperCallGenerator: () -> Unit = {
val additionalStatements = mutableListOf<JsStatement>() val additionalStatements = mutableListOf<JsStatement>()
for (partGenerator in superCallGenerators) { for (partGenerator in superCallGenerators) {
partGenerator(additionalStatements) partGenerator(additionalStatements)
} }
constructorFunction.body.statements.addAll(0, additionalStatements) constructorInitializer.body.statements.addAll(0, additionalStatements)
} }
secondaryConstructors += ConstructorInfo(constructorFunction, context, constructorDescriptor, compositeSuperCallGenerator) secondaryConstructors += ConstructorInfo(constructorInitializer, context, constructorDescriptor, compositeSuperCallGenerator)
secondaryConstructorProperties += constructorInitializer
} }
private val allConstructors: Sequence<ConstructorInfo> private val allConstructors: Sequence<ConstructorInfo>
@@ -317,14 +329,9 @@ class ClassTranslator private constructor(
function.body.statements.addAll(0, additionalStatements) function.body.statements.addAll(0, additionalStatements)
} }
private fun getSuperclassReferences(declarationContext: TranslationContext): JsExpression { private fun addSuperclassReferences() {
val superClassReferences = getSupertypesNameReferences() val supertypeReferences = JsArrayLiteral(getSupertypesNameReferences())
if (superClassReferences.isEmpty()) { metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_SUPERTYPES), supertypeReferences)
return JsLiteral.NULL
}
else {
return simpleReturnFunction(declarationContext.scope(), JsArrayLiteral(superClassReferences))
}
} }
private fun getSupertypesNameReferences(): List<JsExpression> { private fun getSupertypesNameReferences(): List<JsExpression> {
@@ -336,7 +343,7 @@ class ClassTranslator private constructor(
if (supertypes.size == 1) { if (supertypes.size == 1) {
val type = supertypes[0] val type = supertypes[0]
val supertypeDescriptor = getClassDescriptorForType(type) val supertypeDescriptor = getClassDescriptorForType(type)
return listOf<JsExpression>(getClassReference(supertypeDescriptor)) return listOf(context().getInnerReference(supertypeDescriptor))
} }
val supertypeConstructors = mutableSetOf<TypeConstructor>() val supertypeConstructors = mutableSetOf<TypeConstructor>()
@@ -352,43 +359,111 @@ class ClassTranslator private constructor(
for (typeConstructor in sortedAllSuperTypes) { for (typeConstructor in sortedAllSuperTypes) {
if (supertypeConstructors.contains(typeConstructor)) { if (supertypeConstructors.contains(typeConstructor)) {
val supertypeDescriptor = getClassDescriptorForTypeConstructor(typeConstructor) val supertypeDescriptor = getClassDescriptorForTypeConstructor(typeConstructor)
supertypesRefs += getClassReference(supertypeDescriptor) supertypesRefs += context().getInnerReference(supertypeDescriptor)
} }
} }
return supertypesRefs return supertypesRefs
} }
private fun getClassReference(superClassDescriptor: ClassDescriptor): JsNameRef { private fun translatePropertiesAsConstructorParameters(classDeclarationContext: TranslationContext) {
return context().getQualifiedReference(superClassDescriptor)
}
private fun translatePropertiesAsConstructorParameters(classDeclarationContext: TranslationContext,
result: MutableList<JsPropertyInitializer>) {
for (parameter in getPrimaryConstructorParameters(classDeclaration)) { for (parameter in getPrimaryConstructorParameters(classDeclaration)) {
val descriptor = getPropertyDescriptorForConstructorParameter(bindingContext(), parameter) val descriptor = getPropertyDescriptorForConstructorParameter(bindingContext(), parameter)
if (descriptor != null) { if (descriptor != null) {
translateAccessors(descriptor, result, classDeclarationContext) val literal = JsObjectLiteral(true)
translateAccessors(descriptor, literal.propertyInitializers, classDeclarationContext)
if (literal.propertyInitializers.isNotEmpty()) {
classDeclarationContext.addAccessorsToPrototype(this.descriptor, descriptor, literal)
}
} }
} }
} }
private fun generatedBridgeMethods(properties: MutableList<JsPropertyInitializer>) { private fun addInterfaceDefaultMembers() {
val members = descriptor.unsubstitutedMemberScope
.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS)
.mapNotNull { it as? CallableMemberDescriptor }
for (member in members) {
if (member.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue
if (member.modality == Modality.ABSTRACT || member.overriddenDescriptors.size != 1) continue
val overriddenFunction = generateSequence(member) { it.overriddenDescriptors.firstOrNull() }
.drop(1)
.dropWhile { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
DescriptorUtils.isInterface(it.containingDeclaration) }
.firstOrNull() ?: continue
val interfaceDescriptor = overriddenFunction.containingDeclaration as ClassDescriptor
if (interfaceDescriptor.kind != ClassKind.INTERFACE) continue
when (member) {
is FunctionDescriptor -> addDefaultMethodFromInterface(member, interfaceDescriptor)
is PropertyDescriptor -> addDefaultPropertyFromInterface(member, interfaceDescriptor)
}
}
}
private fun addDefaultMethodFromInterface(function: FunctionDescriptor, interfaceDescriptor: ClassDescriptor) {
val classPrototype = JsAstUtils.prototypeOf(context().getInnerReference(descriptor))
val interfacePrototype = JsAstUtils.prototypeOf(context().getInnerReference(interfaceDescriptor))
val functionName = context().getNameForDescriptor(function)
val classFunction = JsNameRef(functionName, classPrototype)
val interfaceFunction = JsNameRef(functionName, interfacePrototype)
context().addRootStatement(JsAstUtils.assignment(classFunction, interfaceFunction).makeStmt())
}
private fun addDefaultPropertyFromInterface(property: PropertyDescriptor, interfaceDescriptor: ClassDescriptor) {
val classPrototype = JsAstUtils.prototypeOf(context().getInnerReference(descriptor))
val interfacePrototype = JsAstUtils.prototypeOf(context().getInnerReference(interfaceDescriptor))
val functionName = context().getNameForDescriptor(property)
val nameLiteral = context().program().getStringLiteral(functionName.ident)
val getPropertyDescriptor = JsInvocation(JsNameRef("getOwnPropertyDescriptor", "Object"), interfacePrototype, nameLiteral)
val defineProperty = JsAstUtils.defineProperty(classPrototype, functionName.ident, getPropertyDescriptor, context().program())
context().addRootStatement(defineProperty.makeStmt())
}
private fun addObjectCache(statements: MutableList<JsStatement>) {
cachedInstanceName = context().createGlobalName(StaticContext.getSuggestedName(descriptor) + Namer.OBJECT_INSTANCE_VAR_SUFFIX)
statements += JsAstUtils.assignment(cachedInstanceName.makeRef(), JsObjectLiteral.THIS).makeStmt()
}
private fun addObjectMethods() {
context().addRootStatement(JsAstUtils.newVar(cachedInstanceName, JsLiteral.NULL))
val instanceFun = JsFunction(context().rootFunction.scope, JsBlock(), "Instance function: " + descriptor)
instanceFun.name = context().getNameForObjectInstance(descriptor)
val instanceCreatedCondition = JsAstUtils.equality(cachedInstanceName.makeRef(), JsLiteral.NULL)
val instanceCreationBlock = JsBlock()
val instanceCreatedGuard = JsIf(instanceCreatedCondition, instanceCreationBlock)
instanceFun.body.statements += instanceCreatedGuard
val objectRef = context().getInnerReference(descriptor)
instanceCreationBlock.statements += JsAstUtils.assignment(cachedInstanceName.makeRef(), JsNew(objectRef)).makeStmt()
instanceFun.body.statements += JsReturn(cachedInstanceName.makeRef())
context().addRootStatement(instanceFun.makeStmt())
}
private fun generatedBridgeMethods() {
if (isAnnotation()) return if (isAnnotation()) return
generateBridgesToTraitImpl(properties) generateBridgesToTraitImpl()
generateOtherBridges(properties) generateOtherBridges()
} }
private fun generateBridgesToTraitImpl(properties: MutableList<JsPropertyInitializer>) { private fun generateBridgesToTraitImpl() {
for ((key, value) in CodegenUtil.getNonPrivateTraitMethods(descriptor)) { for ((key, value) in CodegenUtil.getNonPrivateTraitMethods(descriptor)) {
if (!areNamesEqual(key, value)) { if (!areNamesEqual(key, value)) {
properties += generateDelegateCall(value, key, JsLiteral.THIS, context()) generateDelegateCall(descriptor, value, key, JsLiteral.THIS, context())
} }
} }
} }
private fun generateOtherBridges(properties: MutableList<JsPropertyInitializer>) { private fun generateOtherBridges() {
for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) { for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) {
if (memberDescriptor is FunctionDescriptor) { if (memberDescriptor is FunctionDescriptor) {
val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) { val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) {
@@ -397,13 +472,13 @@ class ClassTranslator private constructor(
} }
for (bridge in bridgesToGenerate) { for (bridge in bridgesToGenerate) {
generateBridge(bridge, properties) generateBridge(bridge)
} }
} }
} }
} }
private fun generateBridge(bridge: Bridge<FunctionDescriptor>, properties: MutableList<JsPropertyInitializer>) { private fun generateBridge(bridge: Bridge<FunctionDescriptor>) {
val fromDescriptor = bridge.from val fromDescriptor = bridge.from
val toDescriptor = bridge.to val toDescriptor = bridge.to
if (areNamesEqual(fromDescriptor, toDescriptor)) return if (areNamesEqual(fromDescriptor, toDescriptor)) return
@@ -411,7 +486,7 @@ class ClassTranslator private constructor(
if (fromDescriptor.kind.isReal && fromDescriptor.modality != Modality.ABSTRACT && !toDescriptor.kind.isReal) if (fromDescriptor.kind.isReal && fromDescriptor.modality != Modality.ABSTRACT && !toDescriptor.kind.isReal)
return return
properties += generateDelegateCall(fromDescriptor, toDescriptor, JsLiteral.THIS, context()) generateDelegateCall(descriptor, fromDescriptor, toDescriptor, JsLiteral.THIS, context())
} }
private fun areNamesEqual(first: FunctionDescriptor, second: FunctionDescriptor): Boolean { private fun areNamesEqual(first: FunctionDescriptor, second: FunctionDescriptor): Boolean {
@@ -421,13 +496,11 @@ class ClassTranslator private constructor(
} }
companion object { companion object {
@JvmStatic fun translate(classDeclaration: KtClassOrObject, context: TranslationContext): TranslationResult { @JvmStatic fun translate(classDeclaration: KtClassOrObject, context: TranslationContext) {
return ClassTranslator(classDeclaration, context).translate() return ClassTranslator(classDeclaration, context).translate()
} }
} }
class TranslationResult(val properties: List<JsPropertyInitializer>, val definitionPlace: DefinitionPlace)
private class ConstructorInfo( private class ConstructorInfo(
val function: JsFunction, val function: JsFunction,
val context: TranslationContext, val context: TranslationContext,
@@ -1,151 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.translate.declaration;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor;
import org.jetbrains.kotlin.js.translate.initializer.ClassInitializerTranslator;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.types.KotlinType;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getClassDescriptor;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor;
import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes;
public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
protected final List<JsPropertyInitializer> result;
private final List<JsPropertyInitializer> staticResult;
private final List<JsPropertyInitializer> enumEntryList = new SmartList<JsPropertyInitializer>();
@NotNull
private final JsScope scope;
@Nullable
private List<JsStatement> initializerStatements;
public DeclarationBodyVisitor(
@NotNull List<JsPropertyInitializer> result, @NotNull List<JsPropertyInitializer> staticResult,
@NotNull JsScope scope
) {
this.result = result;
this.staticResult = staticResult;
this.scope = scope;
}
@NotNull
public List<JsPropertyInitializer> getResult() {
return result;
}
public List<JsPropertyInitializer> getEnumEntryList() {
return enumEntryList;
}
@Override
protected Void emptyResult(@NotNull TranslationContext context) {
return null;
}
@Override
public Void visitClassOrObject(@NotNull KtClassOrObject declaration, TranslationContext context) {
staticResult.addAll(ClassTranslator.translate(declaration, context).getProperties());
if (declaration instanceof KtObjectDeclaration) {
KtObjectDeclaration objectDeclaration = (KtObjectDeclaration) declaration;
if (objectDeclaration.isCompanion()) {
DeclarationDescriptor descriptor = BindingUtils.getDescriptorForElement(context.bindingContext(), declaration);
addInitializerStatement(context.getQualifiedReference(descriptor).makeStmt());
}
}
return null;
}
@Override
public Void visitEnumEntry(@NotNull KtEnumEntry enumEntry, TranslationContext data) {
ClassDescriptor descriptor = getClassDescriptor(data.bindingContext(), enumEntry);
List<KotlinType> supertypes = getSupertypesWithoutFakes(descriptor);
if (enumEntry.getBody() != null || supertypes.size() > 1) {
enumEntryList.addAll(ClassTranslator.translate(enumEntry, data).getProperties());
} else {
assert supertypes.size() == 1 : "Simple Enum entry must have one supertype";
JsExpression jsEnumEntryCreation = new ClassInitializerTranslator(enumEntry, data)
.generateEnumEntryInstanceCreation(supertypes.get(0));
jsEnumEntryCreation = TranslationUtils.simpleReturnFunction(data.scope(), jsEnumEntryCreation);
enumEntryList.add(new JsPropertyInitializer(data.getNameForDescriptor(descriptor).makeRef(), jsEnumEntryCreation));
}
return null;
}
@Override
public Void visitNamedFunction(@NotNull KtNamedFunction expression, TranslationContext context) {
FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
if (descriptor.getModality() == Modality.ABSTRACT) {
return null;
}
context = context.newDeclaration(descriptor, context.getDefinitionPlace());
JsPropertyInitializer methodAsPropertyInitializer = Translation.functionTranslator(expression, context).translateAsMethod();
result.add(methodAsPropertyInitializer);
return null;
}
@Override
public Void visitProperty(@NotNull KtProperty expression, TranslationContext context) {
PropertyDescriptor propertyDescriptor = BindingUtils.getPropertyDescriptor(context.bindingContext(), expression);
context = context.newDeclaration(propertyDescriptor, context.getDefinitionPlace());
PropertyTranslatorKt.translateAccessors(propertyDescriptor, expression, result, context);
return null;
}
@Override
public Void visitAnonymousInitializer(@NotNull KtAnonymousInitializer expression, TranslationContext context) {
// parsed it in initializer visitor => no additional actions are needed
return null;
}
@Override
public Void visitSecondaryConstructor(@NotNull KtSecondaryConstructor constructor, TranslationContext data) {
return null;
}
private void addInitializerStatement(@NotNull JsStatement statement) {
if (initializerStatements == null) {
initializerStatements = new ArrayList<JsStatement>();
JsFunction initializerFunction = new JsFunction(scope, new JsBlock(initializerStatements), "class initializer");
staticResult.add(new JsPropertyInitializer(new JsNameRef("object_initializer$"), initializerFunction));
}
initializerStatements.add(statement);
}
@Override
public Void visitTypeAlias(@NotNull KtTypeAlias typeAlias, TranslationContext data) {
// Resolved by front-end, not used by backend
return null;
}
}
@@ -0,0 +1,116 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.translate.declaration
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.initializer.ClassInitializerTranslator
import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getClassDescriptor
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes
import org.jetbrains.kotlin.psi.*
import java.util.*
class DeclarationBodyVisitor(
private val containingClass: ClassDescriptor,
private val context: TranslationContext
) : AbstractDeclarationVisitor() {
private var enumEntryOrdinal: Int = 0
val initializerStatements = ArrayList<JsStatement>()
override fun visitClassOrObject(classOrObject: KtClassOrObject, context: TranslationContext) {
super.visitClassOrObject(classOrObject, context)
if (classOrObject is KtObjectDeclaration) {
if (classOrObject.isCompanion()) {
val descriptor = BindingUtils.getDescriptorForElement(context.bindingContext(), classOrObject) as ClassDescriptor
addInitializerStatement(JsInvocation(context.getNameForObjectInstance(descriptor).makeRef()).makeStmt())
}
}
}
override fun visitEnumEntry(enumEntry: KtEnumEntry, context: TranslationContext) {
val descriptor = getClassDescriptor(context.bindingContext(), enumEntry)
val supertypes = getSupertypesWithoutFakes(descriptor)
if (enumEntry.getBody() != null || supertypes.size > 1) {
ClassTranslator.translate(enumEntry, context)
}
else {
// Simplify by omitting _getInstance() function
val enumName = context.getInnerNameForDescriptor(descriptor)
val enumInstanceName = context.createGlobalName(enumName.ident + "_instance")
assert(supertypes.size == 1) { "Simple Enum entry must have one supertype" }
val jsEnumEntryCreation = ClassInitializerTranslator
.generateEnumEntryInstanceCreation(context, supertypes[0], enumEntry, enumEntryOrdinal)
val jsEnumEntryFunction = context.createTopLevelAnonymousFunction(descriptor)
jsEnumEntryFunction.body.statements.add(JsReturn(jsEnumEntryCreation))
val enumInstanceFunction = context.createTopLevelAnonymousFunction(descriptor)
enumInstanceFunction.name = context.getNameForObjectInstance(descriptor)
context.addRootStatement(enumInstanceFunction.makeStmt())
val classRef = context.getInnerReference(descriptor.containingDeclaration)
val enumExtName = context.getNameForDescriptor(descriptor)
val enumExtFq = JsNameRef(enumExtName, classRef)
context.addRootStatement(JsAstUtils.assignment(enumExtFq, enumInstanceFunction.name.makeRef()).makeStmt())
context.addRootStatement(JsAstUtils.newVar(enumInstanceName, jsEnumEntryCreation))
enumInstanceFunction.body.statements.add(JsReturn(enumInstanceName.makeRef()))
}
enumEntryOrdinal++
}
override fun visitAnonymousInitializer(expression: KtAnonymousInitializer, context: TranslationContext) {
// parsed it in initializer visitor => no additional actions are needed
}
override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor, data: TranslationContext) { }
private fun addInitializerStatement(statement: JsStatement) {
initializerStatements.add(statement)
}
override fun addClass(descriptor: ClassDescriptor) {
//context.export(descriptor)
}
override fun addFunction(descriptor: FunctionDescriptor, expression: JsExpression) {
context.addFunctionToPrototype(containingClass, descriptor, expression)
}
override fun addProperty(descriptor: PropertyDescriptor, getter: JsExpression, setter: JsExpression?) {
if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) {
val literal = JsObjectLiteral(true)
literal.propertyInitializers += JsPropertyInitializer(context.program().getStringLiteral("get"), getter)
if (setter != null) {
literal.propertyInitializers += JsPropertyInitializer(context.program().getStringLiteral("set"), setter)
}
context.addAccessorsToPrototype(containingClass, descriptor, literal)
}
}
override fun getBackingFieldReference(descriptor: PropertyDescriptor): JsExpression {
return Namer.getDelegateNameRef(descriptor.name.asString())
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.js.translate.declaration package org.jetbrains.kotlin.js.translate.declaration
import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
@@ -25,15 +24,14 @@ import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.BindingUtils import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.translateFunctionAsEcma5PropertyDescriptor import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.translateFunctionAsEcma5PropertyDescriptor
import org.jetbrains.kotlin.js.translate.utils.generateDelegateCall
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
import org.jetbrains.kotlin.psi.KtSuperTypeListEntry import org.jetbrains.kotlin.psi.KtSuperTypeListEntry
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
class DelegationTranslator( class DelegationTranslator(
classDeclaration: KtClassOrObject, classDeclaration: KtClassOrObject,
@@ -53,7 +51,6 @@ class DelegationTranslator(
for (specifier in delegationBySpecifiers) { for (specifier in delegationBySpecifiers) {
val expression = specifier.delegateExpression ?: val expression = specifier.delegateExpression ?:
throw IllegalArgumentException("delegate expression should not be null: ${specifier.text}") throw IllegalArgumentException("delegate expression should not be null: ${specifier.text}")
val descriptor = getSuperClass(specifier)
val propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, classDescriptor, bindingContext()) val propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, classDescriptor, bindingContext())
if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext())) { if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext())) {
@@ -84,22 +81,22 @@ class DelegationTranslator(
} }
} }
fun generateDelegated(properties: MutableList<JsPropertyInitializer>) { fun generateDelegated() {
for (specifier in delegationBySpecifiers) { for (specifier in delegationBySpecifiers) {
generateDelegates(getSuperClass(specifier), fields[specifier]!!, properties) generateDelegates(getSuperClass(specifier), fields[specifier]!!)
} }
} }
private fun getSuperClass(specifier: KtSuperTypeListEntry): ClassDescriptor = private fun getSuperClass(specifier: KtSuperTypeListEntry): ClassDescriptor =
CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext()) CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext())
private fun generateDelegates(toClass: ClassDescriptor, field: Field, properties: MutableList<JsPropertyInitializer>) { private fun generateDelegates(toClass: ClassDescriptor, field: Field) {
for ((descriptor, overriddenDescriptor) in CodegenUtil.getDelegates(classDescriptor, toClass)) { for ((descriptor, overriddenDescriptor) in CodegenUtil.getDelegates(classDescriptor, toClass)) {
when (descriptor) { when (descriptor) {
is PropertyDescriptor -> is PropertyDescriptor ->
generateDelegateCallForPropertyMember(descriptor, field.name, properties) generateDelegateCallForPropertyMember(descriptor, field.name)
is FunctionDescriptor -> is FunctionDescriptor ->
generateDelegateCallForFunctionMember(descriptor, overriddenDescriptor as FunctionDescriptor, field.name, properties) generateDelegateCallForFunctionMember(descriptor, overriddenDescriptor as FunctionDescriptor, field.name)
else -> else ->
throw IllegalArgumentException("Expected property or function $descriptor") throw IllegalArgumentException("Expected property or function $descriptor")
} }
@@ -108,8 +105,7 @@ class DelegationTranslator(
private fun generateDelegateCallForPropertyMember( private fun generateDelegateCallForPropertyMember(
descriptor: PropertyDescriptor, descriptor: PropertyDescriptor,
delegateName: JsName, delegateName: JsName
properties: MutableList<JsPropertyInitializer>
) { ) {
val propertyName: String = descriptor.name.asString() val propertyName: String = descriptor.name.asString()
@@ -174,17 +170,29 @@ class DelegationTranslator(
return generateDelegateAccessor(setterDescriptor, generateDelegateSetterFunction(setterDescriptor)) return generateDelegateAccessor(setterDescriptor, generateDelegateSetterFunction(setterDescriptor))
} }
properties.addGetterAndSetter(descriptor, context(), ::generateDelegateGetter, ::generateDelegateSetter) // TODO: same logic as in AbstractDeclarationVisitor
if (descriptor.isExtensionProperty || TranslationUtils.shouldAccessViaFunctions(descriptor)) {
val getter = descriptor.getter!!
context().addFunctionToPrototype(classDescriptor, getter, generateDelegateGetterFunction(getter))
if (descriptor.isVar) {
val setter = descriptor.setter!!
context().addFunctionToPrototype(classDescriptor, setter, generateDelegateSetterFunction(setter))
}
}
else {
val literal = JsObjectLiteral(true)
literal.propertyInitializers.addGetterAndSetter(descriptor, ::generateDelegateGetter, ::generateDelegateSetter)
context().addAccessorsToPrototype(classDescriptor, descriptor, literal)
}
} }
private fun generateDelegateCallForFunctionMember( private fun generateDelegateCallForFunctionMember(
descriptor: FunctionDescriptor, descriptor: FunctionDescriptor,
overriddenDescriptor: FunctionDescriptor, overriddenDescriptor: FunctionDescriptor,
delegateName: JsName, delegateName: JsName
properties: MutableList<JsPropertyInitializer>
) { ) {
val delegateRef = JsNameRef(delegateName, JsLiteral.THIS) val delegateRef = JsNameRef(delegateName, JsLiteral.THIS)
properties.add(generateDelegateCall(descriptor, overriddenDescriptor, delegateRef, context())) generateDelegateCall(classDescriptor, descriptor, overriddenDescriptor, delegateRef, context())
} }
} }
@@ -1,71 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.translate.declaration
import com.google.dart.compiler.backend.js.ast.JsFunction
import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer
import com.google.dart.compiler.backend.js.ast.JsScope
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.initializer.InitializerUtils.generateInitializerForDelegate
import org.jetbrains.kotlin.js.translate.initializer.InitializerUtils.generateInitializerForProperty
import org.jetbrains.kotlin.js.translate.initializer.InitializerVisitor
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getPropertyDescriptor
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.psi.*
class FileDeclarationVisitor(
val context: TranslationContext,
scope: JsScope,
initializers: List<JsPropertyInitializer> = SmartList()
) : DeclarationBodyVisitor(initializers, SmartList(), scope) {
private val initializer = JsAstUtils.createFunctionWithEmptyBody(context.scope())
private val initializerContext = context.contextWithScope(initializer).innerBlock(initializer.body)
private val initializerVisitor = InitializerVisitor()
fun computeInitializer(): JsFunction? {
return if (initializer.body.statements.isNotEmpty()) initializer else null
}
override fun visitClassOrObject(declaration: KtClassOrObject, context: TranslationContext?): Void? {
result += ClassTranslator.translate(declaration, context!!).properties
return null
}
override fun visitProperty(expression: KtProperty, context: TranslationContext?): Void? {
context!! // hack
super.visitProperty(expression, context)
val initializer = expression.initializer
if (initializer != null) {
val value = Translation.translateAsExpression(initializer, initializerContext)
val propertyDescriptor: PropertyDescriptor = getPropertyDescriptor(context.bindingContext(), expression)
this.initializer.body.statements += generateInitializerForProperty(context, propertyDescriptor, value)
}
generateInitializerForDelegate(context, expression)?.let { this.initializer.body.statements += it }
return null
}
override fun visitAnonymousInitializer(expression: KtAnonymousInitializer, context: TranslationContext?): Void? {
expression.accept(initializerVisitor, initializerContext)
return null
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.translate.declaration
import com.google.dart.compiler.backend.js.ast.JsExpression
import com.google.dart.compiler.backend.js.ast.JsFunction
import com.google.dart.compiler.backend.js.ast.JsName
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.BindingContext
class FileDeclarationVisitor(private val context: TranslationContext) : AbstractDeclarationVisitor() {
override fun visitProperty(expression: KtProperty, context: TranslationContext) {
val propertyDescriptor = BindingUtils.getPropertyDescriptor(context.bindingContext(), expression)
val innerName = context.getInnerNameForDescriptor(propertyDescriptor)
val backingFieldRequired = context.bindingContext()[BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor] ?: false
if (backingFieldRequired || expression.delegateExpression != null) {
val initializer = expression.delegateExpressionOrInitializer?.let { Translation.translateAsExpression(it, context) }
context.addRootStatement(JsAstUtils.newVar(innerName, initializer))
}
super.visitProperty(expression, context)
}
override fun addClass(descriptor: ClassDescriptor) {
context.export(descriptor)
}
override fun addFunction(descriptor: FunctionDescriptor, expression: JsExpression) {
addFunctionButNotExport(descriptor, expression)
context.export(descriptor)
}
override fun addProperty(descriptor: PropertyDescriptor, getter: JsExpression, setter: JsExpression?) {
if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) {
addFunctionButNotExport(descriptor.getter!!, getter)
if (setter != null) {
addFunctionButNotExport(descriptor.setter!!, setter)
}
}
context.export(descriptor)
}
override fun getBackingFieldReference(descriptor: PropertyDescriptor): JsExpression {
return context.getInnerReference(descriptor)
}
private fun addFunctionButNotExport(descriptor: FunctionDescriptor, expression: JsExpression): JsName {
val name = context.getInnerNameForDescriptor(descriptor)
when (expression) {
is JsFunction -> {
expression.name = name
context.addRootStatement(expression.makeStmt())
}
else -> {
context.addRootStatement(JsAstUtils.newVar(name, expression))
}
}
return name
}
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
import org.jetbrains.kotlin.psi.KtClassOrObject; import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.psi.KtParameter; import org.jetbrains.kotlin.psi.KtParameter;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
@@ -37,12 +38,10 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.or;
class JsDataClassGenerator extends DataClassMethodGenerator { class JsDataClassGenerator extends DataClassMethodGenerator {
private final TranslationContext context; private final TranslationContext context;
private final List<? super JsPropertyInitializer> output;
JsDataClassGenerator(KtClassOrObject klass, TranslationContext context, List<? super JsPropertyInitializer> output) { JsDataClassGenerator(KtClassOrObject klass, TranslationContext context) {
super(klass, context.bindingContext()); super(klass, context.bindingContext());
this.context = context; this.context = context;
this.output = output;
} }
@Override @Override
@@ -94,7 +93,7 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
ClassDescriptor classDescriptor = (ClassDescriptor) function.getContainingDeclaration(); ClassDescriptor classDescriptor = (ClassDescriptor) function.getContainingDeclaration();
ClassConstructorDescriptor constructor = classDescriptor.getConstructors().iterator().next(); ClassConstructorDescriptor constructor = classDescriptor.getConstructors().iterator().next();
JsExpression constructorRef = context.getQualifiedReference(constructor); JsExpression constructorRef = context.getInnerReference(constructor);
JsExpression returnExpression = new JsNew(constructorRef, constructorArguments); JsExpression returnExpression = new JsNew(constructorRef, constructorArguments);
if (context.shouldBeDeferred(constructor)) { if (context.shouldBeDeferred(constructor)) {
@@ -190,11 +189,9 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
} }
private JsFunction generateJsMethod(@NotNull FunctionDescriptor functionDescriptor) { private JsFunction generateJsMethod(@NotNull FunctionDescriptor functionDescriptor) {
JsName functionName = context.getNameForDescriptor(functionDescriptor); JsFunction functionObject = context.createTopLevelAnonymousFunction(functionDescriptor);
JsScope enclosingScope = context.scope(); ClassDescriptor containingClass = (ClassDescriptor) functionDescriptor.getContainingDeclaration();
JsFunction functionObject = JsAstUtils.createFunctionWithEmptyBody(enclosingScope); UtilsKt.addFunctionToPrototype(context, containingClass, functionDescriptor, functionObject);
JsPropertyInitializer initializer = new JsPropertyInitializer(functionName.makeRef(), functionObject);
output.add(initializer);
return functionObject; return functionObject;
} }
} }
@@ -16,30 +16,22 @@
package org.jetbrains.kotlin.js.translate.declaration; package org.jetbrains.kotlin.js.translate.declaration;
import com.google.dart.compiler.backend.js.ast.*;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
import org.jetbrains.kotlin.js.facade.exceptions.TranslationRuntimeException; import org.jetbrains.kotlin.js.facade.exceptions.TranslationRuntimeException;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.BindingContextUtils;
import java.util.*; import java.util.Collection;
import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
public final class PackageDeclarationTranslator extends AbstractTranslator { public final class PackageDeclarationTranslator extends AbstractTranslator {
private final Iterable<KtFile> files; private final Iterable<KtFile> files;
private final Map<PackageFragmentDescriptor, PackageTranslator> packageFragmentToTranslator =
new LinkedHashMap<PackageFragmentDescriptor, PackageTranslator>();
public static List<JsStatement> translateFiles(@NotNull Collection<KtFile> files, @NotNull TranslationContext context) { public static void translateFiles(@NotNull Collection<KtFile> files, @NotNull TranslationContext context) {
return new PackageDeclarationTranslator(files, context).translate(); new PackageDeclarationTranslator(files, context).translate();
} }
private PackageDeclarationTranslator(@NotNull Iterable<KtFile> files, @NotNull TranslationContext context) { private PackageDeclarationTranslator(@NotNull Iterable<KtFile> files, @NotNull TranslationContext context) {
@@ -48,21 +40,12 @@ public final class PackageDeclarationTranslator extends AbstractTranslator {
this.files = files; this.files = files;
} }
@NotNull private void translate() {
private List<JsStatement> translate() {
// predictable order
Map<FqName, DefineInvocation> packageFqNameToDefineInvocation = new THashMap<FqName, DefineInvocation>();
for (KtFile file : files) { for (KtFile file : files) {
PackageFragmentDescriptor packageFragment = PackageFragmentDescriptor packageFragment =
BindingContextUtils.getNotNull(context().bindingContext(), BindingContext.FILE_TO_PACKAGE_FRAGMENT, file); BindingContextUtils.getNotNull(context().bindingContext(), BindingContext.FILE_TO_PACKAGE_FRAGMENT, file);
PackageTranslator translator = packageFragmentToTranslator.get(packageFragment); PackageTranslator translator = PackageTranslator.create(packageFragment, context());
if (translator == null) {
createRootPackageDefineInvocationIfNeeded(packageFqNameToDefineInvocation);
translator = PackageTranslator.create(packageFragment, context());
packageFragmentToTranslator.put(packageFragment, translator);
}
try { try {
translator.translate(file); translator.translate(file);
@@ -77,26 +60,5 @@ public final class PackageDeclarationTranslator extends AbstractTranslator {
throw new TranslationRuntimeException(file, e); throw new TranslationRuntimeException(file, e);
} }
} }
for (PackageTranslator translator : packageFragmentToTranslator.values()) {
translator.add(packageFqNameToDefineInvocation);
}
JsVars vars = new JsVars(true);
vars.addIfHasInitializer(getRootPackageDeclaration(packageFqNameToDefineInvocation.get(FqName.ROOT)));
return Collections.<JsStatement>singletonList(vars);
}
private void createRootPackageDefineInvocationIfNeeded(@NotNull Map<FqName, DefineInvocation> packageFqNameToDefineInvocation) {
if (!packageFqNameToDefineInvocation.containsKey(FqName.ROOT)) {
packageFqNameToDefineInvocation.put(
FqName.ROOT, DefineInvocation.create(FqName.ROOT, null, new JsObjectLiteral(true), context()));
}
}
private JsVar getRootPackageDeclaration(@NotNull DefineInvocation defineInvocation) {
JsExpression rootPackageVar = new JsInvocation(context().namer().rootPackageDefinitionMethodReference(), defineInvocation.asList());
return new JsVar(context().scope().declareName(Namer.getRootPackageName()), rootPackageVar);
} }
} }
@@ -16,51 +16,32 @@
package org.jetbrains.kotlin.js.translate.declaration; package org.jetbrains.kotlin.js.translate.declaration;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
import org.jetbrains.kotlin.js.translate.context.DefinitionPlace;
import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtDeclaration; import org.jetbrains.kotlin.psi.KtDeclaration;
import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFile;
import java.util.List;
import java.util.Map;
final class PackageTranslator extends AbstractTranslator { final class PackageTranslator extends AbstractTranslator {
static PackageTranslator create( static PackageTranslator create(
@NotNull PackageFragmentDescriptor descriptor, @NotNull PackageFragmentDescriptor descriptor,
@NotNull TranslationContext context @NotNull TranslationContext context
) { ) {
JsScope scope = context.getScopeForDescriptor(descriptor); TranslationContext newContext = context.newDeclaration(descriptor);
JsNameRef reference = context.getQualifiedReference(descriptor); FileDeclarationVisitor visitor = new FileDeclarationVisitor(newContext);
SmartList<JsPropertyInitializer> properties = new SmartList<JsPropertyInitializer>(); return new PackageTranslator(newContext, visitor);
DefinitionPlace definitionPlace = new DefinitionPlace((JsObjectScope) scope, reference, properties);
TranslationContext newContext = context.newDeclaration(descriptor, definitionPlace);
FileDeclarationVisitor visitor = new FileDeclarationVisitor(newContext, scope, definitionPlace.getProperties());
return new PackageTranslator(descriptor, newContext, visitor);
} }
@NotNull
private final PackageFragmentDescriptor descriptor;
private final FileDeclarationVisitor visitor; private final FileDeclarationVisitor visitor;
private PackageTranslator( private PackageTranslator(
@NotNull PackageFragmentDescriptor descriptor,
@NotNull TranslationContext context, @NotNull TranslationContext context,
@NotNull FileDeclarationVisitor visitor @NotNull FileDeclarationVisitor visitor
) { ) {
super(context); super(context);
this.descriptor = descriptor;
this.visitor = visitor; this.visitor = visitor;
} }
@@ -71,69 +52,4 @@ final class PackageTranslator extends AbstractTranslator {
} }
} }
} }
private void createDefinitionPlace(
@Nullable JsExpression initializer,
Map<FqName, DefineInvocation> packageFqNameToDefineInvocation
) {
FqName fqName = descriptor.getFqName();
DefineInvocation place = DefineInvocation.create(fqName, initializer, new JsObjectLiteral(visitor.getResult(), true), context());
packageFqNameToDefineInvocation.put(fqName, place);
addToParent(fqName.parent(), getEntry(fqName, place), packageFqNameToDefineInvocation);
}
public void add(@NotNull Map<FqName, DefineInvocation> packageFqNameToDefineInvocation) {
JsExpression initializer = visitor.computeInitializer();
DefineInvocation defineInvocation = packageFqNameToDefineInvocation.get(descriptor.getFqName());
if (defineInvocation == null) {
if (initializer != null || !visitor.getResult().isEmpty()) {
createDefinitionPlace(initializer, packageFqNameToDefineInvocation);
}
}
else {
if (initializer != null) {
assert defineInvocation.getInitializer() == JsLiteral.NULL;
defineInvocation.setInitializer(initializer);
}
List<JsPropertyInitializer> listFromPlace = defineInvocation.getMembers();
// if equals, so, inner functions was added
if (listFromPlace != visitor.getResult()) {
listFromPlace.addAll(visitor.getResult());
}
}
}
private JsPropertyInitializer getEntry(@NotNull FqName fqName, DefineInvocation defineInvocation) {
return new JsPropertyInitializer(context().getNameForPackage(fqName).makeRef(),
new JsInvocation(context().namer().packageDefinitionMethodReference(), defineInvocation.asList()));
}
private static boolean addEntryIfParentExists(
FqName parentFqName,
JsPropertyInitializer entry,
Map<FqName, DefineInvocation> packageFqNameToDeclarationPlace
) {
DefineInvocation parentDefineInvocation = packageFqNameToDeclarationPlace.get(parentFqName);
if (parentDefineInvocation != null) {
parentDefineInvocation.getMembers().add(entry);
return true;
}
return false;
}
private void addToParent(@NotNull FqName parentFqName,
JsPropertyInitializer entry,
Map<FqName, DefineInvocation> packageFqNameToDefineInvocation) {
while (!addEntryIfParentExists(parentFqName, entry, packageFqNameToDefineInvocation)) {
JsObjectLiteral members = new JsObjectLiteral(new SmartList<JsPropertyInitializer>(entry), true);
DefineInvocation defineInvocation = DefineInvocation.create(parentFqName, null, members, context());
entry = getEntry(parentFqName, defineInvocation);
packageFqNameToDefineInvocation.put(parentFqName, defineInvocation);
parentFqName = parentFqName.parent();
}
}
} }
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.js.translate.declaration package org.jetbrains.kotlin.js.translate.declaration
import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.*
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
@@ -27,12 +26,10 @@ import org.jetbrains.kotlin.js.translate.context.Namer.getReceiverParameterName
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.*
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.assignmentToBackingField
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.backingFieldReference
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.translateFunctionAsEcma5PropertyDescriptor
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addParameter import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addParameter
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addStatement import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addStatement
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
@@ -68,87 +65,40 @@ fun translateAccessors(
fun MutableList<JsPropertyInitializer>.addGetterAndSetter( fun MutableList<JsPropertyInitializer>.addGetterAndSetter(
descriptor: VariableDescriptorWithAccessors, descriptor: VariableDescriptorWithAccessors,
context: TranslationContext,
generateGetter: () -> JsPropertyInitializer, generateGetter: () -> JsPropertyInitializer,
generateSetter: () -> JsPropertyInitializer generateSetter: () -> JsPropertyInitializer
) { ) {
val to: MutableList<JsPropertyInitializer> add(generateGetter())
if (!descriptor.isExtension && !TranslationUtils.shouldGenerateAccessors(descriptor)) {
to = SmartList<JsPropertyInitializer>()
this.add(JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), JsObjectLiteral(to, true)))
}
else {
to = this
}
to.add(generateGetter())
if (descriptor.isVar) { if (descriptor.isVar) {
to.add(generateSetter()) add(generateSetter())
} }
} }
private class PropertyTranslator( class DefaultPropertyTranslator(
val descriptor: VariableDescriptorWithAccessors, val descriptor: VariableDescriptorWithAccessors,
val declaration: KtProperty?, context: TranslationContext,
context: TranslationContext val delegateReference: JsExpression
) : AbstractTranslator(context) { ) : AbstractTranslator(context) {
fun generateDefaultGetterFunction(getterDescriptor: VariableAccessorDescriptor, function: JsFunction) {
private val propertyName: String = descriptor.name.asString() val delegatedCall = bindingContext()[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor]
fun translate(result: MutableList<JsPropertyInitializer>) {
result.addGetterAndSetter(descriptor, context(), { generateGetter() }, { generateSetter() })
}
private fun generateGetter(): JsPropertyInitializer =
if (hasCustomGetter()) translateCustomAccessor(getCustomGetterDeclaration()) else generateDefaultGetter()
private fun generateSetter(): JsPropertyInitializer =
if (hasCustomSetter()) translateCustomAccessor(getCustomSetterDeclaration()) else generateDefaultSetter()
private fun hasCustomGetter() = declaration?.getter != null && getCustomGetterDeclaration().hasBody()
private fun hasCustomSetter() = declaration?.setter != null && getCustomSetterDeclaration().hasBody()
private fun getCustomGetterDeclaration(): KtPropertyAccessor =
declaration?.getter ?:
throw IllegalStateException("declaration and getter should not be null descriptor=$descriptor declaration=$declaration")
private fun getCustomSetterDeclaration(): KtPropertyAccessor =
declaration?.setter ?:
throw IllegalStateException("declaration and setter should not be null descriptor=$descriptor declaration=$declaration")
private fun generateDefaultGetter(): JsPropertyInitializer {
val getterDescriptor = descriptor.getter ?: throw IllegalStateException("Getter descriptor should not be null")
return generateDefaultAccessor(getterDescriptor, generateDefaultGetterFunction(getterDescriptor))
}
private fun generateDefaultGetterFunction(getterDescriptor: VariableAccessorDescriptor): JsFunction {
val delegatedCall = bindingContext().get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor)
if (delegatedCall != null) { if (delegatedCall != null) {
return generateDelegatedGetterFunction(getterDescriptor, delegatedCall) return generateDelegatedGetterFunction(getterDescriptor, delegatedCall, function)
} }
assert(!descriptor.isExtension) { "Unexpected extension property $descriptor}" } assert(!descriptor.isExtension) { "Unexpected extension property $descriptor}" }
assert(descriptor is PropertyDescriptor) { "Property descriptor expected: $descriptor" } assert(descriptor is PropertyDescriptor) { "Property descriptor expected: $descriptor" }
val scope = context().getScopeForDescriptor(getterDescriptor.containingDeclaration)
val result = backingFieldReference(context(), descriptor as PropertyDescriptor) val result = backingFieldReference(context(), descriptor as PropertyDescriptor)
val body = JsBlock(JsReturn(result)) function.body.statements += JsReturn(result)
return JsFunction(scope, body, accessorDescription(getterDescriptor))
} }
private fun generateDelegatedGetterFunction( private fun generateDelegatedGetterFunction(
getterDescriptor: VariableAccessorDescriptor, getterDescriptor: VariableAccessorDescriptor,
delegatedCall: ResolvedCall<FunctionDescriptor> delegatedCall: ResolvedCall<FunctionDescriptor>,
): JsFunction { function: JsFunction
val scope = context().getScopeForDescriptor(getterDescriptor.containingDeclaration) ) {
val function = JsFunction(scope, JsBlock(), accessorDescription(getterDescriptor)) val delegateContext = contextWithPropertyMetadataCreationIntrinsified(context(), delegatedCall, getterDescriptor)
val delegatedJsCall = CallTranslator.translate(delegateContext, delegatedCall, delegateReference)
val delegateRef = getDelegateNameRef(propertyName)
val delegatedJsCall = CallTranslator.translate(
contextWithPropertyMetadataCreationIntrinsified(context(), delegatedCall, getterDescriptor), delegatedCall, delegateRef
)
if (getterDescriptor.isExtension) { if (getterDescriptor.isExtension) {
val receiver = function.addParameter(getReceiverParameterName()).name val receiver = function.addParameter(getReceiverParameterName()).name
@@ -158,40 +108,18 @@ private class PropertyTranslator(
val returnResult = JsReturn(delegatedJsCall) val returnResult = JsReturn(delegatedJsCall)
function.addStatement(returnResult) function.addStatement(returnResult)
return function
} }
private fun contextWithPropertyMetadataCreationIntrinsified( fun generateDefaultSetterFunction(setterDescriptor: VariableAccessorDescriptor, function: JsFunction) {
context: TranslationContext, delegatedCall: ResolvedCall<FunctionDescriptor>, accessor: VariableAccessorDescriptor
): TranslationContext {
val propertyNameLiteral = context.program().getStringLiteral(accessor.correspondingVariable.name.asString())
// 0th argument is instance, 1st is KProperty, 2nd (for setter) is value
val fakeArgumentExpression =
(delegatedCall.valueArgumentsByIndex!![1] as ExpressionValueArgument).valueArgument!!.getArgumentExpression()
return context.innerContextWithAliasesForExpressions(mapOf(
fakeArgumentExpression to JsNew(pureFqn("PropertyMetadata", Namer.kotlinObject()), listOf(propertyNameLiteral))
))
}
private fun generateDefaultSetter(): JsPropertyInitializer {
val setterDescriptor = descriptor.setter ?: throw IllegalStateException("Setter descriptor should not be null")
return generateDefaultAccessor(setterDescriptor, generateDefaultSetterFunction(setterDescriptor))
}
private fun generateDefaultSetterFunction(setterDescriptor: VariableAccessorDescriptor): JsFunction {
val function = JsFunction(context().program().rootScope, JsBlock(), accessorDescription(setterDescriptor))
assert(setterDescriptor.valueParameters.size == 1) { "Setter must have 1 parameter" } assert(setterDescriptor.valueParameters.size == 1) { "Setter must have 1 parameter" }
val correspondingPropertyName = setterDescriptor.correspondingVariable.name.asString() val correspondingPropertyName = setterDescriptor.correspondingVariable.name.asString()
val valueParameter = function.addParameter(correspondingPropertyName).name val valueParameter = function.addParameter(correspondingPropertyName).name
val withAliased = context().innerContextWithAliased(setterDescriptor.valueParameters[0], valueParameter.makeRef()) val withAliased = context().innerContextWithAliased(setterDescriptor.valueParameters[0], valueParameter.makeRef())
val delegatedCall = context().bindingContext().get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, setterDescriptor) val delegatedCall = bindingContext()[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, setterDescriptor]
if (delegatedCall != null) { if (delegatedCall != null) {
val delegatedJsCall = CallTranslator.translate( val delegateContext = contextWithPropertyMetadataCreationIntrinsified(withAliased, delegatedCall, setterDescriptor)
contextWithPropertyMetadataCreationIntrinsified(withAliased, delegatedCall, setterDescriptor), val delegatedJsCall = CallTranslator.translate(delegateContext, delegatedCall, delegateReference)
delegatedCall, getDelegateNameRef(correspondingPropertyName)
)
function.addStatement(delegatedJsCall.makeStmt()) function.addStatement(delegatedJsCall.makeStmt())
if (setterDescriptor.isExtension) { if (setterDescriptor.isExtension) {
@@ -205,15 +133,79 @@ private class PropertyTranslator(
val assignment = assignmentToBackingField(withAliased, descriptor as PropertyDescriptor, valueParameter.makeRef()) val assignment = assignmentToBackingField(withAliased, descriptor as PropertyDescriptor, valueParameter.makeRef())
function.addStatement(assignment.makeStmt()) function.addStatement(assignment.makeStmt())
} }
}
return function private fun contextWithPropertyMetadataCreationIntrinsified(
context: TranslationContext, delegatedCall: ResolvedCall<FunctionDescriptor>, accessor: VariableAccessorDescriptor
): TranslationContext {
val propertyNameLiteral = context.program().getStringLiteral(accessor.correspondingVariable.name.asString())
// 0th argument is instance, 1st is KProperty, 2nd (for setter) is value
val fakeArgumentExpression =
(delegatedCall.valueArgumentsByIndex!![1] as ExpressionValueArgument).valueArgument!!.getArgumentExpression()
return context.innerContextWithAliasesForExpressions(mapOf(
fakeArgumentExpression to JsNew(pureFqn("PropertyMetadata", Namer.kotlinObject()), listOf(propertyNameLiteral))
))
}
}
fun KtProperty.hasCustomGetter() = getter?.hasBody() ?: false
fun KtProperty.hasCustomSetter() = setter?.hasBody() ?: false
private class PropertyTranslator(
val descriptor: VariableDescriptorWithAccessors,
val declaration: KtProperty?,
context: TranslationContext
) : AbstractTranslator(context) {
fun translate(result: MutableList<JsPropertyInitializer>) {
result.addGetterAndSetter(descriptor, { generateGetter() }, { generateSetter() })
}
private fun generateGetter(): JsPropertyInitializer =
if (declaration?.hasCustomGetter() ?: false) translateCustomAccessor(getCustomGetterDeclaration()) else generateDefaultGetter()
private fun generateSetter(): JsPropertyInitializer =
if (declaration?.hasCustomSetter() ?: false) translateCustomAccessor(getCustomSetterDeclaration()) else generateDefaultSetter()
private fun getCustomGetterDeclaration(): KtPropertyAccessor =
declaration?.getter ?:
throw IllegalStateException("declaration and getter should not be null descriptor=$descriptor declaration=$declaration")
private fun getCustomSetterDeclaration(): KtPropertyAccessor =
declaration?.setter ?:
throw IllegalStateException("declaration and setter should not be null descriptor=$descriptor declaration=$declaration")
private fun generateDefaultGetter(): JsPropertyInitializer {
val getterDescriptor = descriptor.getter ?: throw IllegalStateException("Getter descriptor should not be null")
val defaultFunction = createFunction(getterDescriptor).apply {
val delegateRef = getDelegateNameRef(descriptor.name.asString())
DefaultPropertyTranslator(descriptor, context(), delegateRef).generateDefaultGetterFunction(getterDescriptor, this)
}
return generateDefaultAccessor(getterDescriptor, defaultFunction)
}
private fun generateDefaultSetter(): JsPropertyInitializer {
val setterDescriptor = descriptor.setter ?: throw IllegalStateException("Setter descriptor should not be null")
val defaultFunction = createFunction(setterDescriptor).apply {
val delegateRef = getDelegateNameRef(descriptor.name.asString())
DefaultPropertyTranslator(descriptor, context(), delegateRef).generateDefaultSetterFunction(setterDescriptor, this)
}
return generateDefaultAccessor(setterDescriptor, defaultFunction)
} }
private fun generateDefaultAccessor(accessorDescriptor: VariableAccessorDescriptor, function: JsFunction): JsPropertyInitializer = private fun generateDefaultAccessor(accessorDescriptor: VariableAccessorDescriptor, function: JsFunction): JsPropertyInitializer =
translateFunctionAsEcma5PropertyDescriptor(function, accessorDescriptor, context()) translateFunctionAsEcma5PropertyDescriptor(function, accessorDescriptor, context())
private fun translateCustomAccessor(expression: KtPropertyAccessor): JsPropertyInitializer = private fun translateCustomAccessor(expression: KtPropertyAccessor): JsPropertyInitializer {
Translation.functionTranslator(expression, context()).translateAsEcma5PropertyDescriptor() val descriptor = BindingUtils.getFunctionDescriptor(bindingContext(), expression)
val function = JsFunction(context().getScopeForDescriptor(descriptor), JsBlock(), descriptor.toString())
return Translation.functionTranslator(expression, context(), function).translateAsEcma5PropertyDescriptor()
}
private fun createFunction(descriptor: VariableAccessorDescriptor) =
JsFunction(context().getScopeForDescriptor(descriptor), JsBlock(), accessorDescription(descriptor))
private fun accessorDescription(accessorDescriptor: VariableAccessorDescriptor): String { private fun accessorDescription(accessorDescriptor: VariableAccessorDescriptor): String {
val accessorType = val accessorType =
@@ -201,13 +201,12 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
if (delegateExpression != null) { if (delegateExpression != null) {
SmartList<JsPropertyInitializer> propertyInitializers = new SmartList<JsPropertyInitializer>(); SmartList<JsPropertyInitializer> propertyInitializers = new SmartList<JsPropertyInitializer>();
PropertyTranslatorKt.translateAccessors((VariableDescriptorWithAccessors) descriptor, propertyInitializers, context); PropertyTranslatorKt.translateAccessors((VariableDescriptorWithAccessors) descriptor, propertyInitializers, context);
assert propertyInitializers.size() == 1 : descriptor;
initializer = propertyInitializers.get(0).getValueExpr();
JsPropertyInitializer delegateInitializer = new JsPropertyInitializer( JsPropertyInitializer delegateInitializer = new JsPropertyInitializer(
context.program().getStringLiteral(Namer.getDelegateName(descriptor.getName().asString())), context.program().getStringLiteral(Namer.getDelegateName(descriptor.getName().asString())),
Translation.translateAsExpression(delegateExpression, context) Translation.translateAsExpression(delegateExpression, context)
); );
((JsObjectLiteral) initializer).getPropertyInitializers().add(delegateInitializer); propertyInitializers.add(delegateInitializer);
initializer = new JsObjectLiteral(propertyInitializers, true);
} }
JsName name = context.getNameForDescriptor(descriptor); JsName name = context.getNameForDescriptor(descriptor);
@@ -519,11 +518,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull @NotNull
public JsNode visitObjectLiteralExpression(@NotNull KtObjectLiteralExpression expression, @NotNull TranslationContext context) { public JsNode visitObjectLiteralExpression(@NotNull KtObjectLiteralExpression expression, @NotNull TranslationContext context) {
ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), expression.getObjectDeclaration()); ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), expression.getObjectDeclaration());
ClassTranslator.TranslationResult result = translateClassOrObject(expression.getObjectDeclaration(), descriptor, context); translateClassOrObject(expression.getObjectDeclaration(), descriptor, context);
List<JsPropertyInitializer> properties = result.getProperties();
context.getDefinitionPlace().getProperties().addAll(properties);
JsExpression constructor = context.getQualifiedReference(descriptor); JsExpression constructor = context.getInnerReference(descriptor);
List<DeclarationDescriptor> closure = context.getClassOrConstructorClosure(descriptor); List<DeclarationDescriptor> closure = context.getClassOrConstructorClosure(descriptor);
List<JsExpression> closureArgs = new ArrayList<JsExpression>(); List<JsExpression> closureArgs = new ArrayList<JsExpression>();
if (closure != null) { if (closure != null) {
@@ -551,7 +548,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
if (superCall != null) { if (superCall != null) {
assert context.getDeclarationDescriptor() != null : "This expression should be inside declaration: " + assert context.getDeclarationDescriptor() != null : "This expression should be inside declaration: " +
PsiUtilsKt.getTextWithLocation(expression); PsiUtilsKt.getTextWithLocation(expression);
TranslationContext superCallContext = context.newDeclaration(context.getDeclarationDescriptor(), result.getDefinitionPlace()); TranslationContext superCallContext = context.newDeclaration(context.getDeclarationDescriptor());
closureArgs.addAll(CallArgumentTranslator.translate(superCall, null, superCallContext).getTranslateArguments()); closureArgs.addAll(CallArgumentTranslator.translate(superCall, null, superCallContext).getTranslateArguments());
} }
@@ -583,7 +580,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override @Override
public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) { public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) {
ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass); ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass);
context.getDefinitionPlace().getProperties().addAll(translateClassOrObject(klass, descriptor, context).getProperties()); translateClassOrObject(klass, descriptor, context);
return JsEmpty.INSTANCE; return JsEmpty.INSTANCE;
} }
@@ -593,13 +590,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
return JsEmpty.INSTANCE; return JsEmpty.INSTANCE;
} }
private static ClassTranslator.TranslationResult translateClassOrObject( private static void translateClassOrObject(
@NotNull KtClassOrObject declaration, @NotNull KtClassOrObject declaration,
@NotNull ClassDescriptor descriptor, @NotNull ClassDescriptor descriptor,
@NotNull TranslationContext context @NotNull TranslationContext context
) { ) {
JsScope scope = context.getScopeForDescriptor(descriptor); JsScope scope = context.getScopeForDescriptor(descriptor);
TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor); TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor);
return ClassTranslator.translate(declaration, classContext); ClassTranslator.translate(declaration, classContext);
} }
} }
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.js.translate.context.AliasingContext;
import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.psi.KtDeclarationWithBody; import org.jetbrains.kotlin.psi.KtDeclarationWithBody;
import org.jetbrains.kotlin.psi.KtLambdaExpression; import org.jetbrains.kotlin.psi.KtLambdaExpression;
@@ -44,9 +45,12 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.setParameters;
public final class FunctionTranslator extends AbstractTranslator { public final class FunctionTranslator extends AbstractTranslator {
@NotNull @NotNull
public static FunctionTranslator newInstance(@NotNull KtDeclarationWithBody function, public static FunctionTranslator newInstance(
@NotNull TranslationContext context) { @NotNull KtDeclarationWithBody declaration,
return new FunctionTranslator(function, context); @NotNull TranslationContext context,
@NotNull JsFunction function
) {
return new FunctionTranslator(declaration, context, function);
} }
@NotNull @NotNull
@@ -60,11 +64,12 @@ public final class FunctionTranslator extends AbstractTranslator {
@NotNull @NotNull
private final FunctionDescriptor descriptor; private final FunctionDescriptor descriptor;
private FunctionTranslator(@NotNull KtDeclarationWithBody functionDeclaration, @NotNull TranslationContext context) { private FunctionTranslator(@NotNull KtDeclarationWithBody functionDeclaration, @NotNull TranslationContext context,
@NotNull JsFunction function) {
super(context); super(context);
this.descriptor = getFunctionDescriptor(context.bindingContext(), functionDeclaration); this.descriptor = getFunctionDescriptor(context.bindingContext(), functionDeclaration);
this.functionDeclaration = functionDeclaration; this.functionDeclaration = functionDeclaration;
this.functionObject = context().getFunctionObject(descriptor); this.functionObject = function;
assert this.functionObject.getParameters().isEmpty() assert this.functionObject.getParameters().isEmpty()
: message(descriptor, "Function " + functionDeclaration.getText() + " processed for the second time."); : message(descriptor, "Function " + functionDeclaration.getText() + " processed for the second time.");
//NOTE: it's important we compute the context before we start the computation //NOTE: it's important we compute the context before we start the computation
@@ -94,17 +99,21 @@ public final class FunctionTranslator extends AbstractTranslator {
return TranslationUtils.translateFunctionAsEcma5PropertyDescriptor(functionObject, descriptor, context()); return TranslationUtils.translateFunctionAsEcma5PropertyDescriptor(functionObject, descriptor, context());
} }
@NotNull public void translateAsMethodWithoutMetadata() {
public JsPropertyInitializer translateAsMethod() {
JsName functionName = context().getNameForDescriptor(descriptor);
generateFunctionObject(); generateFunctionObject();
}
@NotNull
public JsExpression translateAsMethod() {
translateAsMethodWithoutMetadata();
if (shouldBeInlined(descriptor) && DescriptorUtilsKt.isEffectivelyPublicApi(descriptor)) { if (shouldBeInlined(descriptor) && DescriptorUtilsKt.isEffectivelyPublicApi(descriptor)) {
InlineMetadata metadata = InlineMetadata.compose(functionObject, descriptor); InlineMetadata metadata = InlineMetadata.compose(functionObject, descriptor);
return new JsPropertyInitializer(functionName.makeRef(), metadata.getFunctionWithMetadata()); return metadata.getFunctionWithMetadata();
}
else {
return functionObject;
} }
return new JsPropertyInitializer(functionName.makeRef(), functionObject);
} }
private void generateFunctionObject() { private void generateFunctionObject() {
@@ -21,6 +21,7 @@ import com.google.dart.compiler.backend.js.ast.metadata.*
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.js.inline.util.getInnerFunction import org.jetbrains.kotlin.js.inline.util.getInnerFunction
import org.jetbrains.kotlin.js.inline.util.refreshLabelNames
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.context.getNameForCapturedDescriptor import org.jetbrains.kotlin.js.translate.context.getNameForCapturedDescriptor
import org.jetbrains.kotlin.js.translate.context.hasCapturedExceptContaining import org.jetbrains.kotlin.js.translate.context.hasCapturedExceptContaining
@@ -58,18 +59,27 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
val isRecursive = tracker.isCaptured(descriptor) val isRecursive = tracker.isCaptured(descriptor)
if (isRecursive) { lambda.name = if (isRecursive) {
lambda.name = tracker.getNameForCapturedDescriptor(descriptor) tracker.getNameForCapturedDescriptor(descriptor)
}
else {
context().getInnerNameForDescriptor(descriptor)
} }
if (tracker.hasCapturedExceptContaining()) { if (tracker.hasCapturedExceptContaining()) {
val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda) val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda)
lambdaCreator.name = context().getInnerNameForDescriptor(descriptor)
lambdaCreator.isLocal = true lambdaCreator.isLocal = true
return lambdaCreator.withCapturedParameters(functionContext, invokingContext, descriptor) if (!isRecursive) {
lambda.name = null
}
return lambdaCreator.withCapturedParameters(functionContext, invokingContext)
} }
lambda.isLocal = true lambda.isLocal = true
return invokingContext.define(descriptor, lambda).apply { sideEffects = SideEffectKind.PURE }
context().addRootStatement(lambda.makeStmt())
return lambda.name.makeRef().apply { sideEffects = SideEffectKind.PURE }
} }
fun ValueParameterDescriptorImpl.WithDestructuringDeclaration.translate(context: TranslationContext): JsVars { fun ValueParameterDescriptorImpl.WithDestructuringDeclaration.translate(context: TranslationContext): JsVars {
@@ -82,8 +92,12 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
} }
} }
fun JsFunction.withCapturedParameters(context: TranslationContext, invokingContext: TranslationContext, descriptor: MemberDescriptor): JsExpression { fun JsFunction.withCapturedParameters(
val ref = invokingContext.define(descriptor, this).apply { sideEffects = SideEffectKind.PURE } context: TranslationContext,
invokingContext: TranslationContext
): JsExpression {
context.addRootStatement(makeStmt())
val ref = name.makeRef().apply { sideEffects = SideEffectKind.PURE }
val invocation = JsInvocation(ref).apply { sideEffects = SideEffectKind.PURE } val invocation = JsInvocation(ref).apply { sideEffects = SideEffectKind.PURE }
val invocationArguments = invocation.arguments val invocationArguments = invocation.arguments
@@ -169,7 +169,7 @@ public final class PatternTranslator extends AbstractTranslator {
ClassDescriptor referencedClass = DescriptorUtils.getClassDescriptorForType(type); ClassDescriptor referencedClass = DescriptorUtils.getClassDescriptorForType(type);
JsNameRef typeName = context().getQualifiedReference(referencedClass); JsNameRef typeName = context().getInnerReference(referencedClass);
return referencedClass.getKind() != ClassKind.OBJECT ? namer().isInstanceOf(typeName) : namer().isInstanceOfObject(typeName); return referencedClass.getKind() != ClassKind.OBJECT ? namer().isInstanceOf(typeName) : namer().isInstanceOfObject(typeName);
} }
@@ -48,7 +48,6 @@ import org.jetbrains.kotlin.psi.KtDeclarationWithBody;
import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtNamedFunction; import org.jetbrains.kotlin.psi.KtNamedFunction;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
@@ -66,7 +65,6 @@ import java.util.List;
import static org.jetbrains.kotlin.js.translate.general.ModuleWrapperTranslation.wrapIfNecessary; import static org.jetbrains.kotlin.js.translate.general.ModuleWrapperTranslation.wrapIfNecessary;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor;
import static org.jetbrains.kotlin.js.translate.utils.ErrorReportingUtils.message;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatement; import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatement;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.toStringLiteralList; import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.toStringLiteralList;
import static org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression; import static org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
@@ -81,9 +79,12 @@ public final class Translation {
} }
@NotNull @NotNull
public static FunctionTranslator functionTranslator(@NotNull KtDeclarationWithBody function, public static FunctionTranslator functionTranslator(
@NotNull TranslationContext context) { @NotNull KtDeclarationWithBody declaration,
return FunctionTranslator.newInstance(function, context); @NotNull TranslationContext context,
@NotNull JsFunction function
) {
return FunctionTranslator.newInstance(declaration, context, function);
} }
@NotNull @NotNull
@@ -252,17 +253,16 @@ public final class Translation {
) { ) {
StaticContext staticContext = StaticContext.generateStaticContext(bindingTrace, config, moduleDescriptor); StaticContext staticContext = StaticContext.generateStaticContext(bindingTrace, config, moduleDescriptor);
JsProgram program = staticContext.getProgram(); JsProgram program = staticContext.getProgram();
// Prohibit using "_" as a variable name
program.getRootScope().declareName("_"); program.getRootScope().declareName("_");
JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope()); JsFunction rootFunction = staticContext.getRootFunction();
JsBlock rootBlock = rootFunction.getBody(); JsBlock rootBlock = rootFunction.getBody();
List<JsStatement> statements = rootBlock.getStatements(); List<JsStatement> statements = rootBlock.getStatements();
statements.add(program.getStringLiteral("use strict").makeStmt());
TranslationContext context = TranslationContext.rootContext(staticContext, rootFunction); TranslationContext context = TranslationContext.rootContext(staticContext, rootFunction);
statements.addAll(PackageDeclarationTranslator.translateFiles(files, context)); PackageDeclarationTranslator.translateFiles(files, context);
staticContext.postProcess();
statements.add(0, program.getStringLiteral("use strict").makeStmt());
defineModule(context, statements, config.getModuleId()); defineModule(context, statements, config.getModuleId());
mayBeGenerateTests(files, config, rootBlock, context); mayBeGenerateTests(files, config, rootBlock, context);
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.js.translate.declaration.DelegationTranslator;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator; import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator;
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.AstUtilsKt; import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.AstUtilsKt;
import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.lexer.KtTokens;
@@ -39,9 +40,11 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument; import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument; import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.KotlinType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -56,16 +59,23 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
private final KtClassOrObject classDeclaration; private final KtClassOrObject classDeclaration;
@NotNull @NotNull
private final JsFunction initFunction; private final JsFunction initFunction;
@NotNull
private final TranslationContext context; private final TranslationContext context;
@NotNull
private final ClassDescriptor classDescriptor;
public ClassInitializerTranslator( public ClassInitializerTranslator(
@NotNull KtClassOrObject classDeclaration, @NotNull KtClassOrObject classDeclaration,
@NotNull TranslationContext context @NotNull TranslationContext context,
@NotNull JsFunction initFunction
) { ) {
super(context); super(context);
this.classDeclaration = classDeclaration; this.classDeclaration = classDeclaration;
this.initFunction = createInitFunction(classDeclaration, context); this.initFunction = initFunction;
this.context = context.contextWithScope(initFunction); this.context = context.contextWithScope(initFunction);
classDescriptor = BindingUtils.getClassDescriptor(bindingContext(), classDeclaration);
fillInitFunction(classDeclaration, context);
} }
@NotNull @NotNull
@@ -74,8 +84,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
return context; return context;
} }
@NotNull private static void fillInitFunction(KtClassOrObject declaration, TranslationContext context) {
private static JsFunction createInitFunction(KtClassOrObject declaration, TranslationContext context) {
//TODO: it's inconsistent that we have scope for class and function for constructor, currently have problems implementing better way //TODO: it's inconsistent that we have scope for class and function for constructor, currently have problems implementing better way
ClassDescriptor classDescriptor = getClassDescriptor(context.bindingContext(), declaration); ClassDescriptor classDescriptor = getClassDescriptor(context.bindingContext(), declaration);
ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
@@ -104,20 +113,16 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
} }
ctorFunction.setName(ctorFunction.getScope().declareName(functionName)); ctorFunction.setName(ctorFunction.getScope().declareName(functionName));
} }
return ctorFunction;
} }
@NotNull public void generateInitializeMethod(DelegationTranslator delegationTranslator) {
public JsFunction generateInitializeMethod(DelegationTranslator delegationTranslator) {
ClassDescriptor classDescriptor = getClassDescriptor(bindingContext(), classDeclaration);
addOuterClassReference(classDescriptor); addOuterClassReference(classDescriptor);
ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
if (primaryConstructor != null) { if (primaryConstructor != null) {
initFunction.getBody().getStatements().addAll(setDefaultValueForArguments(primaryConstructor, context())); initFunction.getBody().getStatements().addAll(setDefaultValueForArguments(primaryConstructor, context()));
mayBeAddCallToSuperMethod(initFunction, classDescriptor); mayBeAddCallToSuperMethod(initFunction);
//NOTE: while we translate constructor parameters we also add property initializer statements //NOTE: while we translate constructor parameters we also add property initializer statements
// for properties declared as constructor parameters // for properties declared as constructor parameters
@@ -126,8 +131,6 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
delegationTranslator.addInitCode(initFunction.getBody().getStatements()); delegationTranslator.addInitCode(initFunction.getBody().getStatements());
new InitializerVisitor().traverseContainer(classDeclaration, context().innerBlock(initFunction.getBody())); new InitializerVisitor().traverseContainer(classDeclaration, context().innerBlock(initFunction.getBody()));
return initFunction;
} }
private void addOuterClassReference(ClassDescriptor classDescriptor) { private void addOuterClassReference(ClassDescriptor classDescriptor) {
@@ -142,26 +145,33 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
} }
@NotNull @NotNull
public JsExpression generateEnumEntryInstanceCreation(@NotNull KotlinType enumClassType) { public static JsExpression generateEnumEntryInstanceCreation(
ResolvedCall<FunctionDescriptor> superCall = getSuperCall(bindingContext(), classDeclaration); @NotNull TranslationContext context,
@NotNull KotlinType enumClassType,
@NotNull KtClassOrObject classDeclaration,
int ordinal
) {
ResolvedCall<FunctionDescriptor> superCall = getSuperCall(context.bindingContext(), classDeclaration);
if (superCall == null) { if (superCall == null) {
ClassDescriptor classDescriptor = getClassDescriptorForType(enumClassType); ClassDescriptor classDescriptor = getClassDescriptorForType(enumClassType);
JsNameRef reference = context().getQualifiedReference(classDescriptor); JsNameRef reference = context.getInnerReference(classDescriptor);
return new JsNew(reference); JsExpression nameArg = context.program().getStringLiteral(classDeclaration.getName());
JsExpression ordinalArg = context.program().getNumberLiteral(ordinal);
return new JsNew(reference, Arrays.asList(nameArg, ordinalArg));
} }
return CallTranslator.translate(context(), superCall); return CallTranslator.translate(context, superCall);
} }
private void mayBeAddCallToSuperMethod(JsFunction initializer, @NotNull ClassDescriptor descriptor) { private void mayBeAddCallToSuperMethod(JsFunction initializer) {
if (classDeclaration.hasModifier(KtTokens.ENUM_KEYWORD)) { if (classDeclaration.hasModifier(KtTokens.ENUM_KEYWORD)) {
addCallToSuperMethod(Collections.<JsExpression>emptyList(), initializer); addCallToSuperMethod(Collections.<JsExpression>emptyList(), initializer);
} }
else if (hasAncestorClass(bindingContext(), classDeclaration)) { else if (hasAncestorClass(bindingContext(), classDeclaration)) {
ResolvedCall<FunctionDescriptor> superCall = getSuperCall(bindingContext(), classDeclaration); ResolvedCall<FunctionDescriptor> superCall = getSuperCall(bindingContext(), classDeclaration);
if (superCall == null) { if (superCall == null) {
if (DescriptorUtils.isEnumEntry(descriptor)) { if (DescriptorUtils.isEnumEntry(classDescriptor)) {
addCallToSuperMethod(Collections.<JsExpression>emptyList(), initializer); addCallToSuperMethod(Collections.<JsExpression>emptyList(), initializer);
} }
return; return;
@@ -182,18 +192,18 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
if (superclassClosure != null) { if (superclassClosure != null) {
UsageTracker tracker = context.usageTracker(); UsageTracker tracker = context.usageTracker();
assert tracker != null : "Closure exists, therefore UsageTracker must exist too. Translating constructor of " + assert tracker != null : "Closure exists, therefore UsageTracker must exist too. Translating constructor of " +
descriptor; classDescriptor;
for (DeclarationDescriptor capturedValue : superclassClosure) { for (DeclarationDescriptor capturedValue : superclassClosure) {
tracker.used(capturedValue); tracker.used(capturedValue);
arguments.add(tracker.getCapturedDescriptorToJsName().get(capturedValue).makeRef()); arguments.add(tracker.getCapturedDescriptorToJsName().get(capturedValue).makeRef());
} }
} }
if (superDescriptor.getContainingDeclaration().isInner() && descriptor.isInner()) { if (superDescriptor.getContainingDeclaration().isInner() && classDescriptor.isInner()) {
arguments.add(pureFqn(Namer.OUTER_FIELD_NAME, JsLiteral.THIS)); arguments.add(pureFqn(Namer.OUTER_FIELD_NAME, JsLiteral.THIS));
} }
if (!DescriptorUtils.isAnonymousObject(descriptor)) { if (!DescriptorUtils.isAnonymousObject(classDescriptor)) {
arguments.addAll(CallArgumentTranslator.translate(superCall, null, context()).getTranslateArguments()); arguments.addAll(CallArgumentTranslator.translate(superCall, null, context()).getTranslateArguments());
} }
else { else {
@@ -225,20 +235,22 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
} }
} }
private void addCallToSuperMethod(@NotNull List<JsExpression> arguments, JsFunction initializer) { private void addCallToSuperMethod(@NotNull List<JsExpression> arguments, @NotNull JsFunction initializer) {
if (initializer.getName() == null) { if (initializer.getName() == null) {
JsName ref = context().scope().declareName(Namer.CALLEE_NAME); JsName ref = context().scope().declareName(Namer.CALLEE_NAME);
initializer.setName(ref); initializer.setName(ref);
} }
JsInvocation call = new JsInvocation(Namer.getFunctionCallRef(Namer.superMethodNameRef(initializer.getName()))); ClassDescriptor superclassDescriptor = DescriptorUtilsKt.getSuperClassOrAny(classDescriptor);
JsExpression superConstructorRef = context().getInnerReference(superclassDescriptor);
JsInvocation call = new JsInvocation(Namer.getFunctionCallRef(superConstructorRef));
call.getArguments().add(JsLiteral.THIS); call.getArguments().add(JsLiteral.THIS);
call.getArguments().addAll(arguments); call.getArguments().addAll(arguments);
initFunction.getBody().getStatements().add(call.makeStmt()); initFunction.getBody().getStatements().add(call.makeStmt());
} }
private void addCallToSuperSecondaryConstructor(@NotNull List<JsExpression> arguments, @NotNull ConstructorDescriptor descriptor) { private void addCallToSuperSecondaryConstructor(@NotNull List<JsExpression> arguments, @NotNull ConstructorDescriptor descriptor) {
JsExpression reference = context.getQualifiedReference(descriptor); JsExpression reference = context.getInnerReference(descriptor);
JsInvocation call = new JsInvocation(reference); JsInvocation call = new JsInvocation(reference);
call.getArguments().addAll(arguments); call.getArguments().addAll(arguments);
call.getArguments().add(JsLiteral.THIS); call.getArguments().add(JsLiteral.THIS);
@@ -257,8 +269,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsParameter translateParameter(@NotNull KtParameter jetParameter) { private JsParameter translateParameter(@NotNull KtParameter jetParameter) {
DeclarationDescriptor parameterDescriptor = DeclarationDescriptor parameterDescriptor = getDescriptorForElement(bindingContext(), jetParameter);
getDescriptorForElement(bindingContext(), jetParameter);
JsName parameterName = context().getNameForDescriptor(parameterDescriptor); JsName parameterName = context().getNameForDescriptor(parameterDescriptor);
JsParameter jsParameter = new JsParameter(parameterName); JsParameter jsParameter = new JsParameter(parameterName);
mayBeAddInitializerStatementForProperty(jsParameter, jetParameter); mayBeAddInitializerStatementForProperty(jsParameter, jetParameter);
@@ -267,8 +278,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
private void mayBeAddInitializerStatementForProperty(@NotNull JsParameter jsParameter, private void mayBeAddInitializerStatementForProperty(@NotNull JsParameter jsParameter,
@NotNull KtParameter jetParameter) { @NotNull KtParameter jetParameter) {
PropertyDescriptor propertyDescriptor = PropertyDescriptor propertyDescriptor = getPropertyDescriptorForConstructorParameter(bindingContext(), jetParameter);
getPropertyDescriptorForConstructorParameter(bindingContext(), jetParameter);
if (propertyDescriptor == null) { if (propertyDescriptor == null) {
return; return;
} }
@@ -108,20 +108,20 @@ object CallableReferenceTranslator {
} }
private fun translateForExtensionProperty(descriptor: PropertyDescriptor, context: TranslationContext): JsExpression { private fun translateForExtensionProperty(descriptor: PropertyDescriptor, context: TranslationContext): JsExpression {
val jsGetterNameRef = context.getQualifiedReference(descriptor.getter!!) val jsGetterNameRef = context.getInnerReference(descriptor.getter!!)
val propertyName = descriptor.name val propertyName = descriptor.name
val jsPropertyNameAsString = context.program().getStringLiteral(propertyName.asString()) val jsPropertyNameAsString = context.program().getStringLiteral(propertyName.asString())
val argumentList = ArrayList<JsExpression>(3) val argumentList = ArrayList<JsExpression>(3)
argumentList.add(jsPropertyNameAsString) argumentList.add(jsPropertyNameAsString)
argumentList.add(jsGetterNameRef) argumentList.add(jsGetterNameRef)
if (descriptor.isVar) { if (descriptor.isVar) {
val jsSetterNameRef = context.getQualifiedReference(descriptor.setter!!) val jsSetterNameRef = context.getInnerReference(descriptor.setter!!)
argumentList.add(jsSetterNameRef) argumentList.add(jsSetterNameRef)
} }
if (AnnotationsUtils.isNativeObject(descriptor)) return if (AnnotationsUtils.isNativeObject(descriptor))
return translateForMemberProperty(descriptor, context) translateForMemberProperty(descriptor, context)
else else
return JsInvocation(context.namer().callableRefForExtensionPropertyReference(), argumentList) JsInvocation(context.namer().callableRefForExtensionPropertyReference(), argumentList)
} }
private fun translateForConstructor(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression { private fun translateForConstructor(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression {
@@ -130,8 +130,8 @@ object CallableReferenceTranslator {
} }
private fun translateForExtensionFunction(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression { private fun translateForExtensionFunction(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression {
val receiverParameterDescriptor = descriptor.extensionReceiverParameter val receiverParameterDescriptor = descriptor.extensionReceiverParameter ?:
assert(receiverParameterDescriptor != null) { "receiverParameter for extension should not be null" } error("receiverParameter for extension should not be null")
val jsFunctionRef = ReferenceTranslator.translateAsFQReference(descriptor, context) val jsFunctionRef = ReferenceTranslator.translateAsFQReference(descriptor, context)
if (descriptor.visibility == Visibilities.LOCAL) { if (descriptor.visibility == Visibilities.LOCAL) {
@@ -139,7 +139,7 @@ object CallableReferenceTranslator {
} }
else if (AnnotationsUtils.isNativeObject(descriptor)) { else if (AnnotationsUtils.isNativeObject(descriptor)) {
val jetType = receiverParameterDescriptor!!.type val jetType = receiverParameterDescriptor.type
val receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(jetType) val receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(jetType)
return translateAsMemberFunctionReference(descriptor, receiverClassDescriptor, context) return translateAsMemberFunctionReference(descriptor, receiverClassDescriptor, context)
} }
@@ -159,7 +159,12 @@ object CallableReferenceTranslator {
classDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor,
context: TranslationContext context: TranslationContext
): JsExpression { ): JsExpression {
val jsClassNameRef = context.getQualifiedReference(classDescriptor) val jsClassNameRef = if (AnnotationsUtils.isNativeObject(classDescriptor)) {
context.getQualifiedReference(classDescriptor)
}
else {
context.getInnerReference(classDescriptor)
}
val funName = context.getNameForDescriptor(descriptor) val funName = context.getNameForDescriptor(descriptor)
val funNameAsString = context.program().getStringLiteral(funName.toString()) val funNameAsString = context.program().getStringLiteral(funName.toString())
return JsInvocation(context.namer().callableRefForMemberFunctionReference(), jsClassNameRef, funNameAsString) return JsInvocation(context.namer().callableRefForMemberFunctionReference(), jsClassNameRef, funNameAsString)
@@ -17,15 +17,16 @@
package org.jetbrains.kotlin.js.translate.reference; package org.jetbrains.kotlin.js.translate.reference;
import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsInvocation;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtQualifiedExpression; import org.jetbrains.kotlin.psi.KtQualifiedExpression;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression; import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelectorAsSimpleName; import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelectorAsSimpleName;
@@ -45,7 +46,20 @@ public final class ReferenceTranslator {
public static JsExpression translateAsFQReference(@NotNull DeclarationDescriptor referencedDescriptor, public static JsExpression translateAsFQReference(@NotNull DeclarationDescriptor referencedDescriptor,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
JsExpression alias = context.getAliasForDescriptor(referencedDescriptor); JsExpression alias = context.getAliasForDescriptor(referencedDescriptor);
return alias != null ? alias : context.getQualifiedReference(referencedDescriptor); if (alias != null) return alias;
if (isLocalVarOrFunction(referencedDescriptor) ||
AnnotationsUtils.isNativeObject(referencedDescriptor) ||
AnnotationsUtils.isLibraryObject(referencedDescriptor)
) {
return context.getQualifiedReference(referencedDescriptor);
}
return context.getInnerReference(referencedDescriptor);
}
private static boolean isLocalVarOrFunction(DeclarationDescriptor descriptor) {
return descriptor.getContainingDeclaration() instanceof FunctionDescriptor && !(descriptor instanceof ClassDescriptor);
} }
@NotNull @NotNull
@@ -57,7 +71,22 @@ public final class ReferenceTranslator {
return alias; return alias;
} }
} }
return context.getQualifiedReference(descriptor); if (DescriptorUtils.isObject(descriptor) || DescriptorUtils.isEnumEntry(descriptor)) {
if (AnnotationsUtils.isNativeObject(descriptor)) {
return context.getQualifiedReference(descriptor);
}
else if (!context.isFromCurrentModule(descriptor)) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
assert container != null : "Object must have containing declaration: " + descriptor;
JsExpression qualifier = context.getInnerReference(container);
return JsAstUtils.pureFqn(context.getNameForDescriptor(descriptor), qualifier);
}
else {
JsExpression functionRef = JsAstUtils.pureFqn(context.getNameForObjectInstance((ClassDescriptor) descriptor), null);
return new JsInvocation(functionRef);
}
}
return context.getInnerReference(descriptor);
} }
@NotNull @NotNull
@@ -339,6 +339,11 @@ public final class JsAstUtils {
return new JsBinaryOperation(JsBinaryOperator.ASG, left, right); return new JsBinaryOperation(JsBinaryOperator.ASG, left, right);
} }
@NotNull
public static JsStatement assignmentToThisField(@NotNull String fieldName, @NotNull JsExpression right) {
return assignment(new JsNameRef(fieldName, JsLiteral.THIS), right).makeStmt();
}
public static JsStatement asSyntheticStatement(@NotNull JsExpression expression) { public static JsStatement asSyntheticStatement(@NotNull JsExpression expression) {
JsExpressionStatement statement = new JsExpressionStatement(expression); JsExpressionStatement statement = new JsExpressionStatement(expression);
MetadataProperties.setSynthetic(statement, true); MetadataProperties.setSynthetic(statement, true);
@@ -460,11 +465,12 @@ public final class JsAstUtils {
@NotNull @NotNull
public static JsInvocation defineProperty( public static JsInvocation defineProperty(
@NotNull JsExpression receiver,
@NotNull String name, @NotNull String name,
@NotNull JsObjectLiteral value, @NotNull JsExpression value,
@NotNull TranslationContext context @NotNull JsProgram program
) { ) {
return new JsInvocation(DEFINE_PROPERTY, JsLiteral.THIS, context.program().getStringLiteral(name), value); return new JsInvocation(DEFINE_PROPERTY.deepCopy(), receiver, program.getStringLiteral(name), value);
} }
@NotNull @NotNull
@@ -544,4 +550,21 @@ public final class JsAstUtils {
JsUnaryOperation unary = (JsUnaryOperation) expression; JsUnaryOperation unary = (JsUnaryOperation) expression;
return unary.getOperator() == JsUnaryOperator.VOID; return unary.getOperator() == JsUnaryOperator.VOID;
} }
@NotNull
public static JsStatement defineGetter(
@NotNull JsProgram program,
@NotNull JsExpression receiver,
@NotNull String name,
@NotNull JsExpression body
) {
JsObjectLiteral propertyLiteral = new JsObjectLiteral(true);
propertyLiteral.getPropertyInitializers().add(new JsPropertyInitializer(new JsNameRef("get"), body));
return defineProperty(receiver, name, propertyLiteral, program).makeStmt();
}
@NotNull
public static JsExpression prototypeOf(@NotNull JsExpression expression) {
return pureFqn("prototype", expression);
}
} }
@@ -150,7 +150,7 @@ public final class JsDescriptorUtils {
return !isExtension(propertyDescriptor) && return !isExtension(propertyDescriptor) &&
isDefaultAccessor(propertyDescriptor.getGetter()) && isDefaultAccessor(propertyDescriptor.getGetter()) &&
isDefaultAccessor(propertyDescriptor.getSetter()) && isDefaultAccessor(propertyDescriptor.getSetter()) &&
!TranslationUtils.shouldGenerateAccessors(propertyDescriptor) && !TranslationUtils.shouldAccessViaFunctions(propertyDescriptor) &&
!ModalityKt.isOverridableOrOverrides(propertyDescriptor); !ModalityKt.isOverridableOrOverrides(propertyDescriptor);
} }
@@ -54,7 +54,7 @@ public final class TranslationUtils {
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
if (DescriptorUtils.isExtension(descriptor) || if (DescriptorUtils.isExtension(descriptor) ||
descriptor instanceof PropertyAccessorDescriptor && descriptor instanceof PropertyAccessorDescriptor &&
shouldGenerateAccessors(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()) shouldAccessViaFunctions(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty())
) { ) {
return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context); return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context);
} }
@@ -146,19 +146,20 @@ public final class TranslationUtils {
} }
@NotNull @NotNull
public static JsNameRef backingFieldReference(@NotNull TranslationContext context, public static JsNameRef backingFieldReference(@NotNull TranslationContext context, @NotNull PropertyDescriptor descriptor) {
@NotNull PropertyDescriptor descriptor) { DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
JsName backingFieldName = context.getNameForDescriptor(descriptor); JsName backingFieldName = containingDescriptor instanceof PackageFragmentDescriptor ?
if(!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) { context.getInnerNameForDescriptor(descriptor) :
context.getNameForDescriptor(descriptor);
if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor) && !(containingDescriptor instanceof PackageFragmentDescriptor)) {
JsName backingFieldMangledName = context.getNameForBackingField(descriptor); JsName backingFieldMangledName = context.getNameForBackingField(descriptor);
backingFieldName = context.declarePropertyOrPropertyAccessorName(descriptor, backingFieldMangledName.getIdent(), false); backingFieldName = context.declarePropertyOrPropertyAccessorName(descriptor, backingFieldMangledName.getIdent(), false);
} }
DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
JsExpression receiver; JsExpression receiver;
if (containingDescriptor instanceof PackageFragmentDescriptor) { if (containingDescriptor instanceof PackageFragmentDescriptor) {
// used inside package initializer receiver = null;
receiver = JsLiteral.THIS;
} }
else { else {
receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor)); receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor));
@@ -296,22 +297,22 @@ public final class TranslationUtils {
return !(descriptor instanceof LocalVariableDescriptor) || !((LocalVariableDescriptor) descriptor).isDelegated(); return !(descriptor instanceof LocalVariableDescriptor) || !((LocalVariableDescriptor) descriptor).isDelegated();
} }
public static boolean shouldGenerateAccessors(@NotNull CallableDescriptor descriptor) { public static boolean shouldAccessViaFunctions(@NotNull CallableDescriptor descriptor) {
if (descriptor instanceof PropertyDescriptor) { if (descriptor instanceof PropertyDescriptor) {
return shouldGenerateAccessors((PropertyDescriptor) descriptor); return shouldAccessViaFunctions((PropertyDescriptor) descriptor);
} }
else if (descriptor instanceof PropertyAccessorDescriptor) { else if (descriptor instanceof PropertyAccessorDescriptor) {
return shouldGenerateAccessors(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()); return shouldAccessViaFunctions(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty());
} }
else { else {
return false; return false;
} }
} }
private static boolean shouldGenerateAccessors(@NotNull PropertyDescriptor property) { private static boolean shouldAccessViaFunctions(@NotNull PropertyDescriptor property) {
if (AnnotationsUtils.hasJsNameInAccessors(property)) return true; if (AnnotationsUtils.hasJsNameInAccessors(property)) return true;
for (PropertyDescriptor overriddenProperty : property.getOverriddenDescriptors()) { for (PropertyDescriptor overriddenProperty : property.getOverriddenDescriptors()) {
if (shouldGenerateAccessors(overriddenProperty)) return true; if (shouldAccessViaFunctions(overriddenProperty)) return true;
} }
return false; return false;
} }
@@ -18,10 +18,7 @@ package org.jetbrains.kotlin.js.translate.utils
import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.*
import com.intellij.util.SmartList import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
@@ -29,12 +26,12 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
fun generateDelegateCall( fun generateDelegateCall(
classDescriptor: ClassDescriptor,
fromDescriptor: FunctionDescriptor, fromDescriptor: FunctionDescriptor,
toDescriptor: FunctionDescriptor, toDescriptor: FunctionDescriptor,
thisObject: JsExpression, thisObject: JsExpression,
context: TranslationContext context: TranslationContext
): JsPropertyInitializer { ) {
val delegateMemberFunctionName = context.getNameForDescriptor(fromDescriptor)
val overriddenMemberFunctionName = context.getNameForDescriptor(toDescriptor) val overriddenMemberFunctionName = context.getNameForDescriptor(toDescriptor)
val overriddenMemberFunctionRef = JsNameRef(overriddenMemberFunctionName, thisObject) val overriddenMemberFunctionRef = JsNameRef(overriddenMemberFunctionName, thisObject)
@@ -57,7 +54,8 @@ fun generateDelegateCall(
val functionObject = simpleReturnFunction(context.getScopeForDescriptor(fromDescriptor), JsInvocation(overriddenMemberFunctionRef, args)) val functionObject = simpleReturnFunction(context.getScopeForDescriptor(fromDescriptor), JsInvocation(overriddenMemberFunctionRef, args))
functionObject.parameters.addAll(parameters) functionObject.parameters.addAll(parameters)
return JsPropertyInitializer(delegateMemberFunctionName.makeRef(), functionObject)
context.addFunctionToPrototype(classDescriptor, fromDescriptor, functionObject)
} }
fun <T, S> List<T>.splitToRanges(classifier: (T) -> S): List<Pair<List<T>, S>> { fun <T, S> List<T>.splitToRanges(classifier: (T) -> S): List<Pair<List<T>, S>> {
@@ -86,7 +84,7 @@ fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsName
val classifierDescriptor = type.constructor.declarationDescriptor val classifierDescriptor = type.constructor.declarationDescriptor
if (classifierDescriptor is ClassDescriptor) { if (classifierDescriptor is ClassDescriptor) {
val reference = context.getQualifiedReference(classifierDescriptor) val reference = context.getInnerReference(classifierDescriptor)
if (classifierDescriptor.kind == ClassKind.OBJECT) { if (classifierDescriptor.kind == ClassKind.OBJECT) {
referenceToJsClass = JsAstUtils.pureFqn("constructor", JsInvocation(JsNameRef("getPrototypeOf", JsNameRef("Object")), reference)) referenceToJsClass = JsAstUtils.pureFqn("constructor", JsInvocation(JsNameRef("getPrototypeOf", JsNameRef("Object")), reference))
} }
@@ -105,3 +103,20 @@ fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsName
return referenceToJsClass return referenceToJsClass
} }
fun TranslationContext.addFunctionToPrototype(classDescriptor: ClassDescriptor, descriptor: FunctionDescriptor, function: JsExpression) {
val prototypeRef = JsAstUtils.prototypeOf(getInnerReference(classDescriptor))
val functionRef = JsNameRef(getNameForDescriptor(descriptor), prototypeRef)
addRootStatement(JsAstUtils.assignment(functionRef, function).makeStmt())
}
fun TranslationContext.addAccessorsToPrototype(
containingClass: ClassDescriptor,
propertyDescriptor: PropertyDescriptor,
literal: JsObjectLiteral
) {
val prototypeRef = JsAstUtils.prototypeOf(getInnerReference(containingClass))
val propertyName = getNameForDescriptor(propertyDescriptor)
val defineProperty = JsAstUtils.defineProperty(prototypeRef, propertyName.ident, literal, program())
addRootStatement(defineProperty.makeStmt())
}
@@ -0,0 +1,10 @@
class A
class B {
val A.x: String
get() = "OK"
fun result(a: A) = a.x
}
fun box() = B().result(A())
@@ -0,0 +1,9 @@
interface I {
fun foo() = "OK"
}
abstract class A : I
class B : A()
fun box() = B().foo()
@@ -0,0 +1,7 @@
interface I {
fun foo() = "OK"
}
class A : I
fun box() = A().foo()
@@ -0,0 +1,9 @@
interface I {
fun foo() = "OK"
}
interface J : I
class A : J
fun box() = A().foo()
@@ -0,0 +1,8 @@
interface I {
val foo: String
get() = "OK"
}
class A : I
fun box() = A().foo
@@ -20,7 +20,7 @@ fun box(): String {
if (foo + bar != OK) return "$foo + $bar != $OK" if (foo + bar != OK) return "$foo + $bar != $OK"
val actualAsString = funToString("actual_0") val actualAsString = funToString("actual_0")
val expectedAsString = funToString("expected_0") val expectedAsString = funToString("expected_0").replace("expected", "actual")
if (actualAsString != expectedAsString) return "$actualAsString != $expectedAsString" if (actualAsString != expectedAsString) return "$actualAsString != $expectedAsString"
if (actual("asd", "12345") != "asd12345") return "${actual("asd", "12345")} != \"asd12345\"" if (actual("asd", "12345") != "asd12345") return "${actual("asd", "12345")} != \"asd12345\""