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.descriptors.CallableDescriptor; 024 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; 025 import org.jetbrains.kotlin.descriptors.MemberDescriptor; 026 import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor; 027 import org.jetbrains.kotlin.diagnostics.DiagnosticSink; 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.types.reflect.ReflectionTypes; 035 036 import java.util.HashMap; 037 import java.util.Map; 038 039 import static org.jetbrains.kotlin.js.translate.context.ContextPackage.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 DiagnosticSink getTrace() { return staticContext.getConfig().getTrace(); } 169 170 @NotNull 171 public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) { 172 return staticContext.getScopeForDescriptor(descriptor); 173 } 174 175 @NotNull 176 public JsName getNameForElement(@NotNull PsiElement element) { 177 DeclarationDescriptor descriptor = getDescriptorForElement(bindingContext(), element); 178 return getNameForDescriptor(descriptor); 179 } 180 181 @NotNull 182 public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) { 183 return staticContext.getNameForDescriptor(descriptor); 184 } 185 186 @NotNull 187 public JsName getNameForPackage(@NotNull FqName fqName) { 188 return staticContext.getNameForPackage(fqName); 189 } 190 191 @NotNull 192 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) { 193 return staticContext.declarePropertyOrPropertyAccessorName(descriptor, name, fresh); 194 } 195 196 @NotNull 197 public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) { 198 return staticContext.getQualifiedReference(descriptor); 199 } 200 201 @NotNull 202 public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) { 203 return staticContext.getQualifiedReference(packageFqName); 204 } 205 206 @Nullable 207 public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) { 208 return staticContext.getQualifierForDescriptor(descriptor); 209 } 210 211 @NotNull 212 public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) { 213 return dynamicContext.declareTemporary(initExpression); 214 } 215 216 @NotNull 217 public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) { 218 TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression); 219 220 if (tempVar == null) { 221 TemporaryVariable tmpVar = declareTemporary(expression); 222 223 tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression()); 224 225 expressionToTempConstVariableCache.put(expression, tempVar); 226 expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar); 227 } 228 229 return tempVar; 230 } 231 232 public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) { 233 assert expression == temporaryConstVariable.assignmentExpression(); 234 expressionToTempConstVariableCache.put(expression, temporaryConstVariable); 235 } 236 237 @NotNull 238 public Namer namer() { 239 return staticContext.getNamer(); 240 } 241 242 @NotNull 243 public Intrinsics intrinsics() { 244 return staticContext.getIntrinsics(); 245 } 246 247 @NotNull 248 public ReflectionTypes getReflectionTypes() { 249 return staticContext.getReflectionTypes(); 250 } 251 252 @NotNull 253 public JsProgram program() { 254 return staticContext.getProgram(); 255 } 256 257 @NotNull 258 public Config getConfig() { 259 return staticContext.getConfig(); 260 } 261 262 @NotNull 263 public JsScope scope() { 264 return dynamicContext.getScope(); 265 } 266 267 @NotNull 268 public AliasingContext aliasingContext() { 269 return aliasingContext; 270 } 271 272 @NotNull 273 public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) { 274 return staticContext.getFunctionWithScope(descriptor); 275 } 276 277 public void addStatementToCurrentBlock(@NotNull JsStatement statement) { 278 dynamicContext.jsBlock().getStatements().add(statement); 279 } 280 281 public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) { 282 addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock()); 283 } 284 285 public void addStatementsToCurrentBlockFrom(@NotNull JsBlock block) { 286 dynamicContext.jsBlock().getStatements().addAll(block.getStatements()); 287 } 288 289 public boolean currentBlockIsEmpty() { 290 return dynamicContext.jsBlock().isEmpty(); 291 } 292 293 public void moveVarsFrom(@NotNull TranslationContext context) { 294 dynamicContext.moveVarsFrom(context.dynamicContext()); 295 } 296 297 @NotNull 298 public JsBlock getCurrentBlock() { 299 return dynamicContext.jsBlock(); 300 } 301 302 @NotNull 303 public JsEmpty getEmptyStatement() { 304 return program().getEmptyStatement(); 305 } 306 307 @NotNull 308 public JsExpression getEmptyExpression() { 309 return program().getEmptyExpression(); 310 } 311 312 @Nullable 313 public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) { 314 JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor); 315 if (nameRef != null) { 316 return nameRef; 317 } 318 319 return aliasingContext.getAliasForDescriptor(descriptor); 320 } 321 322 @NotNull 323 public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor) { 324 JsExpression alias = getAliasForDescriptor(descriptor); 325 return alias == null ? JsLiteral.THIS : alias; 326 } 327 328 @NotNull 329 private DefinitionPlace getDefinitionPlace() { 330 if (definitionPlace != null) return definitionPlace; 331 if (parent != null) return parent.getDefinitionPlace(); 332 333 throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)"); 334 } 335 336 @NotNull 337 public JsNameRef define(DeclarationDescriptor descriptor, JsExpression expression) { 338 String suggestedName = TranslationUtils.getSuggestedNameForInnerDeclaration(this, descriptor); 339 return getDefinitionPlace().define(suggestedName, expression); 340 } 341 342 @Nullable 343 private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) { 344 if (usageTracker != null && descriptor instanceof CallableDescriptor) { 345 CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor; 346 347 usageTracker.used(callableDescriptor); 348 349 JsName name = getNameForCapturedDescriptor(usageTracker, callableDescriptor); 350 if (name != null) return name.makeRef(); 351 } 352 353 return null; 354 } 355 }