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.utils.BindingUtils.getClassDescriptor;
042
043 /**
044 * Generates a big block where are all the classes(objects representing them) are created.
045 */
046 public final class ClassDeclarationTranslator extends AbstractTranslator {
047 private final LabelGenerator localLabelGenerator = new LabelGenerator('c');
048
049 @NotNull
050 private final THashMap<ClassDescriptor, OpenClassInfo> openClassDescriptorToItem = new THashMap<ClassDescriptor, OpenClassInfo>();
051
052 private final LinkedList<OpenClassInfo> openList = new LinkedList<OpenClassInfo>();
053 private final List<Pair<JetClassOrObject, JsInvocation>> finalList = new ArrayList<Pair<JetClassOrObject, JsInvocation>>();
054
055 @NotNull
056 private final ClassDescriptorToLabel classDescriptorToLabel = new ClassDescriptorToLabel();
057
058 @NotNull
059 private final JsFunction dummyFunction;
060
061 private final JsNameRef declarationsObjectRef;
062 private final JsVar classesVar;
063
064 public ClassDeclarationTranslator(@NotNull TranslationContext context) {
065 super(context);
066
067 dummyFunction = new JsFunction(context.scope());
068 JsName declarationsObject = context().scope().declareName(Namer.nameForClassesVariable());
069 classesVar = new JsVars.JsVar(declarationsObject);
070 declarationsObjectRef = declarationsObject.makeRef();
071 }
072
073 private final class ClassDescriptorToLabel implements ClassAliasingMap {
074 @Override
075 @Nullable
076 public JsNameRef get(ClassDescriptor descriptor, ClassDescriptor referencedDescriptor) {
077 OpenClassInfo item = openClassDescriptorToItem.get(descriptor);
078 // class declared in library
079 if (item == null) {
080 return null;
081 }
082
083 return item.label;
084 }
085 }
086
087 private static class OpenClassInfo {
088 private final ClassDescriptor descriptor;
089 private final JetClass declaration;
090 private final JsNameRef label;
091 private final JsNameRef qualifiedLabel;
092
093 private OpenClassInfo(JetClass declaration, ClassDescriptor descriptor, JsNameRef label, JsNameRef qualifiedLabel) {
094 this.descriptor = descriptor;
095 this.declaration = declaration;
096 this.label = label;
097 this.qualifiedLabel = qualifiedLabel;
098 }
099 }
100
101 @NotNull
102 public JsVars.JsVar getDeclaration() {
103 return classesVar;
104 }
105
106 public void generateDeclarations() {
107 List<JsVar> vars = new SmartList<JsVar>();
108 List<JsPropertyInitializer> propertyInitializers = new SmartList<JsPropertyInitializer>();
109
110 generateOpenClassDeclarations(vars, propertyInitializers);
111 generateFinalClassDeclarations();
112
113 if (vars.isEmpty()) {
114 if (!propertyInitializers.isEmpty()) {
115 classesVar.setInitExpression(new JsObjectLiteral(propertyInitializers));
116 }
117 return;
118 }
119
120 dummyFunction.setBody(new JsBlock(new JsVars(vars, true), new JsReturn(new JsObjectLiteral(propertyInitializers))));
121 classesVar.setInitExpression(new JsInvocation(dummyFunction));
122 }
123
124 private void generateOpenClassDeclarations(@NotNull List<JsVar> vars, @NotNull List<JsPropertyInitializer> propertyInitializers) {
125 // first pass: set up list order
126 LinkedList<OpenClassInfo> sortedOpenClasses =
127 (LinkedList<OpenClassInfo>) DFS.topologicalOrder(openList, new DFS.Neighbors<OpenClassInfo>() {
128 @NotNull
129 @Override
130 public Iterable<OpenClassInfo> getNeighbors(OpenClassInfo current) {
131 LinkedList<OpenClassInfo> parents = new LinkedList<OpenClassInfo>();
132 ClassDescriptor classDescriptor = getClassDescriptor(context().bindingContext(), current.declaration);
133 Collection<JetType> superTypes = classDescriptor.getTypeConstructor().getSupertypes();
134
135 for (JetType type : superTypes) {
136 ClassDescriptor descriptor = getClassDescriptorForType(type);
137 OpenClassInfo item = openClassDescriptorToItem.get(descriptor);
138 if (item == null) {
139 continue;
140 }
141
142 parents.add(item);
143 }
144
145 return parents;
146 }
147 });
148
149 assert sortedOpenClasses.size() == openList.size();
150
151 // second pass: generate
152 Iterator<OpenClassInfo> it = sortedOpenClasses.descendingIterator();
153 while (it.hasNext()) {
154 OpenClassInfo item = it.next();
155 JsExpression translatedDeclaration =
156 new ClassTranslator(item.declaration, item.descriptor, classDescriptorToLabel, context()).translate();
157 generate(item, propertyInitializers, translatedDeclaration, vars);
158 }
159 }
160
161 private void generateFinalClassDeclarations() {
162 ClassAliasingMap aliasingMap = new ClassAliasingMap() {
163 @Override
164 public JsNameRef get(ClassDescriptor descriptor, ClassDescriptor referencedDescriptor) {
165 OpenClassInfo item = openClassDescriptorToItem.get(descriptor);
166 return item == null ? null : item.qualifiedLabel;
167 }
168 };
169
170 for (Pair<JetClassOrObject, JsInvocation> item : finalList) {
171 new ClassTranslator(item.first, aliasingMap, context()).translate(item.second);
172 }
173 }
174
175 private static void generate(@NotNull OpenClassInfo item,
176 @NotNull List<JsPropertyInitializer> propertyInitializers,
177 @NotNull JsExpression definition,
178 @NotNull List<JsVar> vars) {
179 JsExpression value;
180 if (item.label.getName() == null) {
181 value = definition;
182 }
183 else {
184 assert item.label.getName() != null;
185 vars.add(new JsVar(item.label.getName(), definition));
186 value = item.label;
187 }
188
189 propertyInitializers.add(new JsPropertyInitializer(item.label, value));
190 }
191
192 @NotNull
193 public JsPropertyInitializer translate(@NotNull JetClassOrObject declaration) {
194 ClassDescriptor descriptor = getClassDescriptor(context().bindingContext(), declaration);
195 JsExpression value;
196 if (descriptor.getModality() == Modality.FINAL) {
197 JsInvocation invocation = context().namer().classCreateInvocation(descriptor);
198 finalList.add(new Pair<JetClassOrObject, JsInvocation>(declaration, invocation));
199 value = invocation;
200 }
201 else {
202 String label = localLabelGenerator.generate();
203 JsNameRef labelRef = dummyFunction.getScope().declareName(label).makeRef();
204 OpenClassInfo item = new OpenClassInfo((JetClass) declaration, descriptor, labelRef,
205 new JsNameRef(labelRef.getIdent(), declarationsObjectRef));
206 openList.add(item);
207 openClassDescriptorToItem.put(descriptor, item);
208
209 value = item.qualifiedLabel;
210 }
211
212 return InitializerUtils.createPropertyInitializer(descriptor, value, context());
213 }
214 }