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.util.SmartList;
021 import gnu.trove.THashMap;
022 import gnu.trove.THashSet;
023 import gnu.trove.TObjectObjectProcedure;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.annotations.Nullable;
026 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
027 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
028 import org.jetbrains.jet.lang.descriptors.Modality;
029 import org.jetbrains.jet.lang.psi.JetClass;
030 import org.jetbrains.jet.lang.psi.JetClassOrObject;
031 import org.jetbrains.jet.lang.types.JetType;
032 import org.jetbrains.jet.utils.DFS;
033 import org.jetbrains.k2js.translate.context.Namer;
034 import org.jetbrains.k2js.translate.context.TranslationContext;
035 import org.jetbrains.k2js.translate.general.AbstractTranslator;
036
037 import java.util.Collection;
038 import java.util.Iterator;
039 import java.util.LinkedList;
040 import java.util.List;
041
042 import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
043 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType;
044 import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
045
046 /**
047 * Generates a big block where are all the classes(objects representing them) are created.
048 */
049 public final class ClassDeclarationTranslator extends AbstractTranslator {
050 private final THashSet<String> nameClashGuard = new THashSet<String>();
051
052 @NotNull
053 private final THashMap<ClassDescriptor, OpenClassInfo> openClassDescriptorToItem = new THashMap<ClassDescriptor, OpenClassInfo>();
054
055 private final LinkedList<OpenClassInfo> openList = new LinkedList<OpenClassInfo>();
056 @NotNull
057 private final ClassDescriptorToLabel classDescriptorToLabel = new ClassDescriptorToLabel();
058
059 private final THashMap<ClassDescriptor, JsNameRef> openClassDescriptorToQualifiedLabel = new THashMap<ClassDescriptor, JsNameRef>();
060
061 private final ClassAliasingMap classDescriptorToQualifiedLabel = new ClassAliasingMap() {
062 @NotNull
063 @Override
064 public JsNameRef get(ClassDescriptor descriptor, ClassDescriptor referencedDescriptor) {
065 JsNameRef ref = openClassDescriptorToQualifiedLabel.get(descriptor);
066 if (ref != null) {
067 return ref;
068 }
069
070 // will be resolved later
071 ref = new JsNameRef("<unresolved class>");
072 openClassDescriptorToQualifiedLabel.put(descriptor, ref);
073 return ref;
074 }
075 };
076
077 @NotNull
078 private final JsFunction dummyFunction;
079
080 private final JsNameRef declarationsObjectRef;
081 private final JsVar classesVar;
082
083 public ClassDeclarationTranslator(@NotNull TranslationContext context) {
084 super(context);
085
086 dummyFunction = new JsFunction(context.scope());
087 JsName declarationsObject = context().scope().declareName(Namer.nameForClassesVariable());
088 classesVar = new JsVars.JsVar(declarationsObject);
089 declarationsObjectRef = declarationsObject.makeRef();
090 }
091
092 private final class ClassDescriptorToLabel implements ClassAliasingMap {
093 @Nullable
094 @Override
095 public JsNameRef get(ClassDescriptor descriptor, ClassDescriptor referencedDescriptor) {
096 OpenClassInfo item = openClassDescriptorToItem.get(descriptor);
097 // class declared in library
098 if (item == null) {
099 return null;
100 }
101
102 return item.label;
103 }
104 }
105
106 private static class OpenClassInfo {
107 private final ClassDescriptor descriptor;
108 private final JetClass declaration;
109 private final JsNameRef label;
110 private boolean referencedFromOpenClass = false;
111
112 private OpenClassInfo(JetClass declaration, ClassDescriptor descriptor, JsNameRef label) {
113 this.descriptor = descriptor;
114 this.declaration = declaration;
115 this.label = label;
116 }
117 }
118
119 @NotNull
120 public JsVars.JsVar getDeclaration() {
121 return classesVar;
122 }
123
124 public void generateDeclarations() {
125 List<JsVar> vars = new SmartList<JsVar>();
126 List<JsPropertyInitializer> propertyInitializers = new SmartList<JsPropertyInitializer>();
127
128 generateOpenClassDeclarations(vars, propertyInitializers);
129 fixUnresolvedClassReferences();
130
131 if (vars.isEmpty()) {
132 if (!propertyInitializers.isEmpty()) {
133 classesVar.setInitExpression(new JsObjectLiteral(propertyInitializers, true));
134 }
135 return;
136 }
137
138 dummyFunction.setBody(new JsBlock(new JsVars(vars, true), new JsReturn(new JsObjectLiteral(propertyInitializers))));
139 classesVar.setInitExpression(new JsInvocation(dummyFunction));
140 }
141
142 private void generateOpenClassDeclarations(@NotNull List<JsVar> vars, @NotNull List<JsPropertyInitializer> propertyInitializers) {
143 // first pass: set up list order
144 LinkedList<OpenClassInfo> sortedOpenClasses =
145 (LinkedList<OpenClassInfo>) DFS.topologicalOrder(openList, new DFS.Neighbors<OpenClassInfo>() {
146 @NotNull
147 @Override
148 public Iterable<OpenClassInfo> getNeighbors(OpenClassInfo current) {
149 LinkedList<OpenClassInfo> parents = new LinkedList<OpenClassInfo>();
150 ClassDescriptor classDescriptor = getClassDescriptor(context().bindingContext(), current.declaration);
151 Collection<JetType> superTypes = classDescriptor.getTypeConstructor().getSupertypes();
152
153 for (JetType type : superTypes) {
154 ClassDescriptor descriptor = getClassDescriptorForType(type);
155 OpenClassInfo item = openClassDescriptorToItem.get(descriptor);
156 if (item == null) {
157 continue;
158 }
159
160 item.referencedFromOpenClass = true;
161 parents.add(item);
162 }
163
164 return parents;
165 }
166 });
167
168 assert sortedOpenClasses.size() == openList.size();
169
170 // second pass: generate
171 Iterator<OpenClassInfo> it = sortedOpenClasses.descendingIterator();
172 while (it.hasNext()) {
173 OpenClassInfo item = it.next();
174 JsExpression translatedDeclaration =
175 new ClassTranslator(item.declaration, item.descriptor, classDescriptorToLabel, context()).translate();
176
177 JsExpression value;
178 if (item.referencedFromOpenClass) {
179 vars.add(new JsVar(item.label.getName(), translatedDeclaration));
180 value = item.label;
181 }
182 else {
183 value = translatedDeclaration;
184 }
185
186 propertyInitializers.add(new JsPropertyInitializer(item.label, value));
187 }
188 }
189
190 private void fixUnresolvedClassReferences() {
191 openClassDescriptorToQualifiedLabel.forEachEntry(new TObjectObjectProcedure<ClassDescriptor, JsNameRef>() {
192 @Override
193 public boolean execute(ClassDescriptor descriptor, JsNameRef ref) {
194 if (ref.getName() == null) {
195 // from library
196 ref.resolve(context().getNameForDescriptor(descriptor));
197 ref.setQualifier(context().getQualifierForDescriptor(descriptor));
198 }
199 return true;
200 }
201 });
202 }
203
204 private String createNameForClass(ClassDescriptor descriptor) {
205 String suggestedName = descriptor.getName().asString();
206 String name = suggestedName;
207 DeclarationDescriptor containing = descriptor;
208 while (!nameClashGuard.add(name)) {
209 containing = containing.getContainingDeclaration();
210 assert containing != null;
211 name = suggestedName + '_' + containing.getName().asString();
212 }
213 return name;
214 }
215
216 @Nullable
217 public JsNameRef getQualifiedReference(ClassDescriptor descriptor) {
218 if (descriptor.getModality() != Modality.FINAL) {
219 //noinspection ConstantConditions
220 return classDescriptorToQualifiedLabel.get(descriptor, null);
221 }
222 return null;
223 }
224
225 @Nullable
226 public JsPropertyInitializer translate(@NotNull JetClassOrObject declaration, TranslationContext context) {
227 ClassDescriptor descriptor = getClassDescriptor(context().bindingContext(), declaration);
228 JsExpression value;
229 if (descriptor.getModality() == Modality.FINAL) {
230 value = new ClassTranslator(declaration, classDescriptorToQualifiedLabel, context).translate();
231 }
232 else {
233 String label = createNameForClass(descriptor);
234 JsName name = dummyFunction.getScope().declareName(label);
235 JsNameRef qualifiedLabel = openClassDescriptorToQualifiedLabel.get(descriptor);
236 if (qualifiedLabel == null) {
237 qualifiedLabel = new JsNameRef(name);
238 openClassDescriptorToQualifiedLabel.put(descriptor, qualifiedLabel);
239 }
240 else {
241 qualifiedLabel.resolve(name);
242 }
243 qualifiedLabel.setQualifier(declarationsObjectRef);
244
245 OpenClassInfo item = new OpenClassInfo((JetClass) declaration, descriptor, name.makeRef());
246
247 openList.add(item);
248 openClassDescriptorToItem.put(descriptor, item);
249
250 value = qualifiedLabel;
251
252 // not public api classes referenced to internal var _c
253 if (!descriptor.getVisibility().isPublicAPI()) {
254 return null;
255 }
256 }
257
258 return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), value);
259 }
260 }