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.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 kotlin.KotlinPackage;
023 import kotlin.jvm.functions.Function1;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.kotlin.backend.common.CodegenUtil;
026 import org.jetbrains.kotlin.descriptors.*;
027 import org.jetbrains.kotlin.descriptors.annotations.Annotations;
028 import org.jetbrains.kotlin.descriptors.impl.ConstructorDescriptorImpl;
029 import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
030 import org.jetbrains.kotlin.name.FqNameUnsafe;
031 import org.jetbrains.kotlin.name.Name;
032 import org.jetbrains.kotlin.resolve.DescriptorUtils;
033 import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
034 import org.jetbrains.kotlin.resolve.scopes.JetScope;
035
036 import java.util.*;
037
038 import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsPackage.getJetTypeFqName;
039 import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsPackage.hasPrimaryConstructor;
040 import static org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName;
041
042 public class ManglingUtils {
043 private ManglingUtils() {}
044
045 public static final Comparator<CallableDescriptor> CALLABLE_COMPARATOR = new CallableComparator();
046
047 @NotNull
048 public static String getMangledName(@NotNull PropertyDescriptor descriptor, @NotNull String suggestedName) {
049 return getStableMangledName(suggestedName, getFqName(descriptor).asString());
050 }
051
052 @NotNull
053 public static String getSuggestedName(@NotNull DeclarationDescriptor descriptor) {
054 String suggestedName = descriptor.getName().asString();
055
056 if (descriptor instanceof FunctionDescriptor ||
057 descriptor instanceof PropertyDescriptor && DescriptorUtils.isExtension((PropertyDescriptor) descriptor)
058 ) {
059 suggestedName = getMangledName((CallableMemberDescriptor) descriptor);
060 }
061
062 return suggestedName;
063 }
064
065 @NotNull
066 private static String getMangledName(@NotNull CallableMemberDescriptor descriptor) {
067 if (needsStableMangling(descriptor)) {
068 return getStableMangledName(descriptor);
069 }
070
071 return getSimpleMangledName(descriptor);
072 }
073
074 //TODO extend logic for nested/inner declarations
075 private static boolean needsStableMangling(CallableMemberDescriptor descriptor) {
076 // Use stable mangling for overrides because we use stable mangling when any function inside a overridable declaration
077 // for avoid clashing names when inheritance.
078 if (DescriptorUtils.isOverride(descriptor)) {
079 return true;
080 }
081
082 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
083
084 if (containingDeclaration instanceof PackageFragmentDescriptor) {
085 return descriptor.getVisibility().getIsPublicAPI();
086 }
087 else if (containingDeclaration instanceof ClassDescriptor) {
088 ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
089
090 // Use stable mangling when it's inside an overridable declaration to avoid clashing names on inheritance.
091 if (classDescriptor.getModality().isOverridable()) {
092 return true;
093 }
094
095 // valueOf() is created in the library with a mangled name for every enum class
096 if (descriptor instanceof FunctionDescriptor && CodegenUtil.isEnumValueOfMethod((FunctionDescriptor) descriptor)) {
097 return true;
098 }
099
100 // Don't use stable mangling when it inside a non-public API declaration.
101 if (!classDescriptor.getVisibility().getIsPublicAPI()) {
102 return false;
103 }
104
105 // Ignore the `protected` visibility because it can be use outside a containing declaration
106 // only when the containing declaration is overridable.
107 if (descriptor.getVisibility() == Visibilities.PUBLIC) {
108 return true;
109 }
110
111 return false;
112 }
113
114 assert containingDeclaration instanceof CallableMemberDescriptor :
115 "containingDeclaration for descriptor have unsupported type for mangling, " +
116 "descriptor: " + descriptor + ", containingDeclaration: " + containingDeclaration;
117
118 return false;
119 }
120
121 @NotNull
122 public static String getMangledMemberNameForExplicitDelegation(
123 @NotNull String suggestedName,
124 @NotNull FqNameUnsafe classFqName,
125 @NotNull FqNameUnsafe typeFqName
126 ) {
127 String forCalculateId = classFqName.asString() + ":" + typeFqName.asString();
128 return getStableMangledName(suggestedName, forCalculateId);
129 }
130
131 @NotNull
132 private static String getStableMangledName(@NotNull String suggestedName, String forCalculateId) {
133 int absHashCode = Math.abs(forCalculateId.hashCode());
134 String suffix = absHashCode == 0 ? "" : ("_" + Integer.toString(absHashCode, Character.MAX_RADIX) + "$");
135 return suggestedName + suffix;
136 }
137
138 @NotNull
139 private static String getStableMangledName(@NotNull CallableDescriptor descriptor) {
140 String suggestedName = getSuggestedName(descriptor);
141 return getStableMangledName(suggestedName, getArgumentTypesAsString(descriptor));
142 }
143
144 @NotNull
145 private static String getSuggestedName(@NotNull CallableDescriptor descriptor) {
146 if (descriptor instanceof ConstructorDescriptor && !((ConstructorDescriptor) descriptor).isPrimary()) {
147 DeclarationDescriptor classDescriptor = descriptor.getContainingDeclaration();
148 assert classDescriptor != null;
149 return classDescriptor.getName().asString();
150 }
151 else {
152 return descriptor.getName().asString();
153 }
154 }
155
156 @NotNull
157 private static String getSimpleMangledName(@NotNull CallableMemberDescriptor descriptor) {
158 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
159
160 JetScope jetScope = null;
161
162 String nameToCompare = descriptor.getName().asString();
163
164 if (containingDeclaration != null && descriptor instanceof ConstructorDescriptor) {
165 nameToCompare = containingDeclaration.getName().asString();
166 containingDeclaration = containingDeclaration.getContainingDeclaration();
167 }
168
169 if (containingDeclaration instanceof PackageFragmentDescriptor) {
170 jetScope = ((PackageFragmentDescriptor) containingDeclaration).getMemberScope();
171 }
172 else if (containingDeclaration instanceof ClassDescriptor) {
173 jetScope = ((ClassDescriptor) containingDeclaration).getDefaultType().getMemberScope();
174 }
175
176 int counter = 0;
177
178 if (jetScope != null) {
179 final String finalNameToCompare = nameToCompare;
180
181 Collection<DeclarationDescriptor> declarations = jetScope.getDescriptors(DescriptorKindFilter.CALLABLES, JetScope.ALL_NAME_FILTER);
182 List<CallableDescriptor> overloadedFunctions =
183 KotlinPackage.flatMap(declarations, new Function1<DeclarationDescriptor, Iterable<? extends CallableDescriptor>>() {
184 @Override
185 public Iterable<? extends CallableDescriptor> invoke(DeclarationDescriptor declarationDescriptor) {
186 if (declarationDescriptor instanceof ClassDescriptor && finalNameToCompare.equals(declarationDescriptor.getName().asString())) {
187 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
188 Collection<ConstructorDescriptor> constructors = classDescriptor.getConstructors();
189
190 if (!hasPrimaryConstructor(classDescriptor)) {
191 ConstructorDescriptorImpl fakePrimaryConstructor =
192 ConstructorDescriptorImpl.create(classDescriptor, Annotations.EMPTY, true, SourceElement.NO_SOURCE);
193 return KotlinPackage.plus(constructors, fakePrimaryConstructor);
194 }
195
196 return constructors;
197 }
198
199 if (!(declarationDescriptor instanceof CallableMemberDescriptor)) return Collections.emptyList();
200
201 CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declarationDescriptor;
202
203 String name = AnnotationsUtils.getNameForAnnotatedObjectWithOverrides(callableMemberDescriptor);
204
205 // when name == null it's mean that it's not native.
206 if (name == null) {
207 // skip functions without arguments, because we don't use mangling for them
208 if (needsStableMangling(callableMemberDescriptor) && !callableMemberDescriptor.getValueParameters().isEmpty()) return Collections.emptyList();
209
210 // TODO add prefix for property: get_$name and set_$name
211 name = callableMemberDescriptor.getName().asString();
212 }
213
214 if (finalNameToCompare.equals(name)) return Collections.singletonList(callableMemberDescriptor);
215
216 return Collections.emptyList();
217 }
218 });
219
220 if (overloadedFunctions.size() > 1) {
221 Collections.sort(overloadedFunctions, CALLABLE_COMPARATOR);
222 counter = ContainerUtil.indexOfIdentity(overloadedFunctions, descriptor);
223 assert counter >= 0;
224 }
225 }
226
227 String name = getSuggestedName(descriptor);
228 return counter == 0 ? name : name + '_' + counter;
229 }
230
231 private static String getArgumentTypesAsString(CallableDescriptor descriptor) {
232 StringBuilder argTypes = new StringBuilder();
233
234 ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
235 if (receiverParameter != null) {
236 argTypes.append(getJetTypeFqName(receiverParameter.getType(), true)).append(".");
237 }
238
239 argTypes.append(StringUtil.join(descriptor.getValueParameters(), new Function<ValueParameterDescriptor, String>() {
240 @Override
241 public String fun(ValueParameterDescriptor descriptor) {
242 return getJetTypeFqName(descriptor.getType(), true);
243 }
244 }, ","));
245
246 return argTypes.toString();
247 }
248
249 @NotNull
250 public static String getStableMangledNameForDescriptor(@NotNull ClassDescriptor descriptor, @NotNull String functionName) {
251 Collection<FunctionDescriptor> functions =
252 descriptor.getDefaultType().getMemberScope().getFunctions(Name.identifier(functionName), NoLookupLocation.FROM_BACKEND);
253 assert functions.size() == 1 : "Can't select a single function: " + functionName + " in " + descriptor;
254 return getSuggestedName((DeclarationDescriptor) functions.iterator().next());
255 }
256
257 private static class CallableComparator implements Comparator<CallableDescriptor> {
258 @Override
259 public int compare(@NotNull CallableDescriptor a, @NotNull CallableDescriptor b) {
260 // primary constructors
261 if (a instanceof ConstructorDescriptor && ((ConstructorDescriptor) a).isPrimary()) {
262 if (!(b instanceof ConstructorDescriptor) || !((ConstructorDescriptor) b).isPrimary()) return -1;
263 }
264 else if (b instanceof ConstructorDescriptor && ((ConstructorDescriptor) b).isPrimary()) {
265 return 1;
266 }
267
268 // native functions
269 if (isNativeOrOverrideNative(a)) {
270 if (!isNativeOrOverrideNative(b)) return -1;
271 }
272 else if (isNativeOrOverrideNative(b)) {
273 return 1;
274 }
275
276 // be visibility
277 // Actually "internal" > "private", but we want to have less number for "internal", so compare b with a instead of a with b.
278 Integer result = Visibilities.compare(b.getVisibility(), a.getVisibility());
279 if (result != null && result != 0) return result;
280
281 // by arity
282 int aArity = arity(a);
283 int bArity = arity(b);
284 if (aArity != bArity) return aArity - bArity;
285
286 // by stringify argument types
287 String aArguments = getArgumentTypesAsString(a);
288 String bArguments = getArgumentTypesAsString(b);
289 assert aArguments != bArguments;
290
291 return aArguments.compareTo(bArguments);
292 }
293
294 private static int arity(CallableDescriptor descriptor) {
295 return descriptor.getValueParameters().size() + (descriptor.getExtensionReceiverParameter() == null ? 0 : 1);
296 }
297
298 private static boolean isNativeOrOverrideNative(CallableDescriptor descriptor) {
299 if (!(descriptor instanceof CallableMemberDescriptor)) return false;
300
301 if (AnnotationsUtils.isNativeObject(descriptor)) return true;
302
303 Set<CallableMemberDescriptor> declarations = DescriptorUtils.getAllOverriddenDeclarations((CallableMemberDescriptor) descriptor);
304 for (CallableMemberDescriptor memberDescriptor : declarations) {
305 if (AnnotationsUtils.isNativeObject(memberDescriptor)) return true;
306 }
307 return false;
308 }
309 }
310 }