001    /*
002     * Copyright 2010-2013 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.descriptors.serialization;
018    
019    import com.intellij.openapi.util.Computable;
020    import com.intellij.util.Function;
021    import gnu.trove.TIntObjectHashMap;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedTypeParameterDescriptor;
025    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
026    import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
027    import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
028    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
029    import org.jetbrains.jet.lang.resolve.lazy.storage.*;
030    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
031    import org.jetbrains.jet.lang.types.*;
032    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
033    
034    import java.util.ArrayList;
035    import java.util.Collections;
036    import java.util.List;
037    
038    import static org.jetbrains.jet.lang.resolve.lazy.storage.StorageManager.ReferenceKind.STRONG;
039    
040    public class TypeDeserializer {
041    
042        public interface TypeParameterResolver {
043            TypeParameterResolver NONE = new TypeParameterResolver() {
044                @NotNull
045                @Override
046                public List<DeserializedTypeParameterDescriptor> getTypeParameters(@NotNull TypeDeserializer typeDeserializer) {
047                    return Collections.emptyList();
048                }
049            };
050    
051            @NotNull
052            List<DeserializedTypeParameterDescriptor> getTypeParameters(@NotNull TypeDeserializer typeDeserializer);
053        }
054    
055        private final NameResolver nameResolver;
056        private final DescriptorFinder descriptorFinder;
057        private final TypeDeserializer parent;
058    
059        // never written to after constructor returns
060        private final TIntObjectHashMap<TypeParameterDescriptor> typeParameterDescriptors = new TIntObjectHashMap<TypeParameterDescriptor>();
061    
062        private final MemoizedFunctionToNullable<Integer, ClassDescriptor> classDescriptors;
063    
064        private final String debugName;
065    
066        private final StorageManager storageManager;
067    
068        public TypeDeserializer(
069                @NotNull StorageManager storageManager,
070                @NotNull TypeDeserializer parent,
071                @NotNull String debugName,
072                @NotNull TypeParameterResolver typeParameterResolver
073        ) {
074            this(storageManager, parent, parent.nameResolver, parent.descriptorFinder, debugName, typeParameterResolver);
075        }
076    
077        public TypeDeserializer(
078                @NotNull StorageManager storageManager,
079                @Nullable TypeDeserializer parent,
080                @NotNull NameResolver nameResolver,
081                @NotNull DescriptorFinder descriptorFinder,
082                @NotNull String debugName,
083                @NotNull TypeParameterResolver typeParameterResolver
084        ) {
085            this.storageManager = storageManager;
086            this.parent = parent;
087            this.nameResolver = nameResolver;
088            this.descriptorFinder = descriptorFinder;
089            this.debugName = debugName + (parent == null ? "" : ". Child of " + parent.debugName);
090    
091            for (DeserializedTypeParameterDescriptor typeParameterDescriptor : typeParameterResolver.getTypeParameters(this)) {
092                typeParameterDescriptors.put(typeParameterDescriptor.getProtoId(), typeParameterDescriptor);
093            }
094    
095            this.classDescriptors = storageManager.createMemoizedFunctionWithNullableValues(new Function<Integer, ClassDescriptor>() {
096                @Override
097                public ClassDescriptor fun(Integer fqNameIndex) {
098                    return computeClassDescriptor(fqNameIndex);
099                }
100            }, STRONG);
101        }
102    
103        /* package */ DescriptorFinder getDescriptorFinder() {
104            return descriptorFinder;
105        }
106    
107        @Nullable
108        public JetType typeOrNull(@Nullable ProtoBuf.Type proto) {
109            if (proto == null) {
110                return null;
111            }
112            return type(proto);
113        }
114    
115        @NotNull
116        public JetType type(@NotNull ProtoBuf.Type proto) {
117            return new DeserializedType(proto);
118        }
119    
120        private TypeConstructor typeConstructor(ProtoBuf.Type proto) {
121            ProtoBuf.Type.Constructor constructorProto = proto.getConstructor();
122            int id = constructorProto.getId();
123            TypeConstructor typeConstructor = typeConstructor(constructorProto);
124            if (typeConstructor == null) {
125                String message = constructorProto.getKind() == ProtoBuf.Type.Constructor.Kind.CLASS
126                                 ? nameResolver.getClassId(id).asSingleFqName().asString()
127                                 : "Unknown type parameter " + id;
128                typeConstructor = ErrorUtils.createErrorType(message).getConstructor();
129            }
130            return typeConstructor;
131        }
132    
133        @Nullable
134        private TypeConstructor typeConstructor(@NotNull ProtoBuf.Type.Constructor proto) {
135            switch (proto.getKind()) {
136                case CLASS:
137                    ClassDescriptor classDescriptor = classDescriptors.fun(proto.getId());
138                    if (classDescriptor == null) return null;
139    
140                    return classDescriptor.getTypeConstructor();
141                case TYPE_PARAMETER:
142                    TypeParameterDescriptor descriptor = typeParameterDescriptors.get(proto.getId());
143                    if (descriptor == null && parent != null) {
144                        descriptor = parent.typeParameterDescriptors.get(proto.getId());
145                    }
146                    if (descriptor == null) return null;
147    
148                    return descriptor.getTypeConstructor();
149            }
150            throw new IllegalStateException("Unknown kind " + proto.getKind());
151        }
152    
153        @Nullable
154        private ClassDescriptor computeClassDescriptor(int fqNameIndex) {
155            ClassId classId = nameResolver.getClassId(fqNameIndex);
156            return descriptorFinder.findClass(classId);
157        }
158    
159        private List<TypeProjection> typeArguments(List<ProtoBuf.Type.Argument> protos) {
160            List<TypeProjection> result = new ArrayList<TypeProjection>(protos.size());
161            for (ProtoBuf.Type.Argument proto : protos) {
162                result.add(typeProjection(proto));
163            }
164            return result;
165        }
166    
167        private TypeProjection typeProjection(ProtoBuf.Type.Argument proto) {
168            return new TypeProjection(
169                    variance(proto.getProjection()),
170                    type(proto.getType())
171            );
172        }
173    
174        private static Variance variance(ProtoBuf.Type.Argument.Projection proto) {
175            switch (proto) {
176                case IN:
177                    return Variance.IN_VARIANCE;
178                case OUT:
179                    return Variance.OUT_VARIANCE;
180                case INV:
181                    return Variance.INVARIANT;
182            }
183            throw new IllegalStateException("Unknown projection: " + proto);
184        }
185    
186        @NotNull
187        private static JetScope getTypeMemberScope(@NotNull TypeConstructor constructor, @NotNull List<TypeProjection> typeArguments) {
188            ClassifierDescriptor descriptor = constructor.getDeclarationDescriptor();
189            if (descriptor instanceof TypeParameterDescriptor) {
190                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) descriptor;
191                return typeParameterDescriptor.getDefaultType().getMemberScope();
192            }
193            return ((ClassDescriptor) descriptor).getMemberScope(typeArguments);
194        }
195    
196        @Override
197        public String toString() {
198            return debugName;
199        }
200    
201        private class DeserializedType implements JetType {
202            private final ProtoBuf.Type typeProto;
203            private final NotNullLazyValue<TypeConstructor> constructor;
204            private final List<TypeProjection> arguments;
205            private final NotNullLazyValue<JetScope> memberScope;
206    
207            public DeserializedType(@NotNull ProtoBuf.Type proto) {
208                this.typeProto = proto;
209                this.arguments = typeArguments(proto.getArgumentList());
210    
211                this.constructor = storageManager.createLazyValue(new Computable<TypeConstructor>() {
212                    @Override
213                    public TypeConstructor compute() {
214                        return typeConstructor(typeProto);
215                    }
216                });
217                this.memberScope = storageManager.createLazyValue(new Computable<JetScope>() {
218                    @Override
219                    public JetScope compute() {
220                        return computeMemberScope();
221                    }
222                });
223            }
224    
225            @NotNull
226            @Override
227            public TypeConstructor getConstructor() {
228                return constructor.compute();
229            }
230    
231            @NotNull
232            @Override
233            public List<TypeProjection> getArguments() {
234                return arguments;
235            }
236    
237            @Override
238            public boolean isNullable() {
239                return typeProto.getNullable();
240            }
241    
242            @NotNull
243            private JetScope computeMemberScope() {
244                TypeConstructor typeConstructor = getConstructor();
245                if (ErrorUtils.isError(typeConstructor)) {
246                    return ErrorUtils.createErrorScope(typeConstructor.toString());
247                }
248                else {
249                    return getTypeMemberScope(typeConstructor, getArguments());
250                }
251            }
252    
253            @NotNull
254            @Override
255            public JetScope getMemberScope() {
256                return memberScope.compute();
257            }
258    
259            @Override
260            public List<AnnotationDescriptor> getAnnotations() {
261                return Collections.emptyList();
262            }
263    
264            @Override
265            public String toString() {
266                return TypeUtils.toString(this);
267            }
268    
269            @Override
270            public boolean equals(Object o) {
271                if (this == o) return true;
272                if (!(o instanceof JetType)) return false;
273    
274                JetType type = (JetType) o;
275    
276                return isNullable() == type.isNullable() && JetTypeChecker.INSTANCE.equalTypes(this, type);
277            }
278    
279            @Override
280            public int hashCode() {
281                int result = getConstructor().hashCode();
282                result = 31 * result + getArguments().hashCode();
283                result = 31 * result + (isNullable() ? 1 : 0);
284                return result;
285            }
286        }
287    }