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