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 017package org.jetbrains.k2js.translate.context; 018 019import com.google.common.collect.Maps; 020import com.google.dart.compiler.backend.js.ast.JsExpression; 021import com.google.dart.compiler.backend.js.ast.JsLiteral; 022import com.google.dart.compiler.backend.js.ast.JsName; 023import com.google.dart.compiler.backend.js.ast.JsNameRef; 024import org.jetbrains.annotations.NotNull; 025import org.jetbrains.annotations.Nullable; 026import org.jetbrains.jet.lang.descriptors.CallableDescriptor; 027import org.jetbrains.jet.lang.descriptors.ClassOrNamespaceDescriptor; 028import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 029import org.jetbrains.jet.lang.psi.JetExpression; 030import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; 031import org.jetbrains.jet.lang.resolve.scopes.receivers.*; 032import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; 033import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver; 034 035import java.util.Map; 036 037import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getDeclarationDescriptorForReceiver; 038import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor; 039 040public 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}