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    }