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