JS backend: Add class object support.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package org.jetbrains.k2js.test.semantics;
|
||||
|
||||
import org.jetbrains.k2js.test.SingleFileTranslationTest;
|
||||
|
||||
public final class ClassObjectTest extends SingleFileTranslationTest {
|
||||
|
||||
public ClassObjectTest() {
|
||||
super("class_object/");
|
||||
}
|
||||
|
||||
public void testSimple() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testWithExtension() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testSetVar() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,11 @@ import com.google.dart.compiler.backend.js.ast.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.Named;
|
||||
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
|
||||
import org.jetbrains.jet.plugin.JetLanguage;
|
||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||
|
||||
/**
|
||||
* Encapuslates different types of constants and naming conventions.
|
||||
@@ -46,6 +48,16 @@ public final class Namer {
|
||||
private static final String RECEIVER_PARAMETER_NAME = "$receiver";
|
||||
private static final String CLASSES_OBJECT_NAME = "classes";
|
||||
private static final String THROW_NPE_FUN_NAME = "throwNPE";
|
||||
private static final String CLASS_OBJECT_GETTER = "object$";
|
||||
private static final String CLASS_OBJECT_INITIALIZER = "object_initializer$";
|
||||
|
||||
private static final Named CLASS_OBJECT_INITIALIZER_NAMED = new Named() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Name getName() {
|
||||
return Name.identifier(CLASS_OBJECT_INITIALIZER);
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
public static String getReceiverParameterName() {
|
||||
@@ -101,6 +113,14 @@ public final class Namer {
|
||||
return getNameWithPrefix(propertyName, SETTER_PREFIX);
|
||||
}
|
||||
|
||||
public static JsExpression getClassObjectAccessor(@NotNull JsExpression referenceToClass) {
|
||||
return new JsInvocation(new JsNameRef(CLASS_OBJECT_GETTER, referenceToClass));
|
||||
}
|
||||
|
||||
public static Named getNamedForClassObjectInitializer() {
|
||||
return CLASS_OBJECT_INITIALIZER_NAMED;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getNameWithPrefix(@NotNull String name, @NotNull String prefix) {
|
||||
return prefix + name;
|
||||
@@ -206,6 +226,8 @@ public final class Namer {
|
||||
return new JsInvocation(traitCreationMethodReference());
|
||||
case OBJECT:
|
||||
return new JsInvocation(objectCreationMethodReference());
|
||||
case CLASS_OBJECT:
|
||||
return new JsInvocation(objectCreationMethodReference());
|
||||
|
||||
default:
|
||||
return new JsInvocation(classCreationMethodReference());
|
||||
|
||||
@@ -133,15 +133,13 @@ public final class ClassTranslator extends AbstractTranslator {
|
||||
private void addClassOwnDeclarations(@NotNull List<JsExpression> invocationArguments, @NotNull TranslationContext declarationContext) {
|
||||
final List<JsPropertyInitializer> properties = new SmartList<JsPropertyInitializer>();
|
||||
|
||||
final List<JsPropertyInitializer> staticProperties;
|
||||
final List<JsPropertyInitializer> staticProperties = new SmartList<JsPropertyInitializer>();
|
||||
boolean isTopLevelDeclaration = context() == declarationContext;
|
||||
final JsNameRef qualifiedReference;
|
||||
if (!isTopLevelDeclaration) {
|
||||
staticProperties = null;
|
||||
qualifiedReference = null;
|
||||
}
|
||||
else if (descriptor.getKind().isObject()) {
|
||||
staticProperties = null;
|
||||
qualifiedReference = null;
|
||||
declarationContext.literalFunctionTranslator().setDefinitionPlace(
|
||||
new NotNullLazyValue<Trinity<List<JsPropertyInitializer>, LabelGenerator, JsExpression>>() {
|
||||
@@ -154,7 +152,6 @@ public final class ClassTranslator extends AbstractTranslator {
|
||||
}
|
||||
else {
|
||||
qualifiedReference = getQualifiedReference(declarationContext, descriptor);
|
||||
staticProperties = new SmartList<JsPropertyInitializer>();
|
||||
declarationContext.literalFunctionTranslator().setDefinitionPlace(
|
||||
new NotNullLazyValue<Trinity<List<JsPropertyInitializer>, LabelGenerator, JsExpression>>() {
|
||||
@Override
|
||||
@@ -176,13 +173,13 @@ public final class ClassTranslator extends AbstractTranslator {
|
||||
}
|
||||
|
||||
translatePropertiesAsConstructorParameters(declarationContext, properties);
|
||||
new DeclarationBodyVisitor(properties).traverseContainer(classDeclaration, declarationContext);
|
||||
new DeclarationBodyVisitor(properties, staticProperties).traverseContainer(classDeclaration, declarationContext);
|
||||
|
||||
if (isTopLevelDeclaration) {
|
||||
declarationContext.literalFunctionTranslator().setDefinitionPlace(null);
|
||||
}
|
||||
|
||||
boolean hasStaticProperties = staticProperties != null && !staticProperties.isEmpty();
|
||||
boolean hasStaticProperties = !staticProperties.isEmpty();
|
||||
if (!properties.isEmpty() || hasStaticProperties) {
|
||||
if (properties.isEmpty()) {
|
||||
invocationArguments.add(JsLiteral.NULL);
|
||||
|
||||
+24
-9
@@ -16,14 +16,12 @@
|
||||
|
||||
package org.jetbrains.k2js.translate.declaration;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer;
|
||||
import com.google.dart.compiler.backend.js.ast.*;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.Modality;
|
||||
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.k2js.translate.context.Namer;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
import org.jetbrains.k2js.translate.general.Translation;
|
||||
import org.jetbrains.k2js.translate.general.TranslatorVisitor;
|
||||
@@ -32,18 +30,20 @@ import org.jetbrains.k2js.translate.utils.JsAstUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForObjectDeclaration;
|
||||
import static org.jetbrains.k2js.translate.initializer.InitializerUtils.createPropertyInitializer;
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
|
||||
|
||||
public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
|
||||
protected final List<JsPropertyInitializer> result;
|
||||
protected final List<JsPropertyInitializer> staticResult;
|
||||
|
||||
public DeclarationBodyVisitor() {
|
||||
this(new SmartList<JsPropertyInitializer>());
|
||||
this(new SmartList<JsPropertyInitializer>(), new SmartList<JsPropertyInitializer>());
|
||||
}
|
||||
|
||||
public DeclarationBodyVisitor(List<JsPropertyInitializer> result) {
|
||||
public DeclarationBodyVisitor(@NotNull List<JsPropertyInitializer> result, @NotNull List<JsPropertyInitializer> staticResult) {
|
||||
this.result = result;
|
||||
this.staticResult = staticResult;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -56,6 +56,21 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitClassObject(
|
||||
JetClassObject classObject, TranslationContext context
|
||||
) {
|
||||
JetObjectDeclaration declaration = classObject.getObjectDeclaration();
|
||||
assert declaration != null : "Declaration for class object must be not null";
|
||||
ClassDescriptor descriptor = getClassDescriptor(context.bindingContext(), declaration);
|
||||
JsExpression value = ClassTranslator.generateClassCreation(declaration, descriptor, context);
|
||||
|
||||
JsFunction fun = new JsFunction(context.getScopeForDescriptor(descriptor.getContainingDeclaration()));
|
||||
fun.setBody(new JsBlock(new JsReturn(value)));
|
||||
staticResult.add(createPropertyInitializer(Namer.getNamedForClassObjectInitializer(), fun, context));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) {
|
||||
FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2010-2013 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.k2js.translate.reference;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
|
||||
import org.jetbrains.k2js.translate.context.Namer;
|
||||
import org.jetbrains.k2js.translate.context.TemporaryVariable;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
import org.jetbrains.k2js.translate.general.AbstractTranslator;
|
||||
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.k2js.translate.reference.ReferenceTranslator.translateAsFQReference;
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
|
||||
|
||||
public class ClassObjectAccessTranslator extends AbstractTranslator implements CachedAccessTranslator {
|
||||
@NotNull
|
||||
/*package*/ static ClassObjectAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression,
|
||||
@NotNull TranslationContext context) {
|
||||
DeclarationDescriptor referenceDescriptor = getDescriptorForReferenceExpression(context.bindingContext(), expression);
|
||||
assert referenceDescriptor != null : "JetSimpleName expression must reference a descriptor " + expression.getText();
|
||||
return new ClassObjectAccessTranslator(referenceDescriptor, context);
|
||||
}
|
||||
|
||||
/*package*/ static boolean isClassObjectReference(@NotNull JetReferenceExpression expression,
|
||||
@NotNull TranslationContext context) {
|
||||
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), expression);
|
||||
return descriptor instanceof ClassDescriptor && !AnnotationsUtils.isNativeObject(descriptor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private final JsExpression referenceToClassObject;
|
||||
|
||||
private ClassObjectAccessTranslator(@NotNull DeclarationDescriptor descriptor, @NotNull TranslationContext context) {
|
||||
super(context);
|
||||
this.referenceToClassObject = Namer.getClassObjectAccessor(translateAsFQReference(descriptor, context()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public JsExpression translateAsGet() {
|
||||
return referenceToClassObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public JsExpression translateAsSet(@NotNull JsExpression toSetTo) {
|
||||
throw new IllegalStateException("Class object can't set");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CachedAccessTranslator getCached() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<TemporaryVariable> declaredTemporaries() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,9 @@ public final class ReferenceTranslator {
|
||||
if (PropertyAccessTranslator.canBePropertyAccess(referenceExpression, context)) {
|
||||
return PropertyAccessTranslator.newInstance(referenceExpression, receiver, CallType.NORMAL, context);
|
||||
}
|
||||
if (ClassObjectAccessTranslator.isClassObjectReference(referenceExpression, context)) {
|
||||
return ClassObjectAccessTranslator.newInstance(referenceExpression, context);
|
||||
}
|
||||
return ReferenceAccessTranslator.newInstance(referenceExpression, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2013 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 foo
|
||||
|
||||
class A {
|
||||
var a = 3
|
||||
class object {
|
||||
var a = -2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun box() : String {
|
||||
A.a = 2
|
||||
if (A.a != 2) return "A.a != 2"
|
||||
|
||||
val a = A
|
||||
a.a = 3
|
||||
if (a.a != 3) return "a = A; a.a = 3; a != 3"
|
||||
|
||||
if (A().a != 3) return "A().a != 3"
|
||||
|
||||
val x = A()
|
||||
x.a = 4
|
||||
if (x.a != 4) return "x = A(); x.a = 4; x.a != 4"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2013 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 foo
|
||||
|
||||
class A {
|
||||
val a = 3
|
||||
class object {
|
||||
val a = 2
|
||||
val b = 5
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun box() : String {
|
||||
if (A.a != 2) return "A.a != 2"
|
||||
if (A.b != 5) return "A.b != 5"
|
||||
|
||||
val b = A
|
||||
if (b.a != 2) return "b = A; b != 2"
|
||||
|
||||
if (A().a != 3) return "A().a != 3"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2013 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 foo
|
||||
|
||||
open class A {
|
||||
val a = 3
|
||||
fun foo(): Int {
|
||||
return 5
|
||||
}
|
||||
class object: A() {
|
||||
val c = a
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
class object: A() {
|
||||
}
|
||||
}
|
||||
|
||||
fun box() : String {
|
||||
if (A.a != 3) return "A.a != 3"
|
||||
if (A.foo() != 5) return "A.foo() != 5"
|
||||
|
||||
val a = A
|
||||
if (a.c != 3) return "a = A; a.c != 3"
|
||||
|
||||
if (A().a != 3) return "A().a != 3"
|
||||
|
||||
if (B.a != 3) return "B.a != 3"
|
||||
val b = B
|
||||
if (b.foo() != 5) return "b = B; b.foo() != 5"
|
||||
return "OK"
|
||||
}
|
||||
@@ -111,6 +111,7 @@ var Kotlin = {};
|
||||
klass.addMethods = addMethods;
|
||||
klass.superclass = parent || null;
|
||||
klass.subclasses = [];
|
||||
klass.object$ = object$;
|
||||
|
||||
if (parent) {
|
||||
if (typeof (parent) == "function") {
|
||||
@@ -160,6 +161,14 @@ var Kotlin = {};
|
||||
return this;
|
||||
}
|
||||
|
||||
function object$() {
|
||||
if (typeof this.$object$ === "undefined") {
|
||||
this.$object$ = this.object_initializer$();
|
||||
}
|
||||
|
||||
return this.$object$;
|
||||
}
|
||||
|
||||
return create;
|
||||
})();
|
||||
|
||||
|
||||
@@ -89,6 +89,14 @@ var Kotlin = Object.create(null);
|
||||
return o;
|
||||
};
|
||||
|
||||
function class_object$() {
|
||||
if (typeof this.$object$ === "undefined") {
|
||||
this.$object$ = this.object_initializer$();
|
||||
}
|
||||
|
||||
return this.$object$;
|
||||
}
|
||||
|
||||
function createClass(bases, initializer, properties, staticProperties, isClass) {
|
||||
var proto;
|
||||
var baseInitializer;
|
||||
@@ -111,6 +119,9 @@ var Kotlin = Object.create(null);
|
||||
}
|
||||
|
||||
var constructor = createConstructor();
|
||||
Object.defineProperty(constructor, "object$", {value: class_object$});
|
||||
Object.defineProperty(constructor, "$object$", {value: undefined, writable: true});
|
||||
|
||||
Object.defineProperty(constructor, "proto", {value: proto});
|
||||
Object.defineProperty(constructor, "properties", {value: properties || null});
|
||||
if (isClass) {
|
||||
@@ -124,7 +135,7 @@ var Kotlin = Object.create(null);
|
||||
Object.defineProperties(constructor, staticProperties);
|
||||
}
|
||||
|
||||
Object.freeze(constructor);
|
||||
Object.seal(constructor);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user