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