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