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    }