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