001 /*
002 * Copyright 2010-2015 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.kotlin.js.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.kotlin.builtins.ReflectionTypes;
024 import org.jetbrains.kotlin.descriptors.CallableDescriptor;
025 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
026 import org.jetbrains.kotlin.descriptors.MemberDescriptor;
027 import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
028 import org.jetbrains.kotlin.js.config.Config;
029 import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
030 import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
031 import org.jetbrains.kotlin.name.FqName;
032 import org.jetbrains.kotlin.psi.JetExpression;
033 import org.jetbrains.kotlin.resolve.BindingContext;
034 import org.jetbrains.kotlin.resolve.BindingTrace;
035
036 import java.util.HashMap;
037 import java.util.Map;
038
039 import static org.jetbrains.kotlin.js.translate.context.UsageTrackerKt.getNameForCapturedDescriptor;
040 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForElement;
041
042 /**
043 * All the info about the state of the translation process.
044 */
045 public class TranslationContext {
046 @NotNull
047 private final DynamicContext dynamicContext;
048 @NotNull
049 private final StaticContext staticContext;
050 @NotNull
051 private final AliasingContext aliasingContext;
052 @Nullable
053 private final UsageTracker usageTracker;
054 @Nullable
055 private final TranslationContext parent;
056 @Nullable
057 private final DefinitionPlace definitionPlace;
058
059 @NotNull
060 public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) {
061 DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), rootFunction.getBody());
062 AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
063 return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null);
064 }
065
066 private final Map<JsExpression, TemporaryConstVariable> expressionToTempConstVariableCache = new HashMap<JsExpression, TemporaryConstVariable>();
067
068 private TranslationContext(
069 @Nullable TranslationContext parent,
070 @NotNull StaticContext staticContext,
071 @NotNull DynamicContext dynamicContext,
072 @NotNull AliasingContext aliasingContext,
073 @Nullable UsageTracker usageTracker,
074 @Nullable DefinitionPlace definitionPlace
075 ) {
076 this.parent = parent;
077 this.dynamicContext = dynamicContext;
078 this.staticContext = staticContext;
079 this.aliasingContext = aliasingContext;
080 this.usageTracker = usageTracker;
081 this.definitionPlace = definitionPlace;
082 }
083
084 @Nullable
085 public UsageTracker usageTracker() {
086 return usageTracker;
087 }
088
089 @NotNull
090 public DynamicContext dynamicContext() {
091 return dynamicContext;
092 }
093
094 @NotNull
095 public TranslationContext contextWithScope(@NotNull JsFunction fun) {
096 return this.newFunctionBody(fun, aliasingContext);
097 }
098
099 @NotNull
100 public TranslationContext newFunctionBody(@NotNull JsFunction fun, @Nullable AliasingContext aliasingContext) {
101 DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
102 if (aliasingContext == null) {
103 aliasingContext = this.aliasingContext.inner();
104 }
105
106 return new TranslationContext(this, this.staticContext, dynamicContext, aliasingContext, this.usageTracker, null);
107 }
108
109 @NotNull
110 public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) {
111 DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
112 UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, fun.getScope());
113 return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, this.definitionPlace);
114 }
115
116 @NotNull
117 public TranslationContext innerBlock(@NotNull JsBlock block) {
118 return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker, null);
119 }
120
121 @NotNull
122 public TranslationContext innerBlock() {
123 return innerBlock(new JsBlock());
124 }
125
126 @NotNull
127 public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor, @Nullable DefinitionPlace place) {
128 DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor));
129 return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place);
130 }
131
132 @NotNull
133 private TranslationContext innerWithAliasingContext(AliasingContext aliasingContext) {
134 return new TranslationContext(this, this.staticContext, this.dynamicContext, aliasingContext, this.usageTracker, null);
135 }
136
137 @NotNull
138 public TranslationContext innerContextWithAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsExpression alias) {
139 return this.innerWithAliasingContext(aliasingContext.inner(correspondingDescriptor, alias));
140 }
141
142 @NotNull
143 public TranslationContext innerContextWithAliasesForExpressions(@NotNull Map<JetExpression, JsExpression> aliases) {
144 return this.innerWithAliasingContext(aliasingContext.withExpressionsAliased(aliases));
145 }
146
147 @NotNull
148 public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsExpression> aliases) {
149 return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases));
150 }
151
152 @NotNull
153 public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
154 if (descriptor instanceof CallableDescriptor) {
155 return getFunctionObject((CallableDescriptor) descriptor).getBody();
156 }
157 else {
158 return new JsBlock();
159 }
160 }
161
162 @NotNull
163 public BindingContext bindingContext() {
164 return staticContext.getBindingContext();
165 }
166
167 @NotNull
168 public BindingTrace bindingTrace() {
169 return staticContext.getBindingTrace();
170 }
171
172 @NotNull
173 public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
174 return staticContext.getScopeForDescriptor(descriptor);
175 }
176
177 @NotNull
178 public JsName getNameForElement(@NotNull PsiElement element) {
179 DeclarationDescriptor descriptor = getDescriptorForElement(bindingContext(), element);
180 return getNameForDescriptor(descriptor);
181 }
182
183 @NotNull
184 public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
185 return staticContext.getNameForDescriptor(descriptor);
186 }
187
188 @NotNull
189 public JsName getNameForPackage(@NotNull FqName fqName) {
190 return staticContext.getNameForPackage(fqName);
191 }
192
193 @NotNull
194 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
195 return staticContext.declarePropertyOrPropertyAccessorName(descriptor, name, fresh);
196 }
197
198 @NotNull
199 public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
200 return staticContext.getQualifiedReference(descriptor);
201 }
202
203 @NotNull
204 public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
205 return staticContext.getQualifiedReference(packageFqName);
206 }
207
208 @Nullable
209 public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
210 return staticContext.getQualifierForDescriptor(descriptor);
211 }
212
213 @NotNull
214 public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) {
215 return dynamicContext.declareTemporary(initExpression);
216 }
217
218 @NotNull
219 public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) {
220 TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression);
221
222 if (tempVar == null) {
223 TemporaryVariable tmpVar = declareTemporary(expression);
224
225 tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression());
226
227 expressionToTempConstVariableCache.put(expression, tempVar);
228 expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar);
229 }
230
231 return tempVar;
232 }
233
234 public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) {
235 assert expression == temporaryConstVariable.assignmentExpression();
236 expressionToTempConstVariableCache.put(expression, temporaryConstVariable);
237 }
238
239 @NotNull
240 public Namer namer() {
241 return staticContext.getNamer();
242 }
243
244 @NotNull
245 public Intrinsics intrinsics() {
246 return staticContext.getIntrinsics();
247 }
248
249 @NotNull
250 public ReflectionTypes getReflectionTypes() {
251 return staticContext.getReflectionTypes();
252 }
253
254 @NotNull
255 public JsProgram program() {
256 return staticContext.getProgram();
257 }
258
259 @NotNull
260 public Config getConfig() {
261 return staticContext.getConfig();
262 }
263
264 @NotNull
265 public JsScope scope() {
266 return dynamicContext.getScope();
267 }
268
269 @NotNull
270 public AliasingContext aliasingContext() {
271 return aliasingContext;
272 }
273
274 @NotNull
275 public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) {
276 return staticContext.getFunctionWithScope(descriptor);
277 }
278
279 public void addStatementToCurrentBlock(@NotNull JsStatement statement) {
280 dynamicContext.jsBlock().getStatements().add(statement);
281 }
282
283 public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) {
284 addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock());
285 }
286
287 public void addStatementsToCurrentBlockFrom(@NotNull JsBlock block) {
288 dynamicContext.jsBlock().getStatements().addAll(block.getStatements());
289 }
290
291 public boolean currentBlockIsEmpty() {
292 return dynamicContext.jsBlock().isEmpty();
293 }
294
295 public void moveVarsFrom(@NotNull TranslationContext context) {
296 dynamicContext.moveVarsFrom(context.dynamicContext());
297 }
298
299 @NotNull
300 public JsBlock getCurrentBlock() {
301 return dynamicContext.jsBlock();
302 }
303
304 @NotNull
305 public JsExpression getEmptyExpression() {
306 return program().getEmptyExpression();
307 }
308
309 @Nullable
310 public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
311 JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
312 if (nameRef != null) {
313 return nameRef;
314 }
315
316 return aliasingContext.getAliasForDescriptor(descriptor);
317 }
318
319 @NotNull
320 public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor) {
321 JsExpression alias = getAliasForDescriptor(descriptor);
322 return alias == null ? JsLiteral.THIS : alias;
323 }
324
325 @NotNull
326 private DefinitionPlace getDefinitionPlace() {
327 if (definitionPlace != null) return definitionPlace;
328 if (parent != null) return parent.getDefinitionPlace();
329
330 throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)");
331 }
332
333 @NotNull
334 public JsNameRef define(DeclarationDescriptor descriptor, JsExpression expression) {
335 String suggestedName = TranslationUtils.getSuggestedNameForInnerDeclaration(this, descriptor);
336 return getDefinitionPlace().define(suggestedName, expression);
337 }
338
339 @Nullable
340 private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) {
341 if (usageTracker != null) {
342 usageTracker.used(descriptor);
343
344 JsName name = getNameForCapturedDescriptor(usageTracker, descriptor);
345 if (name != null) return name.makeRef();
346 }
347
348 return null;
349 }
350 }