001    /*
002     * Copyright 2010-2016 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.kotlin.js.translate.context;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Maps;
021    import com.google.dart.compiler.backend.js.ast.*;
022    import com.google.dart.compiler.backend.js.ast.metadata.HasMetadata;
023    import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
024    import com.google.dart.compiler.backend.js.ast.metadata.SideEffectKind;
025    import com.intellij.openapi.util.Factory;
026    import com.intellij.util.containers.ContainerUtil;
027    import com.intellij.util.containers.hash.LinkedHashMap;
028    import org.jetbrains.annotations.NotNull;
029    import org.jetbrains.annotations.Nullable;
030    import org.jetbrains.kotlin.descriptors.*;
031    import org.jetbrains.kotlin.js.config.JsConfig;
032    import org.jetbrains.kotlin.js.naming.NameSuggestion;
033    import org.jetbrains.kotlin.js.naming.SuggestedName;
034    import org.jetbrains.kotlin.js.translate.context.generator.Generator;
035    import org.jetbrains.kotlin.js.translate.context.generator.Rule;
036    import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
037    import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
038    import org.jetbrains.kotlin.name.FqName;
039    import org.jetbrains.kotlin.resolve.BindingContext;
040    import org.jetbrains.kotlin.resolve.BindingTrace;
041    import org.jetbrains.kotlin.resolve.DescriptorUtils;
042    import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
043    
044    import java.util.*;
045    
046    import static org.jetbrains.kotlin.js.config.LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME;
047    import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isLibraryObject;
048    import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject;
049    import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn;
050    import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getContainingDeclaration;
051    import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSuperclass;
052    
053    /**
054     * Aggregates all the static parts of the context.
055     */
056    public final class StaticContext {
057    
058        public static StaticContext generateStaticContext(
059                @NotNull BindingTrace bindingTrace,
060                @NotNull JsConfig config,
061                @NotNull ModuleDescriptor moduleDescriptor) {
062            JsProgram program = new JsProgram("main");
063            Namer namer = Namer.newInstance(program.getRootScope());
064            Intrinsics intrinsics = new Intrinsics();
065            StandardClasses standardClasses = StandardClasses.bindImplementations(namer.getKotlinScope());
066            return new StaticContext(program, bindingTrace, namer, intrinsics, standardClasses, program.getRootScope(), config,
067                                     moduleDescriptor);
068        }
069    
070        @NotNull
071        private final JsProgram program;
072    
073        @NotNull
074        private final BindingTrace bindingTrace;
075        @NotNull
076        private final Namer namer;
077    
078        @NotNull
079        private final Intrinsics intrinsics;
080    
081        @NotNull
082        private final StandardClasses standardClasses;
083    
084        @NotNull
085        private final JsScope rootScope;
086    
087        @NotNull
088        private final Map<FqName, JsName> packageNames = Maps.newHashMap();
089        @NotNull
090        private final Generator<JsScope> scopes = new ScopeGenerator();
091    
092        @NotNull
093        private final Map<JsScope, JsFunction> scopeToFunction = Maps.newHashMap();
094    
095        @NotNull
096        private final Map<MemberDescriptor, List<DeclarationDescriptor>> classOrConstructorClosure = Maps.newHashMap();
097    
098        @NotNull
099        private final Map<ClassDescriptor, List<DeferredCallSite>> deferredCallSites = new HashMap<ClassDescriptor, List<DeferredCallSite>>();
100    
101        @NotNull
102        private final JsConfig config;
103    
104        @NotNull
105        private final ModuleDescriptor currentModule;
106    
107        @NotNull
108        private final NameSuggestion nameSuggestion = new NameSuggestion();
109    
110        @NotNull
111        private final Map<DeclarationDescriptor, JsName> nameCache = new HashMap<DeclarationDescriptor, JsName>();
112    
113        @NotNull
114        private final Map<PropertyDescriptor, JsName> backingFieldNameCache = new HashMap<PropertyDescriptor, JsName>();
115    
116        @NotNull
117        private final Map<DeclarationDescriptor, JsExpression> fqnCache = new HashMap<DeclarationDescriptor, JsExpression>();
118    
119        @NotNull
120        private final Map<String, JsName> importedModules = new LinkedHashMap<String, JsName>();
121    
122        private Map<String, JsName> readOnlyImportedModules;
123    
124        @NotNull
125        private final JsScope rootPackageScope;
126    
127        //TODO: too many parameters in constructor
128        private StaticContext(
129                @NotNull JsProgram program,
130                @NotNull BindingTrace bindingTrace,
131                @NotNull Namer namer,
132                @NotNull Intrinsics intrinsics,
133                @NotNull StandardClasses standardClasses,
134                @NotNull JsScope rootScope,
135                @NotNull JsConfig config,
136                @NotNull ModuleDescriptor moduleDescriptor
137        ) {
138            this.program = program;
139            this.bindingTrace = bindingTrace;
140            this.namer = namer;
141            this.intrinsics = intrinsics;
142            this.rootScope = rootScope;
143            this.standardClasses = standardClasses;
144            this.config = config;
145            currentModule = moduleDescriptor;
146            rootPackageScope = new JsObjectScope(rootScope, "<root package>", "root-package");
147        }
148    
149        @NotNull
150        public JsProgram getProgram() {
151            return program;
152        }
153    
154        @NotNull
155        public BindingTrace getBindingTrace() {
156            return bindingTrace;
157        }
158    
159        @NotNull
160        public BindingContext getBindingContext() {
161            return bindingTrace.getBindingContext();
162        }
163    
164        @NotNull
165        public Intrinsics getIntrinsics() {
166            return intrinsics;
167        }
168    
169        @NotNull
170        public Namer getNamer() {
171            return namer;
172        }
173    
174        @NotNull
175        private JsScope getRootScope() {
176            return rootScope;
177        }
178    
179        @NotNull
180        public Map<String, JsName> getImportedModules() {
181            if (readOnlyImportedModules == null) {
182                readOnlyImportedModules = Collections.unmodifiableMap(importedModules);
183            }
184            return readOnlyImportedModules;
185        }
186    
187        @NotNull
188        public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
189            if (descriptor instanceof ModuleDescriptor) {
190                return rootScope;
191            }
192            JsScope scope = scopes.get(descriptor.getOriginal());
193            assert scope != null : "Must have a scope for descriptor";
194            return scope;
195        }
196    
197        @NotNull
198        public JsFunction getFunctionWithScope(@NotNull CallableDescriptor descriptor) {
199            JsScope scope = getScopeForDescriptor(descriptor);
200            JsFunction function = scopeToFunction.get(scope);
201            assert scope.equals(function.getScope()) : "Inconsistency.";
202            return function;
203        }
204    
205        @NotNull
206        public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
207            return (JsNameRef) getQualifiedExpression(descriptor);
208        }
209    
210        @NotNull
211        private JsExpression getQualifiedExpression(@NotNull DeclarationDescriptor descriptor) {
212            JsExpression fqn = fqnCache.get(descriptor);
213            if (fqn == null) {
214                fqn = buildQualifiedExpression(descriptor);
215                fqnCache.put(descriptor, fqn);
216            }
217            return fqn.deepCopy();
218        }
219    
220        @NotNull
221        private JsExpression buildQualifiedExpression(@NotNull DeclarationDescriptor descriptor) {
222            SuggestedName suggested = nameSuggestion.suggest(descriptor);
223            if (suggested == null) {
224                ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
225                JsExpression result = getModuleExpressionFor(module);
226                return result != null ? result : pureFqn(Namer.getRootPackageName(), null);
227            }
228    
229            JsExpression expression;
230            List<JsName> partNames;
231            if (standardClasses.isStandardObject(suggested.getDescriptor())) {
232                expression = Namer.kotlinObject();
233                partNames = Collections.singletonList(standardClasses.getStandardObjectName(suggested.getDescriptor()));
234            }
235            else {
236                partNames = getActualNameFromSuggested(suggested);
237                if (isLibraryObject(suggested.getDescriptor())) {
238                    expression = Namer.kotlinObject();
239                }
240                // Don't generate qualifier for top-level native declarations
241                // Don't generate qualifier for local declarations
242                else if (isNativeObject(suggested.getDescriptor()) && !isNativeObject(suggested.getScope()) ||
243                         suggested.getDescriptor() instanceof CallableDescriptor && suggested.getScope() instanceof FunctionDescriptor) {
244                    expression = null;
245                }
246                else {
247                    expression = getQualifiedExpression(suggested.getScope());
248                }
249            }
250            for (JsName partName : partNames) {
251                expression = new JsNameRef(partName, expression);
252                applySideEffects(expression, suggested.getDescriptor());
253            }
254            assert expression != null : "Since partNames is not empty, expression must be non-null";
255            return expression;
256        }
257    
258        @NotNull
259        public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
260            JsName packageName = getNameForPackage(packageFqName);
261            return pureFqn(packageName, packageFqName.isRoot() ? null : getQualifierForParentPackage(packageFqName.parent()));
262        }
263    
264        @NotNull
265        public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
266            SuggestedName suggested = nameSuggestion.suggest(descriptor);
267            if (suggested == null) {
268                throw new IllegalArgumentException("Can't generate name for root declarations: " + descriptor);
269            }
270            return getActualNameFromSuggested(suggested).get(0);
271        }
272    
273        @NotNull
274        public JsName getNameForBackingField(@NotNull PropertyDescriptor property) {
275            JsName name = backingFieldNameCache.get(property);
276    
277            if (name == null) {
278                SuggestedName fqn = nameSuggestion.suggest(property);
279                assert fqn != null : "Properties are non-root declarations: " + property;
280                assert fqn.getNames().size() == 1 : "Private names must always consist of exactly one name";
281    
282                JsScope scope = getScopeForDescriptor(fqn.getScope());
283                String baseName = NameSuggestion.getPrivateMangledName(fqn.getNames().get(0), property) + "_0";
284                name = scope.declareFreshName(baseName);
285                backingFieldNameCache.put(property, name);
286            }
287    
288            return name;
289        }
290    
291        @NotNull
292        private List<JsName> getActualNameFromSuggested(@NotNull SuggestedName suggested) {
293            JsScope scope = getScopeForDescriptor(suggested.getScope());
294    
295            if (DynamicCallsKt.isDynamic(suggested.getDescriptor())) {
296                scope = JsDynamicScope.INSTANCE;
297            }
298    
299            List<JsName> names = new ArrayList<JsName>();
300            if (suggested.getStable()) {
301                for (String namePart : suggested.getNames()) {
302                    names.add(scope.declareName(namePart));
303                }
304            }
305            else {
306                // TODO: consider using sealed class to represent FQNs
307                assert suggested.getNames().size() == 1 : "Private names must always consist of exactly one name";
308                JsName name = nameCache.get(suggested.getDescriptor());
309                if (name == null) {
310                    String baseName = suggested.getNames().get(0);
311                    if (!DescriptorUtils.isDescriptorWithLocalVisibility(suggested.getDescriptor())) {
312                        baseName += "_0";
313                    }
314                    name = scope.declareFreshName(baseName);
315                }
316                nameCache.put(suggested.getDescriptor(), name);
317                names.add(name);
318            }
319    
320            return names;
321        }
322    
323        @NotNull
324        public JsName getNameForPackage(@NotNull final FqName packageFqName) {
325            return ContainerUtil.getOrCreate(packageNames, packageFqName, new Factory<JsName>() {
326                @Override
327                public JsName create() {
328                    String name = Namer.generatePackageName(packageFqName);
329                    return rootPackageScope.declareName(name);
330                }
331            });
332        }
333    
334        @NotNull
335        private JsNameRef getQualifierForParentPackage(@NotNull FqName packageFqName) {
336            JsNameRef result = null;
337            JsNameRef qualifier = null;
338    
339            FqName fqName = packageFqName;
340    
341            while (true) {
342                JsNameRef ref = pureFqn(getNameForPackage(fqName), null);
343    
344                if (qualifier == null) {
345                    result = ref;
346                }
347                else {
348                    qualifier.setQualifier(ref);
349                }
350    
351                qualifier = ref;
352    
353                if (fqName.isRoot()) break;
354                fqName = fqName.parent();
355            }
356    
357            return result;
358        }
359    
360        @NotNull
361        public JsConfig getConfig() {
362            return config;
363        }
364    
365        @NotNull
366        public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
367            JsScope scope = getEnclosingScope(descriptor);
368            return fresh ? scope.declareFreshName(name) : scope.declareName(name);
369        }
370    
371        @NotNull
372        private JsScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) {
373            DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor);
374            return getScopeForDescriptor(containingDeclaration.getOriginal());
375        }
376    
377        private final class ScopeGenerator extends Generator<JsScope> {
378    
379            public ScopeGenerator() {
380                Rule<JsScope> generateNewScopesForClassesWithNoAncestors = new Rule<JsScope>() {
381                    @Override
382                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
383                        if (!(descriptor instanceof ClassDescriptor)) {
384                            return null;
385                        }
386                        if (getSuperclass((ClassDescriptor) descriptor) == null) {
387                            return getRootScope().innerObjectScope("Scope for class " + descriptor.getName());
388                        }
389                        return null;
390                    }
391                };
392                Rule<JsScope> generateInnerScopesForDerivedClasses = new Rule<JsScope>() {
393                    @Override
394                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
395                        if (!(descriptor instanceof ClassDescriptor)) {
396                            return null;
397                        }
398                        ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor);
399                        if (superclass == null) {
400                            return null;
401                        }
402                        return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName());
403                    }
404                };
405                Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() {
406                    @Override
407                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
408                        if (!(descriptor instanceof PackageFragmentDescriptor)) {
409                            return null;
410                        }
411                        return getRootScope().innerObjectScope("Package " + descriptor.getName());
412                    }
413                };
414                //TODO: never get there
415                Rule<JsScope> generateInnerScopesForMembers = new Rule<JsScope>() {
416                    @Override
417                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
418                        JsScope enclosingScope = getEnclosingScope(descriptor);
419                        return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName());
420                    }
421                };
422                Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() {
423                    @Override
424                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
425                        if (!(descriptor instanceof CallableDescriptor)) {
426                            return null;
427                        }
428    
429                        JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(getRootScope());
430                        assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
431                        scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
432                        return correspondingFunction.getScope();
433                    }
434                };
435                addRule(createFunctionObjectsForCallableDescriptors);
436                addRule(generateNewScopesForClassesWithNoAncestors);
437                addRule(generateInnerScopesForDerivedClasses);
438                addRule(generateNewScopesForPackageDescriptors);
439                addRule(generateInnerScopesForMembers);
440            }
441        }
442    
443        @Nullable
444        public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) {
445            ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
446            if (currentModule == module) {
447                return pureFqn(Namer.getRootPackageName(), null);
448            }
449            String moduleName;
450            if (module == module.getBuiltIns().getBuiltInsModule()) {
451                moduleName = Namer.KOTLIN_LOWER_NAME;
452            }
453            else {
454                moduleName = module.getName().asString();
455                moduleName = moduleName.substring(1, moduleName.length() - 1);
456            }
457    
458            if (UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) return null;
459    
460            JsName moduleId = moduleName.equals(Namer.KOTLIN_LOWER_NAME) ? rootScope.declareName(Namer.KOTLIN_NAME) :
461                              importedModules.get(moduleName);
462            if (moduleId == null) {
463                moduleId = rootScope.declareFreshName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(moduleName));
464                importedModules.put(moduleName, moduleId);
465            }
466    
467            return JsAstUtils.pureFqn(moduleId, null);
468        }
469    
470        private static JsExpression applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) {
471            if (expression instanceof HasMetadata) {
472                if (descriptor instanceof FunctionDescriptor ||
473                    descriptor instanceof PackageFragmentDescriptor ||
474                    descriptor instanceof ClassDescriptor
475                ) {
476                    MetadataProperties.setSideEffects((HasMetadata) expression, SideEffectKind.PURE);
477                }
478            }
479            return expression;
480        }
481    
482        public void putClassOrConstructorClosure(@NotNull MemberDescriptor localClass, @NotNull List<DeclarationDescriptor> closure) {
483            classOrConstructorClosure.put(localClass, Lists.newArrayList(closure));
484        }
485    
486        @Nullable
487        public List<DeclarationDescriptor> getClassOrConstructorClosure(@NotNull MemberDescriptor descriptor) {
488            List<DeclarationDescriptor> result = classOrConstructorClosure.get(descriptor);
489            return result != null ? Lists.newArrayList(result) : null;
490        }
491    
492        @NotNull
493        public Map<ClassDescriptor, List<DeferredCallSite>> getDeferredCallSites() {
494            return deferredCallSites;
495        }
496    }