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