001/* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jetbrains.k2js.translate.declaration; 018 019import com.google.dart.compiler.backend.js.ast.*; 020import com.intellij.openapi.util.Pair; 021import com.intellij.util.SmartList; 022import gnu.trove.THashMap; 023import org.jetbrains.annotations.NotNull; 024import org.jetbrains.annotations.Nullable; 025import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 026import org.jetbrains.jet.lang.descriptors.Modality; 027import org.jetbrains.jet.lang.psi.JetClass; 028import org.jetbrains.jet.lang.psi.JetClassOrObject; 029import org.jetbrains.jet.lang.types.JetType; 030import org.jetbrains.jet.utils.DFS; 031import org.jetbrains.k2js.translate.LabelGenerator; 032import org.jetbrains.k2js.translate.context.Namer; 033import org.jetbrains.k2js.translate.context.TranslationContext; 034import org.jetbrains.k2js.translate.general.AbstractTranslator; 035import org.jetbrains.k2js.translate.initializer.InitializerUtils; 036 037import java.util.*; 038 039import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar; 040import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType; 041import static org.jetbrains.k2js.translate.general.Translation.translateClassDeclaration; 042import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor; 043 044/** 045 * Generates a big block where are all the classes(objects representing them) are created. 046 */ 047public final class ClassDeclarationTranslator extends AbstractTranslator { 048 private final LabelGenerator localLabelGenerator = new LabelGenerator('c'); 049 050 @NotNull 051 private final THashMap<ClassDescriptor, OpenClassInfo> openClassDescriptorToItem = new THashMap<ClassDescriptor, OpenClassInfo>(); 052 053 private final LinkedList<OpenClassInfo> openList = new LinkedList<OpenClassInfo>(); 054 private final List<Pair<JetClassOrObject, JsInvocation>> finalList = new ArrayList<Pair<JetClassOrObject, JsInvocation>>(); 055 056 @NotNull 057 private final ClassDescriptorToLabel classDescriptorToLabel = new ClassDescriptorToLabel(); 058 059 @NotNull 060 private final JsFunction dummyFunction; 061 062 private final JsNameRef declarationsObjectRef; 063 private final JsVar classesVar; 064 065 public ClassDeclarationTranslator(@NotNull TranslationContext context) { 066 super(context); 067 068 dummyFunction = new JsFunction(context.scope()); 069 JsName declarationsObject = context().scope().declareName(Namer.nameForClassesVariable()); 070 classesVar = new JsVars.JsVar(declarationsObject); 071 declarationsObjectRef = declarationsObject.makeRef(); 072 } 073 074 private final class ClassDescriptorToLabel implements ClassAliasingMap { 075 @Override 076 @Nullable 077 public JsNameRef get(ClassDescriptor descriptor, ClassDescriptor referencedDescriptor) { 078 OpenClassInfo item = openClassDescriptorToItem.get(descriptor); 079 // class declared in library 080 if (item == null) { 081 return null; 082 } 083 084 return item.label; 085 } 086 } 087 088 private static class OpenClassInfo { 089 private final JetClass declaration; 090 private final JsNameRef label; 091 private final JsNameRef qualifiedLabel; 092 093 private OpenClassInfo(JetClass declaration, JsNameRef label, JsNameRef qualifiedLabel) { 094 this.declaration = declaration; 095 this.label = label; 096 this.qualifiedLabel = qualifiedLabel; 097 } 098 } 099 100 @NotNull 101 public JsVars.JsVar getDeclaration() { 102 return classesVar; 103 } 104 105 public void generateDeclarations() { 106 List<JsVar> vars = new SmartList<JsVar>(); 107 List<JsPropertyInitializer> propertyInitializers = new SmartList<JsPropertyInitializer>(); 108 109 generateOpenClassDeclarations(vars, propertyInitializers); 110 generateFinalClassDeclarations(); 111 112 if (vars.isEmpty()) { 113 if (!propertyInitializers.isEmpty()) { 114 classesVar.setInitExpression(new JsObjectLiteral(propertyInitializers)); 115 } 116 return; 117 } 118 119 dummyFunction.setBody(new JsBlock(new JsVars(vars, true), new JsReturn(new JsObjectLiteral(propertyInitializers)))); 120 classesVar.setInitExpression(new JsInvocation(dummyFunction)); 121 } 122 123 private void generateOpenClassDeclarations(@NotNull List<JsVar> vars, @NotNull List<JsPropertyInitializer> propertyInitializers) { 124 // first pass: set up list order 125 LinkedList<OpenClassInfo> sortedOpenClasses = 126 (LinkedList<OpenClassInfo>) DFS.topologicalOrder(openList, new DFS.Neighbors<OpenClassInfo>() { 127 @NotNull 128 @Override 129 public Iterable<OpenClassInfo> getNeighbors(OpenClassInfo current) { 130 LinkedList<OpenClassInfo> parents = new LinkedList<OpenClassInfo>(); 131 ClassDescriptor classDescriptor = getClassDescriptor(context().bindingContext(), current.declaration); 132 Collection<JetType> superTypes = classDescriptor.getTypeConstructor().getSupertypes(); 133 134 for (JetType type : superTypes) { 135 ClassDescriptor descriptor = getClassDescriptorForType(type); 136 OpenClassInfo item = openClassDescriptorToItem.get(descriptor); 137 if (item == null) { 138 continue; 139 } 140 141 parents.add(item); 142 } 143 144 return parents; 145 } 146 }); 147 148 assert sortedOpenClasses.size() == openList.size(); 149 150 // second pass: generate 151 Iterator<OpenClassInfo> it = sortedOpenClasses.descendingIterator(); 152 while (it.hasNext()) { 153 OpenClassInfo item = it.next(); 154 JsExpression translatedDeclaration = translateClassDeclaration(item.declaration, classDescriptorToLabel, context()); 155 generate(item, propertyInitializers, translatedDeclaration, vars); 156 } 157 } 158 159 private void generateFinalClassDeclarations() { 160 ClassAliasingMap aliasingMap = new ClassAliasingMap() { 161 @Override 162 public JsNameRef get(ClassDescriptor descriptor, ClassDescriptor referencedDescriptor) { 163 OpenClassInfo item = openClassDescriptorToItem.get(descriptor); 164 return item == null ? null : item.qualifiedLabel; 165 } 166 }; 167 168 for (Pair<JetClassOrObject, JsInvocation> item : finalList) { 169 new ClassTranslator(item.first, aliasingMap, context()).translateClassOrObjectCreation(item.second); 170 } 171 } 172 173 private static void generate(@NotNull OpenClassInfo item, 174 @NotNull List<JsPropertyInitializer> propertyInitializers, 175 @NotNull JsExpression definition, 176 @NotNull List<JsVar> vars) { 177 JsExpression value; 178 if (item.label.getName() == null) { 179 value = definition; 180 } 181 else { 182 assert item.label.getName() != null; 183 vars.add(new JsVar(item.label.getName(), definition)); 184 value = item.label; 185 } 186 187 propertyInitializers.add(new JsPropertyInitializer(item.label, value)); 188 } 189 190 @NotNull 191 public JsPropertyInitializer translate(@NotNull JetClassOrObject declaration) { 192 ClassDescriptor descriptor = getClassDescriptor(context().bindingContext(), declaration); 193 JsExpression value; 194 if (descriptor.getModality() == Modality.FINAL) { 195 JsInvocation invocation = context().namer().classCreateInvocation(descriptor); 196 finalList.add(new Pair<JetClassOrObject, JsInvocation>(declaration, invocation)); 197 value = invocation; 198 } 199 else { 200 String label = localLabelGenerator.generate(); 201 JsNameRef labelRef = dummyFunction.getScope().declareName(label).makeRef(); 202 OpenClassInfo 203 item = new OpenClassInfo((JetClass) declaration, labelRef, new JsNameRef(labelRef.getIdent(), declarationsObjectRef)); 204 openList.add(item); 205 openClassDescriptorToItem.put(descriptor, item); 206 207 value = item.qualifiedLabel; 208 } 209 210 return InitializerUtils.createPropertyInitializer(descriptor, value, context()); 211 } 212}