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.common.collect.Maps;
020 import com.google.dart.compiler.backend.js.ast.JsExpression;
021 import com.google.dart.compiler.backend.js.ast.JsLiteral;
022 import com.google.dart.compiler.backend.js.ast.JsName;
023 import com.google.dart.compiler.backend.js.ast.JsNameRef;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.annotations.Nullable;
026 import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
027 import org.jetbrains.jet.lang.descriptors.ClassOrNamespaceDescriptor;
028 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
029 import org.jetbrains.jet.lang.psi.JetExpression;
030 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
031 import org.jetbrains.jet.lang.resolve.scopes.receivers.*;
032 import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
033 import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
034
035 import java.util.Map;
036
037 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getDeclarationDescriptorForReceiver;
038 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor;
039
040 public class AliasingContext {
041 private static final ThisAliasProvider EMPTY_THIS_ALIAS_PROVIDER = new ThisAliasProvider() {
042 @Nullable
043 @Override
044 public JsNameRef get(@NotNull DeclarationDescriptor descriptor) {
045 return null;
046 }
047
048 @Nullable
049 @Override
050 public JsExpression get(@NotNull ResolvedCall<?> call) {
051 ReceiverValue callThisObject = call.getThisObject();
052 return callThisObject.exists() && (callThisObject instanceof ClassReceiver || callThisObject instanceof ExtensionReceiver)
053 ? JsLiteral.THIS
054 : null;
055 }
056 };
057
058 private static final AliasingContext ROOT = new AliasingContext(null, EMPTY_THIS_ALIAS_PROVIDER) {
059 @Override
060 public JsName getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
061 return null;
062 }
063
064 @Override
065 public JsName getAliasForExpression(@NotNull JetExpression element) {
066 return null;
067 }
068 };
069
070 public static AliasingContext getCleanContext() {
071 return new AliasingContext(ROOT, ROOT.thisAliasProvider);
072 }
073
074 @NotNull
075 private final Map<DeclarationDescriptor, JsName> aliasesForDescriptors = Maps.newHashMap();
076
077 @NotNull
078 final ThisAliasProvider thisAliasProvider;
079 @NotNull
080 private final Map<JetExpression, JsName> aliasesForExpressions = Maps.newHashMap();
081
082 @Nullable
083 private final AliasingContext parent;
084
085 private AliasingContext(@Nullable AliasingContext parent, @NotNull ThisAliasProvider thisAliasProvider) {
086 this.parent = parent;
087 this.thisAliasProvider = thisAliasProvider;
088 }
089
090 public interface ThisAliasProvider {
091 @Nullable
092 JsNameRef get(@NotNull DeclarationDescriptor descriptor);
093 @Nullable
094 JsExpression get(@NotNull ResolvedCall<?> call);
095 }
096
097 public abstract static class AbstractThisAliasProvider implements ThisAliasProvider {
098 @NotNull
099 protected static DeclarationDescriptor normalize(@NotNull DeclarationDescriptor descriptor) {
100 if (descriptor instanceof ClassOrNamespaceDescriptor) {
101 return descriptor;
102 }
103 else if (descriptor instanceof CallableDescriptor) {
104 DeclarationDescriptor receiverDescriptor = getExpectedReceiverDescriptor((CallableDescriptor) descriptor);
105 assert receiverDescriptor != null;
106 return receiverDescriptor;
107 }
108
109 return descriptor;
110 }
111
112 @Nullable
113 @Override
114 public JsExpression get(@NotNull ResolvedCall<?> call) {
115 ReceiverValue thisObject = call.getThisObject();
116 if (!thisObject.exists()) {
117 return null;
118 }
119
120 if (thisObject instanceof ExtensionReceiver || thisObject instanceof ClassReceiver) {
121 JsNameRef ref = get(((ThisReceiver) thisObject).getDeclarationDescriptor());
122 if (ref != null) {
123 return ref;
124 }
125 }
126
127 JsNameRef ref = get(getDeclarationDescriptorForReceiver(thisObject));
128 return ref == null ? JsLiteral.THIS : ref;
129 }
130 }
131
132 @NotNull
133 public AliasingContext inner(@NotNull ThisAliasProvider thisAliasProvider) {
134 return new AliasingContext(this, thisAliasProvider);
135 }
136
137 @NotNull
138 public AliasingContext inner(@NotNull final DeclarationDescriptor correspondingDescriptor, @NotNull final JsName alias) {
139 return inner(new AbstractThisAliasProvider() {
140 @Nullable
141 @Override
142 public JsNameRef get(@NotNull DeclarationDescriptor descriptor) {
143 return correspondingDescriptor == normalize(descriptor) ? alias.makeRef() : null;
144 }
145 });
146 }
147
148 @NotNull
149 public AliasingContext withAliasesForExpressions(@NotNull Map<JetExpression, JsName> aliasesForExpressions) {
150 AliasingContext newContext = new AliasingContext(this, thisAliasProvider);
151 newContext.aliasesForExpressions.putAll(aliasesForExpressions);
152 return newContext;
153 }
154
155 @NotNull
156 public AliasingContext withDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsName> aliases) {
157 AliasingContext newContext = new AliasingContext(this, thisAliasProvider);
158 newContext.aliasesForDescriptors.putAll(aliases);
159 return newContext;
160 }
161
162 @Nullable
163 public JsName getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
164 JsName alias = aliasesForDescriptors.get(descriptor.getOriginal());
165 if (alias != null) {
166 return alias;
167 }
168 assert parent != null;
169 return parent.getAliasForDescriptor(descriptor);
170 }
171
172 @Nullable
173 public JsName getAliasForExpression(@NotNull JetExpression element) {
174 JsName alias = aliasesForExpressions.get(element);
175 if (alias != null) {
176 return alias;
177 }
178 assert parent != null;
179 return parent.getAliasForExpression(element);
180 }
181 }