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.jet.lang.resolve.kotlin;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf;
022    import org.jetbrains.jet.descriptors.serialization.NameResolver;
023    import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
024    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025    import org.jetbrains.jet.lang.descriptors.ClassOrPackageFragmentDescriptor;
026    import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor;
027    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028    import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
029    import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
030    import org.jetbrains.jet.lang.resolve.java.resolver.ErrorReporter;
031    import org.jetbrains.jet.lang.resolve.name.FqName;
032    import org.jetbrains.jet.lang.resolve.name.Name;
033    import org.jetbrains.jet.lang.types.DependencyClassByQualifiedNameResolver;
034    
035    import static org.jetbrains.jet.descriptors.serialization.descriptors.Deserializers.AnnotatedCallableKind;
036    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject;
037    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait;
038    import static org.jetbrains.jet.lang.resolve.kotlin.DescriptorDeserializersStorage.MemberSignature;
039    import static org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils.kotlinFqNameToJavaFqName;
040    import static org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils.naiveKotlinFqName;
041    
042    public abstract class BaseDescriptorDeserializer {
043        protected DependencyClassByQualifiedNameResolver classResolver;
044        protected KotlinClassFinder kotlinClassFinder;
045        protected ErrorReporter errorReporter;
046    
047        protected DescriptorDeserializersStorage storage;
048    
049        public abstract void setClassResolver(@NotNull DependencyClassByQualifiedNameResolver classResolver);
050    
051        public abstract void setKotlinClassFinder(@NotNull KotlinClassFinder kotlinClassFinder);
052    
053        public abstract void setErrorReporter(@NotNull ErrorReporter errorReporter);
054    
055        public abstract void setStorage(@NotNull DescriptorDeserializersStorage storage);
056    
057        @Nullable
058        protected static MemberSignature getCallableSignature(
059                @NotNull ProtoBuf.Callable proto,
060                @NotNull NameResolver nameResolver,
061                @NotNull AnnotatedCallableKind kind
062        ) {
063            SignatureDeserializer deserializer = new SignatureDeserializer(nameResolver);
064            switch (kind) {
065                case FUNCTION:
066                    if (proto.hasExtension(JavaProtoBuf.methodSignature)) {
067                        return deserializer.methodSignature(proto.getExtension(JavaProtoBuf.methodSignature));
068                    }
069                    break;
070                case PROPERTY_GETTER:
071                    if (proto.hasExtension(JavaProtoBuf.propertySignature)) {
072                        return deserializer.methodSignature(proto.getExtension(JavaProtoBuf.propertySignature).getGetter());
073                    }
074                    break;
075                case PROPERTY_SETTER:
076                    if (proto.hasExtension(JavaProtoBuf.propertySignature)) {
077                        return deserializer.methodSignature(proto.getExtension(JavaProtoBuf.propertySignature).getSetter());
078                    }
079                    break;
080                case PROPERTY:
081                    if (proto.hasExtension(JavaProtoBuf.propertySignature)) {
082                        JavaProtoBuf.JavaPropertySignature propertySignature = proto.getExtension(JavaProtoBuf.propertySignature);
083    
084                        if (propertySignature.hasField()) {
085                            JavaProtoBuf.JavaFieldSignature field = propertySignature.getField();
086                            String type = deserializer.typeDescriptor(field.getType());
087                            Name name = nameResolver.getName(field.getName());
088                            return MemberSignature.fromFieldNameAndDesc(name, type);
089                        }
090                        else if (propertySignature.hasSyntheticMethod()) {
091                            return deserializer.methodSignature(propertySignature.getSyntheticMethod());
092                        }
093                    }
094                    break;
095            }
096            return null;
097        }
098    
099        @Nullable
100        protected KotlinJvmBinaryClass findClassWithAnnotationsAndInitializers(
101                @NotNull ClassOrPackageFragmentDescriptor container,
102                @NotNull ProtoBuf.Callable proto,
103                @NotNull NameResolver nameResolver,
104                @NotNull AnnotatedCallableKind kind
105        ) {
106            if (container instanceof PackageFragmentDescriptor) {
107                return loadPackageFragmentClassFqName((PackageFragmentDescriptor) container, proto, nameResolver);
108            }
109            else if (isClassObject(container) && isStaticFieldInOuter(proto)) {
110                // Backing fields of properties of a class object are generated in the outer class
111                return findKotlinClassByDescriptor((ClassOrPackageFragmentDescriptor) container.getContainingDeclaration());
112            }
113            else if (isTrait(container) && kind == AnnotatedCallableKind.PROPERTY) {
114                PackageFragmentDescriptor containingPackage = DescriptorUtils.getParentOfType(container, PackageFragmentDescriptor.class);
115                assert containingPackage != null : "Trait must have a package fragment among his parents: " + container;
116    
117                if (proto.hasExtension(JavaProtoBuf.implClassName)) {
118                    Name tImplName = nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName));
119                    return kotlinClassFinder.findKotlinClass(containingPackage.getFqName().child(tImplName));
120                }
121                return null;
122            }
123    
124            return findKotlinClassByDescriptor(container);
125        }
126    
127        @Nullable
128        private KotlinJvmBinaryClass loadPackageFragmentClassFqName(
129                @NotNull PackageFragmentDescriptor container,
130                @NotNull ProtoBuf.Callable proto,
131                @NotNull NameResolver nameResolver
132        ) {
133            if (proto.hasExtension(JavaProtoBuf.implClassName)) {
134                Name name = nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName));
135                FqName fqName = PackageClassUtils.getPackageClassFqName(container.getFqName()).parent().child(name);
136                return kotlinClassFinder.findKotlinClass(fqName);
137            }
138            return null;
139        }
140    
141        private static boolean isStaticFieldInOuter(@NotNull ProtoBuf.Callable proto) {
142            if (!proto.hasExtension(JavaProtoBuf.propertySignature)) return false;
143            JavaProtoBuf.JavaPropertySignature propertySignature = proto.getExtension(JavaProtoBuf.propertySignature);
144            return propertySignature.hasField() && propertySignature.getField().getIsStaticInOuter();
145        }
146    
147        @Nullable
148        protected KotlinJvmBinaryClass findKotlinClassByDescriptor(@NotNull ClassOrPackageFragmentDescriptor descriptor) {
149            if (descriptor instanceof ClassDescriptor) {
150                return kotlinClassFinder.findKotlinClass(kotlinFqNameToJavaFqName(naiveKotlinFqName((ClassDescriptor) descriptor)));
151            }
152            else if (descriptor instanceof PackageFragmentDescriptor) {
153                return kotlinClassFinder.findKotlinClass(
154                        PackageClassUtils.getPackageClassFqName(((PackageFragmentDescriptor) descriptor).getFqName()));
155            }
156            else {
157                throw new IllegalStateException("Unrecognized descriptor: " + descriptor);
158            }
159        }
160    
161        protected static class SignatureDeserializer {
162            // These types are ordered according to their sorts, this is significant for deserialization
163            private static final char[] PRIMITIVE_TYPES = new char[] { 'V', 'Z', 'C', 'B', 'S', 'I', 'F', 'J', 'D' };
164    
165            private final NameResolver nameResolver;
166    
167            public SignatureDeserializer(@NotNull NameResolver nameResolver) {
168                this.nameResolver = nameResolver;
169            }
170    
171            @NotNull
172            public MemberSignature methodSignature(@NotNull JavaProtoBuf.JavaMethodSignature signature) {
173                Name name = nameResolver.getName(signature.getName());
174    
175                StringBuilder sb = new StringBuilder();
176                sb.append('(');
177                for (int i = 0, length = signature.getParameterTypeCount(); i < length; i++) {
178                    typeDescriptor(signature.getParameterType(i), sb);
179                }
180                sb.append(')');
181                typeDescriptor(signature.getReturnType(), sb);
182    
183                return MemberSignature.fromMethodNameAndDesc(name, sb.toString());
184            }
185    
186            @NotNull
187            public String typeDescriptor(@NotNull JavaProtoBuf.JavaType type) {
188                return typeDescriptor(type, new StringBuilder()).toString();
189            }
190    
191            @NotNull
192            private StringBuilder typeDescriptor(@NotNull JavaProtoBuf.JavaType type, @NotNull StringBuilder sb) {
193                for (int i = 0; i < type.getArrayDimension(); i++) {
194                    sb.append('[');
195                }
196    
197                if (type.hasPrimitiveType()) {
198                    sb.append(PRIMITIVE_TYPES[type.getPrimitiveType().ordinal()]);
199                }
200                else {
201                    sb.append("L");
202                    sb.append(fqNameToInternalName(nameResolver.getFqName(type.getClassFqName())));
203                    sb.append(";");
204                }
205    
206                return sb;
207            }
208    
209            @NotNull
210            private static String fqNameToInternalName(@NotNull FqName fqName) {
211                return fqName.asString().replace('.', '/');
212            }
213        }
214    }