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 }