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    }