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.context;
018
019 import com.google.common.collect.Maps;
020 import com.google.dart.compiler.backend.js.ast.*;
021 import com.intellij.openapi.util.Factory;
022 import com.intellij.psi.PsiElement;
023 import com.intellij.util.containers.ContainerUtil;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.annotations.Nullable;
026 import org.jetbrains.jet.lang.descriptors.*;
027 import org.jetbrains.jet.lang.reflect.ReflectionTypes;
028 import org.jetbrains.jet.lang.resolve.BindingContext;
029 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
030 import org.jetbrains.jet.lang.resolve.name.FqName;
031 import org.jetbrains.k2js.config.EcmaVersion;
032 import org.jetbrains.k2js.config.LibrarySourcesConfig;
033 import org.jetbrains.k2js.translate.context.generator.Generator;
034 import org.jetbrains.k2js.translate.context.generator.Rule;
035 import org.jetbrains.k2js.translate.intrinsic.Intrinsics;
036 import org.jetbrains.k2js.translate.utils.JsAstUtils;
037
038 import java.util.Map;
039
040 import static org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
041 import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.*;
042 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.*;
043 import static org.jetbrains.k2js.translate.utils.TranslationUtils.getMangledName;
044 import static org.jetbrains.k2js.translate.utils.TranslationUtils.getSuggestedName;
045
046 /**
047 * Aggregates all the static parts of the context.
048 */
049 public final class StaticContext {
050
051 public static StaticContext generateStaticContext(@NotNull BindingContext bindingContext, @NotNull EcmaVersion ecmaVersion, @NotNull ModuleDescriptor moduleDescriptor) {
052 JsProgram program = new JsProgram("main");
053 Namer namer = Namer.newInstance(program.getRootScope());
054 Intrinsics intrinsics = new Intrinsics();
055 StandardClasses standardClasses = StandardClasses.bindImplementations(namer.getKotlinScope());
056 return new StaticContext(program, bindingContext, namer, intrinsics, standardClasses, program.getRootScope(), ecmaVersion, moduleDescriptor);
057 }
058
059 @NotNull
060 private final JsProgram program;
061
062 @NotNull
063 private final BindingContext bindingContext;
064 @NotNull
065 private final Namer namer;
066
067 @NotNull
068 private final Intrinsics intrinsics;
069
070 @NotNull
071 private final StandardClasses standardClasses;
072
073 @NotNull
074 private final ReflectionTypes reflectionTypes;
075
076 @NotNull
077 private final JsScope rootScope;
078
079 @NotNull
080 private final Generator<JsName> names = new NameGenerator();
081 @NotNull
082 private final Map<FqName, JsName> packageNames = Maps.newHashMap();
083 @NotNull
084 private final Generator<JsScope> scopes = new ScopeGenerator();
085 @NotNull
086 private final Generator<JsNameRef> qualifiers = new QualifierGenerator();
087 @NotNull
088 private final Generator<Boolean> qualifierIsNull = new QualifierIsNullGenerator();
089
090 @NotNull
091 private final Map<JsScope, JsFunction> scopeToFunction = Maps.newHashMap();
092
093 @NotNull
094 private final EcmaVersion ecmaVersion;
095
096 //TODO: too many parameters in constructor
097 private StaticContext(@NotNull JsProgram program, @NotNull BindingContext bindingContext,
098 @NotNull Namer namer, @NotNull Intrinsics intrinsics,
099 @NotNull StandardClasses standardClasses, @NotNull JsScope rootScope, @NotNull EcmaVersion ecmaVersion, @NotNull ModuleDescriptor moduleDescriptor) {
100 this.program = program;
101 this.bindingContext = bindingContext;
102 this.namer = namer;
103 this.intrinsics = intrinsics;
104 this.rootScope = rootScope;
105 this.standardClasses = standardClasses;
106 this.ecmaVersion = ecmaVersion;
107 this.reflectionTypes = new ReflectionTypes(moduleDescriptor);
108 }
109
110 public boolean isEcma5() {
111 return ecmaVersion == EcmaVersion.v5;
112 }
113
114 @NotNull
115 public JsProgram getProgram() {
116 return program;
117 }
118
119 @NotNull
120 public BindingContext getBindingContext() {
121 return bindingContext;
122 }
123
124 @NotNull
125 public Intrinsics getIntrinsics() {
126 return intrinsics;
127 }
128
129 @NotNull
130 public Namer getNamer() {
131 return namer;
132 }
133
134 @NotNull
135 public ReflectionTypes getReflectionTypes() {
136 return reflectionTypes;
137 }
138
139 @NotNull
140 public JsScope getRootScope() {
141 return rootScope;
142 }
143
144 @NotNull
145 public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
146 JsScope scope = scopes.get(descriptor.getOriginal());
147 assert scope != null : "Must have a scope for descriptor";
148 return scope;
149 }
150
151 @NotNull
152 public JsFunction getFunctionWithScope(@NotNull CallableDescriptor descriptor) {
153 JsScope scope = getScopeForDescriptor(descriptor);
154 JsFunction function = scopeToFunction.get(scope);
155 assert scope.equals(function.getScope()) : "Inconsistency.";
156 return function;
157 }
158
159 @NotNull
160 public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
161 if (descriptor instanceof PackageViewDescriptor) {
162 return getQualifiedReference(((PackageViewDescriptor) descriptor).getFqName());
163 }
164 if (descriptor instanceof PackageFragmentDescriptor) {
165 return getQualifiedReference(((PackageFragmentDescriptor) descriptor).getFqName());
166 }
167
168 return new JsNameRef(getNameForDescriptor(descriptor), getQualifierForDescriptor(descriptor));
169 }
170
171 @NotNull
172 public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
173 return new JsNameRef(getNameForPackage(packageFqName),
174 packageFqName.isRoot() ? null : getQualifierForParentPackage(packageFqName.parent()));
175 }
176
177 @NotNull
178 public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
179 JsName name = names.get(descriptor.getOriginal());
180 assert name != null : "Must have name for descriptor";
181 return name;
182 }
183
184 @NotNull
185 public JsName getNameForPackage(@NotNull final FqName packageFqName) {
186 return ContainerUtil.getOrCreate(packageNames, packageFqName, new Factory<JsName>() {
187 @Override
188 public JsName create() {
189 String name = Namer.generatePackageName(packageFqName);
190 return getRootScope().declareName(name);
191 }
192 });
193 }
194
195 @NotNull
196 private JsNameRef getQualifierForParentPackage(@NotNull FqName packageFqName) {
197 JsNameRef result = null;
198 JsNameRef qualifier = null;
199
200 for (FqName pathElement : ContainerUtil.reverse(packageFqName.path())) {
201 JsNameRef ref = getNameForPackage(pathElement).makeRef();
202
203 if (qualifier == null) {
204 result = ref;
205 }
206 else {
207 qualifier.setQualifier(ref);
208 }
209
210 qualifier = ref;
211 }
212
213 assert result != null : "didn't iterate: " + packageFqName;
214 return result;
215 }
216
217 private final class NameGenerator extends Generator<JsName> {
218
219 public NameGenerator() {
220 Rule<JsName> namesForStandardClasses = new Rule<JsName>() {
221 @Override
222 @Nullable
223 public JsName apply(@NotNull DeclarationDescriptor data) {
224 if (!standardClasses.isStandardObject(data)) {
225 return null;
226 }
227 return standardClasses.getStandardObjectName(data);
228 }
229 };
230 Rule<JsName> memberDeclarationsInsideParentsScope = new Rule<JsName>() {
231 @Override
232 @Nullable
233 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
234 JsScope scope = getEnclosingScope(descriptor);
235 return scope.declareFreshName(getSuggestedName(descriptor));
236 }
237 };
238 Rule<JsName> constructorOrClassObjectHasTheSameNameAsTheClass = new Rule<JsName>() {
239 @Override
240 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
241 if (descriptor instanceof ConstructorDescriptor || (DescriptorUtils.isClassObject(descriptor))) {
242 //noinspection ConstantConditions
243 return getNameForDescriptor(descriptor.getContainingDeclaration());
244 }
245 return null;
246 }
247 };
248
249 // ecma 5 property name never declares as obfuscatable:
250 // 1) property cannot be overloaded, so, name collision is not possible
251 // 2) main reason: if property doesn't have any custom accessor, value holder will have the same name as accessor, so, the same name will be declared more than once
252 //
253 // But extension property may obfuscatable, because transform into function. Example: String.foo = 1, Int.foo = 2
254 Rule<JsName> propertyOrPropertyAccessor = new Rule<JsName>() {
255 @Override
256 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
257 PropertyDescriptor propertyDescriptor;
258 if (descriptor instanceof PropertyAccessorDescriptor) {
259 propertyDescriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty();
260 }
261 else if (descriptor instanceof PropertyDescriptor) {
262 propertyDescriptor = (PropertyDescriptor) descriptor;
263 }
264 else {
265 return null;
266 }
267
268 String nameFromAnnotation = getNameForAnnotatedObjectWithOverrides(propertyDescriptor);
269 if (nameFromAnnotation != null) {
270 return declarePropertyOrPropertyAccessorName(descriptor, nameFromAnnotation, false);
271 }
272
273 String propertyName = propertyDescriptor.getName().asString();
274
275 if (!isExtension(propertyDescriptor)) {
276 if (propertyDescriptor.getVisibility() == Visibilities.PRIVATE) {
277 propertyName = getMangledName(propertyDescriptor, propertyName);
278 }
279 return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false);
280 } else {
281 if (descriptor instanceof PropertyDescriptor) {
282 return declarePropertyOrPropertyAccessorName(descriptor, propertyName, true);
283 } else {
284 String propertyJsName = getNameForDescriptor(propertyDescriptor).getIdent();
285 boolean isGetter = descriptor instanceof PropertyGetterDescriptor;
286 String accessorName = Namer.getNameForAccessor(propertyJsName, isGetter, false);
287 return declarePropertyOrPropertyAccessorName(descriptor, accessorName, false);
288 }
289 }
290 }
291 };
292
293 Rule<JsName> predefinedObjectsHasUnobfuscatableNames = new Rule<JsName>() {
294 @Override
295 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
296 // The mixing of override and rename by annotation(e.g. native) is forbidden.
297 if (descriptor instanceof CallableMemberDescriptor &&
298 !((CallableMemberDescriptor) descriptor).getOverriddenDescriptors().isEmpty()) {
299 return null;
300 }
301
302 String name = getNameForAnnotatedObjectWithOverrides(descriptor);
303 if (name != null) return getEnclosingScope(descriptor).declareName(name);
304 return null;
305 }
306 };
307
308 Rule<JsName> overridingDescriptorsReferToOriginalName = new Rule<JsName>() {
309 @Override
310 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
311 //TODO: refactor
312 if (!(descriptor instanceof FunctionDescriptor)) {
313 return null;
314 }
315 FunctionDescriptor overriddenDescriptor = getOverriddenDescriptor((FunctionDescriptor) descriptor);
316 if (overriddenDescriptor == null) {
317 return null;
318 }
319
320 JsScope scope = getEnclosingScope(descriptor);
321 JsName result = getNameForDescriptor(overriddenDescriptor);
322 scope.declareName(result.getIdent());
323 return result;
324 }
325 };
326 addRule(namesForStandardClasses);
327 addRule(constructorOrClassObjectHasTheSameNameAsTheClass);
328 addRule(propertyOrPropertyAccessor);
329 addRule(predefinedObjectsHasUnobfuscatableNames);
330 addRule(overridingDescriptorsReferToOriginalName);
331 addRule(memberDeclarationsInsideParentsScope);
332 }
333 }
334
335 @NotNull
336 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
337 JsScope scope = getEnclosingScope(descriptor);
338 return fresh ? scope.declareFreshName(name) : scope.declareName(name);
339 }
340
341 @NotNull
342 private JsScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) {
343 DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor);
344 return getScopeForDescriptor(containingDeclaration.getOriginal());
345 }
346
347 private final class ScopeGenerator extends Generator<JsScope> {
348
349 public ScopeGenerator() {
350 Rule<JsScope> generateNewScopesForClassesWithNoAncestors = new Rule<JsScope>() {
351 @Override
352 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
353 if (!(descriptor instanceof ClassDescriptor)) {
354 return null;
355 }
356 if (getSuperclass((ClassDescriptor) descriptor) == null) {
357 return getRootScope().innerObjectScope("Scope for class " + descriptor.getName());
358 }
359 return null;
360 }
361 };
362 Rule<JsScope> generateInnerScopesForDerivedClasses = new Rule<JsScope>() {
363 @Override
364 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
365 if (!(descriptor instanceof ClassDescriptor)) {
366 return null;
367 }
368 ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor);
369 if (superclass == null) {
370 return null;
371 }
372 return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName());
373 }
374 };
375 Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() {
376 @Override
377 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
378 if (!(descriptor instanceof PackageFragmentDescriptor)) {
379 return null;
380 }
381 return getRootScope().innerObjectScope("Package " + descriptor.getName());
382 }
383 };
384 //TODO: never get there
385 Rule<JsScope> generateInnerScopesForMembers = new Rule<JsScope>() {
386 @Override
387 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
388 JsScope enclosingScope = getEnclosingScope(descriptor);
389 return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName());
390 }
391 };
392 Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() {
393 @Override
394 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
395 if (!(descriptor instanceof CallableDescriptor)) {
396 return null;
397 }
398 JsScope enclosingScope = getEnclosingScope(descriptor);
399
400 JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(enclosingScope);
401 assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
402 scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
403 return correspondingFunction.getScope();
404 }
405 };
406 addRule(createFunctionObjectsForCallableDescriptors);
407 addRule(generateNewScopesForClassesWithNoAncestors);
408 addRule(generateInnerScopesForDerivedClasses);
409 addRule(generateNewScopesForPackageDescriptors);
410 addRule(generateInnerScopesForMembers);
411 }
412 }
413
414 @Nullable
415 public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
416 if (qualifierIsNull.get(descriptor.getOriginal()) != null) {
417 return null;
418 }
419 return qualifiers.get(descriptor.getOriginal());
420 }
421
422 private final class QualifierGenerator extends Generator<JsNameRef> {
423 public QualifierGenerator() {
424 Rule<JsNameRef> standardObjectsHaveKotlinQualifier = new Rule<JsNameRef>() {
425 @Override
426 public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
427 if (!standardClasses.isStandardObject(descriptor)) {
428 return null;
429 }
430 return namer.kotlinObject();
431 }
432 };
433 //TODO: review and refactor
434 Rule<JsNameRef> packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier = new Rule<JsNameRef>() {
435 @Override
436 public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
437 if (isNativeObject(descriptor)) return null;
438
439 DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor);
440 if (!(containingDescriptor instanceof PackageFragmentDescriptor)) {
441 return null;
442 }
443
444 JsNameRef result = getQualifierForParentPackage(((PackageFragmentDescriptor) containingDescriptor).getFqName());
445
446 String moduleName = getExternalModuleName(descriptor);
447 if (moduleName == null) {
448 return result;
449 }
450
451 if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) {
452 return null;
453 }
454
455 JsAstUtils.replaceRootReference(
456 result, new JsArrayAccess(namer.kotlin("modules"), program.getStringLiteral(moduleName)));
457 return result;
458 }
459
460 private String getExternalModuleName(DeclarationDescriptor descriptor) {
461 PsiElement element = descriptorToDeclaration(descriptor);
462 if (element == null && descriptor instanceof PropertyAccessorDescriptor) {
463 element = descriptorToDeclaration(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty());
464 }
465
466 if (element == null) {
467 return null;
468 }
469 return element.getContainingFile().getUserData(LibrarySourcesConfig.EXTERNAL_MODULE_NAME);
470 }
471 };
472 Rule<JsNameRef> constructorOrClassObjectHasTheSameQualifierAsTheClass = new Rule<JsNameRef>() {
473 @Override
474 public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
475 if (descriptor instanceof ConstructorDescriptor || DescriptorUtils.isClassObject(descriptor)) {
476 //noinspection ConstantConditions
477 return getQualifierForDescriptor(descriptor.getContainingDeclaration());
478 }
479 return null;
480 }
481 };
482 Rule<JsNameRef> libraryObjectsHaveKotlinQualifier = new Rule<JsNameRef>() {
483 @Override
484 public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
485 if (isLibraryObject(descriptor)) {
486 return namer.kotlinObject();
487 }
488 return null;
489 }
490 };
491 Rule<JsNameRef> nativeObjectsHaveNativePartOfFullQualifier = new Rule<JsNameRef>() {
492 @Override
493 public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
494 if (descriptor instanceof ConstructorDescriptor || !isNativeObject(descriptor)) return null;
495
496 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
497 if (containingDeclaration != null && isNativeObject(containingDeclaration)) {
498 return getQualifiedReference(containingDeclaration);
499 }
500
501 return null;
502 }
503 };
504 Rule<JsNameRef> staticMembersHaveContainerQualifier = new Rule<JsNameRef>() {
505 @Override
506 public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
507 if (descriptor instanceof CallableDescriptor && !isNativeObject(descriptor)) {
508 CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
509 if (DescriptorUtils.isStaticDeclaration(callableDescriptor)) {
510 return getQualifiedReference(callableDescriptor.getContainingDeclaration());
511 }
512 }
513
514 return null;
515 }
516 };
517
518 addRule(libraryObjectsHaveKotlinQualifier);
519 addRule(constructorOrClassObjectHasTheSameQualifierAsTheClass);
520 addRule(standardObjectsHaveKotlinQualifier);
521 addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier);
522 addRule(nativeObjectsHaveNativePartOfFullQualifier);
523 addRule(staticMembersHaveContainerQualifier);
524 }
525 }
526
527 private static class QualifierIsNullGenerator extends Generator<Boolean> {
528
529 private QualifierIsNullGenerator() {
530 Rule<Boolean> propertiesInClassHaveNoQualifiers = new Rule<Boolean>() {
531 @Override
532 public Boolean apply(@NotNull DeclarationDescriptor descriptor) {
533 if ((descriptor instanceof PropertyDescriptor) && descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
534 return true;
535 }
536 return null;
537 }
538 };
539 addRule(propertiesInClassHaveNoQualifiers);
540 }
541 }
542 }