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