001 /*
002 * Copyright 2010-2014 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.utils;
018
019 import com.intellij.openapi.util.text.StringUtil;
020 import com.intellij.util.Function;
021 import com.intellij.util.containers.ContainerUtil;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.jet.backend.common.CodegenUtil;
024 import org.jetbrains.jet.lang.descriptors.*;
025 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
026 import org.jetbrains.jet.lang.resolve.name.FqName;
027 import org.jetbrains.jet.lang.resolve.name.Name;
028 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
029
030 import java.util.*;
031
032 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName;
033 import static org.jetbrains.k2js.translate.utils.TranslationUtils.getJetTypeFqName;
034
035 public class ManglingUtils {
036 private ManglingUtils() {}
037
038 public static final Comparator<CallableMemberDescriptor> OVERLOADED_MEMBER_COMPARATOR = new OverloadedMemberComparator();
039
040 @NotNull
041 public static String getMangledName(@NotNull PropertyDescriptor descriptor, @NotNull String suggestedName) {
042 return getStableMangledName(suggestedName, getFqName(descriptor).asString());
043 }
044
045 @NotNull
046 public static String getSuggestedName(@NotNull DeclarationDescriptor descriptor) {
047 String suggestedName = descriptor.getName().asString();
048
049 if (descriptor instanceof FunctionDescriptor ||
050 descriptor instanceof PropertyDescriptor && JsDescriptorUtils.isExtension((PropertyDescriptor) descriptor)
051 ) {
052 suggestedName = getMangledName((CallableMemberDescriptor) descriptor);
053 }
054
055 return suggestedName;
056 }
057
058 @NotNull
059 private static String getMangledName(@NotNull CallableMemberDescriptor descriptor) {
060 if (needsStableMangling(descriptor)) {
061 return getStableMangledName(descriptor);
062 }
063
064 return getSimpleMangledName(descriptor);
065 }
066
067 //TODO extend logic for nested/inner declarations
068 private static boolean needsStableMangling(CallableMemberDescriptor descriptor) {
069 // Use stable mangling for overrides because we use stable mangling when any function inside a overridable declaration
070 // for avoid clashing names when inheritance.
071 if (JsDescriptorUtils.isOverride(descriptor)) {
072 return true;
073 }
074
075 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
076
077 if (containingDeclaration instanceof PackageFragmentDescriptor) {
078 return descriptor.getVisibility().isPublicAPI();
079 }
080 else if (containingDeclaration instanceof ClassDescriptor) {
081 ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
082
083 // Use stable mangling when it's inside an overridable declaration to avoid clashing names on inheritance.
084 if (classDescriptor.getModality().isOverridable()) {
085 return true;
086 }
087
088 // valueOf() is created in the library with a mangled name for every enum class
089 if (descriptor instanceof FunctionDescriptor && CodegenUtil.isEnumValueOfMethod((FunctionDescriptor) descriptor)) {
090 return true;
091 }
092
093 // Don't use stable mangling when it inside a non-public API declaration.
094 if (!classDescriptor.getVisibility().isPublicAPI()) {
095 return false;
096 }
097
098 // Ignore the `protected` visibility because it can be use outside a containing declaration
099 // only when the containing declaration is overridable.
100 if (descriptor.getVisibility() == Visibilities.PUBLIC) {
101 return true;
102 }
103
104 return false;
105 }
106
107 assert containingDeclaration instanceof CallableMemberDescriptor :
108 "containingDeclaration for descriptor have unsupported type for mangling, " +
109 "descriptor: " + descriptor + ", containingDeclaration: " + containingDeclaration;
110
111 return false;
112 }
113
114 @NotNull
115 public static String getMangledMemberNameForExplicitDelegation(@NotNull String suggestedName, FqName classFqName, FqName typeFqName) {
116 String forCalculateId = classFqName.asString() + ":" + typeFqName.asString();
117 return getStableMangledName(suggestedName, forCalculateId);
118 }
119
120 @NotNull
121 private static String getStableMangledName(@NotNull String suggestedName, String forCalculateId) {
122 int absHashCode = Math.abs(forCalculateId.hashCode());
123 String suffix = absHashCode == 0 ? "" : ("_" + Integer.toString(absHashCode, Character.MAX_RADIX) + "$");
124 return suggestedName + suffix;
125 }
126
127 @NotNull
128 private static String getStableMangledName(@NotNull CallableDescriptor descriptor) {
129 return getStableMangledName(descriptor.getName().asString(), getArgumentTypesAsString(descriptor));
130 }
131
132 @NotNull
133 private static String getSimpleMangledName(@NotNull final CallableMemberDescriptor descriptor) {
134 DeclarationDescriptor declaration = descriptor.getContainingDeclaration();
135
136 JetScope jetScope = null;
137 if (declaration instanceof PackageFragmentDescriptor) {
138 jetScope = ((PackageFragmentDescriptor) declaration).getMemberScope();
139 }
140 else if (declaration instanceof ClassDescriptor) {
141 jetScope = ((ClassDescriptor) declaration).getDefaultType().getMemberScope();
142 }
143
144 int counter = 0;
145
146 if (jetScope != null) {
147 Collection<DeclarationDescriptor> declarations = jetScope.getAllDescriptors();
148 List<CallableMemberDescriptor>
149 overloadedFunctions = ContainerUtil.mapNotNull(declarations, new Function<DeclarationDescriptor, CallableMemberDescriptor>() {
150 @Override
151 public CallableMemberDescriptor fun(DeclarationDescriptor declarationDescriptor) {
152 if (!(declarationDescriptor instanceof CallableMemberDescriptor)) return null;
153
154 CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declarationDescriptor;
155
156 String name = AnnotationsUtils.getNameForAnnotatedObjectWithOverrides(callableMemberDescriptor);
157
158 // when name == null it's mean that it's not native.
159 if (name == null) {
160 // skip functions without arguments, because we don't use mangling for them
161 if (needsStableMangling(callableMemberDescriptor) && !callableMemberDescriptor.getValueParameters().isEmpty()) return null;
162
163 // TODO add prefix for property: get_$name and set_$name
164 name = callableMemberDescriptor.getName().asString();
165 }
166
167 return descriptor.getName().asString().equals(name) ? callableMemberDescriptor : null;
168 }
169 });
170
171 if (overloadedFunctions.size() > 1) {
172 Collections.sort(overloadedFunctions, OVERLOADED_MEMBER_COMPARATOR);
173 counter = ContainerUtil.indexOfIdentity(overloadedFunctions, descriptor);
174 assert counter >= 0;
175 }
176 }
177
178 String name = descriptor.getName().asString();
179 return counter == 0 ? name : name + '_' + counter;
180 }
181
182 private static String getArgumentTypesAsString(CallableDescriptor descriptor) {
183 StringBuilder argTypes = new StringBuilder();
184
185 ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
186 if (receiverParameter != null) {
187 argTypes.append(getJetTypeFqName(receiverParameter.getType(), true)).append(".");
188 }
189
190 argTypes.append(StringUtil.join(descriptor.getValueParameters(), new Function<ValueParameterDescriptor, String>() {
191 @Override
192 public String fun(ValueParameterDescriptor descriptor) {
193 return getJetTypeFqName(descriptor.getType(), true);
194 }
195 }, ","));
196
197 return argTypes.toString();
198 }
199
200 @NotNull
201 public static String getStableMangledNameForDescriptor(@NotNull ClassDescriptor descriptor, @NotNull String functionName) {
202 Collection<FunctionDescriptor> functions = descriptor.getDefaultType().getMemberScope().getFunctions(Name.identifier(functionName));
203 assert functions.size() == 1 : "Can't select a single function: " + functionName + " in " + descriptor;
204 return getSuggestedName(functions.iterator().next());
205 }
206
207 private static class OverloadedMemberComparator implements Comparator<CallableMemberDescriptor> {
208 @Override
209 public int compare(@NotNull CallableMemberDescriptor a, @NotNull CallableMemberDescriptor b) {
210 // native functions first
211 if (isNativeOrOverrideNative(a)) {
212 if (!isNativeOrOverrideNative(b)) return -1;
213 }
214 else if (isNativeOrOverrideNative(b)) {
215 return 1;
216 }
217
218 // be visibility
219 // Actually "internal" > "private", but we want to have less number for "internal", so compare b with a instead of a with b.
220 Integer result = Visibilities.compare(b.getVisibility(), a.getVisibility());
221 if (result != null && result != 0) return result;
222
223 // by arity
224 int aArity = arity(a);
225 int bArity = arity(b);
226 if (aArity != bArity) return aArity - bArity;
227
228 // by stringify argument types
229 String aArguments = getArgumentTypesAsString(a);
230 String bArguments = getArgumentTypesAsString(b);
231 assert aArguments != bArguments;
232
233 return aArguments.compareTo(bArguments);
234 }
235
236 private static int arity(CallableMemberDescriptor descriptor) {
237 return descriptor.getValueParameters().size() + (descriptor.getExtensionReceiverParameter() == null ? 0 : 1);
238 }
239
240 private static boolean isNativeOrOverrideNative(CallableMemberDescriptor descriptor) {
241 if (AnnotationsUtils.isNativeObject(descriptor)) return true;
242
243 Set<CallableMemberDescriptor> declarations = DescriptorUtils.getAllOverriddenDeclarations(descriptor);
244 for (CallableMemberDescriptor memberDescriptor : declarations) {
245 if (AnnotationsUtils.isNativeObject(memberDescriptor)) return true;
246 }
247 return false;
248 }
249 }
250 }