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.dart.compiler.backend.js.ast.JsExpression;
020 import gnu.trove.THashMap;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
024 import org.jetbrains.jet.lang.psi.JetExpression;
025
026 import java.util.Collections;
027 import java.util.Map;
028
029 public class AliasingContext {
030 private static final AliasingContext ROOT = new AliasingContext(null) {
031 @Override
032 protected JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor, boolean fromChild) {
033 return null;
034 }
035
036 @Override
037 public JsExpression getAliasForExpression(@NotNull JetExpression element) {
038 return null;
039 }
040
041 @Override
042 public void registerAlias(@NotNull DeclarationDescriptor descriptor, @NotNull JsExpression alias) {
043 throw new IllegalStateException();
044 }
045 };
046
047 public static AliasingContext getCleanContext() {
048 return new AliasingContext(ROOT);
049 }
050
051 @Nullable
052 private Map<DeclarationDescriptor, JsExpression> aliasesForDescriptors;
053
054 @Nullable
055 private final Map<JetExpression, JsExpression> aliasesForExpressions;
056
057 @Nullable
058 private final AliasingContext parent;
059
060 /*package*/ AliasingContext(@Nullable AliasingContext parent) {
061 this(parent, null, null);
062 }
063
064 private AliasingContext(
065 @Nullable AliasingContext parent,
066 @Nullable Map<DeclarationDescriptor, JsExpression> aliasesForDescriptors,
067 @Nullable Map<JetExpression, JsExpression> aliasesForExpressions
068 ) {
069 this.parent = parent;
070 this.aliasesForDescriptors = aliasesForDescriptors;
071 this.aliasesForExpressions = aliasesForExpressions;
072 }
073
074 @NotNull
075 public AliasingContext inner(@NotNull DeclarationDescriptor descriptor, @NotNull JsExpression alias) {
076 return new AliasingContext(this, Collections.singletonMap(descriptor, alias), null);
077 }
078
079 @NotNull
080 public AliasingContext notShareableThisAliased(@NotNull final DeclarationDescriptor thisDescriptor, @NotNull final JsExpression alias) {
081 return new AliasingContext(this) {
082 @Nullable
083 @Override
084 protected JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor, boolean fromChild) {
085 if (!fromChild && thisDescriptor == descriptor) {
086 return alias;
087 }
088 return super.getAliasForDescriptor(descriptor, fromChild);
089 }
090 };
091 }
092
093 @NotNull
094 public AliasingContext withExpressionsAliased(@NotNull Map<JetExpression, JsExpression> aliasesForExpressions) {
095 return new AliasingContext(this, null, aliasesForExpressions);
096 }
097
098 @NotNull
099 public AliasingContext withDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsExpression> aliases) {
100 return new AliasingContext(this, aliases, null);
101 }
102
103
104 @Nullable
105 public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
106 // these aliases cannot be shared and applicable only in current context
107 return getAliasForDescriptor(descriptor, false);
108 }
109
110 @Nullable
111 protected JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor, boolean fromChild) {
112 JsExpression alias = aliasesForDescriptors == null ? null : aliasesForDescriptors.get(descriptor.getOriginal());
113 return alias != null || parent == null ? alias : parent.getAliasForDescriptor(descriptor, true);
114 }
115
116 @Nullable
117 public JsExpression getAliasForExpression(@NotNull JetExpression element) {
118 JsExpression alias = aliasesForExpressions == null ? null : aliasesForExpressions.get(element);
119 return alias != null || parent == null ? alias : parent.getAliasForExpression(element);
120 }
121
122 /**
123 * Usages:
124 * 1) Local variable captured in closure. If captured in closure, any modification in closure should affect captured variable.
125 * So, "var count = n" wrapped as "var count = {v: n}". descriptor wil be property descriptor, alias will be JsObjectLiteral
126 *
127 * 2) Local named function.
128 */
129 public void registerAlias(@NotNull DeclarationDescriptor descriptor, @NotNull JsExpression alias) {
130 if (aliasesForDescriptors == null) {
131 aliasesForDescriptors = Collections.singletonMap(descriptor, alias);
132 }
133 else {
134 if (aliasesForDescriptors.size() == 1) {
135 Map<DeclarationDescriptor, JsExpression> singletonMap = aliasesForDescriptors;
136 aliasesForDescriptors = new THashMap<DeclarationDescriptor, JsExpression>();
137 aliasesForDescriptors.put(singletonMap.keySet().iterator().next(), singletonMap.values().iterator().next());
138 }
139 JsExpression prev = aliasesForDescriptors.put(descriptor, alias);
140 assert prev == null : "Alias for descriptor already registered." +
141 " Descriptor: " + descriptor +
142 " prev alias: " + prev +
143 " new alias: " + alias;
144 }
145 }
146 }