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.KtExpression; 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<KtExpression, 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 }