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