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