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