diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java index 7481c056cc3..8c26780826d 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java @@ -132,8 +132,7 @@ public abstract class JsScope { return names.containsKey(name); } - @Nullable - public boolean hasName(@NotNull String name) { + private boolean hasName(@NotNull String name) { return hasOwnName(name) || (parent != null && parent.hasName(name)); } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/descriptorUtils/descriptorUtils.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/descriptorUtils/descriptorUtils.kt index fbd0cb9a6bb..59dad9926eb 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/descriptorUtils/descriptorUtils.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/descriptorUtils/descriptorUtils.kt @@ -20,7 +20,9 @@ import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.addToStdlib.check diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index fe250c96883..cfe1a74d26f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -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); } + @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") public void testPropertyWithGetterAndSetter() throws Exception { 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"); 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") diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfoExtensions.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfoExtensions.kt index f5fda687726..f595e3c1c9c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfoExtensions.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfoExtensions.kt @@ -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.SideEffectKind import com.google.dart.compiler.backend.js.ast.metadata.sideEffects -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.js.translate.context.TranslationContext 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 } -fun CallInfo.isExtension(): Boolean = extensionReceiver != null - fun CallInfo.isNative(): Boolean = AnnotationsUtils.isNativeObject(callableDescriptor) fun CallInfo.isSuperInvocation(): Boolean { @@ -65,11 +60,16 @@ val VariableAccessInfo.variableName: JsName fun VariableAccessInfo.isGetAccess(): Boolean = value == null -fun VariableAccessInfo.getAccessDescriptor(): DeclarationDescriptor { - val descriptor = variableDescriptor - if (descriptor is PropertyDescriptor && (descriptor.isExtension || TranslationUtils.shouldGenerateAccessors(descriptor))) { - val propertyAccessorDescriptor = if (isGetAccess()) descriptor.getter else descriptor.setter - return propertyAccessorDescriptor!! +fun VariableAccessInfo.getAccessDescriptor(): PropertyAccessorDescriptor { + val property = variableDescriptor as PropertyDescriptor + return if (isGetAccess()) property.getter!! else property.setter!! +} + +fun VariableAccessInfo.getAccessDescriptorIfNeeded(): CallableDescriptor { + if (variableDescriptor is PropertyDescriptor && + (variableDescriptor.isExtension || TranslationUtils.shouldAccessViaFunctions(variableDescriptor)) + ) { + return getAccessDescriptor() } else { return variableDescriptor diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt index 504f99d6b69..2c6856faf0b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt @@ -70,9 +70,11 @@ object CallTranslator { val isNative = AnnotationsUtils.isNativeObject(functionDescriptor) val hasSpreadOperator = false if (dispatchReceiver != null) { - return DefaultFunctionCallCase.buildDefaultCallWithDispatchReceiver(argumentsInfo, dispatchReceiver, functionName, isNative, hasSpreadOperator) + return DefaultFunctionCallCase.buildDefaultCallWithDispatchReceiver(argumentsInfo, dispatchReceiver, functionName, isNative, + hasSpreadOperator) } else { - return DefaultFunctionCallCase.buildDefaultCallWithoutReceiver(context, argumentsInfo, functionDescriptor, isNative, hasSpreadOperator) + return DefaultFunctionCallCase.buildDefaultCallWithoutReceiver(context, argumentsInfo, functionDescriptor, isNative, + hasSpreadOperator) } } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt index bcf9b890b7f..45145eb7fde 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt @@ -19,15 +19,13 @@ package org.jetbrains.kotlin.js.translate.callTranslator import com.google.dart.compiler.backend.js.ast.* import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.builtins.isExtensionFunctionType -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.js.PredefinedAnnotation import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.operation.OperatorTable 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.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn @@ -83,7 +81,7 @@ object DefaultFunctionCallCase : FunctionCallCase() { 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) } @@ -103,7 +101,7 @@ object DefaultFunctionCallCase : FunctionCallCase() { return JsInvocation(JsNameRef(functionName, extensionReceiver), argumentsInfo.translateArguments) } - val functionRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(it) } + val functionRef = ReferenceTranslator.translateAsFQReference(callableDescriptor, context) val referenceToCall = if (callableDescriptor.visibility == Visibilities.LOCAL) { @@ -214,9 +212,7 @@ object ConstructorCallCase : FunctionCallCase() { private inline fun FunctionCallInfo.doTranslate( getArguments: CallArgumentTranslator.ArgumentsInfo.() -> List ): JsExpression { - val fqName = context.getQualifiedReference(callableDescriptor) - val functionRef = if (isNative()) fqName else context.aliasOrValue(callableDescriptor) { fqName } - + val functionRef = ReferenceTranslator.translateAsFQReference(callableDescriptor, context) val invocationArguments = mutableListOf() val constructorDescriptor = callableDescriptor as ClassConstructorDescriptor @@ -245,7 +241,7 @@ object SuperCallCase : FunctionCallCase() { override fun FunctionCallInfo.dispatchReceiver(): JsExpression { // TODO: spread operator - val prototypeClass = pureFqn(Namer.getPrototypeName(), calleeOwner) + val prototypeClass = JsAstUtils.prototypeOf(calleeOwner) val functionRef = Namer.getFunctionCallRef(JsNameRef(functionName, prototypeClass)) return JsInvocation(functionRef, argumentsInfo.argsWithReceiver(dispatchReceiver!!)) } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt index a380992f75e..e738ffdd126 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt @@ -26,13 +26,14 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.js.translate.context.Namer 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.JsDescriptorUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure +import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import java.util.* - object NativeVariableAccessCase : VariableAccessCase() { override fun VariableAccessInfo.extensionReceiver(): JsExpression { return constructAccessExpression(JsNameRef(variableName, extensionReceiver!!)) @@ -40,8 +41,8 @@ object NativeVariableAccessCase : VariableAccessCase() { override fun VariableAccessInfo.dispatchReceiver(): JsExpression { val descriptor = resolvedCall.resultingDescriptor - return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { - val methodRef = context.getNameForDescriptor(getAccessDescriptor()) + return if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) { + val methodRef = context.getNameForDescriptor(getAccessDescriptorIfNeeded()) JsInvocation(pureFqn(methodRef, dispatchReceiver!!), *additionalArguments.toTypedArray()) } else { @@ -51,8 +52,8 @@ object NativeVariableAccessCase : VariableAccessCase() { override fun VariableAccessInfo.noReceivers(): JsExpression { val descriptor = resolvedCall.resultingDescriptor - return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { - val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptor()) } + return if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) { + val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptorIfNeeded()) } JsInvocation(methodRef, *additionalArguments.toTypedArray()) } else { @@ -63,32 +64,49 @@ object NativeVariableAccessCase : VariableAccessCase() { object DefaultVariableAccessCase : VariableAccessCase() { override fun VariableAccessInfo.noReceivers(): JsExpression { - val descriptor = resolvedCall.resultingDescriptor - if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { - val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptor()) } + val variableDescriptor = this.variableDescriptor + + if (variableDescriptor is PropertyDescriptor && + !JsDescriptorUtils.isSimpleFinalProperty(variableDescriptor) && + context.isFromCurrentModule(variableDescriptor) + ) { + val methodRef = context.getInnerReference(getAccessDescriptor()) return JsInvocation(methodRef, *additionalArguments.toTypedArray()) } - val functionRef = context.aliasOrValue(callableDescriptor) { - context.getQualifiedReference(variableDescriptor) + val descriptor = resolvedCall.resultingDescriptor + if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) { + val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptorIfNeeded()) } + return JsInvocation(methodRef, *additionalArguments.toTypedArray()) } - val ref = - if (isVarCapturedInClosure(context.bindingContext(), callableDescriptor)) { - getCapturedVarAccessor(functionRef) - } else { - functionRef - } + if (descriptor is FakeCallableDescriptorForObject) { + return JsInvocation(pureFqn(context.getNameForObjectInstance (descriptor.getReferencedObject()), null)) + } + + 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 accessorDescriptor = if (isGetAccess()) localVariableDescriptor?.getter else localVariableDescriptor?.setter if (accessorDescriptor != null) { val funRef = JsNameRef(TranslationUtils.getAccessorFunctionName(accessorDescriptor), ref) - return if (isGetAccess()) { - JsInvocation(funRef) - } else { - JsInvocation(funRef, value!!) - } + return JsInvocation(funRef, *additionalArguments.toTypedArray()) } return constructAccessExpression(ref) @@ -96,8 +114,8 @@ object DefaultVariableAccessCase : VariableAccessCase() { override fun VariableAccessInfo.dispatchReceiver(): JsExpression { val descriptor = resolvedCall.resultingDescriptor - return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { - val callExpr = pureFqn(context.getNameForDescriptor(getAccessDescriptor()), dispatchReceiver!!) + return if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) { + val callExpr = pureFqn(context.getNameForDescriptor(getAccessDescriptorIfNeeded()), dispatchReceiver!!) JsInvocation(callExpr, *additionalArguments.toTypedArray()) } else { @@ -110,12 +128,12 @@ object DefaultVariableAccessCase : VariableAccessCase() { } 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()) } 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()) } } @@ -150,16 +168,16 @@ object SuperPropertyAccessCase : VariableAccessCase() { val variableName = context.program().getStringLiteral(this.variableName.ident) val descriptor = resolvedCall.resultingDescriptor - return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { - val accessor = getAccessDescriptor() - val prototype = pureFqn(Namer.getPrototypeName(), context.getQualifiedReference(descriptor.containingDeclaration)) + return if (descriptor is PropertyDescriptor && TranslationUtils.shouldAccessViaFunctions(descriptor)) { + val accessor = getAccessDescriptorIfNeeded() + val prototype = pureFqn(Namer.getPrototypeName(), context.getInnerReference(descriptor.containingDeclaration)) val funRef = Namer.getFunctionCallRef(pureFqn(context.getNameForDescriptor(accessor), prototype)) val arguments = listOf(dispatchReceiver!!) + additionalArguments JsInvocation(funRef, *arguments.toTypedArray()) } else { 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()) } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DefinitionPlace.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DefinitionPlace.kt deleted file mode 100644 index 71960c81045..00000000000 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DefinitionPlace.kt +++ /dev/null @@ -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 -) { - 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) - } -} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java index 975cde0cf0c..951c323c1fe 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.naming.SuggestedName; 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.FqNameUnsafe; import org.jetbrains.kotlin.name.Name; @@ -77,10 +78,6 @@ public final class Namer { 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_EXTENSION_FUNCTION_NAME = "getCallableRefForExtensionFunction"; 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_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 SUPER_METHOD_NAME = "baseInitializer"; - private static final String ROOT_PACKAGE = "_"; 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); 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) { if (expr instanceof JsPrefixOperation) { @@ -148,50 +148,11 @@ public final class Namer { 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 public static String getPrototypeName() { return PROTOTYPE_NAME; } - @NotNull - private static JsNameRef getRefToPrototype(@NotNull JsExpression classOrTraitExpression) { - return pureFqn(getPrototypeName(), classOrTraitExpression); - } - @NotNull public static String getDelegatePrefix() { return DELEGATE; @@ -207,11 +168,6 @@ public final class Namer { return new JsNameRef(getDelegateName(propertyName), JsLiteral.THIS); } - @NotNull - private static String getNameWithPrefix(@NotNull String name, @NotNull String prefix) { - return prefix + name; - } - @NotNull public static JsNameRef getFunctionCallRef(@NotNull JsExpression functionExpression) { return pureFqn(CALL_FUNCTION, functionExpression); @@ -224,7 +180,7 @@ public final class Namer { @NotNull 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 @@ -245,18 +201,6 @@ public final class Namer { @NotNull private final JsObjectScope kotlinScope; @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; @NotNull private final JsName callableRefForExtensionFunctionName; @@ -280,17 +224,10 @@ public final class Namer { private Namer(@NotNull JsScope rootScope) { kotlinScope = JsObjectScope(rootScope, "Kotlin standard object"); - interfaceCreationMethodName = kotlinScope.declareName(TRAIT_OBJECT_NAME); - - definePackage = kotlin("definePackage"); - defineRootPackage = kotlin("defineRootPackage"); callGetProperty = kotlin("callGetter"); 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); callableRefForExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_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); } - @NotNull - public JsExpression packageDefinitionMethodReference() { - return definePackage; - } - - @NotNull - public JsExpression rootPackageDefinitionMethodReference() { - return defineRootPackage; - } - @NotNull public JsExpression callableRefForMemberFunctionReference() { return kotlin(callableRefForMemberFunctionName); @@ -463,24 +390,6 @@ public final class Namer { 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 public static JsExpression getUndefinedExpression() { return new JsPrefixOperation(JsUnaryOperator.VOID, JsNumberLiteral.ZERO); @@ -488,12 +397,12 @@ public final class Namer { @NotNull public JsExpression getCallGetProperty() { - return callGetProperty; + return callGetProperty.deepCopy(); } @NotNull public JsExpression getCallSetProperty() { - return callSetProperty; + return callSetProperty.deepCopy(); } public static JsNameRef kotlinLong() { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StandardClasses.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StandardClasses.java index cfb02fbd035..e6c16efbf7d 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StandardClasses.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StandardClasses.java @@ -113,6 +113,7 @@ public final class StandardClasses { standardClasses.declare().forFQ("kotlin.Comparable").kotlinClass("Comparable"); standardClasses.declare().forFQ("kotlin.Throwable").kotlinClass("Throwable"); + standardClasses.declare().forFQ("kotlin.Annotation").kotlinClass("Annotation"); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 3581dedba56..b6bf6731e94 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -27,7 +27,9 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.LinkedHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.naming.NameSuggestion; 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.utils.JsAstUtils; import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt; +import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import java.util.*; @@ -63,7 +67,8 @@ public final class StaticContext { Namer namer = Namer.newInstance(program.getRootScope()); Intrinsics intrinsics = new Intrinsics(); 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); } @@ -84,10 +89,14 @@ public final class StaticContext { @NotNull private final JsScope rootScope; + @NotNull + private final Generator innerNames = new InnerNameGenerator(); @NotNull private final Map packageNames = Maps.newHashMap(); @NotNull private final Generator scopes = new ScopeGenerator(); + @NotNull + private final Generator objectInstanceNames = new ObjectInstanceNameGenerator(); @NotNull private final Map scopeToFunction = Maps.newHashMap(); @@ -124,9 +133,31 @@ public final class StaticContext { @NotNull private final JsScope rootPackageScope; + @NotNull + private JsFunction rootFunction; + + @NotNull + private final List declarationStatements = new ArrayList(); + + @NotNull + private final List importStatements = new ArrayList(); + + @NotNull + private final List exportStatements = new ArrayList(); + + @NotNull + private final Set classes = new HashSet(); + + @NotNull + private final ExportedPackage rootPackage = new ExportedPackage(""); + + @NotNull + private final JsObjectLiteral exportObject = rootPackage.objectLiteral; + //TODO: too many parameters in constructor private StaticContext( @NotNull JsProgram program, + @NotNull JsFunction rootFunction, @NotNull BindingTrace bindingTrace, @NotNull Namer namer, @NotNull Intrinsics intrinsics, @@ -136,13 +167,15 @@ public final class StaticContext { @NotNull ModuleDescriptor moduleDescriptor ) { this.program = program; + this.rootFunction = rootFunction; this.bindingTrace = bindingTrace; this.namer = namer; this.intrinsics = intrinsics; this.rootScope = rootScope; this.standardClasses = standardClasses; this.config = config; - currentModule = moduleDescriptor; + this.currentModule = moduleDescriptor; + this.rootFunction = rootFunction; rootPackageScope = new JsObjectScope(rootScope, "", "root-package"); } @@ -171,11 +204,6 @@ public final class StaticContext { return namer; } - @NotNull - private JsScope getRootScope() { - return rootScope; - } - @NotNull public Map getImportedModules() { if (readOnlyImportedModules == null) { @@ -219,6 +247,10 @@ public final class StaticContext { @NotNull private JsExpression buildQualifiedExpression(@NotNull DeclarationDescriptor descriptor) { + if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isAny((ClassDescriptor) descriptor)) { + return new JsNameRef("Object"); + } + SuggestedName suggested = nameSuggestion.suggest(descriptor); if (suggested == null) { ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor); @@ -288,6 +320,20 @@ public final class StaticContext { 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 private List getActualNameFromSuggested(@NotNull SuggestedName suggested) { JsScope scope = getScopeForDescriptor(suggested.getScope()); @@ -362,6 +408,100 @@ public final class StaticContext { return config; } + private final class InnerNameGenerator extends Generator { + public InnerNameGenerator() { + addRule(new Rule() { + @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 { + public ObjectInstanceNameGenerator() { + addRule(new Rule() { + @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 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) { JsScope scope = getEnclosingScope(descriptor); @@ -384,7 +524,7 @@ public final class StaticContext { return null; } if (getSuperclass((ClassDescriptor) descriptor) == null) { - return getRootScope().innerObjectScope("Scope for class " + descriptor.getName()); + return rootFunction.getScope(); } return null; } @@ -405,10 +545,7 @@ public final class StaticContext { Rule generateNewScopesForPackageDescriptors = new Rule() { @Override public JsScope apply(@NotNull DeclarationDescriptor descriptor) { - if (!(descriptor instanceof PackageFragmentDescriptor)) { - return null; - } - return getRootScope().innerObjectScope("Package " + descriptor.getName()); + return rootFunction.getScope(); } }; //TODO: never get there @@ -426,7 +563,7 @@ public final class StaticContext { 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; scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction); return correspondingFunction.getScope(); @@ -442,9 +579,15 @@ public final class StaticContext { @Nullable 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); if (currentModule == module) { - return pureFqn(Namer.getRootPackageName(), null); + return rootScope.declareName(Namer.getRootPackageName()); } String moduleName; if (module == module.getBuiltIns().getBuiltInsModule()) { @@ -464,7 +607,7 @@ public final class StaticContext { importedModules.put(moduleName, moduleId); } - return JsAstUtils.pureFqn(moduleId, null); + return moduleId; } private static JsExpression applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) { @@ -493,4 +636,115 @@ public final class StaticContext { public Map> getDeferredCallSites() { return deferredCallSites; } + + @NotNull + public JsFunction getRootFunction() { + return rootFunction; + } + + @NotNull + public List 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 visited = new HashSet(); + for (ClassDescriptor cls : classes) { + addClassPrototypes(cls, visited); + } + } + + private void addClassPrototypes(@NotNull ClassDescriptor cls, @NotNull Set visited) { + if (DescriptorUtilsKt.getModule(cls) != currentModule) return; + if (!visited.add(cls)) return; + + ClassDescriptor superclass = DescriptorUtilsKt.getSuperClassNotAny(cls); + if (superclass != null) { + addClassPrototypes(superclass, visited); + + List 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 subpackages = new HashMap(); + @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; + } + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index b69839d7473..c7c5427efe0 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -24,12 +24,14 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor; import org.jetbrains.kotlin.js.config.JsConfig; 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.name.FqName; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.DescriptorUtils; +import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; import java.util.*; @@ -52,17 +54,19 @@ public class TranslationContext { @Nullable private final TranslationContext parent; @Nullable - private final DefinitionPlace definitionPlace; - @Nullable private final DeclarationDescriptor declarationDescriptor; @Nullable private final ClassDescriptor classDescriptor; @NotNull - public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) { - DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), rootFunction.getBody()); + public static TranslationContext rootContext( + @NotNull StaticContext staticContext, + @NotNull JsFunction rootFunction + ) { + JsBlock block = new JsBlock(staticContext.getDeclarationStatements()); + DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), block); 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 expressionToTempConstVariableCache = new HashMap(); @@ -73,7 +77,6 @@ public class TranslationContext { @NotNull DynamicContext dynamicContext, @NotNull AliasingContext aliasingContext, @Nullable UsageTracker usageTracker, - @Nullable DefinitionPlace definitionPlace, @Nullable DeclarationDescriptor declarationDescriptor ) { this.parent = parent; @@ -81,7 +84,6 @@ public class TranslationContext { this.staticContext = staticContext; this.aliasingContext = aliasingContext; this.usageTracker = usageTracker; - this.definitionPlace = definitionPlace; this.declarationDescriptor = declarationDescriptor; if (declarationDescriptor instanceof ClassDescriptor) { this.classDescriptor = (ClassDescriptor) declarationDescriptor; @@ -119,27 +121,25 @@ public class TranslationContext { 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 public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) { DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody()); UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, fun.getScope()); - return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, - this.definitionPlace, descriptor); + return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, descriptor); } @NotNull public TranslationContext innerWithUsageTracker(@NotNull JsScope scope, @NotNull MemberDescriptor descriptor) { UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, scope); - return new TranslationContext(this, staticContext, dynamicContext, aliasingContext.inner(), usageTracker, definitionPlace, - descriptor); + return new TranslationContext(this, staticContext, dynamicContext, aliasingContext.inner(), usageTracker, descriptor); } @NotNull 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); } @@ -149,14 +149,18 @@ public class TranslationContext { } @NotNull - public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor, @Nullable DefinitionPlace place) { - DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor)); - return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place, descriptor); + public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor) { + JsBlock innerBlock = getBlockForDescriptor(descriptor); + if (innerBlock == null) { + innerBlock = dynamicContext.jsBlock(); + } + DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), innerBlock); + return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, descriptor); } @NotNull 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 @@ -174,13 +178,13 @@ public class TranslationContext { return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases)); } - @NotNull + @Nullable private JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) { if (descriptor instanceof CallableDescriptor) { return getFunctionObject((CallableDescriptor) descriptor).getBody(); } else { - return new JsBlock(); + return null; } } @@ -211,8 +215,13 @@ public class TranslationContext { } @NotNull - public JsName getNameForPackage(@NotNull FqName fqName) { - return staticContext.getNameForPackage(fqName); + public JsName getInnerNameForDescriptor(@NotNull DeclarationDescriptor descriptor) { + return staticContext.getInnerNameForDescriptor(descriptor); + } + + @NotNull + public JsName getNameForObjectInstance(@NotNull ClassDescriptor descriptor) { + return staticContext.getNameForObjectInstance(descriptor); } @NotNull @@ -225,6 +234,11 @@ public class TranslationContext { return staticContext.getQualifiedReference(descriptor); } + @NotNull + public JsNameRef getInnerReference(@NotNull DeclarationDescriptor descriptor) { + return JsAstUtils.pureFqn(getInnerNameForDescriptor(descriptor), null); + } + @NotNull public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) { return staticContext.getQualifiedReference(packageFqName); @@ -358,7 +372,9 @@ public class TranslationContext { return JsLiteral.THIS; } 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) { - if (declarationDescriptor instanceof ClassDescriptor && !DescriptorUtils.isCompanionObject(declarationDescriptor)) { + if (declarationDescriptor instanceof ClassDescriptor) { return descriptor == declarationDescriptor; } else { @@ -402,8 +418,7 @@ public class TranslationContext { return thisExpression; } - ClassDescriptor parentDescriptor = parent.classDescriptor; - if (classDescriptor != parentDescriptor) { + if (classDescriptor != parent.classDescriptor) { return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression)); } 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 private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) { if (usageTracker != null) { @@ -560,4 +561,49 @@ public class TranslationContext { public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor 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); + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/exportDeclaration.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/exportDeclaration.kt new file mode 100644 index 00000000000..92b8625decd --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/exportDeclaration.kt @@ -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): 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) { + 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) { + 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() + 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() +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt new file mode 100644 index 00000000000..96769e6dfd5 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt @@ -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() { + 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 +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index 5f86d14170f..d518498b463 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.js.translate.declaration 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.bridges.Bridge 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.general.AbstractTranslator 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.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.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.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtSecondaryConstructor import org.jetbrains.kotlin.resolve.BindingContextUtils 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.SimpleType import org.jetbrains.kotlin.types.TypeConstructor @@ -60,20 +57,13 @@ class ClassTranslator private constructor( ) : AbstractTranslator(context) { private val descriptor = getClassDescriptor(context.bindingContext(), classDeclaration) - private val invocationArguments = mutableListOf() private val secondaryConstructors = mutableListOf() - private val secondaryConstructorProperties = mutableListOf() 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() - - 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 @@ -81,35 +71,35 @@ class ClassTranslator private constructor( private fun isAnnotation(): Boolean = descriptor.kind == ClassKind.ANNOTATION_CLASS private fun generateClassCreateInvocationArguments() { - val properties = SmartList() - val staticProperties = SmartList() - - val qualifiedReference = context().getQualifiedReference(descriptor) val scope = context().getScopeForDescriptor(descriptor) - val definitionPlace = DefinitionPlace(scope as JsObjectScope, qualifiedReference, staticProperties) - val context = context().newDeclaration(descriptor, definitionPlace) + val context = context().newDeclaration(descriptor) - invocationArguments += getSuperclassReferences(context) + val constructorFunction = context.defineTopLevelFunction(descriptor) val nonConstructorContext = context.innerWithUsageTracker(scope, descriptor) nonConstructorContext.startDeclaration() val delegationTranslator = DelegationTranslator(classDeclaration, nonConstructorContext) - translatePropertiesAsConstructorParameters(nonConstructorContext, properties) - val bodyVisitor = DeclarationBodyVisitor(properties, staticProperties, scope) + translatePropertiesAsConstructorParameters(nonConstructorContext) + val bodyVisitor = DeclarationBodyVisitor(descriptor, 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) } - generatedBridgeMethods(properties) - if (descriptor.isData) { - JsDataClassGenerator(classDeclaration, context, properties).generate() + if (descriptor.kind != ClassKind.INTERFACE) { + addInterfaceDefaultMembers() } - if (isEnumClass(descriptor)) { - val enumEntries = JsObjectLiteral(bodyVisitor.enumEntryList, true) - invocationArguments += simpleReturnFunction(nonConstructorContext.getScopeForDescriptor(descriptor), enumEntries) + generatedBridgeMethods() + + if (descriptor.isData) { + JsDataClassGenerator(classDeclaration, context).generate() } emitConstructors(nonConstructorContext, nonConstructorContext.endDeclaration()) @@ -117,40 +107,63 @@ class ClassTranslator private constructor( addClosureParameters(constructor, nonConstructorContext) } - // ExpressionVisitor.visitObjectLiteralExpression uses DefinitionPlace of the translated class to generate call to - // super constructor. Sometimes, when generating super call, we may translate another anonymous class passed as an - // 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 (isObjectLike()) { + addObjectMethods() } - if (hasStaticProperties) { - invocationArguments += JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, qualifiedReference) - invocationArguments += JsObjectLiteral(staticProperties, true) - } - - this.definitionPlace = definitionPlace } - private fun translatePrimaryConstructor(classContext: TranslationContext, delegationTranslator: DelegationTranslator) { - if (isTrait()) return + private fun translatePrimaryConstructor( + 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") - val constructorContext = classContext.innerWithUsageTracker(scope, descriptor) - val initializer = ClassInitializerTranslator(classDeclaration, constructorContext).generateInitializeMethod(delegationTranslator) - invocationArguments += initializer + if (descriptor.kind == ClassKind.ENUM_CLASS) { + addEnumClassParameters(constructorFunction) + } + } + } - 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) { @@ -177,12 +190,12 @@ class ClassTranslator private constructor( } // Translate constructor body - val constructorInitializer = FunctionTranslator.newInstance(constructor, context).translateAsMethod() - val constructorFunction = constructorInitializer.valueExpr as JsFunction + val constructorInitializer = context.defineTopLevelFunction(constructorDescriptor) + FunctionTranslator.newInstance(constructor, context, constructorInitializer).translateAsMethodWithoutMetadata() // Translate super/this call val superCallGenerators = mutableListOf<(MutableList) -> Unit>() - val referenceToClass = context.getQualifiedReference(classDescriptor) + val referenceToClass = context.getInnerReference(classDescriptor) superCallGenerators += { it += FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, context) } @@ -194,11 +207,11 @@ class ClassTranslator private constructor( val leadingArgs = mutableListOf() if (outerClassName != null) { - constructorFunction.parameters.add(0, JsParameter(outerClassName)) + constructorInitializer.parameters.add(0, JsParameter(outerClassName)) leadingArgs += outerClassName.makeRef() } - constructorFunction.parameters += JsParameter(thisName) + constructorInitializer.parameters += JsParameter(thisName) // Generate super/this call to insert to beginning of the function 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 additionalStatements = mutableListOf() for (partGenerator in superCallGenerators) { partGenerator(additionalStatements) } - constructorFunction.body.statements.addAll(0, additionalStatements) + constructorInitializer.body.statements.addAll(0, additionalStatements) } - secondaryConstructors += ConstructorInfo(constructorFunction, context, constructorDescriptor, compositeSuperCallGenerator) - secondaryConstructorProperties += constructorInitializer + secondaryConstructors += ConstructorInfo(constructorInitializer, context, constructorDescriptor, compositeSuperCallGenerator) } private val allConstructors: Sequence @@ -317,14 +329,9 @@ class ClassTranslator private constructor( function.body.statements.addAll(0, additionalStatements) } - private fun getSuperclassReferences(declarationContext: TranslationContext): JsExpression { - val superClassReferences = getSupertypesNameReferences() - if (superClassReferences.isEmpty()) { - return JsLiteral.NULL - } - else { - return simpleReturnFunction(declarationContext.scope(), JsArrayLiteral(superClassReferences)) - } + private fun addSuperclassReferences() { + val supertypeReferences = JsArrayLiteral(getSupertypesNameReferences()) + metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_SUPERTYPES), supertypeReferences) } private fun getSupertypesNameReferences(): List { @@ -336,7 +343,7 @@ class ClassTranslator private constructor( if (supertypes.size == 1) { val type = supertypes[0] val supertypeDescriptor = getClassDescriptorForType(type) - return listOf(getClassReference(supertypeDescriptor)) + return listOf(context().getInnerReference(supertypeDescriptor)) } val supertypeConstructors = mutableSetOf() @@ -352,43 +359,111 @@ class ClassTranslator private constructor( for (typeConstructor in sortedAllSuperTypes) { if (supertypeConstructors.contains(typeConstructor)) { val supertypeDescriptor = getClassDescriptorForTypeConstructor(typeConstructor) - supertypesRefs += getClassReference(supertypeDescriptor) + supertypesRefs += context().getInnerReference(supertypeDescriptor) } } return supertypesRefs } - private fun getClassReference(superClassDescriptor: ClassDescriptor): JsNameRef { - return context().getQualifiedReference(superClassDescriptor) - } - - private fun translatePropertiesAsConstructorParameters(classDeclarationContext: TranslationContext, - result: MutableList) { + private fun translatePropertiesAsConstructorParameters(classDeclarationContext: TranslationContext) { for (parameter in getPrimaryConstructorParameters(classDeclaration)) { val descriptor = getPropertyDescriptorForConstructorParameter(bindingContext(), parameter) 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) { + 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) { + 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 - generateBridgesToTraitImpl(properties) + generateBridgesToTraitImpl() - generateOtherBridges(properties) + generateOtherBridges() } - private fun generateBridgesToTraitImpl(properties: MutableList) { + private fun generateBridgesToTraitImpl() { for ((key, value) in CodegenUtil.getNonPrivateTraitMethods(descriptor)) { if (!areNamesEqual(key, value)) { - properties += generateDelegateCall(value, key, JsLiteral.THIS, context()) + generateDelegateCall(descriptor, value, key, JsLiteral.THIS, context()) } } } - private fun generateOtherBridges(properties: MutableList) { + private fun generateOtherBridges() { for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) { if (memberDescriptor is FunctionDescriptor) { val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) { @@ -397,13 +472,13 @@ class ClassTranslator private constructor( } for (bridge in bridgesToGenerate) { - generateBridge(bridge, properties) + generateBridge(bridge) } } } } - private fun generateBridge(bridge: Bridge, properties: MutableList) { + private fun generateBridge(bridge: Bridge) { val fromDescriptor = bridge.from val toDescriptor = bridge.to if (areNamesEqual(fromDescriptor, toDescriptor)) return @@ -411,7 +486,7 @@ class ClassTranslator private constructor( if (fromDescriptor.kind.isReal && fromDescriptor.modality != Modality.ABSTRACT && !toDescriptor.kind.isReal) return - properties += generateDelegateCall(fromDescriptor, toDescriptor, JsLiteral.THIS, context()) + generateDelegateCall(descriptor, fromDescriptor, toDescriptor, JsLiteral.THIS, context()) } private fun areNamesEqual(first: FunctionDescriptor, second: FunctionDescriptor): Boolean { @@ -421,13 +496,11 @@ class ClassTranslator private constructor( } companion object { - @JvmStatic fun translate(classDeclaration: KtClassOrObject, context: TranslationContext): TranslationResult { + @JvmStatic fun translate(classDeclaration: KtClassOrObject, context: TranslationContext) { return ClassTranslator(classDeclaration, context).translate() } } - class TranslationResult(val properties: List, val definitionPlace: DefinitionPlace) - private class ConstructorInfo( val function: JsFunction, val context: TranslationContext, diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.java deleted file mode 100644 index a79a00e62c7..00000000000 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.java +++ /dev/null @@ -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 { - protected final List result; - private final List staticResult; - private final List enumEntryList = new SmartList(); - - @NotNull - private final JsScope scope; - - @Nullable - private List initializerStatements; - - public DeclarationBodyVisitor( - @NotNull List result, @NotNull List staticResult, - @NotNull JsScope scope - ) { - this.result = result; - this.staticResult = staticResult; - this.scope = scope; - } - - @NotNull - public List getResult() { - return result; - } - - public List 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 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(); - 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; - } -} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt new file mode 100644 index 00000000000..fcaee0d6450 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt @@ -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() + + 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()) + } +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt index 6271dca51c5..c0214a59179 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.js.translate.declaration - import com.google.dart.compiler.backend.js.ast.* import org.jetbrains.kotlin.backend.common.CodegenUtil 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.general.AbstractTranslator 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.* 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.generateDelegateCall import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry import org.jetbrains.kotlin.psi.KtSuperTypeListEntry import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty class DelegationTranslator( classDeclaration: KtClassOrObject, @@ -53,7 +51,6 @@ class DelegationTranslator( for (specifier in delegationBySpecifiers) { val expression = specifier.delegateExpression ?: throw IllegalArgumentException("delegate expression should not be null: ${specifier.text}") - val descriptor = getSuperClass(specifier) val propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, classDescriptor, bindingContext()) if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext())) { @@ -84,22 +81,22 @@ class DelegationTranslator( } } - fun generateDelegated(properties: MutableList) { + fun generateDelegated() { for (specifier in delegationBySpecifiers) { - generateDelegates(getSuperClass(specifier), fields[specifier]!!, properties) + generateDelegates(getSuperClass(specifier), fields[specifier]!!) } } private fun getSuperClass(specifier: KtSuperTypeListEntry): ClassDescriptor = CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext()) - private fun generateDelegates(toClass: ClassDescriptor, field: Field, properties: MutableList) { + private fun generateDelegates(toClass: ClassDescriptor, field: Field) { for ((descriptor, overriddenDescriptor) in CodegenUtil.getDelegates(classDescriptor, toClass)) { when (descriptor) { is PropertyDescriptor -> - generateDelegateCallForPropertyMember(descriptor, field.name, properties) + generateDelegateCallForPropertyMember(descriptor, field.name) is FunctionDescriptor -> - generateDelegateCallForFunctionMember(descriptor, overriddenDescriptor as FunctionDescriptor, field.name, properties) + generateDelegateCallForFunctionMember(descriptor, overriddenDescriptor as FunctionDescriptor, field.name) else -> throw IllegalArgumentException("Expected property or function $descriptor") } @@ -108,8 +105,7 @@ class DelegationTranslator( private fun generateDelegateCallForPropertyMember( descriptor: PropertyDescriptor, - delegateName: JsName, - properties: MutableList + delegateName: JsName ) { val propertyName: String = descriptor.name.asString() @@ -174,17 +170,29 @@ class DelegationTranslator( 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( descriptor: FunctionDescriptor, overriddenDescriptor: FunctionDescriptor, - delegateName: JsName, - properties: MutableList + delegateName: JsName ) { val delegateRef = JsNameRef(delegateName, JsLiteral.THIS) - properties.add(generateDelegateCall(descriptor, overriddenDescriptor, delegateRef, context())) + generateDelegateCall(classDescriptor, descriptor, overriddenDescriptor, delegateRef, context()) } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt deleted file mode 100644 index f65a8b51458..00000000000 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclaration.kt +++ /dev/null @@ -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 = 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 - } -} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclarationVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclarationVisitor.kt new file mode 100644 index 00000000000..33a80152be8 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclarationVisitor.kt @@ -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 + } +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java index ebb0d8aa028..f969d0ade99 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.TranslationContext; 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.KtParameter; import org.jetbrains.kotlin.resolve.BindingContext; @@ -37,12 +38,10 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.or; class JsDataClassGenerator extends DataClassMethodGenerator { private final TranslationContext context; - private final List output; - JsDataClassGenerator(KtClassOrObject klass, TranslationContext context, List output) { + JsDataClassGenerator(KtClassOrObject klass, TranslationContext context) { super(klass, context.bindingContext()); this.context = context; - this.output = output; } @Override @@ -94,7 +93,7 @@ class JsDataClassGenerator extends DataClassMethodGenerator { ClassDescriptor classDescriptor = (ClassDescriptor) function.getContainingDeclaration(); ClassConstructorDescriptor constructor = classDescriptor.getConstructors().iterator().next(); - JsExpression constructorRef = context.getQualifiedReference(constructor); + JsExpression constructorRef = context.getInnerReference(constructor); JsExpression returnExpression = new JsNew(constructorRef, constructorArguments); if (context.shouldBeDeferred(constructor)) { @@ -190,11 +189,9 @@ class JsDataClassGenerator extends DataClassMethodGenerator { } private JsFunction generateJsMethod(@NotNull FunctionDescriptor functionDescriptor) { - JsName functionName = context.getNameForDescriptor(functionDescriptor); - JsScope enclosingScope = context.scope(); - JsFunction functionObject = JsAstUtils.createFunctionWithEmptyBody(enclosingScope); - JsPropertyInitializer initializer = new JsPropertyInitializer(functionName.makeRef(), functionObject); - output.add(initializer); + JsFunction functionObject = context.createTopLevelAnonymousFunction(functionDescriptor); + ClassDescriptor containingClass = (ClassDescriptor) functionDescriptor.getContainingDeclaration(); + UtilsKt.addFunctionToPrototype(context, containingClass, functionDescriptor, functionObject); return functionObject; } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java index 18babaafe12..64fc843a453 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java @@ -16,30 +16,22 @@ 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.kotlin.descriptors.PackageFragmentDescriptor; 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.general.AbstractTranslator; -import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContextUtils; -import java.util.*; - -import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar; +import java.util.Collection; public final class PackageDeclarationTranslator extends AbstractTranslator { private final Iterable files; - private final Map packageFragmentToTranslator = - new LinkedHashMap(); - public static List translateFiles(@NotNull Collection files, @NotNull TranslationContext context) { - return new PackageDeclarationTranslator(files, context).translate(); + public static void translateFiles(@NotNull Collection files, @NotNull TranslationContext context) { + new PackageDeclarationTranslator(files, context).translate(); } private PackageDeclarationTranslator(@NotNull Iterable files, @NotNull TranslationContext context) { @@ -48,21 +40,12 @@ public final class PackageDeclarationTranslator extends AbstractTranslator { this.files = files; } - @NotNull - private List translate() { - // predictable order - Map packageFqNameToDefineInvocation = new THashMap(); - + private void translate() { for (KtFile file : files) { PackageFragmentDescriptor packageFragment = BindingContextUtils.getNotNull(context().bindingContext(), BindingContext.FILE_TO_PACKAGE_FRAGMENT, file); - PackageTranslator translator = packageFragmentToTranslator.get(packageFragment); - if (translator == null) { - createRootPackageDefineInvocationIfNeeded(packageFqNameToDefineInvocation); - translator = PackageTranslator.create(packageFragment, context()); - packageFragmentToTranslator.put(packageFragment, translator); - } + PackageTranslator translator = PackageTranslator.create(packageFragment, context()); try { translator.translate(file); @@ -77,26 +60,5 @@ public final class PackageDeclarationTranslator extends AbstractTranslator { 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.singletonList(vars); - } - - private void createRootPackageDefineInvocationIfNeeded(@NotNull Map 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); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageTranslator.java index 98a2988d6ea..5d15c43519e 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageTranslator.java @@ -16,51 +16,32 @@ 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.PackageFragmentDescriptor; -import org.jetbrains.kotlin.js.translate.context.DefinitionPlace; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; 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.KtFile; -import java.util.List; -import java.util.Map; - final class PackageTranslator extends AbstractTranslator { static PackageTranslator create( @NotNull PackageFragmentDescriptor descriptor, @NotNull TranslationContext context ) { - JsScope scope = context.getScopeForDescriptor(descriptor); - JsNameRef reference = context.getQualifiedReference(descriptor); - SmartList properties = new SmartList(); - - 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); + TranslationContext newContext = context.newDeclaration(descriptor); + FileDeclarationVisitor visitor = new FileDeclarationVisitor(newContext); + return new PackageTranslator(newContext, visitor); } - @NotNull - private final PackageFragmentDescriptor descriptor; - private final FileDeclarationVisitor visitor; private PackageTranslator( - @NotNull PackageFragmentDescriptor descriptor, @NotNull TranslationContext context, @NotNull FileDeclarationVisitor visitor ) { super(context); - this.descriptor = descriptor; this.visitor = visitor; } @@ -71,69 +52,4 @@ final class PackageTranslator extends AbstractTranslator { } } } - - private void createDefinitionPlace( - @Nullable JsExpression initializer, - Map 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 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 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 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 packageFqNameToDefineInvocation) { - while (!addEntryIfParentExists(parentFqName, entry, packageFqNameToDefineInvocation)) { - JsObjectLiteral members = new JsObjectLiteral(new SmartList(entry), true); - DefineInvocation defineInvocation = DefineInvocation.create(parentFqName, null, members, context()); - entry = getEntry(parentFqName, defineInvocation); - - packageFqNameToDefineInvocation.put(parentFqName, defineInvocation); - parentFqName = parentFqName.parent(); - } - } - } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt index d38bb7acaa4..ae12a9de3c1 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.js.translate.declaration import com.google.dart.compiler.backend.js.ast.* -import com.intellij.util.SmartList import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor 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.general.AbstractTranslator 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.JsDescriptorUtils -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.TranslationUtils.* import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addParameter import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addStatement import org.jetbrains.kotlin.psi.KtProperty @@ -68,87 +65,40 @@ fun translateAccessors( fun MutableList.addGetterAndSetter( descriptor: VariableDescriptorWithAccessors, - context: TranslationContext, generateGetter: () -> JsPropertyInitializer, generateSetter: () -> JsPropertyInitializer ) { - val to: MutableList - if (!descriptor.isExtension && !TranslationUtils.shouldGenerateAccessors(descriptor)) { - to = SmartList() - this.add(JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), JsObjectLiteral(to, true))) - } - else { - to = this - } - - to.add(generateGetter()) + add(generateGetter()) if (descriptor.isVar) { - to.add(generateSetter()) + add(generateSetter()) } } -private class PropertyTranslator( +class DefaultPropertyTranslator( val descriptor: VariableDescriptorWithAccessors, - val declaration: KtProperty?, - context: TranslationContext + context: TranslationContext, + val delegateReference: JsExpression ) : AbstractTranslator(context) { - - private val propertyName: String = descriptor.name.asString() - - fun translate(result: MutableList) { - 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) + fun generateDefaultGetterFunction(getterDescriptor: VariableAccessorDescriptor, function: JsFunction) { + val delegatedCall = bindingContext()[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor] if (delegatedCall != null) { - return generateDelegatedGetterFunction(getterDescriptor, delegatedCall) + return generateDelegatedGetterFunction(getterDescriptor, delegatedCall, function) } assert(!descriptor.isExtension) { "Unexpected extension property $descriptor}" } assert(descriptor is PropertyDescriptor) { "Property descriptor expected: $descriptor" } - val scope = context().getScopeForDescriptor(getterDescriptor.containingDeclaration) val result = backingFieldReference(context(), descriptor as PropertyDescriptor) - val body = JsBlock(JsReturn(result)) - - return JsFunction(scope, body, accessorDescription(getterDescriptor)) + function.body.statements += JsReturn(result) } private fun generateDelegatedGetterFunction( - getterDescriptor: VariableAccessorDescriptor, - delegatedCall: ResolvedCall - ): JsFunction { - val scope = context().getScopeForDescriptor(getterDescriptor.containingDeclaration) - val function = JsFunction(scope, JsBlock(), accessorDescription(getterDescriptor)) - - val delegateRef = getDelegateNameRef(propertyName) - val delegatedJsCall = CallTranslator.translate( - contextWithPropertyMetadataCreationIntrinsified(context(), delegatedCall, getterDescriptor), delegatedCall, delegateRef - ) + getterDescriptor: VariableAccessorDescriptor, + delegatedCall: ResolvedCall, + function: JsFunction + ) { + val delegateContext = contextWithPropertyMetadataCreationIntrinsified(context(), delegatedCall, getterDescriptor) + val delegatedJsCall = CallTranslator.translate(delegateContext, delegatedCall, delegateReference) if (getterDescriptor.isExtension) { val receiver = function.addParameter(getReceiverParameterName()).name @@ -158,40 +108,18 @@ private class PropertyTranslator( val returnResult = JsReturn(delegatedJsCall) function.addStatement(returnResult) - return function } - private fun contextWithPropertyMetadataCreationIntrinsified( - context: TranslationContext, delegatedCall: ResolvedCall, 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)) - + fun generateDefaultSetterFunction(setterDescriptor: VariableAccessorDescriptor, function: JsFunction) { assert(setterDescriptor.valueParameters.size == 1) { "Setter must have 1 parameter" } val correspondingPropertyName = setterDescriptor.correspondingVariable.name.asString() val valueParameter = function.addParameter(correspondingPropertyName).name 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) { - val delegatedJsCall = CallTranslator.translate( - contextWithPropertyMetadataCreationIntrinsified(withAliased, delegatedCall, setterDescriptor), - delegatedCall, getDelegateNameRef(correspondingPropertyName) - ) + val delegateContext = contextWithPropertyMetadataCreationIntrinsified(withAliased, delegatedCall, setterDescriptor) + val delegatedJsCall = CallTranslator.translate(delegateContext, delegatedCall, delegateReference) function.addStatement(delegatedJsCall.makeStmt()) if (setterDescriptor.isExtension) { @@ -205,15 +133,79 @@ private class PropertyTranslator( val assignment = assignmentToBackingField(withAliased, descriptor as PropertyDescriptor, valueParameter.makeRef()) function.addStatement(assignment.makeStmt()) } + } - return function + private fun contextWithPropertyMetadataCreationIntrinsified( + context: TranslationContext, delegatedCall: ResolvedCall, 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) { + 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 = translateFunctionAsEcma5PropertyDescriptor(function, accessorDescriptor, context()) - private fun translateCustomAccessor(expression: KtPropertyAccessor): JsPropertyInitializer = - Translation.functionTranslator(expression, context()).translateAsEcma5PropertyDescriptor() + private fun translateCustomAccessor(expression: KtPropertyAccessor): JsPropertyInitializer { + 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 { val accessorType = diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index b417706f78e..798c1def743 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -201,13 +201,12 @@ public final class ExpressionVisitor extends TranslatorVisitor { if (delegateExpression != null) { SmartList propertyInitializers = new SmartList(); PropertyTranslatorKt.translateAccessors((VariableDescriptorWithAccessors) descriptor, propertyInitializers, context); - assert propertyInitializers.size() == 1 : descriptor; - initializer = propertyInitializers.get(0).getValueExpr(); JsPropertyInitializer delegateInitializer = new JsPropertyInitializer( context.program().getStringLiteral(Namer.getDelegateName(descriptor.getName().asString())), Translation.translateAsExpression(delegateExpression, context) ); - ((JsObjectLiteral) initializer).getPropertyInitializers().add(delegateInitializer); + propertyInitializers.add(delegateInitializer); + initializer = new JsObjectLiteral(propertyInitializers, true); } JsName name = context.getNameForDescriptor(descriptor); @@ -519,11 +518,9 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull public JsNode visitObjectLiteralExpression(@NotNull KtObjectLiteralExpression expression, @NotNull TranslationContext context) { ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), expression.getObjectDeclaration()); - ClassTranslator.TranslationResult result = translateClassOrObject(expression.getObjectDeclaration(), descriptor, context); - List properties = result.getProperties(); - context.getDefinitionPlace().getProperties().addAll(properties); + translateClassOrObject(expression.getObjectDeclaration(), descriptor, context); - JsExpression constructor = context.getQualifiedReference(descriptor); + JsExpression constructor = context.getInnerReference(descriptor); List closure = context.getClassOrConstructorClosure(descriptor); List closureArgs = new ArrayList(); if (closure != null) { @@ -551,7 +548,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { if (superCall != null) { assert context.getDeclarationDescriptor() != null : "This expression should be inside declaration: " + 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()); } @@ -583,7 +580,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { @Override public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) { ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass); - context.getDefinitionPlace().getProperties().addAll(translateClassOrObject(klass, descriptor, context).getProperties()); + translateClassOrObject(klass, descriptor, context); return JsEmpty.INSTANCE; } @@ -593,13 +590,13 @@ public final class ExpressionVisitor extends TranslatorVisitor { return JsEmpty.INSTANCE; } - private static ClassTranslator.TranslationResult translateClassOrObject( + private static void translateClassOrObject( @NotNull KtClassOrObject declaration, @NotNull ClassDescriptor descriptor, @NotNull TranslationContext context ) { JsScope scope = context.getScopeForDescriptor(descriptor); TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor); - return ClassTranslator.translate(declaration, classContext); + ClassTranslator.translate(declaration, classContext); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java index 4aee7e42f91..97cb28dbde7 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java @@ -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.TranslationContext; 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.psi.KtDeclarationWithBody; 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 { @NotNull - public static FunctionTranslator newInstance(@NotNull KtDeclarationWithBody function, - @NotNull TranslationContext context) { - return new FunctionTranslator(function, context); + public static FunctionTranslator newInstance( + @NotNull KtDeclarationWithBody declaration, + @NotNull TranslationContext context, + @NotNull JsFunction function + ) { + return new FunctionTranslator(declaration, context, function); } @NotNull @@ -60,11 +64,12 @@ public final class FunctionTranslator extends AbstractTranslator { @NotNull 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); this.descriptor = getFunctionDescriptor(context.bindingContext(), functionDeclaration); this.functionDeclaration = functionDeclaration; - this.functionObject = context().getFunctionObject(descriptor); + this.functionObject = function; assert this.functionObject.getParameters().isEmpty() : message(descriptor, "Function " + functionDeclaration.getText() + " processed for the second time."); //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()); } - @NotNull - public JsPropertyInitializer translateAsMethod() { - JsName functionName = context().getNameForDescriptor(descriptor); + public void translateAsMethodWithoutMetadata() { generateFunctionObject(); + } + + @NotNull + public JsExpression translateAsMethod() { + translateAsMethodWithoutMetadata(); if (shouldBeInlined(descriptor) && DescriptorUtilsKt.isEffectivelyPublicApi(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() { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt index 10ad72f7f73..ddb45dff492 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt @@ -21,6 +21,7 @@ import com.google.dart.compiler.backend.js.ast.metadata.* import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl 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.getNameForCapturedDescriptor import org.jetbrains.kotlin.js.translate.context.hasCapturedExceptContaining @@ -58,18 +59,27 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato val isRecursive = tracker.isCaptured(descriptor) - if (isRecursive) { - lambda.name = tracker.getNameForCapturedDescriptor(descriptor) + lambda.name = if (isRecursive) { + tracker.getNameForCapturedDescriptor(descriptor) + } + else { + context().getInnerNameForDescriptor(descriptor) } if (tracker.hasCapturedExceptContaining()) { val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda) + lambdaCreator.name = context().getInnerNameForDescriptor(descriptor) lambdaCreator.isLocal = true - return lambdaCreator.withCapturedParameters(functionContext, invokingContext, descriptor) + if (!isRecursive) { + lambda.name = null + } + return lambdaCreator.withCapturedParameters(functionContext, invokingContext) } 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 { @@ -82,8 +92,12 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato } } -fun JsFunction.withCapturedParameters(context: TranslationContext, invokingContext: TranslationContext, descriptor: MemberDescriptor): JsExpression { - val ref = invokingContext.define(descriptor, this).apply { sideEffects = SideEffectKind.PURE } +fun JsFunction.withCapturedParameters( + 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 invocationArguments = invocation.arguments diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java index 14044cbc9be..5848a8d682f 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java @@ -169,7 +169,7 @@ public final class PatternTranslator extends AbstractTranslator { 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); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java index 391a1db5139..dbe65b8c70d 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java @@ -48,7 +48,6 @@ import org.jetbrains.kotlin.psi.KtDeclarationWithBody; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtNamedFunction; -import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt; 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.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.toStringLiteralList; import static org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression; @@ -81,9 +79,12 @@ public final class Translation { } @NotNull - public static FunctionTranslator functionTranslator(@NotNull KtDeclarationWithBody function, - @NotNull TranslationContext context) { - return FunctionTranslator.newInstance(function, context); + public static FunctionTranslator functionTranslator( + @NotNull KtDeclarationWithBody declaration, + @NotNull TranslationContext context, + @NotNull JsFunction function + ) { + return FunctionTranslator.newInstance(declaration, context, function); } @NotNull @@ -252,17 +253,16 @@ public final class Translation { ) { StaticContext staticContext = StaticContext.generateStaticContext(bindingTrace, config, moduleDescriptor); JsProgram program = staticContext.getProgram(); - - // Prohibit using "_" as a variable name program.getRootScope().declareName("_"); - JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope()); + JsFunction rootFunction = staticContext.getRootFunction(); JsBlock rootBlock = rootFunction.getBody(); List statements = rootBlock.getStatements(); - statements.add(program.getStringLiteral("use strict").makeStmt()); 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()); mayBeGenerateTests(files, config, rootBlock, context); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java index a1cfe71016b..f126b960b72 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java @@ -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.reference.CallArgumentTranslator; 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.AstUtilsKt; 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.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument; +import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.types.KotlinType; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -56,16 +59,23 @@ public final class ClassInitializerTranslator extends AbstractTranslator { private final KtClassOrObject classDeclaration; @NotNull private final JsFunction initFunction; + @NotNull private final TranslationContext context; + @NotNull + private final ClassDescriptor classDescriptor; public ClassInitializerTranslator( @NotNull KtClassOrObject classDeclaration, - @NotNull TranslationContext context + @NotNull TranslationContext context, + @NotNull JsFunction initFunction ) { super(context); this.classDeclaration = classDeclaration; - this.initFunction = createInitFunction(classDeclaration, context); + this.initFunction = initFunction; this.context = context.contextWithScope(initFunction); + classDescriptor = BindingUtils.getClassDescriptor(bindingContext(), classDeclaration); + + fillInitFunction(classDeclaration, context); } @NotNull @@ -74,8 +84,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator { return context; } - @NotNull - private static JsFunction createInitFunction(KtClassOrObject declaration, TranslationContext context) { + private static void fillInitFunction(KtClassOrObject declaration, TranslationContext context) { //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); ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); @@ -104,20 +113,16 @@ public final class ClassInitializerTranslator extends AbstractTranslator { } ctorFunction.setName(ctorFunction.getScope().declareName(functionName)); } - - return ctorFunction; } - @NotNull - public JsFunction generateInitializeMethod(DelegationTranslator delegationTranslator) { - ClassDescriptor classDescriptor = getClassDescriptor(bindingContext(), classDeclaration); + public void generateInitializeMethod(DelegationTranslator delegationTranslator) { addOuterClassReference(classDescriptor); ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); if (primaryConstructor != null) { initFunction.getBody().getStatements().addAll(setDefaultValueForArguments(primaryConstructor, context())); - mayBeAddCallToSuperMethod(initFunction, classDescriptor); + mayBeAddCallToSuperMethod(initFunction); //NOTE: while we translate constructor parameters we also add property initializer statements // for properties declared as constructor parameters @@ -126,8 +131,6 @@ public final class ClassInitializerTranslator extends AbstractTranslator { delegationTranslator.addInitCode(initFunction.getBody().getStatements()); new InitializerVisitor().traverseContainer(classDeclaration, context().innerBlock(initFunction.getBody())); - - return initFunction; } private void addOuterClassReference(ClassDescriptor classDescriptor) { @@ -142,26 +145,33 @@ public final class ClassInitializerTranslator extends AbstractTranslator { } @NotNull - public JsExpression generateEnumEntryInstanceCreation(@NotNull KotlinType enumClassType) { - ResolvedCall superCall = getSuperCall(bindingContext(), classDeclaration); + public static JsExpression generateEnumEntryInstanceCreation( + @NotNull TranslationContext context, + @NotNull KotlinType enumClassType, + @NotNull KtClassOrObject classDeclaration, + int ordinal + ) { + ResolvedCall superCall = getSuperCall(context.bindingContext(), classDeclaration); if (superCall == null) { ClassDescriptor classDescriptor = getClassDescriptorForType(enumClassType); - JsNameRef reference = context().getQualifiedReference(classDescriptor); - return new JsNew(reference); + JsNameRef reference = context.getInnerReference(classDescriptor); + 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)) { addCallToSuperMethod(Collections.emptyList(), initializer); } else if (hasAncestorClass(bindingContext(), classDeclaration)) { ResolvedCall superCall = getSuperCall(bindingContext(), classDeclaration); if (superCall == null) { - if (DescriptorUtils.isEnumEntry(descriptor)) { + if (DescriptorUtils.isEnumEntry(classDescriptor)) { addCallToSuperMethod(Collections.emptyList(), initializer); } return; @@ -182,18 +192,18 @@ public final class ClassInitializerTranslator extends AbstractTranslator { if (superclassClosure != null) { UsageTracker tracker = context.usageTracker(); assert tracker != null : "Closure exists, therefore UsageTracker must exist too. Translating constructor of " + - descriptor; + classDescriptor; for (DeclarationDescriptor capturedValue : superclassClosure) { tracker.used(capturedValue); 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)); } - if (!DescriptorUtils.isAnonymousObject(descriptor)) { + if (!DescriptorUtils.isAnonymousObject(classDescriptor)) { arguments.addAll(CallArgumentTranslator.translate(superCall, null, context()).getTranslateArguments()); } else { @@ -225,20 +235,22 @@ public final class ClassInitializerTranslator extends AbstractTranslator { } } - private void addCallToSuperMethod(@NotNull List arguments, JsFunction initializer) { + private void addCallToSuperMethod(@NotNull List arguments, @NotNull JsFunction initializer) { if (initializer.getName() == null) { JsName ref = context().scope().declareName(Namer.CALLEE_NAME); 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().addAll(arguments); initFunction.getBody().getStatements().add(call.makeStmt()); } private void addCallToSuperSecondaryConstructor(@NotNull List arguments, @NotNull ConstructorDescriptor descriptor) { - JsExpression reference = context.getQualifiedReference(descriptor); + JsExpression reference = context.getInnerReference(descriptor); JsInvocation call = new JsInvocation(reference); call.getArguments().addAll(arguments); call.getArguments().add(JsLiteral.THIS); @@ -257,8 +269,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator { @NotNull private JsParameter translateParameter(@NotNull KtParameter jetParameter) { - DeclarationDescriptor parameterDescriptor = - getDescriptorForElement(bindingContext(), jetParameter); + DeclarationDescriptor parameterDescriptor = getDescriptorForElement(bindingContext(), jetParameter); JsName parameterName = context().getNameForDescriptor(parameterDescriptor); JsParameter jsParameter = new JsParameter(parameterName); mayBeAddInitializerStatementForProperty(jsParameter, jetParameter); @@ -267,8 +278,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator { private void mayBeAddInitializerStatementForProperty(@NotNull JsParameter jsParameter, @NotNull KtParameter jetParameter) { - PropertyDescriptor propertyDescriptor = - getPropertyDescriptorForConstructorParameter(bindingContext(), jetParameter); + PropertyDescriptor propertyDescriptor = getPropertyDescriptorForConstructorParameter(bindingContext(), jetParameter); if (propertyDescriptor == null) { return; } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt index 2b2ea7ca2d8..1d15738da05 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt @@ -108,20 +108,20 @@ object CallableReferenceTranslator { } 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 jsPropertyNameAsString = context.program().getStringLiteral(propertyName.asString()) val argumentList = ArrayList(3) argumentList.add(jsPropertyNameAsString) argumentList.add(jsGetterNameRef) if (descriptor.isVar) { - val jsSetterNameRef = context.getQualifiedReference(descriptor.setter!!) + val jsSetterNameRef = context.getInnerReference(descriptor.setter!!) argumentList.add(jsSetterNameRef) } - if (AnnotationsUtils.isNativeObject(descriptor)) - return translateForMemberProperty(descriptor, context) + return if (AnnotationsUtils.isNativeObject(descriptor)) + translateForMemberProperty(descriptor, context) else - return JsInvocation(context.namer().callableRefForExtensionPropertyReference(), argumentList) + JsInvocation(context.namer().callableRefForExtensionPropertyReference(), argumentList) } private fun translateForConstructor(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression { @@ -130,8 +130,8 @@ object CallableReferenceTranslator { } private fun translateForExtensionFunction(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression { - val receiverParameterDescriptor = descriptor.extensionReceiverParameter - assert(receiverParameterDescriptor != null) { "receiverParameter for extension should not be null" } + val receiverParameterDescriptor = descriptor.extensionReceiverParameter ?: + error("receiverParameter for extension should not be null") val jsFunctionRef = ReferenceTranslator.translateAsFQReference(descriptor, context) if (descriptor.visibility == Visibilities.LOCAL) { @@ -139,7 +139,7 @@ object CallableReferenceTranslator { } else if (AnnotationsUtils.isNativeObject(descriptor)) { - val jetType = receiverParameterDescriptor!!.type + val jetType = receiverParameterDescriptor.type val receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(jetType) return translateAsMemberFunctionReference(descriptor, receiverClassDescriptor, context) } @@ -159,7 +159,12 @@ object CallableReferenceTranslator { classDescriptor: ClassDescriptor, context: TranslationContext ): 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 funNameAsString = context.program().getStringLiteral(funName.toString()) return JsInvocation(context.namer().callableRefForMemberFunctionReference(), jsClassNameRef, funNameAsString) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java index fb2208226c1..630ee0bde4d 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java @@ -17,15 +17,16 @@ package org.jetbrains.kotlin.js.translate.reference; 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.kotlin.descriptors.DeclarationDescriptor; -import org.jetbrains.kotlin.descriptors.FunctionDescriptor; -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; -import org.jetbrains.kotlin.descriptors.VariableDescriptor; +import org.jetbrains.kotlin.descriptors.*; 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.KtQualifiedExpression; 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.PsiUtils.getSelectorAsSimpleName; @@ -45,7 +46,20 @@ public final class ReferenceTranslator { public static JsExpression translateAsFQReference(@NotNull DeclarationDescriptor referencedDescriptor, @NotNull TranslationContext context) { 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 @@ -57,7 +71,22 @@ public final class ReferenceTranslator { 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 diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java index f00dd8c4df4..4b1b84f3971 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java @@ -339,6 +339,11 @@ public final class JsAstUtils { 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) { JsExpressionStatement statement = new JsExpressionStatement(expression); MetadataProperties.setSynthetic(statement, true); @@ -460,11 +465,12 @@ public final class JsAstUtils { @NotNull public static JsInvocation defineProperty( + @NotNull JsExpression receiver, @NotNull String name, - @NotNull JsObjectLiteral value, - @NotNull TranslationContext context + @NotNull JsExpression value, + @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 @@ -544,4 +550,21 @@ public final class JsAstUtils { JsUnaryOperation unary = (JsUnaryOperation) expression; 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); + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsDescriptorUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsDescriptorUtils.java index 90885e80cd4..3cd515d4a34 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsDescriptorUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsDescriptorUtils.java @@ -150,7 +150,7 @@ public final class JsDescriptorUtils { return !isExtension(propertyDescriptor) && isDefaultAccessor(propertyDescriptor.getGetter()) && isDefaultAccessor(propertyDescriptor.getSetter()) && - !TranslationUtils.shouldGenerateAccessors(propertyDescriptor) && + !TranslationUtils.shouldAccessViaFunctions(propertyDescriptor) && !ModalityKt.isOverridableOrOverrides(propertyDescriptor); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index 1569b243b0c..c7b41f8116b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -54,7 +54,7 @@ public final class TranslationUtils { @NotNull TranslationContext context) { if (DescriptorUtils.isExtension(descriptor) || descriptor instanceof PropertyAccessorDescriptor && - shouldGenerateAccessors(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()) + shouldAccessViaFunctions(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()) ) { return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context); } @@ -146,19 +146,20 @@ public final class TranslationUtils { } @NotNull - public static JsNameRef backingFieldReference(@NotNull TranslationContext context, - @NotNull PropertyDescriptor descriptor) { - JsName backingFieldName = context.getNameForDescriptor(descriptor); - if(!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) { + public static JsNameRef backingFieldReference(@NotNull TranslationContext context, @NotNull PropertyDescriptor descriptor) { + DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration(); + JsName backingFieldName = containingDescriptor instanceof PackageFragmentDescriptor ? + context.getInnerNameForDescriptor(descriptor) : + context.getNameForDescriptor(descriptor); + + if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor) && !(containingDescriptor instanceof PackageFragmentDescriptor)) { JsName backingFieldMangledName = context.getNameForBackingField(descriptor); backingFieldName = context.declarePropertyOrPropertyAccessorName(descriptor, backingFieldMangledName.getIdent(), false); } - DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration(); JsExpression receiver; if (containingDescriptor instanceof PackageFragmentDescriptor) { - // used inside package initializer - receiver = JsLiteral.THIS; + receiver = null; } else { receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor)); @@ -296,22 +297,22 @@ public final class TranslationUtils { 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) { - return shouldGenerateAccessors((PropertyDescriptor) descriptor); + return shouldAccessViaFunctions((PropertyDescriptor) descriptor); } else if (descriptor instanceof PropertyAccessorDescriptor) { - return shouldGenerateAccessors(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()); + return shouldAccessViaFunctions(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()); } else { return false; } } - private static boolean shouldGenerateAccessors(@NotNull PropertyDescriptor property) { + private static boolean shouldAccessViaFunctions(@NotNull PropertyDescriptor property) { if (AnnotationsUtils.hasJsNameInAccessors(property)) return true; for (PropertyDescriptor overriddenProperty : property.getOverriddenDescriptors()) { - if (shouldGenerateAccessors(overriddenProperty)) return true; + if (shouldAccessViaFunctions(overriddenProperty)) return true; } return false; } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt index 106aa456423..ff354ea8aa2 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt @@ -18,10 +18,7 @@ package org.jetbrains.kotlin.js.translate.utils import com.google.dart.compiler.backend.js.ast.* import com.intellij.util.SmartList -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext 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 fun generateDelegateCall( + classDescriptor: ClassDescriptor, fromDescriptor: FunctionDescriptor, toDescriptor: FunctionDescriptor, thisObject: JsExpression, context: TranslationContext -): JsPropertyInitializer { - val delegateMemberFunctionName = context.getNameForDescriptor(fromDescriptor) +) { val overriddenMemberFunctionName = context.getNameForDescriptor(toDescriptor) val overriddenMemberFunctionRef = JsNameRef(overriddenMemberFunctionName, thisObject) @@ -57,7 +54,8 @@ fun generateDelegateCall( val functionObject = simpleReturnFunction(context.getScopeForDescriptor(fromDescriptor), JsInvocation(overriddenMemberFunctionRef, args)) functionObject.parameters.addAll(parameters) - return JsPropertyInitializer(delegateMemberFunctionName.makeRef(), functionObject) + + context.addFunctionToPrototype(classDescriptor, fromDescriptor, functionObject) } fun List.splitToRanges(classifier: (T) -> S): List, S>> { @@ -86,7 +84,7 @@ fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsName val classifierDescriptor = type.constructor.declarationDescriptor if (classifierDescriptor is ClassDescriptor) { - val reference = context.getQualifiedReference(classifierDescriptor) + val reference = context.getInnerReference(classifierDescriptor) if (classifierDescriptor.kind == ClassKind.OBJECT) { referenceToJsClass = JsAstUtils.pureFqn("constructor", JsInvocation(JsNameRef("getPrototypeOf", JsNameRef("Object")), reference)) } @@ -105,3 +103,20 @@ fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsName 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()) +} \ No newline at end of file diff --git a/js/js.translator/testData/box/extensionProperty/inClass.kt b/js/js.translator/testData/box/extensionProperty/inClass.kt new file mode 100644 index 00000000000..38972320b7a --- /dev/null +++ b/js/js.translator/testData/box/extensionProperty/inClass.kt @@ -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()) diff --git a/js/js.translator/testData/box/inheritance/interfaces/abstractClassInheritingDefaultMethod.kt b/js/js.translator/testData/box/inheritance/interfaces/abstractClassInheritingDefaultMethod.kt new file mode 100644 index 00000000000..59007371c5a --- /dev/null +++ b/js/js.translator/testData/box/inheritance/interfaces/abstractClassInheritingDefaultMethod.kt @@ -0,0 +1,9 @@ +interface I { + fun foo() = "OK" +} + +abstract class A : I + +class B : A() + +fun box() = B().foo() \ No newline at end of file diff --git a/js/js.translator/testData/box/inheritance/interfaces/withDefaultMethod.kt b/js/js.translator/testData/box/inheritance/interfaces/withDefaultMethod.kt new file mode 100644 index 00000000000..f2c9d22e2b0 --- /dev/null +++ b/js/js.translator/testData/box/inheritance/interfaces/withDefaultMethod.kt @@ -0,0 +1,7 @@ +interface I { + fun foo() = "OK" +} + +class A : I + +fun box() = A().foo() \ No newline at end of file diff --git a/js/js.translator/testData/box/inheritance/interfaces/withDefaultMethodFromSuperInterface.kt b/js/js.translator/testData/box/inheritance/interfaces/withDefaultMethodFromSuperInterface.kt new file mode 100644 index 00000000000..4e9a70a1003 --- /dev/null +++ b/js/js.translator/testData/box/inheritance/interfaces/withDefaultMethodFromSuperInterface.kt @@ -0,0 +1,9 @@ +interface I { + fun foo() = "OK" +} + +interface J : I + +class A : J + +fun box() = A().foo() \ No newline at end of file diff --git a/js/js.translator/testData/box/inheritance/interfaces/withDefaultProperty.kt b/js/js.translator/testData/box/inheritance/interfaces/withDefaultProperty.kt new file mode 100644 index 00000000000..ee9fe79b334 --- /dev/null +++ b/js/js.translator/testData/box/inheritance/interfaces/withDefaultProperty.kt @@ -0,0 +1,8 @@ +interface I { + val foo: String + get() = "OK" +} + +class A : I + +fun box() = A().foo \ No newline at end of file diff --git a/js/js.translator/testData/box/native/nativePropertyWithCustomName.kt b/js/js.translator/testData/box/native/nativePropertyWithCustomName.kt index 1f8c30a654b..9eb9edacb5a 100644 --- a/js/js.translator/testData/box/native/nativePropertyWithCustomName.kt +++ b/js/js.translator/testData/box/native/nativePropertyWithCustomName.kt @@ -20,7 +20,7 @@ fun box(): String { if (foo + bar != OK) return "$foo + $bar != $OK" 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 (actual("asd", "12345") != "asd12345") return "${actual("asd", "12345")} != \"asd12345\""