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.expression.LiteralFunctionTranslator;
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.Arrays;
039 import java.util.Collection;
040 import java.util.Comparator;
041 import java.util.Map;
042
043 import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.getNameForAnnotatedObject;
044 import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.isLibraryObject;
045 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.*;
046 import static org.jetbrains.k2js.translate.utils.TranslationUtils.getMangledName;
047
048 /**
049 * Aggregates all the static parts of the context.
050 */
051 public final class StaticContext {
052
053 public static StaticContext generateStaticContext(@NotNull BindingContext bindingContext, @NotNull EcmaVersion ecmaVersion) {
054 JsProgram program = new JsProgram("main");
055 Namer namer = Namer.newInstance(program.getRootScope());
056 Intrinsics intrinsics = new Intrinsics();
057 StandardClasses standardClasses = StandardClasses.bindImplementations(namer.getKotlinScope());
058 return new StaticContext(program, bindingContext, namer, intrinsics, standardClasses, program.getRootScope(), ecmaVersion);
059 }
060
061 @NotNull
062 private final JsProgram program;
063
064 @NotNull
065 private final BindingContext bindingContext;
066 @NotNull
067 private final Namer namer;
068
069 @NotNull
070 private final Intrinsics intrinsics;
071
072 @NotNull
073 private final StandardClasses standardClasses;
074
075 @NotNull
076 private final JsScope rootScope;
077
078 @NotNull
079 private final Generator<JsName> names = new NameGenerator();
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 @NotNull
094 private LiteralFunctionTranslator literalFunctionTranslator;
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) {
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 }
108
109 public void initTranslators(TranslationContext programContext) {
110 literalFunctionTranslator = new LiteralFunctionTranslator(programContext);
111 }
112
113 @NotNull
114 public LiteralFunctionTranslator getLiteralFunctionTranslator() {
115 return literalFunctionTranslator;
116 }
117
118 public boolean isEcma5() {
119 return ecmaVersion == EcmaVersion.v5;
120 }
121
122 @NotNull
123 public JsProgram getProgram() {
124 return program;
125 }
126
127 @NotNull
128 public BindingContext getBindingContext() {
129 return bindingContext;
130 }
131
132 @NotNull
133 public Intrinsics getIntrinsics() {
134 return intrinsics;
135 }
136
137 @NotNull
138 public Namer getNamer() {
139 return namer;
140 }
141
142 @NotNull
143 public JsScope getRootScope() {
144 return rootScope;
145 }
146
147 @NotNull
148 public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
149 JsScope scope = scopes.get(descriptor.getOriginal());
150 assert scope != null : "Must have a scope for descriptor";
151 return scope;
152 }
153
154 @NotNull
155 public JsFunction getFunctionWithScope(@NotNull CallableDescriptor descriptor) {
156 JsScope scope = getScopeForDescriptor(descriptor);
157 JsFunction function = scopeToFunction.get(scope);
158 assert scope.equals(function.getScope()) : "Inconsistency.";
159 return function;
160 }
161
162 @NotNull
163 public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
164 return new JsNameRef(getNameForDescriptor(descriptor), getQualifierForDescriptor(descriptor));
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 private final class NameGenerator extends Generator<JsName> {
175
176 public NameGenerator() {
177 Rule<JsName> namesForStandardClasses = new Rule<JsName>() {
178 @Override
179 @Nullable
180 public JsName apply(@NotNull DeclarationDescriptor data) {
181 if (!standardClasses.isStandardObject(data)) {
182 return null;
183 }
184 return standardClasses.getStandardObjectName(data);
185 }
186 };
187 Rule<JsName> namespacesShouldBeDefinedInRootScope = new Rule<JsName>() {
188 @Override
189 @Nullable
190 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
191 if (!(descriptor instanceof NamespaceDescriptor)) {
192 return null;
193 }
194
195 String name = Namer.generateNamespaceName(descriptor);
196 return getRootScope().declareName(name);
197 }
198 };
199 Rule<JsName> memberDeclarationsInsideParentsScope = new Rule<JsName>() {
200 @Override
201 @Nullable
202 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
203 JsScope scope = getEnclosingScope(descriptor);
204 DeclarationDescriptor declaration = descriptor.getContainingDeclaration();
205 if (!(descriptor instanceof FunctionDescriptor) || !(declaration instanceof ClassDescriptor)) {
206 return scope.declareFreshName(descriptor.getName().asString());
207 }
208
209 Collection<FunctionDescriptor> functions =
210 ((ClassDescriptor) declaration).getDefaultType().getMemberScope().getFunctions(descriptor.getName());
211 String name = descriptor.getName().asString();
212 int counter = -1;
213 if (functions.size() > 1) {
214 // see testOverloadedFun
215 FunctionDescriptor[] sorted = functions.toArray(new FunctionDescriptor[functions.size()]);
216 Arrays.sort(sorted, new Comparator<FunctionDescriptor>() {
217 @Override
218 public int compare(@NotNull FunctionDescriptor a, @NotNull FunctionDescriptor b) {
219 Integer result = Visibilities.compare(b.getVisibility(), a.getVisibility());
220 if (result == null) {
221 return 0;
222 }
223 else if (result == 0) {
224 // open fun > not open fun
225 int aWeight = a.getModality().isOverridable() ? 1 : 0;
226 int bWeight = b.getModality().isOverridable() ? 1 : 0;
227 return bWeight - aWeight;
228 }
229
230 return result;
231 }
232 });
233 for (FunctionDescriptor function : sorted) {
234 if (function == descriptor) {
235 break;
236 }
237 counter++;
238 }
239 }
240
241 return scope.declareName(counter == -1 ? name : name + '_' + counter);
242 }
243 };
244 Rule<JsName> constructorHasTheSameNameAsTheClass = new Rule<JsName>() {
245 @Override
246 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
247 if (!(descriptor instanceof ConstructorDescriptor)) {
248 return null;
249 }
250 ClassDescriptor containingClass = getContainingClass(descriptor);
251 assert containingClass != null : "Can't have constructor without a class";
252 return getNameForDescriptor(containingClass);
253 }
254 };
255
256 // ecma 5 property name never declares as obfuscatable:
257 // 1) property cannot be overloaded, so, name collision is not possible
258 // 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
259 //
260 // But extension property may obfuscatable, because transform into function. Example: String.foo = 1, Int.foo = 2
261 Rule<JsName> propertyOrPropertyAccessor = new Rule<JsName>() {
262 @Override
263 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
264 PropertyDescriptor propertyDescriptor;
265 if (descriptor instanceof PropertyAccessorDescriptor) {
266 propertyDescriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty();
267 }
268 else if (descriptor instanceof PropertyDescriptor) {
269 propertyDescriptor = (PropertyDescriptor) descriptor;
270 }
271 else {
272 return null;
273 }
274
275 String nameFromAnnotation = getNameForAnnotatedObject(propertyDescriptor);
276 if (nameFromAnnotation != null) {
277 return declarePropertyOrPropertyAccessorName(descriptor, nameFromAnnotation, false);
278 }
279
280 String propertyName = propertyDescriptor.getName().asString();
281
282 if (!isExtension(propertyDescriptor)) {
283 if (propertyDescriptor.getVisibility() == Visibilities.PRIVATE) {
284 propertyName = getMangledName(propertyDescriptor, propertyName);
285 }
286 return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false);
287 } else {
288 if (descriptor instanceof PropertyDescriptor) {
289 return declarePropertyOrPropertyAccessorName(descriptor, propertyName, true);
290 } else {
291 String propertyJsName = getNameForDescriptor(propertyDescriptor).getIdent();
292 boolean isGetter = descriptor instanceof PropertyGetterDescriptor;
293 String accessorName = Namer.getNameForAccessor(propertyJsName, isGetter, false);
294 return declarePropertyOrPropertyAccessorName(descriptor, accessorName, false);
295 }
296 }
297 }
298 };
299
300 Rule<JsName> predefinedObjectsHasUnobfuscatableNames = new Rule<JsName>() {
301 @Override
302 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
303 String name = getNameForAnnotatedObject(descriptor);
304 if (name != null) return getEnclosingScope(descriptor).declareName(name);
305 return null;
306 }
307 };
308
309 Rule<JsName> overridingDescriptorsReferToOriginalName = new Rule<JsName>() {
310 @Override
311 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
312 //TODO: refactor
313 if (!(descriptor instanceof FunctionDescriptor)) {
314 return null;
315 }
316 FunctionDescriptor overriddenDescriptor = getOverriddenDescriptor((FunctionDescriptor) descriptor);
317 if (overriddenDescriptor == null) {
318 return null;
319 }
320
321 JsScope scope = getEnclosingScope(descriptor);
322 JsName result = getNameForDescriptor(overriddenDescriptor);
323 scope.declareName(result.getIdent());
324 return result;
325 }
326 };
327 addRule(namesForStandardClasses);
328 addRule(constructorHasTheSameNameAsTheClass);
329 addRule(propertyOrPropertyAccessor);
330 addRule(predefinedObjectsHasUnobfuscatableNames);
331 addRule(namespacesShouldBeDefinedInRootScope);
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> generateNewScopesForNamespaceDescriptors = new Rule<JsScope>() {
378 @Override
379 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
380 if (!(descriptor instanceof NamespaceDescriptor)) {
381 return null;
382 }
383 return getRootScope().innerScope("Namespace " + 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(generateNewScopesForNamespaceDescriptors);
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> namespaceLevelDeclarationsHaveEnclosingNamespacesNamesAsQualifier = new Rule<JsNameRef>() {
437 @Override
438 public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
439 DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor);
440 if (!(containingDescriptor instanceof NamespaceDescriptor)) {
441 return null;
442 }
443
444 JsNameRef result = new JsNameRef(getNameForDescriptor(containingDescriptor));
445 if (DescriptorUtils.isRootNamespace((NamespaceDescriptor) containingDescriptor)) {
446 return result;
447 }
448
449 JsNameRef qualifier = result;
450 while ((containingDescriptor = getContainingDeclaration(containingDescriptor)) instanceof NamespaceDescriptor &&
451 !DescriptorUtils.isRootNamespace((NamespaceDescriptor) containingDescriptor)) {
452 JsNameRef ref = getNameForDescriptor(containingDescriptor).makeRef();
453 qualifier.setQualifier(ref);
454 qualifier = ref;
455 }
456
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 PsiFile file = element.getContainingFile();
465 String moduleName = file.getUserData(LibrarySourcesConfig.EXTERNAL_MODULE_NAME);
466 if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) {
467 return null;
468 }
469 else if (moduleName != null) {
470 qualifier.setQualifier(new JsArrayAccess(namer.kotlin("modules"), program.getStringLiteral(moduleName)));
471 }
472 }
473
474 if (qualifier.getQualifier() == null) {
475 qualifier.setQualifier(new JsNameRef(Namer.getRootNamespaceName()));
476 }
477
478 return result;
479 }
480 };
481 Rule<JsNameRef> constructorHaveTheSameQualifierAsTheClass = new Rule<JsNameRef>() {
482 @Override
483 public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
484 if (!(descriptor instanceof ConstructorDescriptor)) {
485 return null;
486 }
487 ClassDescriptor containingClass = getContainingClass(descriptor);
488 assert containingClass != null : "Can't have constructor without a class";
489 return getQualifierForDescriptor(containingClass);
490 }
491 };
492 Rule<JsNameRef> libraryObjectsHaveKotlinQualifier = new Rule<JsNameRef>() {
493 @Override
494 public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
495 if (isLibraryObject(descriptor)) {
496 return namer.kotlinObject();
497 }
498 return null;
499 }
500 };
501 addRule(libraryObjectsHaveKotlinQualifier);
502 addRule(constructorHaveTheSameQualifierAsTheClass);
503 addRule(standardObjectsHaveKotlinQualifier);
504 addRule(namespaceLevelDeclarationsHaveEnclosingNamespacesNamesAsQualifier);
505 }
506 }
507
508 private static class QualifierIsNullGenerator extends Generator<Boolean> {
509
510 private QualifierIsNullGenerator() {
511 Rule<Boolean> propertiesHaveNoQualifiers = new Rule<Boolean>() {
512 @Override
513 public Boolean apply(@NotNull DeclarationDescriptor descriptor) {
514 if (!(descriptor instanceof PropertyDescriptor)) {
515 return null;
516 }
517 return true;
518 }
519 };
520 //TODO: hack!
521 Rule<Boolean> nativeObjectsHaveNoQualifiers = new Rule<Boolean>() {
522 @Override
523 public Boolean apply(@NotNull DeclarationDescriptor descriptor) {
524 if (!AnnotationsUtils.isNativeObject(descriptor)) {
525 return null;
526 }
527 return true;
528 }
529 };
530 Rule<Boolean> topLevelNamespaceHaveNoQualifier = new Rule<Boolean>() {
531 @Override
532 public Boolean apply(@NotNull DeclarationDescriptor descriptor) {
533 if (descriptor instanceof NamespaceDescriptor && DescriptorUtils.isRootNamespace((NamespaceDescriptor) descriptor)) {
534 return true;
535 }
536 return null;
537 }
538 };
539 addRule(topLevelNamespaceHaveNoQualifier);
540 addRule(propertiesHaveNoQualifiers);
541 addRule(nativeObjectsHaveNoQualifiers);
542 }
543 }
544 }