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.psi.JetExpression;
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
030 import java.util.HashMap;
031 import java.util.Map;
032
033 import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement;
034 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor;
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 =
056 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 HashMap<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 private TranslationContext(
080 @NotNull TranslationContext parent,
081 @NotNull AliasingContext aliasingContext
082 ) {
083 this(parent, parent.staticContext, parent.dynamicContext, aliasingContext, parent.usageTracker, null);
084 }
085
086 private TranslationContext(
087 @NotNull TranslationContext parent,
088 @NotNull JsFunction fun,
089 @NotNull AliasingContext aliasingContext,
090 @Nullable UsageTracker usageTracker
091 ) {
092 this(parent, parent.staticContext, DynamicContext.newContext(fun.getScope(), fun.getBody()), aliasingContext,
093 usageTracker == null ? parent.usageTracker : usageTracker, null);
094 }
095
096 @Nullable
097 public UsageTracker usageTracker() {
098 return usageTracker;
099 }
100
101 public DynamicContext dynamicContext() {
102 return dynamicContext;
103 }
104
105 @NotNull
106 public TranslationContext contextWithScope(@NotNull JsFunction fun) {
107 return new TranslationContext(this, fun, aliasingContext, null);
108 }
109
110 @NotNull
111 public TranslationContext newFunctionBody(
112 @NotNull JsFunction fun,
113 @Nullable AliasingContext aliasingContext,
114 @Nullable UsageTracker usageTracker
115 ) {
116 return new TranslationContext(this, fun, aliasingContext == null ? new AliasingContext(this.aliasingContext) : aliasingContext,
117 usageTracker);
118 }
119
120 @NotNull
121 public TranslationContext innerBlock(@NotNull JsBlock block) {
122 return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker, null);
123 }
124
125 @NotNull
126 public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor, @Nullable DefinitionPlace place) {
127 DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor));
128 return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place);
129 }
130
131 @NotNull
132 public TranslationContext innerContextWithAliased(@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, JsExpression> 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 JsName getNameForPackage(@NotNull FqName fqName) {
179 return staticContext.getNameForPackage(fqName);
180 }
181
182 @NotNull
183 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
184 return staticContext.declarePropertyOrPropertyAccessorName(descriptor, name, fresh);
185 }
186
187 @NotNull
188 public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
189 return staticContext.getQualifiedReference(descriptor);
190 }
191
192 @NotNull
193 public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
194 return staticContext.getQualifiedReference(packageFqName);
195 }
196
197 @Nullable
198 public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
199 return staticContext.getQualifierForDescriptor(descriptor);
200 }
201
202 @NotNull
203 public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) {
204 return dynamicContext.declareTemporary(initExpression);
205 }
206
207 @NotNull
208 public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) {
209 TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression);
210
211 if (tempVar == null) {
212 TemporaryVariable tmpVar = declareTemporary(expression);
213
214 tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression());
215
216 expressionToTempConstVariableCache.put(expression, tempVar);
217 expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar);
218 }
219
220 return tempVar;
221 }
222
223 public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) {
224 assert expression == temporaryConstVariable.assignmentExpression();
225 expressionToTempConstVariableCache.put(expression, temporaryConstVariable);
226 }
227
228 @NotNull
229 public Namer namer() {
230 return staticContext.getNamer();
231 }
232
233 @NotNull
234 public Intrinsics intrinsics() {
235 return staticContext.getIntrinsics();
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 descriptorUsedInThisContext(descriptor);
265 return aliasingContext.getAliasForDescriptor(descriptor);
266 }
267
268 @NotNull
269 public JsExpression getThisObject(@NotNull DeclarationDescriptor descriptor) {
270 DeclarationDescriptor effectiveDescriptor;
271 if (descriptor instanceof CallableDescriptor) {
272 effectiveDescriptor = getExpectedReceiverDescriptor((CallableDescriptor) descriptor);
273 assert effectiveDescriptor != null;
274 }
275 else {
276 effectiveDescriptor = descriptor;
277 }
278
279 descriptorUsedInThisContext(effectiveDescriptor);
280
281 JsExpression alias = aliasingContext.getAliasForDescriptor(effectiveDescriptor);
282 return alias == null ? JsLiteral.THIS : alias;
283 }
284
285 @NotNull
286 private DefinitionPlace getDefinitionPlace() {
287 if (definitionPlace != null) return definitionPlace;
288 if (parent != null) return parent.getDefinitionPlace();
289
290 throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)");
291 }
292
293 public JsNameRef define(String name, JsExpression expression) {
294 return getDefinitionPlace().define(name, expression);
295 }
296
297 private void descriptorUsedInThisContext(DeclarationDescriptor effectiveDescriptor) {
298 if (usageTracker != null) {
299 usageTracker.triggerUsed(effectiveDescriptor);
300 }
301 }
302 }