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