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.*;
020 import com.intellij.psi.PsiElement;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
024 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
025 import org.jetbrains.jet.lang.psi.JetExpression;
026 import org.jetbrains.jet.lang.resolve.BindingContext;
027 import org.jetbrains.k2js.translate.expression.LiteralFunctionTranslator;
028 import org.jetbrains.k2js.translate.intrinsic.Intrinsics;
029
030 import java.util.HashMap;
031 import java.util.Map;
032
033 import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement;
034 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor;
035
036 /**
037 * All the info about the state of the translation process.
038 */
039 public class TranslationContext {
040 @NotNull
041 private final DynamicContext dynamicContext;
042 @NotNull
043 private final StaticContext staticContext;
044 @NotNull
045 private final AliasingContext aliasingContext;
046 @Nullable
047 private final UsageTracker usageTracker;
048
049 @NotNull
050 public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) {
051 DynamicContext rootDynamicContext =
052 DynamicContext.rootContext(rootFunction.getScope(), rootFunction.getBody());
053 AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
054 return new TranslationContext(staticContext, rootDynamicContext, rootAliasingContext, null);
055 }
056
057 private final HashMap<JsExpression, TemporaryConstVariable> expressionToTempConstVariableCache = new HashMap<JsExpression, TemporaryConstVariable>();
058
059 private TranslationContext(
060 @NotNull StaticContext staticContext,
061 @NotNull DynamicContext dynamicContext,
062 @NotNull AliasingContext aliasingContext,
063 @Nullable UsageTracker usageTracker
064 ) {
065 this.dynamicContext = dynamicContext;
066 this.staticContext = staticContext;
067 this.aliasingContext = aliasingContext;
068 this.usageTracker = usageTracker;
069 }
070
071 private TranslationContext(@NotNull TranslationContext parent, @NotNull AliasingContext aliasingContext) {
072 this(parent.staticContext, parent.dynamicContext, aliasingContext, parent.usageTracker);
073 }
074
075 private TranslationContext(
076 @NotNull TranslationContext parent,
077 @NotNull JsFunction fun,
078 @NotNull AliasingContext aliasingContext,
079 @Nullable UsageTracker usageTracker
080 ) {
081 this(parent.staticContext, DynamicContext.newContext(fun.getScope(), fun.getBody()), aliasingContext,
082 usageTracker == null ? parent.usageTracker : usageTracker);
083 }
084
085 @Nullable
086 public UsageTracker usageTracker() {
087 return usageTracker;
088 }
089
090 public DynamicContext dynamicContext() {
091 return dynamicContext;
092 }
093
094 @NotNull
095 public TranslationContext contextWithScope(@NotNull JsFunction fun) {
096 return new TranslationContext(this, fun, aliasingContext, null);
097 }
098
099 @NotNull
100 private TranslationContext contextWithScope(@NotNull JsScope newScope,
101 @NotNull JsBlock block,
102 @NotNull AliasingContext aliasingContext,
103 @Nullable UsageTracker usageTracker) {
104 return new TranslationContext(staticContext, DynamicContext.newContext(newScope, block), aliasingContext, usageTracker);
105 }
106
107 @NotNull
108 public TranslationContext newFunctionBody(
109 @NotNull JsFunction fun,
110 @Nullable AliasingContext aliasingContext,
111 @Nullable UsageTracker usageTracker
112 ) {
113 return new TranslationContext(this, fun, aliasingContext == null ? new AliasingContext(this.aliasingContext) : aliasingContext,
114 usageTracker);
115 }
116
117 @NotNull
118 public TranslationContext innerBlock(@NotNull JsBlock block) {
119 return new TranslationContext(staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker);
120 }
121
122 @NotNull
123 public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor) {
124 return contextWithScope(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor), aliasingContext, usageTracker);
125 }
126
127 @NotNull
128 public TranslationContext innerContextWithThisAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsNameRef alias) {
129 return new TranslationContext(this, aliasingContext.inner(correspondingDescriptor, alias));
130 }
131
132 @NotNull
133 public TranslationContext innerContextWithAliasesForExpressions(@NotNull Map<JetExpression, JsName> aliases) {
134 return new TranslationContext(this, aliasingContext.withExpressionsAliased(aliases));
135 }
136
137 @NotNull
138 public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsExpression> aliases) {
139 return new TranslationContext(this, aliasingContext.withDescriptorsAliased(aliases));
140 }
141
142 @NotNull
143 public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
144 if (descriptor instanceof CallableDescriptor) {
145 return getFunctionObject((CallableDescriptor)descriptor).getBody();
146 }
147 else {
148 return new JsBlock();
149 }
150 }
151
152 @NotNull
153 public BindingContext bindingContext() {
154 return staticContext.getBindingContext();
155 }
156
157 @NotNull
158 public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
159 return staticContext.getScopeForDescriptor(descriptor);
160 }
161
162 @NotNull
163 public JsName getNameForElement(@NotNull PsiElement element) {
164 DeclarationDescriptor descriptor = getDescriptorForElement(bindingContext(), element);
165 return getNameForDescriptor(descriptor);
166 }
167
168 @NotNull
169 public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
170 return staticContext.getNameForDescriptor(descriptor);
171 }
172
173 @NotNull
174 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
175 return staticContext.declarePropertyOrPropertyAccessorName(descriptor, name, fresh);
176 }
177
178 @NotNull
179 public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
180 return staticContext.getQualifiedReference(descriptor);
181 }
182
183 @Nullable
184 public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
185 return staticContext.getQualifierForDescriptor(descriptor);
186 }
187
188 @NotNull
189 public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) {
190 return dynamicContext.declareTemporary(initExpression);
191 }
192
193 @NotNull
194 public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) {
195 TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression);
196
197 if (tempVar == null) {
198 TemporaryVariable tmpVar = declareTemporary(expression);
199
200 tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression());
201
202 expressionToTempConstVariableCache.put(expression, tempVar);
203 expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar);
204 }
205
206 return tempVar;
207 }
208
209 public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) {
210 assert expression == temporaryConstVariable.assignmentExpression();
211 expressionToTempConstVariableCache.put(expression, temporaryConstVariable);
212 }
213
214 @NotNull
215 public Namer namer() {
216 return staticContext.getNamer();
217 }
218
219 @NotNull
220 public Intrinsics intrinsics() {
221 return staticContext.getIntrinsics();
222 }
223
224 @NotNull
225 public JsProgram program() {
226 return staticContext.getProgram();
227 }
228
229 @NotNull
230 public JsScope scope() {
231 return dynamicContext.getScope();
232 }
233
234 @NotNull
235 public AliasingContext aliasingContext() {
236 return aliasingContext;
237 }
238
239 @NotNull
240 public LiteralFunctionTranslator literalFunctionTranslator() {
241 return staticContext.getLiteralFunctionTranslator();
242 }
243
244 @NotNull
245 public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) {
246 return staticContext.getFunctionWithScope(descriptor);
247 }
248
249 public void addStatementToCurrentBlock(@NotNull JsStatement statement) {
250 dynamicContext.jsBlock().getStatements().add(statement);
251 }
252
253 @Nullable
254 public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
255 if (usageTracker != null) {
256 usageTracker.triggerUsed(descriptor);
257 }
258 return aliasingContext.getAliasForDescriptor(descriptor);
259 }
260
261 @NotNull
262 public JsExpression getThisObject(@NotNull DeclarationDescriptor descriptor) {
263 DeclarationDescriptor effectiveDescriptor;
264 if (descriptor instanceof CallableDescriptor) {
265 effectiveDescriptor = getExpectedReceiverDescriptor((CallableDescriptor) descriptor);
266 assert effectiveDescriptor != null;
267 }
268 else {
269 effectiveDescriptor = descriptor;
270 }
271
272 if (usageTracker != null) {
273 usageTracker.triggerUsed(effectiveDescriptor);
274 }
275
276 JsExpression alias = aliasingContext.getAliasForDescriptor(effectiveDescriptor);
277 return alias == null ? JsLiteral.THIS : alias;
278 }
279 }