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
017 package org.jetbrains.k2js.translate.declaration;
018
019 import com.google.dart.compiler.backend.js.ast.*;
020 import com.intellij.openapi.util.Pair;
021 import com.intellij.util.SmartList;
022 import gnu.trove.THashMap;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
026 import org.jetbrains.jet.lang.descriptors.Modality;
027 import org.jetbrains.jet.lang.psi.JetClass;
028 import org.jetbrains.jet.lang.psi.JetClassOrObject;
029 import org.jetbrains.jet.lang.types.JetType;
030 import org.jetbrains.jet.utils.DFS;
031 import org.jetbrains.k2js.translate.LabelGenerator;
032 import org.jetbrains.k2js.translate.context.Namer;
033 import org.jetbrains.k2js.translate.context.TranslationContext;
034 import org.jetbrains.k2js.translate.general.AbstractTranslator;
035 import org.jetbrains.k2js.translate.initializer.InitializerUtils;
036
037 import java.util.*;
038
039 import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
040 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType;
041 import static org.jetbrains.k2js.translate.general.Translation.translateClassDeclaration;
042 import 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 */
047 public 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 }