diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/native/nested/nativeNestedClassProhibited.kt b/compiler/testData/diagnostics/testsWithJsStdLib/native/nested/nativeNestedClassProhibited.kt new file mode 100644 index 00000000000..7462def2508 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/native/nested/nativeNestedClassProhibited.kt @@ -0,0 +1,5 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +class A { + @native inner class B +} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/native/nested/nativeNestedClassProhibited.txt b/compiler/testData/diagnostics/testsWithJsStdLib/native/nested/nativeNestedClassProhibited.txt new file mode 100644 index 00000000000..92a395f74b1 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/native/nested/nativeNestedClassProhibited.txt @@ -0,0 +1,15 @@ +package + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + @kotlin.js.native() public final inner class B { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index 94f0ce16195..7c50d0cc262 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -490,6 +490,21 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes } } + @TestMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/nested") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Nested extends AbstractDiagnosticsTestWithJsStdLib { + public void testAllFilesPresentInNested() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithJsStdLib/native/nested"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("nativeNestedClassProhibited.kt") + public void testNativeNestedClassProhibited() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/nested/nativeNestedClassProhibited.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithJsStdLib/native/optionlBody") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index 2e7a84755d1..d94efb7032d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -61,10 +61,6 @@ public class DescriptorUtils { ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration; return classDescriptor.getThisAsReceiverParameter(); } - else if (containingDeclaration instanceof ScriptDescriptor) { - ScriptDescriptor scriptDescriptor = (ScriptDescriptor) containingDeclaration; - return scriptDescriptor.getThisAsReceiverParameter(); - } return null; } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt index a84e3be98de..ae01da101bd 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.container.useImpl import org.jetbrains.kotlin.container.useInstance import org.jetbrains.kotlin.js.resolve.diagnostics.JsCallChecker import org.jetbrains.kotlin.js.resolve.diagnostics.LocalClassChecker +import org.jetbrains.kotlin.js.resolve.diagnostics.NativeInnerClassChecker import org.jetbrains.kotlin.resolve.IdentifierChecker import org.jetbrains.kotlin.resolve.OverloadFilter import org.jetbrains.kotlin.resolve.PlatformConfigurator @@ -29,7 +30,8 @@ import org.jetbrains.kotlin.types.DynamicTypesAllowed object JsPlatformConfigurator : PlatformConfigurator( DynamicTypesAllowed(), - additionalDeclarationCheckers = listOf(NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(), LocalClassChecker()), + additionalDeclarationCheckers = listOf(NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(), LocalClassChecker(), + NativeInnerClassChecker()), additionalCallCheckers = listOf(), additionalTypeCheckers = listOf(), additionalSymbolUsageValidators = listOf(), diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt index c16fd34c07b..820e2ced558 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt @@ -37,6 +37,7 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { put(ErrorsJs.REFERENCE_TO_BUILTIN_MEMBERS_NOT_SUPPORTED, "Callable references for builtin members are not supported yet: ''{0}''", RenderFirstLineOfElementText) put(ErrorsJs.NON_TOPLEVEL_CLASS_DECLARATION, "Non-toplevel {0} declarations not supported yet", Renderers.STRING) put(ErrorsJs.JSCODE_NO_JAVASCRIPT_PRODUCED, "Argument must be non-empty JavaScript code") + put(ErrorsJs.NATIVE_INNER_CLASS_PROHIBITED, "Native inner classes are prohibited") this } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java index f0cd041bf5b..0edcc496edf 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java @@ -44,6 +44,7 @@ public interface ErrorsJs { DiagnosticFactory1 REFERENCE_TO_BUILTIN_MEMBERS_NOT_SUPPORTED = DiagnosticFactory1.create(ERROR, DEFAULT); DiagnosticFactory1 NON_TOPLEVEL_CLASS_DECLARATION = DiagnosticFactory1.create(ERROR, DECLARATION_NAME); DiagnosticFactory0 JSCODE_NO_JAVASCRIPT_PRODUCED = DiagnosticFactory0.create(ERROR, DEFAULT); + DiagnosticFactory0 NATIVE_INNER_CLASS_PROHIBITED = DiagnosticFactory0.create(ERROR); @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/LocalClassChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/LocalClassChecker.kt index 533eee5897c..45382be0fb3 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/LocalClassChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/LocalClassChecker.kt @@ -18,18 +18,22 @@ package org.jetbrains.kotlin.js.resolve.diagnostics import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.rendering.renderKind import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DeclarationChecker import org.jetbrains.kotlin.resolve.DescriptorUtils.* class LocalClassChecker : DeclarationChecker { - override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink, - bindingContext: BindingContext) { + override fun check( + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + diagnosticHolder: DiagnosticSink, + bindingContext: BindingContext + ) { if (descriptor !is ClassDescriptor || declaration !is KtNamedDeclaration) { return; } @@ -37,25 +41,8 @@ class LocalClassChecker : DeclarationChecker { return } - // hack to avoid to get diagnostics when compile kotlin builtins - val fqNameUnsafe = getFqName(descriptor) - if (fqNameUnsafe.asString().startsWith("kotlin.")) { - return - } - - if (hasEnclosingFunction(descriptor)) { + if (isLocal(descriptor)) { diagnosticHolder.report(ErrorsJs.NON_TOPLEVEL_CLASS_DECLARATION.on(declaration, descriptor.renderKind())) } } - - private fun hasEnclosingFunction(descriptor: DeclarationDescriptor?): Boolean { - var d = descriptor - while (d != null) { - if (d is FunctionDescriptor) { - return true - } - d = d.containingDeclaration - } - return false - } } \ No newline at end of file diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/NativeInnerClassChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/NativeInnerClassChecker.kt new file mode 100644 index 00000000000..e67ba303e1f --- /dev/null +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/NativeInnerClassChecker.kt @@ -0,0 +1,37 @@ +/* + * 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.resolve.diagnostics + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.diagnostics.DiagnosticSink +import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DeclarationChecker +import org.jetbrains.kotlin.resolve.DescriptorUtils + +class NativeInnerClassChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink, + bindingContext: BindingContext) { + if (descriptor !is ClassDescriptor || !AnnotationsUtils.isNativeObject(descriptor)) return + + if (descriptor.isInner && !AnnotationsUtils.isNativeObject(DescriptorUtils.getContainingClass(descriptor)!!)) { + diagnosticHolder.report(ErrorsJs.NATIVE_INNER_CLASS_PROHIBITED.on(declaration)) + } + } +} diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NestedTypesTest.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NestedTypesTest.java index 0af939d993a..3dbdb063065 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NestedTypesTest.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NestedTypesTest.java @@ -54,6 +54,10 @@ public class NestedTypesTest extends SingleFileTranslationTest { checkFooBoxIsOk(); } + public void testOuterNative() throws Exception { + checkFooBoxIsOk(); + } + @NotNull @Override protected List additionalJsFiles(@NotNull EcmaVersion ecmaVersion) { 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 fa707b5c007..0dcfe8b2fa2 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 @@ -204,35 +204,27 @@ object ConstructorCallCase : FunctionCallCase() { } override fun FunctionCallInfo.noReceivers(): JsExpression { - val fqName = context.getQualifiedReference(callableDescriptor) - - val functionRef = if (isNative()) fqName else context.aliasOrValue(callableDescriptor) { fqName } - - val constructorDescriptor = callableDescriptor as ConstructorDescriptor - if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) { - return JsNew(functionRef, argumentsInfo.translateArguments) - } - else { - return JsInvocation(functionRef, argumentsInfo.translateArguments) - } + return receiver { it } } override fun FunctionCallInfo.dispatchReceiver(): JsExpression { + return receiver { + val receiver = superCallReceiver + if (receiver != null) (sequenceOf(receiver) + it).toList() else it + } + } + + private inline fun FunctionCallInfo.receiver(argumentTranslator: (List) -> List): JsExpression { val fqName = context.getQualifiedReference(callableDescriptor) - val functionRef = context.aliasOrValue(callableDescriptor) { fqName } + val functionRef = if (isNative()) fqName else context.aliasOrValue(callableDescriptor) { fqName } + val arguments = argumentTranslator(argumentsInfo.translateArguments) val constructorDescriptor = callableDescriptor as ConstructorDescriptor - val receiver = superCallReceiver - var allArguments = when (receiver) { - null -> argumentsInfo.translateArguments - else -> (sequenceOf(receiver) + argumentsInfo.translateArguments).toList() - } - if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) { - return JsNew(functionRef, allArguments) + return JsNew(functionRef, arguments) } else { - return JsInvocation(functionRef, allArguments) + return JsInvocation(functionRef, arguments) } } } 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 9498d723463..9941dc347f8 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 @@ -65,6 +65,8 @@ public final class Namer { public static final String CALL_FUNCTION = "call"; private static final String APPLY_FUNCTION = "apply"; + 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"; 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 23aa6534314..652c40e2fc6 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 @@ -421,14 +421,15 @@ public class TranslationContext { } @Nullable - private static ClassDescriptor getNearestClass(@Nullable DeclarationDescriptor declaration) { - while (declaration != null) { - if (declaration instanceof ClassDescriptor) { - if (!DescriptorUtils.isAnonymousObject(declaration) && !DescriptorUtils.isObject(declaration)) { - return (ClassDescriptor) declaration; + private static ClassDescriptor getNearestClass(@NotNull DeclarationDescriptor declaration) { + DeclarationDescriptor decl = declaration; + while (decl != null) { + if (decl instanceof ClassDescriptor) { + if (!DescriptorUtils.isAnonymousObject(decl) && !DescriptorUtils.isObject(decl)) { + return (ClassDescriptor) decl; } } - declaration = declaration.getContainingDeclaration(); + decl = decl.getContainingDeclaration(); } return null; } @@ -445,14 +446,8 @@ public class TranslationContext { return descriptor; } - public boolean hasEnclosingFunction() { + public boolean isLocal() { DeclarationDescriptor descriptor = declarationDescriptor; - while (descriptor != null) { - if (descriptor instanceof FunctionDescriptor) { - return true; - } - descriptor = descriptor.getContainingDeclaration(); - } - return false; + return descriptor != null && DescriptorUtils.isLocal(descriptor); } } 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 93883cd3099..24ecbf6e0b5 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 @@ -24,7 +24,6 @@ import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDes import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.js.descriptorUtils.hasPrimaryConstructor -import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.context.DefinitionPlace import org.jetbrains.kotlin.js.translate.context.Namer @@ -48,7 +47,6 @@ import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtObjectDeclaration 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.types.CommonSupertypes.topologicallySortSuperclassesAndRecordAllInstances import org.jetbrains.kotlin.types.KotlinType @@ -79,11 +77,6 @@ class ClassTranslator private constructor( private fun isTrait(): Boolean = descriptor.kind == ClassKind.INTERFACE private fun getClassCreateInvocationArguments(declarationContext: TranslationContext): List { - if (!DescriptorUtils.isAnonymousObject(descriptor) && !DescriptorUtils.isObject(descriptor) && - declarationContext.hasEnclosingFunction()) { - declarationContext.bindingTrace().report(ErrorsJs.NOT_SUPPORTED.on(classDeclaration, classDeclaration)) - return emptyList() - } var context = declarationContext val invocationArguments = ArrayList() @@ -188,7 +181,7 @@ class ClassTranslator private constructor( return emptyList() } if (supertypes.size == 1) { - val type = supertypes.get(0) + val type = supertypes[0] val supertypeDescriptor = getClassDescriptorForType(type) return listOf(getClassReference(supertypeDescriptor)) } 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 9a031ec3c40..2d03ace8ec2 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 @@ -124,7 +124,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator { } // TODO: avoid name clashing - JsName outerName = initFunction.getScope().declareName("$outer"); + JsName outerName = initFunction.getScope().declareName(Namer.OUTER_FIELD_NAME); initFunction.getParameters().add(0, new JsParameter(outerName)); JsExpression target = new JsNameRef(outerName, JsLiteral.THIS); diff --git a/js/js.translator/testData/nestedTypes/cases/outerNative.kt b/js/js.translator/testData/nestedTypes/cases/outerNative.kt new file mode 100644 index 00000000000..8bf7c9393d8 --- /dev/null +++ b/js/js.translator/testData/nestedTypes/cases/outerNative.kt @@ -0,0 +1,31 @@ +package foo + +@native class A(x: Int) { + var x: Int + get() = noImpl + set(value) = noImpl + + fun foo(): Int = noImpl + + class B(val value: Int) { + fun bar(): Int = noImpl + } + + inner class C(val value: Int) { + fun bar(): Int = noImpl + fun dec(): Unit = noImpl + } +} + +fun box(): String { + var b = A.B(23) + if (b.bar() != 10023) return "failed1: ${b.bar()}" + + var c = A(11).C(23) + if (c.bar() != 10034) return "failed2: ${c.bar()}" + c.dec() + if (c.bar() != 10033) return "failed3: ${c.bar()}" + + return "OK" +} + diff --git a/js/js.translator/testData/nestedTypes/native/outerNative.js b/js/js.translator/testData/nestedTypes/native/outerNative.js new file mode 100644 index 00000000000..f571f074466 --- /dev/null +++ b/js/js.translator/testData/nestedTypes/native/outerNative.js @@ -0,0 +1,30 @@ +function A(x) { + this.x = x; +} +A.prototype = { + foo : function() { + return this.x; + } +}; + +A.B = function(value) { + this.value = value; +}; +A.B.prototype = { + bar : function() { + return 10000 + this.value; + } +}; + +A.C = function(outer, value) { + this.outer = outer; + this.value = value; +}; +A.C.prototype = { + bar : function() { + return this.outer.foo() + this.value + 10000; + }, + dec : function() { + this.outer.x--; + } +}; \ No newline at end of file