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