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