[KT-4124] Add support for simple nested classes

This commit is contained in:
Alexey Andreev
2016-02-04 19:54:00 +03:00
parent ae7b048fdb
commit a8014dec50
12 changed files with 230 additions and 74 deletions
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.types.DynamicTypesAllowed
object JsPlatformConfigurator : PlatformConfigurator(
DynamicTypesAllowed(),
additionalDeclarationCheckers = listOf(NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(), ClassDeclarationChecker()),
additionalDeclarationCheckers = listOf(NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker()),
additionalCallCheckers = listOf(),
additionalTypeCheckers = listOf(),
additionalSymbolUsageValidators = listOf(),
@@ -1,50 +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.resolve
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.rendering.renderKind
import org.jetbrains.kotlin.diagnostics.rendering.renderKindWithName
import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DeclarationChecker
import org.jetbrains.kotlin.resolve.DescriptorUtils
class ClassDeclarationChecker : DeclarationChecker {
override fun check(
declaration: KtDeclaration,
descriptor: DeclarationDescriptor,
diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext
) {
if (declaration !is KtClassOrObject || declaration is KtObjectDeclaration || declaration is KtEnumEntry) return
// hack to avoid to get diagnostics when compile kotlin builtins
val fqNameUnsafe = DescriptorUtils.getFqName(descriptor)
if (fqNameUnsafe.asString().startsWith("kotlin.")) return
if (!DescriptorUtils.isTopLevelDeclaration(descriptor) && !AnnotationsUtils.isNativeObject(descriptor)) {
diagnosticHolder.report(ErrorsJs.NON_TOPLEVEL_CLASS_DECLARATION.on(declaration, (descriptor as ClassDescriptor).renderKind()))
}
}
}
@@ -0,0 +1,33 @@
/*
* 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.test.semantics;
import org.jetbrains.kotlin.js.test.SingleFileTranslationTest;
public class NestedTypesTest extends SingleFileTranslationTest {
public NestedTypesTest() {
super("nestedTypes/");
}
public void testNested() throws Exception {
checkFooBoxIsOk();
}
public void testInner() throws Exception {
checkFooBoxIsOk();
}
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.Visibilities
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.general.Translation
import org.jetbrains.kotlin.js.translate.operation.OperatorTable
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
@@ -32,6 +33,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.util.OperatorNameConventions
import java.util.ArrayList
@@ -209,13 +211,35 @@ object ConstructorCallCase : FunctionCallCase() {
val functionRef = if (isNative()) fqName else context.aliasOrValue(callableDescriptor) { fqName }
val constructorDescriptor = callableDescriptor as ConstructorDescriptor
if(constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) {
if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) {
return JsNew(functionRef, argumentsInfo.translateArguments)
}
else {
return JsInvocation(functionRef, argumentsInfo.translateArguments)
}
}
override fun FunctionCallInfo.dispatchReceiver(): JsExpression {
val fqName = context.getQualifiedReference(callableDescriptor)
val functionRef = context.aliasOrValue(callableDescriptor) { fqName }
val constructorDescriptor = callableDescriptor as ConstructorDescriptor
val receiver = this.resolvedCall.dispatchReceiver
var allArguments = when (receiver) {
is ExpressionReceiver -> {
val jsReceiver = Translation.translateAsExpression(receiver.expression, context)
(sequenceOf(jsReceiver) + argumentsInfo.translateArguments).toList()
}
else -> argumentsInfo.translateArguments
}
if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) {
return JsNew(functionRef, allArguments)
}
else {
return JsInvocation(functionRef, allArguments)
}
}
}
object SuperCallCase : FunctionCallCase() {
@@ -541,6 +541,27 @@ public final class StaticContext {
return null;
}
};
Rule<JsExpression> nestedClassesHaveContainerQualifier = new Rule<JsExpression>() {
@Nullable
@Override
public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
if (isNativeObject(descriptor) || isBuiltin(descriptor)) {
return null;
}
if (!(descriptor instanceof ClassDescriptor)) {
return null;
}
ClassDescriptor cls = (ClassDescriptor) descriptor;
if (cls.getKind() == ClassKind.ENUM_ENTRY || cls.getKind() == ClassKind.OBJECT) {
return null;
}
DeclarationDescriptor container = descriptor.getContainingDeclaration();
if (container == null) {
return null;
}
return getQualifiedReference(container);
}
};
addRule(libraryObjectsHaveKotlinQualifier);
addRule(constructorOrCompanionObjectHasTheSameQualifierAsTheClass);
@@ -548,6 +569,7 @@ public final class StaticContext {
addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier);
addRule(nativeObjectsHaveNativePartOfFullQualifier);
addRule(staticMembersHaveContainerQualifier);
addRule(nestedClassesHaveContainerQualifier);
}
}
@@ -21,10 +21,7 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.MemberDescriptor;
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.config.Config;
import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
@@ -32,6 +29,7 @@ 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 java.util.HashMap;
import java.util.Map;
@@ -55,12 +53,16 @@ public class TranslationContext {
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());
AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null);
return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null, null);
}
private final Map<JsExpression, TemporaryConstVariable> expressionToTempConstVariableCache = new HashMap<JsExpression, TemporaryConstVariable>();
@@ -71,7 +73,8 @@ public class TranslationContext {
@NotNull DynamicContext dynamicContext,
@NotNull AliasingContext aliasingContext,
@Nullable UsageTracker usageTracker,
@Nullable DefinitionPlace definitionPlace
@Nullable DefinitionPlace definitionPlace,
@Nullable DeclarationDescriptor declarationDescriptor
) {
this.parent = parent;
this.dynamicContext = dynamicContext;
@@ -79,6 +82,14 @@ public class TranslationContext {
this.aliasingContext = aliasingContext;
this.usageTracker = usageTracker;
this.definitionPlace = definitionPlace;
this.declarationDescriptor = declarationDescriptor;
if (declarationDescriptor instanceof ClassDescriptor
&& !DescriptorUtils.isAnonymousObject(declarationDescriptor)
&& !DescriptorUtils.isObject(declarationDescriptor)) {
this.classDescriptor = (ClassDescriptor) declarationDescriptor;
} else {
this.classDescriptor = parent != null ? parent.classDescriptor : null;
}
}
@Nullable
@@ -103,19 +114,22 @@ public class TranslationContext {
aliasingContext = this.aliasingContext.inner();
}
return new TranslationContext(this, this.staticContext, dynamicContext, aliasingContext, this.usageTracker, null);
return new TranslationContext(this, this.staticContext, dynamicContext, aliasingContext, this.usageTracker, null,
this.declarationDescriptor);
}
@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);
return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, this.definitionPlace,
this.declarationDescriptor);
}
@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, null,
this.declarationDescriptor);
}
@NotNull
@@ -126,12 +140,14 @@ 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);
return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place,
descriptor);
}
@NotNull
private TranslationContext innerWithAliasingContext(AliasingContext aliasingContext) {
return new TranslationContext(this, this.staticContext, this.dynamicContext, aliasingContext, this.usageTracker, null);
return new TranslationContext(this, this.staticContext, this.dynamicContext, aliasingContext, this.usageTracker, null,
this.declarationDescriptor);
}
@NotNull
@@ -319,7 +335,29 @@ public class TranslationContext {
@NotNull
public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor) {
JsExpression alias = getAliasForDescriptor(descriptor);
return alias == null ? JsLiteral.THIS : alias;
if (alias != null) {
return alias;
}
return getDispatchReceiverPath(getNearestClass(descriptor));
}
@NotNull
private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls) {
if (cls != null) {
JsExpression alias = getAliasForDescriptor(cls);
if (alias != null) {
return alias;
}
}
if (cls == classDescriptor || parent == null) {
return JsLiteral.THIS;
}
ClassDescriptor parentDescriptor = parent.classDescriptor;
if (classDescriptor != parentDescriptor) {
return new JsNameRef("$outer", parent.getDispatchReceiverPath(cls));
} else {
return parent.getDispatchReceiverPath(cls);
}
}
@NotNull
@@ -347,4 +385,17 @@ public class TranslationContext {
return null;
}
private static ClassDescriptor getNearestClass(DeclarationDescriptor declaration) {
while (declaration != null) {
if (declaration instanceof ClassDescriptor) {
if (!DescriptorUtils.isAnonymousObject(declaration)
&& !DescriptorUtils.isObject(declaration)) {
return (ClassDescriptor) declaration;
}
}
declaration = declaration.getContainingDeclaration();
}
return null;
}
}
@@ -67,7 +67,8 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
}
@Override
public Void visitClass(@NotNull KtClass expression, TranslationContext context) {
public Void visitClass(@NotNull KtClass declaration, TranslationContext context) {
staticResult.addAll(ClassTranslator.Companion.translate(declaration, context));
return null;
}
@@ -50,8 +50,8 @@ class FileDeclarationVisitor(
}
}
override fun visitClass(expression: KtClass, context: TranslationContext?): Void? {
result.addAll(ClassTranslator.translate(expression, context!!))
override fun visitClass(declaration: KtClass, context: TranslationContext?): Void? {
result.addAll(ClassTranslator.translate(declaration, context!!))
return null
}
@@ -84,6 +84,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
@NotNull
public JsFunction generateInitializeMethod(DelegationTranslator delegationTranslator) {
ClassDescriptor classDescriptor = getClassDescriptor(bindingContext(), classDeclaration);
addOuterClassReference(classDescriptor);
ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
if (primaryConstructor != null) {
@@ -113,6 +114,22 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
return initFunction;
}
private void addOuterClassReference(ClassDescriptor classDescriptor) {
DeclarationDescriptor container = classDescriptor.getContainingDeclaration();
if (!(container instanceof ClassDescriptor) || !classDescriptor.isInner()) {
return;
}
// TODO: avoid name clashing
JsName outerName = initFunction.getScope().declareName("$outer");
initFunction.getParameters().add(0, new JsParameter(outerName));
JsExpression target = new JsNameRef(outerName, JsLiteral.THIS);
JsExpression paramRef = new JsNameRef(outerName);
JsExpression assignment = new JsBinaryOperation(JsBinaryOperator.ASG, target, paramRef);
initFunction.getBody().getStatements().add(new JsExpressionStatement(assignment));
}
@NotNull
public JsExpression generateEnumEntryInstanceCreation(@NotNull KotlinType enumClassType) {
ResolvedCall<FunctionDescriptor> superCall = getSuperCall();
+41 -7
View File
@@ -91,19 +91,22 @@ var Kotlin = {};
}
}
function computeMetadata(bases, properties) {
function computeMetadata(bases, properties, staticProperties) {
var metadata = {};
var p, property;
metadata.baseClasses = toArray(bases);
metadata.baseClass = getClass(metadata.baseClasses);
metadata.classIndex = Kotlin.newClassIndex();
metadata.functions = {};
metadata.properties = {};
metadata.types = {};
metadata.staticMembers = {};
if (!(properties == null)) {
for (var p in properties) {
for (p in properties) {
if (properties.hasOwnProperty(p)) {
var property = properties[p];
property = properties[p];
property.$classIndex$ = metadata.classIndex;
if (typeof property === "function") {
metadata.functions[p] = property;
@@ -114,6 +117,19 @@ var Kotlin = {};
}
}
}
if (typeof staticProperties !== 'undefined') {
for (p in staticProperties) {
if (!staticProperties.hasOwnProperty(p)) {
continue;
}
property = staticProperties[p];
if (typeof property === "function" && typeof property.type !== "undefined" && property.type === Kotlin.TYPE.INIT_FUN) {
metadata.types[p] = property;
} else {
metadata.staticMembers[p] = property;
}
}
}
applyExtension(metadata.functions, metadata.baseClasses, function (it) {
return it.$metadata$.functions
});
@@ -146,10 +162,10 @@ var Kotlin = {};
if (constructor == null) {
constructor = emptyFunction();
}
copyProperties(constructor, staticProperties);
var metadata = computeMetadata(bases, properties);
var metadata = computeMetadata(bases, properties, staticProperties);
metadata.type = Kotlin.TYPE.CLASS;
copyProperties(constructor, metadata.staticMembers);
var prototypeObj;
if (metadata.baseClass !== null) {
@@ -161,6 +177,14 @@ var Kotlin = {};
Object.defineProperties(prototypeObj, metadata.properties);
copyProperties(prototypeObj, metadata.functions);
prototypeObj.constructor = constructor;
for (var innerType in metadata.types) {
if (metadata.types.hasOwnProperty(innerType)) {
Object.defineProperty(constructor, innerType, {
get: metadata.types[innerType],
configurable: true
});
}
}
if (metadata.baseClass != null) {
constructor.baseInitializer = metadata.baseClass;
@@ -181,15 +205,25 @@ var Kotlin = {};
Kotlin.createTraitNow = function (bases, properties, staticProperties) {
var obj = function () {};
copyProperties(obj, staticProperties);
obj.$metadata$ = computeMetadata(bases, properties);
obj.$metadata$ = computeMetadata(bases, properties, staticProperties);
obj.$metadata$.type = Kotlin.TYPE.TRAIT;
copyProperties(obj, obj.$metadata$.staticMembers);
obj.prototype = {};
Object.defineProperties(obj.prototype, obj.$metadata$.properties);
copyProperties(obj.prototype, obj.$metadata$.functions);
Object.defineProperty(obj, "object", {get: class_object, configurable: true});
for (var innerType in obj.$metadata$.types) {
if (obj.$metadata$.types.hasOwnProperty(innerType)) {
Object.defineProperty(constructor, innerType, {
get: obj.$metadata$.types[innerType],
configurable: true
});
}
}
return obj;
};
+14
View File
@@ -0,0 +1,14 @@
package foo
open class A(val x: Int, val y: Int) {
inner class B(val z: Int) {
fun foo() = x + y + z
}
}
fun box(): String {
val a = A(2, 3)
val b = a.B(4)
return if (b.foo() == 9) "OK" else "failure"
}
+10
View File
@@ -0,0 +1,10 @@
package foo
open class A(val x: Int) {
class B : A(5)
}
fun box(): String {
return if (A(7).x + A.B().x == 12) "OK" else "failed"
}