Rework logic in class declaration and namespace declaration.

Adapted from https://github.com/develar/kotlin/commit/a9e0a42fb1347fa8e21c86b5a073ef8a7c873da0.
This commit is contained in:
Pavel V. Talanov
2012-08-13 15:18:26 +04:00
parent 124ba541ac
commit 29391e6c92
9 changed files with 440 additions and 445 deletions
@@ -34,7 +34,6 @@ import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import static org.jetbrains.k2js.config.Config.withJsLibAdded;
import static org.jetbrains.k2js.facade.FacadeUtils.parseString; import static org.jetbrains.k2js.facade.FacadeUtils.parseString;
import static org.jetbrains.k2js.generate.CodeGenerator.generateProgramToString; import static org.jetbrains.k2js.generate.CodeGenerator.generateProgramToString;
@@ -93,7 +92,7 @@ public final class K2JSTranslator {
throws TranslationException { throws TranslationException {
JetStandardLibrary.initialize(config.getProject()); JetStandardLibrary.initialize(config.getProject());
BindingContext bindingContext = AnalyzerFacadeForJS.analyzeFilesAndCheckErrors(filesToTranslate, config); BindingContext bindingContext = AnalyzerFacadeForJS.analyzeFilesAndCheckErrors(filesToTranslate, config);
return Translation.generateAst(bindingContext, withJsLibAdded(filesToTranslate, config), mainCallParameters, config); return Translation.generateAst(bindingContext, filesToTranslate, mainCallParameters, config);
} }
@NotNull @NotNull
@@ -18,9 +18,9 @@ package org.jetbrains.k2js.translate.declaration;
import com.google.dart.compiler.backend.js.ast.JsNameRef; import com.google.dart.compiler.backend.js.ast.JsNameRef;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
public interface ClassAliasingMap { public interface ClassAliasingMap {
@Nullable @Nullable
JsNameRef get(JetClass declaration, JetClass referencedDeclaration); JsNameRef get(ClassDescriptor descriptor, ClassDescriptor referencedDescriptor);
} }
@@ -17,6 +17,8 @@
package org.jetbrains.k2js.translate.declaration; package org.jetbrains.k2js.translate.declaration;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.openapi.util.Pair;
import com.intellij.util.SmartList;
import gnu.trove.THashMap; import gnu.trove.THashMap;
import gnu.trove.TLinkable; import gnu.trove.TLinkable;
import gnu.trove.TLinkableAdaptor; import gnu.trove.TLinkableAdaptor;
@@ -26,18 +28,20 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.Modality; import org.jetbrains.jet.lang.descriptors.Modality;
import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.k2js.translate.LabelGenerator;
import org.jetbrains.k2js.translate.context.Namer; import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.utils.JsAstUtils; import org.jetbrains.k2js.translate.initializer.InitializerUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
import static org.jetbrains.k2js.translate.general.Translation.translateClassDeclaration; import static org.jetbrains.k2js.translate.general.Translation.translateClassDeclaration;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor; import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message; import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.newBlock;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
@@ -45,43 +49,44 @@ import static org.jetbrains.k2js.translate.utils.JsAstUtils.newBlock;
* Generates a big block where are all the classes(objects representing them) are created. * Generates a big block where are all the classes(objects representing them) are created.
*/ */
public final class ClassDeclarationTranslator extends AbstractTranslator { public final class ClassDeclarationTranslator extends AbstractTranslator {
private int localNameCounter; private final LabelGenerator localLabelGenerator = new LabelGenerator('c');
@NotNull @NotNull
private final THashMap<JetClass, ListItem> openClassToItem = new THashMap<JetClass, ListItem>(); private final THashMap<ClassDescriptor, FinalListItem> openClassDescriptorToItem = new THashMap<ClassDescriptor, FinalListItem>();
private final TLinkedList<ListItem> openList = new TLinkedList<ListItem>(); private final TLinkedList<FinalListItem> openList = new TLinkedList<FinalListItem>();
private final List<ListItem> finalList = new ArrayList<ListItem>(); private final List<Pair<JetClassOrObject, JsInvocation>> finalList = new ArrayList<Pair<JetClassOrObject, JsInvocation>>();
@NotNull @NotNull
private final JsFunction dummyFunction; private final JsFunction dummyFunction;
private final JsName declarationsObject; private final JsNameRef declarationsObjectRef;
private final JsVars.JsVar classesVar; private final JsVar classesVar;
public ClassDeclarationTranslator(@NotNull TranslationContext context) { public ClassDeclarationTranslator(@NotNull TranslationContext context) {
super(context); super(context);
dummyFunction = new JsFunction(context.scope()); dummyFunction = new JsFunction(context.scope());
declarationsObject = context().scope().declareName(Namer.nameForClassesVariable()); JsName declarationsObject = context().scope().declareName(Namer.nameForClassesVariable());
classesVar = new JsVars.JsVar(declarationsObject); classesVar = new JsVars.JsVar(declarationsObject);
declarationsObjectRef = declarationsObject.makeRef();
} }
private final class OpenClassRefProvider implements ClassAliasingMap { private final class OpenClassRefProvider implements ClassAliasingMap {
@Override @Override
@Nullable @Nullable
public JsNameRef get(JetClass declaration, JetClass referencedDeclaration) { public JsNameRef get(ClassDescriptor descriptor, ClassDescriptor referencedDescriptor) {
ListItem item = openClassToItem.get(declaration); FinalListItem item = openClassDescriptorToItem.get(descriptor);
// class declared in library // class declared in library
if (item == null) { if (item == null) {
return null; return null;
} }
addAfter(item, openClassToItem.get(referencedDeclaration)); addAfter(item, openClassDescriptorToItem.get(referencedDescriptor));
return item.label; return item.label;
} }
private void addAfter(@NotNull ListItem item, @NotNull ListItem referencedItem) { private void addAfter(@NotNull FinalListItem item, @NotNull FinalListItem referencedItem) {
for (TLinkable link = item.getNext(); link != null; link = link.getNext()) { for (TLinkable link = item.getNext(); link != null; link = link.getNext()) {
if (link == referencedItem) { if (link == referencedItem) {
return; return;
@@ -89,65 +94,55 @@ public final class ClassDeclarationTranslator extends AbstractTranslator {
} }
openList.remove(referencedItem); openList.remove(referencedItem);
openList.addBefore((ListItem) item.getNext(), referencedItem); openList.addBefore((FinalListItem) item.getNext(), referencedItem);
} }
} }
private final class FinalClassRefProvider implements ClassAliasingMap { private static class FinalListItem extends TLinkableAdaptor {
@Override
public JsNameRef get(JetClass declaration, JetClass referencedDeclaration) {
ListItem item = openClassToItem.get(declaration);
return item == null ? null : item.label;
}
}
private static class ListItem extends TLinkableAdaptor {
private final JetClass declaration; private final JetClass declaration;
private final JsNameRef label; private final JsNameRef label;
private final JsNameRef qualifiedLabel;
private JsExpression translatedDeclaration; private JsExpression translatedDeclaration;
private ListItem(JetClass declaration, JsNameRef label) { private FinalListItem(JetClass declaration, JsNameRef label, JsNameRef qualifiedLabel) {
this.declaration = declaration; this.declaration = declaration;
this.label = label; this.label = label;
this.qualifiedLabel = qualifiedLabel;
} }
} }
@NotNull @NotNull
public JsVars getDeclarationsStatement() { public JsVars.JsVar getDeclaration() {
JsVars vars = new JsVars(); return classesVar;
vars.add(classesVar);
return vars;
} }
public void generateDeclarations() { public void generateDeclarations() {
JsObjectLiteral valueLiteral = new JsObjectLiteral(); List<JsVar> vars = new SmartList<JsVar>();
JsVars vars = new JsVars(); List<JsPropertyInitializer> propertyInitializers = new SmartList<JsPropertyInitializer>();
List<JsPropertyInitializer> propertyInitializers = valueLiteral.getPropertyInitializers();
generateOpenClassDeclarations(vars, propertyInitializers); generateOpenClassDeclarations(vars, propertyInitializers);
generateFinalClassDeclarations(vars, propertyInitializers); generateFinalClassDeclarations();
if (vars.isEmpty()) { if (vars.isEmpty()) {
classesVar.setInitExpression(valueLiteral); if (!propertyInitializers.isEmpty()) {
classesVar.setInitExpression(new JsObjectLiteral(propertyInitializers));
}
return; return;
} }
List<JsStatement> result = new ArrayList<JsStatement>(); dummyFunction.setBody(new JsBlock(new JsVars(vars, true), new JsReturn(new JsObjectLiteral(propertyInitializers))));
result.add(vars);
result.add(new JsReturn(valueLiteral));
dummyFunction.setBody(newBlock(result));
classesVar.setInitExpression(new JsInvocation(dummyFunction)); classesVar.setInitExpression(new JsInvocation(dummyFunction));
} }
private void generateOpenClassDeclarations(@NotNull JsVars vars, @NotNull List<JsPropertyInitializer> propertyInitializers) { private void generateOpenClassDeclarations(@NotNull List<JsVar> vars, @NotNull List<JsPropertyInitializer> propertyInitializers) {
ClassAliasingMap classAliasingMap = new OpenClassRefProvider(); ClassAliasingMap classAliasingMap = new OpenClassRefProvider();
// first pass: set up list order // first pass: set up list order
for (ListItem item : openList) { for (FinalListItem item : openList) {
item.translatedDeclaration = translateClassDeclaration(item.declaration, classAliasingMap, context()); item.translatedDeclaration = translateClassDeclaration(item.declaration, classAliasingMap, context());
} }
// second pass: generate // second pass: generate
for (ListItem item : openList) { for (FinalListItem item : openList) {
JsExpression translatedDeclaration = item.translatedDeclaration; JsExpression translatedDeclaration = item.translatedDeclaration;
if (translatedDeclaration == null) { if (translatedDeclaration == null) {
throw new IllegalStateException(message(item.declaration, "Could not translate class declaration)")); throw new IllegalStateException(message(item.declaration, "Could not translate class declaration)"));
@@ -156,24 +151,31 @@ public final class ClassDeclarationTranslator extends AbstractTranslator {
} }
} }
private void generateFinalClassDeclarations(@NotNull JsVars vars, @NotNull List<JsPropertyInitializer> propertyInitializers) { private void generateFinalClassDeclarations() {
ClassAliasingMap classAliasingMap = new FinalClassRefProvider(); ClassAliasingMap aliasingMap = new ClassAliasingMap() {
for (ListItem item : finalList) { @Override
generate(item, propertyInitializers, translateClassDeclaration(item.declaration, classAliasingMap, context()), vars); public JsNameRef get(ClassDescriptor descriptor, ClassDescriptor referencedDescriptor) {
FinalListItem item = openClassDescriptorToItem.get(descriptor);
return item == null ? null : item.qualifiedLabel;
}
};
for (Pair<JetClassOrObject, JsInvocation> item : finalList) {
new ClassTranslator(item.first, aliasingMap, context()).translateClassOrObjectCreation(item.second);
} }
} }
private static void generate(@NotNull ListItem item, private static void generate(@NotNull FinalListItem item,
@NotNull List<JsPropertyInitializer> propertyInitializers, @NotNull List<JsPropertyInitializer> propertyInitializers,
@NotNull JsExpression definition, @NotNull JsExpression definition,
@NotNull JsVars vars) { @NotNull List<JsVar> vars) {
JsExpression value; JsExpression value;
if (item.label.getName() == null) { if (item.label.getName() == null) {
value = definition; value = definition;
} }
else { else {
assert item.label.getName() != null; assert item.label.getName() != null;
vars.add(new JsVars.JsVar(item.label.getName(), definition)); vars.add(new JsVar(item.label.getName(), definition));
value = item.label; value = item.label;
} }
@@ -181,38 +183,24 @@ public final class ClassDeclarationTranslator extends AbstractTranslator {
} }
@NotNull @NotNull
public JsPropertyInitializer translateAndGetClassNameToClassObject(@NotNull JetClass declaration) { public JsPropertyInitializer translate(@NotNull JetClassOrObject declaration) {
ClassDescriptor descriptor = getClassDescriptor(context().bindingContext(), declaration); ClassDescriptor descriptor = getClassDescriptor(context().bindingContext(), declaration);
JsNameRef labelRef;
String label = 'c' + Integer.toString(localNameCounter++, 36);
boolean isFinal = descriptor.getModality() == Modality.FINAL;
if (isFinal) {
labelRef = new JsNameRef(label);
}
else {
labelRef = dummyFunction.getScope().declareName(label).makeRef();
}
ListItem item = new ListItem(declaration, labelRef);
if (isFinal) {
finalList.add(item);
}
else {
openList.add(item);
openClassToItem.put(declaration, item);
}
JsNameRef qualifiedLabelRef = new JsNameRef(labelRef.getIdent());
qualifiedLabelRef.setQualifier(declarationsObject.makeRef());
JsExpression value; JsExpression value;
if (context().isEcma5()) { if (descriptor.getModality() == Modality.FINAL) {
value = JsAstUtils.createDataDescriptor(qualifiedLabelRef); JsInvocation invocation = context().namer().classCreateInvocation(descriptor);
finalList.add(new Pair<JetClassOrObject, JsInvocation>(declaration, invocation));
value = invocation;
} }
else { else {
value = qualifiedLabelRef; String label = localLabelGenerator.generate();
JsNameRef labelRef = dummyFunction.getScope().declareName(label).makeRef();
FinalListItem item = new FinalListItem((JetClass) declaration, labelRef, new JsNameRef(labelRef.getIdent(), declarationsObjectRef));
openList.add(item);
openClassDescriptorToItem.put(descriptor, item);
value = item.qualifiedLabel;
} }
return new JsPropertyInitializer(context().program().getStringLiteral(descriptor.getName().getName()), value); return InitializerUtils.createPropertyInitializer(descriptor, value, context());
} }
} }
@@ -17,32 +17,34 @@
package org.jetbrains.k2js.translate.declaration; package org.jetbrains.k2js.translate.declaration;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ClassKind; import org.jetbrains.jet.lang.descriptors.ClassKind;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.psi.JetObjectLiteralExpression;
import org.jetbrains.k2js.translate.context.TemporaryVariable; import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.initializer.InitializerUtils; import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import static com.google.dart.compiler.util.AstUtil.newSequence; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isNotAny;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor; import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter; import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.findAncestorClass;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass; import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters; import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getQualifiedReference; import static org.jetbrains.k2js.translate.utils.TranslationUtils.getQualifiedReference;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getThisObject;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
@@ -50,114 +52,105 @@ import static org.jetbrains.k2js.translate.utils.TranslationUtils.getThisObject;
* Generates a definition of a single class. * Generates a definition of a single class.
*/ */
public final class ClassTranslator extends AbstractTranslator { public final class ClassTranslator extends AbstractTranslator {
@NotNull
public static JsPropertyInitializer translateAsProperty(@NotNull JetClassOrObject classDeclaration,
@NotNull TranslationContext context) {
JsExpression classCreationExpression =
generateClassCreationExpression(classDeclaration, context);
JsName className = context.getNameForElement(classDeclaration);
return new JsPropertyInitializer(className.makeRef(), classCreationExpression);
}
@NotNull
public static JsExpression generateClassCreationExpression(@NotNull JetClassOrObject classDeclaration,
@NotNull ClassAliasingMap aliasingMap,
@NotNull TranslationContext context) {
return (new ClassTranslator(classDeclaration, aliasingMap, context)).translateClassOrObjectCreation();
}
@NotNull
public static JsExpression generateClassCreationExpression(@NotNull JetClassOrObject classDeclaration,
@NotNull TranslationContext context) {
return (new ClassTranslator(classDeclaration, null, context)).translateClassOrObjectCreation();
}
@NotNull
public static JsExpression generateObjectLiteralExpression(@NotNull JetObjectLiteralExpression objectLiteralExpression,
@NotNull TranslationContext outerContext) {
JetObjectDeclaration objectDeclaration = objectLiteralExpression.getObjectDeclaration();
ClassTranslator classTranslator = new ClassTranslator(objectDeclaration, null, outerContext);
ClassDescriptor containingClass = getContainingClass(classTranslator.descriptor);
if (containingClass == null) {
return classTranslator.translateClassOrObjectCreation();
}
return classTranslator.translateObjectLiteralWithThisAliased(containingClass, outerContext);
}
@NotNull @NotNull
private final DeclarationBodyVisitor declarationBodyVisitor = new DeclarationBodyVisitor(); private final DeclarationBodyVisitor declarationBodyVisitor = new DeclarationBodyVisitor();
@NotNull @NotNull
private final JetClassOrObject classDeclaration; private final JetClassOrObject classDeclaration;
@Nullable
private TemporaryVariable aliasForContainingClassThis = null;
@NotNull @NotNull
private final ClassDescriptor descriptor; private final ClassDescriptor descriptor;
@Nullable @Nullable
private final ClassAliasingMap aliasingMap; private final ClassAliasingMap aliasingMap;
private ClassTranslator(@NotNull JetClassOrObject classDeclaration, @NotNull
public static JsExpression generateClassCreation(@NotNull JetClassOrObject classDeclaration,
@NotNull ClassAliasingMap aliasingMap,
@NotNull TranslationContext context) {
return new ClassTranslator(classDeclaration, aliasingMap, context).translateClassOrObjectCreation(context);
}
@NotNull
public static JsExpression generateClassCreation(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) {
return new ClassTranslator(classDeclaration, null, context).translateClassOrObjectCreation(context);
}
@NotNull
public static JsExpression generateClassCreation(@NotNull JetClassOrObject classDeclaration,
@NotNull ClassDescriptor descriptor,
@NotNull TranslationContext context) {
return new ClassTranslator(classDeclaration, descriptor, null, context).translateClassOrObjectCreation(context);
}
@NotNull
public static JsExpression generateObjectLiteral(@NotNull JetObjectLiteralExpression objectLiteralExpression,
@NotNull TranslationContext context) {
return new ClassTranslator(objectLiteralExpression.getObjectDeclaration(), null, context).translateObjectLiteralExpression();
}
ClassTranslator(@NotNull JetClassOrObject classDeclaration,
@Nullable ClassAliasingMap aliasingMap, @Nullable ClassAliasingMap aliasingMap,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
super(context.newDeclaration(classDeclaration)); this(classDeclaration, getClassDescriptor(context.bindingContext(), classDeclaration), aliasingMap, context);
}
ClassTranslator(@NotNull JetClassOrObject classDeclaration,
@NotNull ClassDescriptor descriptor,
@Nullable ClassAliasingMap aliasingMap,
@NotNull TranslationContext context) {
super(context);
this.aliasingMap = aliasingMap; this.aliasingMap = aliasingMap;
this.descriptor = getClassDescriptor(context.bindingContext(), classDeclaration); this.descriptor = descriptor;
this.classDeclaration = classDeclaration; this.classDeclaration = classDeclaration;
} }
@NotNull @NotNull
private JsExpression translateClassOrObjectCreation() { private JsExpression translateObjectLiteralExpression() {
JsInvocation jsClassDeclaration = classCreateMethodInvocation(); ClassDescriptor containingClass = getContainingClass(descriptor);
if (!isObject()) { if (containingClass == null) {
addSuperclassReferences(jsClassDeclaration); return translateClassOrObjectCreation(context());
} }
addClassOwnDeclarations(jsClassDeclaration); return translateAsObjectCreationExpressionWithEnclosingThisSaved(containingClass);
return jsClassDeclaration;
} }
@NotNull @NotNull
private JsInvocation classCreateMethodInvocation() { private JsExpression translateAsObjectCreationExpressionWithEnclosingThisSaved(@NotNull ClassDescriptor containingClass) {
if (isObject()) { return context().literalFunctionTranslator().translate(containingClass, classDeclaration, descriptor, this);
return new JsInvocation(context().namer().objectCreationMethodReference());
}
else if (isTrait() && !context().isEcma5()) {
return new JsInvocation(context().namer().traitCreationMethodReference());
}
else {
return new JsInvocation(context().namer().classCreationMethodReference());
}
} }
private boolean isObject() { @NotNull
return descriptor.getKind().equals(ClassKind.OBJECT); public JsExpression translateClassOrObjectCreation(@NotNull TranslationContext declarationContext) {
JsInvocation createInvocation = context().namer().classCreateInvocation(descriptor);
translateClassOrObjectCreation(createInvocation, declarationContext);
return createInvocation;
}
public void translateClassOrObjectCreation(@NotNull JsInvocation createInvocation) {
translateClassOrObjectCreation(createInvocation, context());
}
private void translateClassOrObjectCreation(@NotNull JsInvocation createInvocation, @NotNull TranslationContext context) {
addSuperclassReferences(createInvocation);
addClassOwnDeclarations(createInvocation, context);
} }
private boolean isTrait() { private boolean isTrait() {
return descriptor.getKind().equals(ClassKind.TRAIT); return descriptor.getKind().equals(ClassKind.TRAIT);
} }
private void addClassOwnDeclarations(@NotNull JsInvocation jsClassDeclaration) { private void addClassOwnDeclarations(@NotNull JsInvocation jsClassDeclaration, @NotNull TranslationContext classDeclarationContext) {
TranslationContext classDeclarationContext = getClassDeclarationContext(); JsObjectLiteral properties = new JsObjectLiteral(true);
JsObjectLiteral properties = new JsObjectLiteral();
List<JsPropertyInitializer> propertyList = properties.getPropertyInitializers(); List<JsPropertyInitializer> propertyList = properties.getPropertyInitializers();
if (!isTrait()) { if (!isTrait()) {
JsFunction initializer = Translation.generateClassInitializerMethod(classDeclaration, classDeclarationContext); JsFunction initializer = Translation.generateClassInitializerMethod(classDeclaration, classDeclarationContext);
if (context().isEcma5()) { if (context().isEcma5()) {
jsClassDeclaration.getArguments() jsClassDeclaration.getArguments().add(initializer.getBody().getStatements().isEmpty() ? JsLiteral.NULL : initializer);
.add(initializer.getName() == null ? initializer : JsAstUtils.encloseFunction(initializer));
} }
else { else {
propertyList.add(InitializerUtils.generateInitializeMethod(initializer)); propertyList.add(new JsPropertyInitializer(Namer.initializeMethodReference(), initializer));
} }
} }
else if (context().isEcma5()) {
jsClassDeclaration.getArguments().add(JsLiteral.NULL);
}
propertyList.addAll(translatePropertiesAsConstructorParameters(classDeclarationContext)); propertyList.addAll(translatePropertiesAsConstructorParameters(classDeclarationContext));
propertyList.addAll(declarationBodyVisitor.traverseClass(classDeclaration, classDeclarationContext)); propertyList.addAll(declarationBodyVisitor.traverseClass(classDeclaration, classDeclarationContext));
@@ -168,7 +161,7 @@ public final class ClassTranslator extends AbstractTranslator {
} }
private void addSuperclassReferences(@NotNull JsInvocation jsClassDeclaration) { private void addSuperclassReferences(@NotNull JsInvocation jsClassDeclaration) {
List<JsExpression> superClassReferences = getSuperclassNameReferences(); List<JsExpression> superClassReferences = getSupertypesNameReferences();
List<JsExpression> expressions = jsClassDeclaration.getArguments(); List<JsExpression> expressions = jsClassDeclaration.getArguments();
if (context().isEcma5()) { if (context().isEcma5()) {
if (superClassReferences.isEmpty()) { if (superClassReferences.isEmpty()) {
@@ -188,37 +181,49 @@ public final class ClassTranslator extends AbstractTranslator {
} }
@NotNull @NotNull
private List<JsExpression> getSuperclassNameReferences() { private List<JsExpression> getSupertypesNameReferences() {
List<JsExpression> superclassReferences = new ArrayList<JsExpression>(); Collection<? extends JetType> supertypes = descriptor.getTypeConstructor().getSupertypes();
List<ClassDescriptor> superclassDescriptors = DescriptorUtils.getSuperclassDescriptors(descriptor); if (supertypes.isEmpty()) {
addAncestorClass(superclassReferences, superclassDescriptors); return Collections.emptyList();
addTraits(superclassReferences, superclassDescriptors);
return superclassReferences;
}
private void addTraits(@NotNull List<JsExpression> superclassReferences,
@NotNull List<ClassDescriptor> superclassDescriptors) {
for (ClassDescriptor superClassDescriptor : superclassDescriptors) {
assert (superClassDescriptor.getKind() == ClassKind.TRAIT) : "Only traits are expected here";
superclassReferences.add(getClassReference(superClassDescriptor));
} }
}
private void addAncestorClass(@NotNull List<JsExpression> superclassReferences, JsExpression base = null;
@NotNull List<ClassDescriptor> superclassDescriptors) { List<JsExpression> list = null;
//here we remove ancestor class from the list for (JetType type : supertypes) {
ClassDescriptor ancestorClass = findAndRemoveAncestorClass(superclassDescriptors); ClassDescriptor result = getClassDescriptorForType(type);
if (ancestorClass != null) { if (isNotAny(result) && !AnnotationsUtils.isNativeObject(result)) {
superclassReferences.add(getClassReference(ancestorClass)); switch (result.getKind()) {
case CLASS:
base = getClassReference(result);
break;
case TRAIT:
if (list == null) {
list = new ArrayList<JsExpression>();
}
list.add(getClassReference(result));
break;
default:
throw new UnsupportedOperationException("unsupported super class kind " + result.getKind().name());
}
}
} }
if (list == null) {
return base == null ? Collections.<JsExpression>emptyList() : Collections.singletonList(base);
}
else if (base != null) {
list.add(0, base);
}
return list;
} }
@NotNull @NotNull
private JsExpression getClassReference(@NotNull ClassDescriptor superClassDescriptor) { private JsExpression getClassReference(@NotNull ClassDescriptor superClassDescriptor) {
// aliasing here is needed for the declaration generation step // aliasing here is needed for the declaration generation step
if (aliasingMap != null) { if (aliasingMap != null) {
JsNameRef name = aliasingMap.get(BindingUtils.getClassForDescriptor(bindingContext(), superClassDescriptor), JsNameRef name = aliasingMap.get(superClassDescriptor, descriptor);
(JetClass) classDeclaration);
if (name != null) { if (name != null) {
return name; return name;
} }
@@ -228,40 +233,20 @@ public final class ClassTranslator extends AbstractTranslator {
return getQualifiedReference(context(), superClassDescriptor); return getQualifiedReference(context(), superClassDescriptor);
} }
@Nullable
private static ClassDescriptor findAndRemoveAncestorClass(@NotNull List<ClassDescriptor> superclassDescriptors) {
ClassDescriptor ancestorClass = findAncestorClass(superclassDescriptors);
superclassDescriptors.remove(ancestorClass);
return ancestorClass;
}
@NotNull @NotNull
private List<JsPropertyInitializer> translatePropertiesAsConstructorParameters(@NotNull TranslationContext classDeclarationContext) { private List<JsPropertyInitializer> translatePropertiesAsConstructorParameters(@NotNull TranslationContext classDeclarationContext) {
List<JsPropertyInitializer> result = new ArrayList<JsPropertyInitializer>(); List<JetParameter> parameters = getPrimaryConstructorParameters(classDeclaration);
for (JetParameter parameter : getPrimaryConstructorParameters(classDeclaration)) { if (parameters.isEmpty()) {
PropertyDescriptor descriptor = return Collections.emptyList();
getPropertyDescriptorForConstructorParameter(bindingContext(), parameter); }
List<JsPropertyInitializer> result = new SmartList<JsPropertyInitializer>();
for (JetParameter parameter : parameters) {
PropertyDescriptor descriptor = getPropertyDescriptorForConstructorParameter(bindingContext(), parameter);
if (descriptor != null) { if (descriptor != null) {
result.addAll(PropertyTranslator.translateAccessors(descriptor, classDeclarationContext)); PropertyTranslator.translateAccessors(descriptor, result, classDeclarationContext);
} }
} }
return result; return result;
} }
@NotNull
private TranslationContext getClassDeclarationContext() {
if (aliasForContainingClassThis == null) {
return context();
}
ClassDescriptor containingClass = getContainingClass(descriptor);
assert containingClass != null;
return context().innerContextWithThisAliased(containingClass, aliasForContainingClassThis.name());
}
@NotNull
private JsExpression translateObjectLiteralWithThisAliased(@NotNull ClassDescriptor containingClass,
@NotNull TranslationContext outerContext) {
aliasForContainingClassThis = outerContext.declareTemporary(getThisObject(outerContext, containingClass));
return newSequence(aliasForContainingClassThis.assignmentExpression(), translateClassOrObjectCreation());
}
} }
@@ -18,10 +18,10 @@ package org.jetbrains.k2js.translate.declaration;
import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.descriptors.Modality;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
@@ -30,104 +30,77 @@ import org.jetbrains.k2js.translate.general.TranslatorVisitor;
import org.jetbrains.k2js.translate.utils.BindingUtils; import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.JsAstUtils; import org.jetbrains.k2js.translate.utils.JsAstUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.*; import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForObjectDeclaration;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
*/ */
public final class DeclarationBodyVisitor extends TranslatorVisitor<List<JsPropertyInitializer>> { public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
@Nullable protected final List<JsPropertyInitializer> result = new SmartList<JsPropertyInitializer>();
private final ClassDeclarationTranslator classDeclarationTranslator;
public DeclarationBodyVisitor() { public DeclarationBodyVisitor() {
classDeclarationTranslator = null;
} }
public DeclarationBodyVisitor(ClassDeclarationTranslator classDeclarationTranslator) { @NotNull
this.classDeclarationTranslator = classDeclarationTranslator; public List<JsPropertyInitializer> getResult() {
return result;
} }
@NotNull @NotNull
public List<JsPropertyInitializer> traverseClass(@NotNull JetClassOrObject jetClass, public List<JsPropertyInitializer> traverseClass(@NotNull JetClassOrObject jetClass,
@NotNull TranslationContext context) {
List<JsPropertyInitializer> properties = new ArrayList<JsPropertyInitializer>();
for (JetDeclaration declaration : jetClass.getDeclarations()) {
properties.addAll(declaration.accept(this, context));
}
return properties;
}
@NotNull
public List<JsPropertyInitializer> traverseNamespace(@NotNull NamespaceDescriptor namespace,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
List<JsPropertyInitializer> properties = new ArrayList<JsPropertyInitializer>(); for (JetDeclaration declaration : jetClass.getDeclarations()) {
for (JetDeclaration declaration : getDeclarationsForNamespace(context.bindingContext(), namespace)) { declaration.accept(this, context);
properties.addAll(declaration.accept(this, context));
} }
return properties; return result;
} }
@Override @Override
@NotNull public Void visitClass(@NotNull JetClass expression, @NotNull TranslationContext context) {
public List<JsPropertyInitializer> visitClass(@NotNull JetClass expression, @NotNull TranslationContext context) { return null;
if (classDeclarationTranslator == null) {
return Collections.emptyList();
}
return Collections.singletonList(classDeclarationTranslator.translateAndGetClassNameToClassObject(expression));
} }
@Override @Override
@NotNull public Void visitNamedFunction(@NotNull JetNamedFunction expression,
public List<JsPropertyInitializer> visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) {
@NotNull TranslationContext context) { FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
if (descriptor.getModality() == Modality.ABSTRACT) {
return null;
}
JsPropertyInitializer methodAsPropertyInitializer = Translation.functionTranslator(expression, context).translateAsMethod(); JsPropertyInitializer methodAsPropertyInitializer = Translation.functionTranslator(expression, context).translateAsMethod();
if (context.isEcma5()) { if (context.isEcma5()) {
FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
JsExpression methodBodyExpression = methodAsPropertyInitializer.getValueExpr(); JsExpression methodBodyExpression = methodAsPropertyInitializer.getValueExpr();
methodAsPropertyInitializer.setValueExpr(JsAstUtils.createPropertyDataDescriptor(descriptor, methodBodyExpression)); methodAsPropertyInitializer.setValueExpr(JsAstUtils.createPropertyDataDescriptor(descriptor, methodBodyExpression));
} }
return Collections.singletonList(methodAsPropertyInitializer); result.add(methodAsPropertyInitializer);
return null;
} }
@Override @Override
@NotNull public Void visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) {
public List<JsPropertyInitializer> visitProperty(@NotNull JetProperty expression, PropertyDescriptor propertyDescriptor = BindingUtils.getPropertyDescriptor(context.bindingContext(), expression);
@NotNull TranslationContext context) { PropertyTranslator.translateAccessors(propertyDescriptor, expression, result, context);
PropertyDescriptor propertyDescriptor = return null;
BindingUtils.getPropertyDescriptor(context.bindingContext(), expression);
return PropertyTranslator.translateAccessors(propertyDescriptor, context);
} }
@Override @Override
@NotNull public Void visitObjectDeclarationName(@NotNull JetObjectDeclarationName expression,
public List<JsPropertyInitializer> visitObjectDeclaration(@NotNull JetObjectDeclaration expression, @NotNull TranslationContext context) {
@NotNull TranslationContext context) { if (!context.isEcma5()) {
return Collections.singletonList(ClassTranslator.translateAsProperty(expression, context)); PropertyTranslator
} .translateAccessors(getPropertyDescriptorForObjectDeclaration(context.bindingContext(), expression), result, context);
@Override
@NotNull
public List<JsPropertyInitializer> visitObjectDeclarationName(@NotNull JetObjectDeclarationName expression,
@NotNull TranslationContext context) {
if (context.isEcma5()) {
return Collections.emptyList();
} }
PropertyDescriptor propertyDescriptor = return null;
getPropertyDescriptorForObjectDeclaration(context.bindingContext(), expression);
return PropertyTranslator.translateAccessors(propertyDescriptor, context);
} }
@Override @Override
@NotNull public Void visitAnonymousInitializer(@NotNull JetClassInitializer expression, @NotNull TranslationContext context) {
public List<JsPropertyInitializer> visitAnonymousInitializer(@NotNull JetClassInitializer expression,
@NotNull TranslationContext context) {
// parsed it in initializer visitor => no additional actions are needed // parsed it in initializer visitor => no additional actions are needed
return Collections.emptyList(); return null;
} }
} }
@@ -16,116 +16,114 @@
package org.jetbrains.k2js.translate.declaration; package org.jetbrains.k2js.translate.declaration;
import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.utils.JsAstUtils; import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getAllNonNativeNamespaceDescriptors; import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
*/ */
public final class NamespaceDeclarationTranslator extends AbstractTranslator { public final class NamespaceDeclarationTranslator extends AbstractTranslator {
private final Iterable<JetFile> files;
private final Map<NamespaceDescriptor,NamespaceTranslator> descriptorToTranslator =
new LinkedHashMap<NamespaceDescriptor, NamespaceTranslator>();
public static List<JsStatement> translateFiles(@NotNull Collection<JetFile> files, @NotNull TranslationContext context) { public static List<JsStatement> translateFiles(@NotNull Collection<JetFile> files, @NotNull TranslationContext context) {
Set<NamespaceDescriptor> namespaceDescriptorSet = getAllNonNativeNamespaceDescriptors(context.bindingContext(), files); return new NamespaceDeclarationTranslator(files, context).translate();
return (new NamespaceDeclarationTranslator(Lists.newArrayList(namespaceDescriptorSet), context)).translate();
} }
@NotNull private NamespaceDeclarationTranslator(@NotNull Iterable<JetFile> files, @NotNull TranslationContext context) {
private final ClassDeclarationTranslator classDeclarationTranslator;
@NotNull
private final List<NamespaceDescriptor> namespaceDescriptors;
private NamespaceDeclarationTranslator(@NotNull List<NamespaceDescriptor> namespaceDescriptors,
@NotNull TranslationContext context) {
super(context); super(context);
this.namespaceDescriptors = namespaceDescriptors;
classDeclarationTranslator = new ClassDeclarationTranslator(context); this.files = files;
} }
@NotNull @NotNull
private List<JsStatement> translate() { private List<JsStatement> translate() {
List<JsStatement> result = new ArrayList<JsStatement>(); // predictable order
result.add(classDeclarationTranslator.getDeclarationsStatement()); Map<NamespaceDescriptor, List<JsExpression>> descriptorToDefineInvocation = new THashMap<NamespaceDescriptor, List<JsExpression>>();
namespacesDeclarations(result); JsObjectLiteral rootNamespaceDefinition = null;
classDeclarationTranslator.generateDeclarations();
return result;
}
private void namespacesDeclarations(List<JsStatement> statements) { ClassDeclarationTranslator classDeclarationTranslator = new ClassDeclarationTranslator(context());
List<NamespaceTranslator> namespaceTranslators = getTranslatorsForNonEmptyNamespaces(); for (JetFile file : files) {
declarationStatements(namespaceTranslators, statements); NamespaceDescriptor descriptor = context().bindingContext().get(BindingContext.FILE_TO_NAMESPACE, file);
initializeStatements(namespaceTranslators, statements); assert descriptor != null;
} NamespaceTranslator translator = descriptorToTranslator.get(descriptor);
if (translator == null) {
if (rootNamespaceDefinition == null) {
rootNamespaceDefinition = getRootPackage(descriptorToDefineInvocation, descriptor);
}
translator = new NamespaceTranslator(descriptor, classDeclarationTranslator, context());
descriptorToTranslator.put(descriptor, translator);
}
@NotNull translator.translate(file);
private List<NamespaceTranslator> getTranslatorsForNonEmptyNamespaces() {
List<NamespaceTranslator> namespaceTranslators = Lists.newArrayList();
for (NamespaceDescriptor descriptor : filterNonEmptyNamespaces(filterTopLevelAndRootNamespaces(namespaceDescriptors))) {
namespaceTranslators.add(new NamespaceTranslator(descriptor, classDeclarationTranslator, context()));
} }
return namespaceTranslators;
}
private void declarationStatements(@NotNull List<NamespaceTranslator> namespaceTranslators, if (rootNamespaceDefinition == null) {
@NotNull List<JsStatement> statements) { return Collections.emptyList();
JsObjectLiteral objectLiteral = new JsObjectLiteral(); }
JsNameRef packageMapNameRef = context().scope().declareName("_").makeRef();
JsExpression packageMapValue; JsVars vars = new JsVars(true);
if (context().isNotEcma3()) { List<JsStatement> result;
packageMapValue = new JsInvocation(JsAstUtils.CREATE_OBJECT, JsLiteral.NULL, objectLiteral); if (context().isEcma5()) {
result = Collections.<JsStatement>singletonList(vars);
} }
else { else {
packageMapValue = objectLiteral; result = new ArrayList<JsStatement>();
result.add(vars);
} }
statements.add(JsAstUtils.newVar(packageMapNameRef.getName(), packageMapValue));
for (NamespaceTranslator translator : namespaceTranslators) { classDeclarationTranslator.generateDeclarations();
translator.addNamespaceDeclaration(objectLiteral.getPropertyInitializers()); for (NamespaceTranslator translator : descriptorToTranslator.values()) {
translator.add(descriptorToDefineInvocation, result);
} }
}
private static void initializeStatements(@NotNull List<NamespaceTranslator> namespaceTranslators, vars.addIfHasInitializer(context().literalFunctionTranslator().getDeclaration());
@NotNull List<JsStatement> statements) { vars.addIfHasInitializer(classDeclarationTranslator.getDeclaration());
for (NamespaceTranslator translator : namespaceTranslators) { vars.addIfHasInitializer(getDeclaration(rootNamespaceDefinition));
for (JsExpression expression : translator.getInitializers()) {
statements.add(expression.makeStmt());
}
}
}
@NotNull
private static List<NamespaceDescriptor> filterTopLevelAndRootNamespaces(@NotNull List<NamespaceDescriptor> namespaceDescriptors) {
List<NamespaceDescriptor> result = Lists.newArrayList();
for (NamespaceDescriptor descriptor : namespaceDescriptors) {
if (DescriptorUtils.isTopLevelNamespace(descriptor) || DescriptorUtils.isRootNamespace(descriptor)) {
result.add(descriptor);
}
}
return result; return result;
} }
@NotNull private JsObjectLiteral getRootPackage(Map<NamespaceDescriptor, List<JsExpression>> descriptorToDefineInvocation,
private List<NamespaceDescriptor> filterNonEmptyNamespaces(@NotNull List<NamespaceDescriptor> namespaceDescriptors) { NamespaceDescriptor descriptor) {
List<NamespaceDescriptor> result = Lists.newArrayList(); NamespaceDescriptor rootNamespace = descriptor;
for (NamespaceDescriptor descriptor : namespaceDescriptors) { while (rootNamespace.getContainingDeclaration() instanceof NamespaceDescriptor) {
if (!JsDescriptorUtils.isNamespaceEmpty(descriptor, context().bindingContext())) { rootNamespace = (NamespaceDescriptor) rootNamespace.getContainingDeclaration();
result.add(descriptor);
}
} }
return result;
List<JsExpression> args;
JsObjectLiteral rootNamespaceDefinition = new JsObjectLiteral(true);
if (context().isEcma5()) {
args = Arrays.<JsExpression>asList(JsLiteral.NULL, rootNamespaceDefinition);
}
else {
args = Collections.<JsExpression>singletonList(rootNamespaceDefinition);
}
descriptorToDefineInvocation.put(rootNamespace, args);
return rootNamespaceDefinition;
}
private JsVar getDeclaration(@NotNull JsObjectLiteral rootNamespaceDefinition) {
JsExpression packageMapValue;
if (context().isEcma5()) {
packageMapValue = new JsInvocation(JsAstUtils.CREATE_OBJECT, JsLiteral.NULL, rootNamespaceDefinition);
}
else {
packageMapValue = rootNamespaceDefinition;
}
return new JsVar(context().scope().declareName(Namer.getRootNamespaceName()), packageMapValue);
} }
} }
@@ -16,125 +16,177 @@
package org.jetbrains.k2js.translate.declaration; package org.jetbrains.k2js.translate.declaration;
import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.initializer.InitializerUtils;
import org.jetbrains.k2js.translate.initializer.InitializerVisitor;
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.JsAstUtils; import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils; import org.jetbrains.k2js.translate.utils.TranslationUtils;
import java.util.ArrayList; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.newObjectLiteral; import static org.jetbrains.k2js.translate.initializer.InitializerUtils.generateInitializerForProperty;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptor;
/** /**
* @author Pavel.Talanov * @author Pavel.Talanov
* <p/>
* Genereate code for a single descriptor.
*/ */
public final class NamespaceTranslator extends AbstractTranslator { final class NamespaceTranslator extends AbstractTranslator {
@NotNull @NotNull
private final NamespaceDescriptor descriptor; private final NamespaceDescriptor descriptor;
@NotNull @NotNull
private final JsName namespaceName;
@NotNull
private final ClassDeclarationTranslator classDeclarationTranslator; private final ClassDeclarationTranslator classDeclarationTranslator;
@NotNull private final FileDeclarationVisitor visitor;
private final List<JsExpression> initializers = new ArrayList<JsExpression>();
/*package*/ NamespaceTranslator(@NotNull NamespaceDescriptor descriptor, NamespaceTranslator(@NotNull NamespaceDescriptor descriptor,
@NotNull ClassDeclarationTranslator classDeclarationTranslator, @NotNull ClassDeclarationTranslator classDeclarationTranslator,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
super(context.newDeclaration(descriptor)); super(context.newDeclaration(descriptor));
this.descriptor = descriptor; this.descriptor = descriptor;
this.namespaceName = context.getNameForDescriptor(descriptor);
this.classDeclarationTranslator = classDeclarationTranslator; this.classDeclarationTranslator = classDeclarationTranslator;
visitor = new FileDeclarationVisitor();
} }
@NotNull public void translate(JetFile file) {
public List<JsExpression> getInitializers() { for (JetDeclaration declaration : file.getDeclarations()) {
return initializers; if (!AnnotationsUtils.isNativeObject(BindingUtils.getDescriptorForElement(bindingContext(), declaration))) {
} declaration.accept(visitor, context());
}
@NotNull
public JsPropertyInitializer getDeclarationAsInitializer() {
addNamespaceInitializer();
return new JsPropertyInitializer(namespaceName.makeRef(), getNamespaceDeclaration());
}
public void addNamespaceDeclaration(List<JsPropertyInitializer> list) {
addNamespaceInitializer();
if (DescriptorUtils.isRootNamespace(descriptor)) {
list.addAll(getFunctionsAndClasses());
return;
}
JsExpression value = getNamespaceDeclaration();
if (context().isNotEcma3()) {
value = JsAstUtils.createDataDescriptor(value);
}
list.add(new JsPropertyInitializer(namespaceName.makeRef(), value));
}
@NotNull
private JsInvocation getNamespaceDeclaration() {
JsInvocation namespaceDeclaration = namespaceCreateMethodInvocation();
addIfNeed(getFunctionsAndClasses(), namespaceDeclaration.getArguments());
addIfNeed(getNestedNamespaceDeclarations(), namespaceDeclaration.getArguments());
return namespaceDeclaration;
}
private void addNamespaceInitializer() {
JsFunction initializer = Translation.generateNamespaceInitializerMethod(descriptor, context());
if (!initializer.getBody().getStatements().isEmpty()) {
JsNameRef call = new JsNameRef("call");
call.setQualifier(initializer);
JsInvocation invocation = new JsInvocation();
invocation.setQualifier(call);
invocation.getArguments().add(TranslationUtils.getQualifiedReference(context(), descriptor));
initializers.add(invocation);
} }
} }
private List<JsPropertyInitializer> getFunctionsAndClasses() { public void add(@NotNull Map<NamespaceDescriptor, List<JsExpression>> descriptorToDefineInvocation,
return new DeclarationBodyVisitor(classDeclarationTranslator).traverseNamespace(descriptor, context()); @NotNull List<JsStatement> initializers) {
} JsExpression initializer;
if (visitor.initializerStatements.isEmpty()) {
@NotNull initializer = null;
private JsInvocation namespaceCreateMethodInvocation() {
return new JsInvocation(context().namer().packageDefinitionMethodReference());
}
private void addIfNeed(@NotNull List<JsPropertyInitializer> declarations, @NotNull List<JsExpression> expressions) {
// ecma5 expects strict number of arguments, but ecma3 doesn't
if (!declarations.isEmpty()) {
expressions.add(newObjectLiteral(declarations));
} }
else if (context().isNotEcma3()) { else {
expressions.add(JsLiteral.NULL); initializer = visitor.initializer;
if (!context().isEcma5()) {
initializers.add(new JsInvocation(new JsNameRef("call", initializer),
TranslationUtils.getQualifiedReference(context(), descriptor)).makeStmt());
}
}
List<JsExpression> defineInvocation = descriptorToDefineInvocation.get(descriptor);
if (defineInvocation == null) {
defineInvocation = createDefineInvocation(initializer, new JsObjectLiteral(visitor.getResult(), true));
descriptorToDefineInvocation.put(descriptor, defineInvocation);
addToParent((NamespaceDescriptor) descriptor.getContainingDeclaration(), getEntry(descriptor, defineInvocation),
descriptorToDefineInvocation);
}
else {
if (context().isEcma5() && initializer != null) {
assert defineInvocation.get(0) == JsLiteral.NULL;
defineInvocation.set(0, initializer);
}
((JsObjectLiteral) defineInvocation.get(context().isEcma5() ? 1 : 0)).getPropertyInitializers().addAll(visitor.getResult());
} }
} }
@NotNull private List<JsExpression> createDefineInvocation(@Nullable JsExpression initializer, @NotNull JsObjectLiteral members) {
private List<JsPropertyInitializer> getNestedNamespaceDeclarations() { if (context().isEcma5()) {
List<JsPropertyInitializer> result = Lists.newArrayList(); return Arrays.asList(initializer == null ? JsLiteral.NULL : initializer, members);
List<NamespaceDescriptor> nestedNamespaces = JsDescriptorUtils.getNestedNamespaces(descriptor, context().bindingContext()); }
for (NamespaceDescriptor nestedNamespace : nestedNamespaces) { else {
NamespaceTranslator nestedNamespaceTranslator = new NamespaceTranslator(nestedNamespace, classDeclarationTranslator, context()); return Collections.<JsExpression>singletonList(members);
result.add(nestedNamespaceTranslator.getDeclarationAsInitializer()); }
}
initializers.addAll(nestedNamespaceTranslator.getInitializers());
private JsPropertyInitializer getEntry(@NotNull NamespaceDescriptor descriptor, List<JsExpression> defineInvocation) {
return new JsPropertyInitializer(context().getNameForDescriptor(descriptor).makeRef(),
new JsInvocation(context().namer().packageDefinitionMethodReference(), defineInvocation));
}
private boolean addEntryIfParentExists(NamespaceDescriptor parentDescriptor,
JsPropertyInitializer entry,
Map<NamespaceDescriptor, List<JsExpression>> descriptorToDeclarationPlace) {
List<JsExpression> parentDefineInvocation = descriptorToDeclarationPlace.get(parentDescriptor);
if (parentDefineInvocation != null) {
((JsObjectLiteral) parentDefineInvocation.get(context().isEcma5() ? 1 : 0)).getPropertyInitializers().add(entry);
return true;
}
return false;
}
private void addToParent(NamespaceDescriptor parentDescriptor,
JsPropertyInitializer entry,
Map<NamespaceDescriptor, List<JsExpression>> descriptorToDefineInvocation) {
while (!addEntryIfParentExists(parentDescriptor, entry, descriptorToDefineInvocation)) {
List<JsExpression> defineInvocation =
createDefineInvocation(null, new JsObjectLiteral(new SmartList<JsPropertyInitializer>(entry), true));
entry = getEntry(parentDescriptor, defineInvocation);
descriptorToDefineInvocation.put(parentDescriptor, defineInvocation);
parentDescriptor = (NamespaceDescriptor) parentDescriptor.getContainingDeclaration();
}
}
private class FileDeclarationVisitor extends DeclarationBodyVisitor {
private final JsFunction initializer;
private final TranslationContext initializerContext;
private final List<JsStatement> initializerStatements;
private final InitializerVisitor initializerVisitor;
private FileDeclarationVisitor() {
initializer = JsAstUtils.createFunctionWithEmptyBody(context().scope());
initializerContext = context().contextWithScope(initializer);
initializerStatements = initializer.getBody().getStatements();
initializerVisitor = new InitializerVisitor(initializerStatements);
}
@Override
public Void visitClass(@NotNull JetClass expression, @NotNull TranslationContext context) {
JsPropertyInitializer value = classDeclarationTranslator.translate(expression);
result.add(value);
return null;
}
@Override
public Void visitObjectDeclaration(@NotNull JetObjectDeclaration declaration, @NotNull TranslationContext context) {
InitializerUtils.generate(declaration, initializerStatements, result, context);
return null;
}
@Override
public Void visitProperty(@NotNull JetProperty property, @NotNull TranslationContext context) {
super.visitProperty(property, context);
JetExpression initializer = property.getInitializer();
if (initializer != null) {
JsExpression value = Translation.translateAsExpression(initializer, initializerContext);
PropertyDescriptor propertyDescriptor = getPropertyDescriptor(context.bindingContext(), property);
if (value instanceof JsLiteral) {
result.add(new JsPropertyInitializer(context.getNameForDescriptor(propertyDescriptor).makeRef(),
context().isEcma5() ? JsAstUtils
.createPropertyDataDescriptor(propertyDescriptor, value) : value));
}
else {
initializerStatements.add(generateInitializerForProperty(context, propertyDescriptor, value));
}
}
return null;
}
@Override
public Void visitAnonymousInitializer(@NotNull JetClassInitializer expression, @NotNull TranslationContext context) {
expression.accept(initializerVisitor, initializerContext);
return null;
} }
return result;
} }
} }
@@ -377,7 +377,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull @NotNull
public JsNode visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression, public JsNode visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return ClassTranslator.generateObjectLiteralExpression(expression, context); return ClassTranslator.generateObjectLiteral(expression, context);
} }
@Override @Override
@@ -387,7 +387,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
JetObjectDeclarationName objectDeclarationName = getObjectDeclarationName(expression); JetObjectDeclarationName objectDeclarationName = getObjectDeclarationName(expression);
DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), objectDeclarationName); DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), objectDeclarationName);
JsName propertyName = context.getNameForDescriptor(descriptor); JsName propertyName = context.getNameForDescriptor(descriptor);
JsExpression value = ClassTranslator.generateClassCreationExpression(expression, context); JsExpression value = ClassTranslator.generateClassCreation(expression, context);
return newVar(propertyName, value); return newVar(propertyName, value);
} }
@@ -20,7 +20,6 @@ import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.k2js.config.Config; import org.jetbrains.k2js.config.Config;
@@ -29,6 +28,7 @@ import org.jetbrains.k2js.facade.exceptions.MainFunctionNotFoundException;
import org.jetbrains.k2js.facade.exceptions.TranslationException; import org.jetbrains.k2js.facade.exceptions.TranslationException;
import org.jetbrains.k2js.facade.exceptions.TranslationInternalException; import org.jetbrains.k2js.facade.exceptions.TranslationInternalException;
import org.jetbrains.k2js.facade.exceptions.UnsupportedFeatureException; import org.jetbrains.k2js.facade.exceptions.UnsupportedFeatureException;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.StaticContext; import org.jetbrains.k2js.translate.context.StaticContext;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.declaration.ClassAliasingMap; import org.jetbrains.k2js.translate.declaration.ClassAliasingMap;
@@ -39,12 +39,10 @@ import org.jetbrains.k2js.translate.expression.FunctionTranslator;
import org.jetbrains.k2js.translate.expression.PatternTranslator; import org.jetbrains.k2js.translate.expression.PatternTranslator;
import org.jetbrains.k2js.translate.expression.WhenTranslator; import org.jetbrains.k2js.translate.expression.WhenTranslator;
import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator; import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator;
import org.jetbrains.k2js.translate.initializer.NamespaceInitializerTranslator;
import org.jetbrains.k2js.translate.reference.CallBuilder; import org.jetbrains.k2js.translate.reference.CallBuilder;
import org.jetbrains.k2js.translate.test.JSTestGenerator; import org.jetbrains.k2js.translate.test.JSTestGenerator;
import org.jetbrains.k2js.translate.test.JSTester; import org.jetbrains.k2js.translate.test.JSTester;
import org.jetbrains.k2js.translate.utils.JsAstUtils; import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import org.jetbrains.k2js.translate.utils.dangerous.DangerousData; import org.jetbrains.k2js.translate.utils.dangerous.DangerousData;
import org.jetbrains.k2js.translate.utils.dangerous.DangerousTranslator; import org.jetbrains.k2js.translate.utils.dangerous.DangerousTranslator;
@@ -75,7 +73,7 @@ public final class Translation {
} }
@NotNull @NotNull
public static List<JsStatement> translateFiles(@NotNull Collection<JetFile> files, @NotNull TranslationContext context) { private static List<JsStatement> translateFiles(@NotNull Collection<JetFile> files, @NotNull TranslationContext context) {
return NamespaceDeclarationTranslator.translateFiles(files, context); return NamespaceDeclarationTranslator.translateFiles(files, context);
} }
@@ -83,7 +81,7 @@ public final class Translation {
public static JsExpression translateClassDeclaration(@NotNull JetClass classDeclaration, public static JsExpression translateClassDeclaration(@NotNull JetClass classDeclaration,
@NotNull ClassAliasingMap classAliasingMap, @NotNull ClassAliasingMap classAliasingMap,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return ClassTranslator.generateClassCreationExpression(classDeclaration, classAliasingMap, context); return ClassTranslator.generateClassCreation(classDeclaration, classAliasingMap, context);
} }
@NotNull @NotNull
@@ -136,13 +134,6 @@ public final class Translation {
return classInitializerTranslator.generateInitializeMethod(); return classInitializerTranslator.generateInitializeMethod();
} }
@NotNull
public static JsFunction generateNamespaceInitializerMethod(@NotNull NamespaceDescriptor namespace,
@NotNull TranslationContext context) {
final NamespaceInitializerTranslator namespaceInitializerTranslator = new NamespaceInitializerTranslator(namespace, context);
return namespaceInitializerTranslator.generateInitializeMethod();
}
@NotNull @NotNull
public static JsProgram generateAst(@NotNull BindingContext bindingContext, public static JsProgram generateAst(@NotNull BindingContext bindingContext,
@NotNull Collection<JetFile> files, @NotNull MainCallParameters mainCallParameters, @NotNull Collection<JetFile> files, @NotNull MainCallParameters mainCallParameters,
@@ -174,8 +165,9 @@ public final class Translation {
statements.add(program.getStringLiteral("use strict").makeStmt()); statements.add(program.getStringLiteral("use strict").makeStmt());
TranslationContext context = TranslationContext.rootContext(staticContext); TranslationContext context = TranslationContext.rootContext(staticContext);
staticContext.getLiteralFunctionTranslator().setRootContext(context);
statements.addAll(translateFiles(files, context)); statements.addAll(translateFiles(files, context));
TranslationUtils.defineModule(context, statements, config.getModuleId()); defineModule(context, statements, config.getModuleId());
if (mainCallParameters.shouldBeGenerated()) { if (mainCallParameters.shouldBeGenerated()) {
JsStatement statement = generateCallToMain(context, files, mainCallParameters.arguments()); JsStatement statement = generateCallToMain(context, files, mainCallParameters.arguments());
@@ -187,6 +179,14 @@ public final class Translation {
return context.program(); return context.program();
} }
private static void defineModule(@NotNull TranslationContext context, @NotNull List<JsStatement> statements, @NotNull String moduleId) {
JsName rootNamespaceName = context.scope().findName(Namer.getRootNamespaceName());
if (rootNamespaceName != null) {
statements.add(new JsInvocation(context.namer().kotlin("defineModule"), context.program().getStringLiteral(moduleId),
rootNamespaceName.makeRef()).makeStmt());
}
}
private static void mayBeGenerateTests(@NotNull Collection<JetFile> files, @NotNull Config config, private static void mayBeGenerateTests(@NotNull Collection<JetFile> files, @NotNull Config config,
@NotNull JsBlock rootBlock, @NotNull TranslationContext context) { @NotNull JsBlock rootBlock, @NotNull TranslationContext context) {
JSTester tester = config.getTester(); JSTester tester = config.getTester();