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