001    /*
002     * Copyright 2010-2015 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.kotlin.serialization;
018    
019    import com.google.protobuf.Internal;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.kotlin.descriptors.*;
022    
023    public class Flags {
024        private Flags() {}
025    
026        // Common
027    
028        public static final FlagField<Boolean> HAS_ANNOTATIONS = FlagField.booleanFirst();
029    
030        public static final FlagField<ProtoBuf.Visibility> VISIBILITY = FlagField.after(HAS_ANNOTATIONS, ProtoBuf.Visibility.values());
031    
032        public static final FlagField<ProtoBuf.Modality> MODALITY = FlagField.after(VISIBILITY, ProtoBuf.Modality.values());
033    
034        // Class
035    
036        public static final FlagField<ProtoBuf.Class.Kind> CLASS_KIND = FlagField.after(MODALITY, ProtoBuf.Class.Kind.values());
037    
038        public static final FlagField<Boolean> INNER = FlagField.booleanAfter(CLASS_KIND);
039    
040        // Callables
041    
042        public static final FlagField<ProtoBuf.Callable.CallableKind> CALLABLE_KIND = FlagField.after(MODALITY,
043                                                                                                      ProtoBuf.Callable.CallableKind.values());
044    
045        public static final FlagField<ProtoBuf.Callable.MemberKind> MEMBER_KIND = FlagField.after(CALLABLE_KIND,
046                                                                                                  ProtoBuf.Callable.MemberKind.values());
047        public static final FlagField<Boolean> HAS_GETTER = FlagField.booleanAfter(MEMBER_KIND);
048        public static final FlagField<Boolean> HAS_SETTER = FlagField.booleanAfter(HAS_GETTER);
049        public static final FlagField<Boolean> HAS_CONSTANT = FlagField.booleanAfter(HAS_SETTER);
050    
051        public static final FlagField<Boolean> IS_CONST = FlagField.booleanAfter(HAS_CONSTANT);
052    
053        public static final FlagField<Boolean> LATE_INIT = FlagField.booleanAfter(IS_CONST);
054    
055        public static final FlagField<Boolean> IS_OPERATOR = FlagField.booleanAfter(LATE_INIT);
056    
057        // Parameters
058    
059        public static final FlagField<Boolean> DECLARES_DEFAULT_VALUE = FlagField.booleanAfter(HAS_ANNOTATIONS);
060    
061        // Accessors
062    
063        // It's important that this flag is negated: "is NOT default" instead of "is default"
064        public static final FlagField<Boolean> IS_NOT_DEFAULT = FlagField.booleanAfter(MODALITY);
065    
066        // ---
067    
068        private static <E> int bitWidth(@NotNull E[] enumEntries) {
069            int length = enumEntries.length - 1;
070            if (length == 0) return 1;
071            for (int i = 31; i >= 0; i--) {
072                if ((length & (1 << i)) != 0) return i + 1;
073            }
074            throw new IllegalStateException("Empty enum: " + enumEntries.getClass());
075        }
076    
077        public static int getClassFlags(
078                boolean hasAnnotations,
079                Visibility visibility,
080                Modality modality,
081                ClassKind kind,
082                boolean inner,
083                boolean isCompanionObject
084        ) {
085            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
086                   | MODALITY.toFlags(modality(modality))
087                   | VISIBILITY.toFlags(visibility(visibility))
088                   | CLASS_KIND.toFlags(classKind(kind, isCompanionObject))
089                   | INNER.toFlags(inner)
090                   ;
091        }
092    
093        private static ProtoBuf.Class.Kind classKind(ClassKind kind, boolean isCompanionObject) {
094            if (isCompanionObject) return ProtoBuf.Class.Kind.COMPANION_OBJECT;
095    
096            switch (kind) {
097                case CLASS:
098                    return ProtoBuf.Class.Kind.CLASS;
099                case INTERFACE:
100                    return ProtoBuf.Class.Kind.INTERFACE;
101                case ENUM_CLASS:
102                    return ProtoBuf.Class.Kind.ENUM_CLASS;
103                case ENUM_ENTRY:
104                    return ProtoBuf.Class.Kind.ENUM_ENTRY;
105                case ANNOTATION_CLASS:
106                    return ProtoBuf.Class.Kind.ANNOTATION_CLASS;
107                case OBJECT:
108                    return ProtoBuf.Class.Kind.OBJECT;
109            }
110            throw new IllegalArgumentException("Unknown class kind: " + kind);
111        }
112    
113        public static int getCallableFlags(
114                boolean hasAnnotations,
115                @NotNull Visibility visibility,
116                @NotNull Modality modality,
117                @NotNull CallableMemberDescriptor.Kind memberKind,
118                @NotNull ProtoBuf.Callable.CallableKind callableKind,
119                boolean hasGetter,
120                boolean hasSetter,
121                boolean hasConstant,
122                boolean lateInit,
123                boolean isConst,
124                boolean isOperator
125        ) {
126            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
127                   | MODALITY.toFlags(modality(modality))
128                   | VISIBILITY.toFlags(visibility(visibility))
129                   | MEMBER_KIND.toFlags(memberKind(memberKind))
130                   | CALLABLE_KIND.toFlags(callableKind)
131                   | HAS_GETTER.toFlags(hasGetter)
132                   | HAS_SETTER.toFlags(hasSetter)
133                   | HAS_CONSTANT.toFlags(hasConstant)
134                   | LATE_INIT.toFlags(lateInit)
135                   | IS_CONST.toFlags(isConst)
136                   | IS_OPERATOR.toFlags(isOperator)
137                   ;
138        }
139    
140        public static int getAccessorFlags(
141                boolean hasAnnotations,
142                @NotNull Visibility visibility,
143                @NotNull Modality modality,
144                boolean isNotDefault
145        ) {
146            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
147                   | MODALITY.toFlags(modality(modality))
148                   | VISIBILITY.toFlags(visibility(visibility))
149                   | IS_NOT_DEFAULT.toFlags(isNotDefault)
150                   ;
151        }
152    
153        @NotNull
154        private static ProtoBuf.Visibility visibility(@NotNull Visibility visibility) {
155            if (visibility == Visibilities.INTERNAL) {
156                return ProtoBuf.Visibility.INTERNAL;
157            }
158            else if (visibility == Visibilities.PUBLIC) {
159                return ProtoBuf.Visibility.PUBLIC;
160            }
161            else if (visibility == Visibilities.PRIVATE) {
162                return ProtoBuf.Visibility.PRIVATE;
163            }
164            else if (visibility == Visibilities.PRIVATE_TO_THIS) {
165                return ProtoBuf.Visibility.PRIVATE_TO_THIS;
166            }
167            else if (visibility == Visibilities.PROTECTED) {
168                return ProtoBuf.Visibility.PROTECTED;
169            }
170            else if (visibility == Visibilities.LOCAL) {
171                return ProtoBuf.Visibility.LOCAL;
172            }
173            throw new IllegalArgumentException("Unknown visibility: " + visibility);
174        }
175    
176        @NotNull
177        private static ProtoBuf.Modality modality(@NotNull Modality modality) {
178            switch (modality) {
179                case FINAL:
180                    return ProtoBuf.Modality.FINAL;
181                case OPEN:
182                    return ProtoBuf.Modality.OPEN;
183                case ABSTRACT:
184                    return ProtoBuf.Modality.ABSTRACT;
185                case SEALED:
186                    return ProtoBuf.Modality.SEALED;
187            }
188            throw new IllegalArgumentException("Unknown modality: " + modality);
189        }
190    
191        @NotNull
192        private static ProtoBuf.Callable.MemberKind memberKind(@NotNull CallableMemberDescriptor.Kind kind) {
193            switch (kind) {
194                case DECLARATION:
195                    return ProtoBuf.Callable.MemberKind.DECLARATION;
196                case FAKE_OVERRIDE:
197                    return ProtoBuf.Callable.MemberKind.FAKE_OVERRIDE;
198                case DELEGATION:
199                    return ProtoBuf.Callable.MemberKind.DELEGATION;
200                case SYNTHESIZED:
201                    return ProtoBuf.Callable.MemberKind.SYNTHESIZED;
202            }
203            throw new IllegalArgumentException("Unknown member kind: " + kind);
204        }
205    
206        public static int getValueParameterFlags(boolean hasAnnotations, boolean declaresDefaultValue) {
207            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
208                   | DECLARES_DEFAULT_VALUE.toFlags(declaresDefaultValue)
209                   ;
210        }
211    
212        // Infrastructure
213    
214        public static abstract class FlagField<E> {
215            public static <E extends Internal.EnumLite> FlagField<E> after(FlagField<?> previousField, E[] values) {
216                int offset = previousField.offset + previousField.bitWidth;
217                return new EnumLiteFlagField<E>(offset, values);
218            }
219    
220            public static <E extends Internal.EnumLite> FlagField<E> first(E[] values) {
221                return new EnumLiteFlagField<E>(0, values);
222            }
223    
224            public static FlagField<Boolean> booleanFirst() {
225                return new BooleanFlagField(0);
226            }
227    
228            public static FlagField<Boolean> booleanAfter(FlagField<?> previousField) {
229                int offset = previousField.offset + previousField.bitWidth;
230                return new BooleanFlagField(offset);
231            }
232    
233            private final int offset;
234            private final int bitWidth;
235            private final E[] values;
236    
237            private FlagField(int offset, E[] values) {
238                this.offset = offset;
239                this.bitWidth = bitWidth(values);
240                this.values = values;
241            }
242    
243            public E get(int flags) {
244                int maskUnshifted = (1 << bitWidth) - 1;
245                int mask = maskUnshifted << offset;
246                int value = (flags & mask) >> offset;
247                for (E e : values) {
248                    if (getIntValue(e) == value) {
249                        return e;
250                    }
251                }
252                throw new IllegalStateException("Flag not found: " + value);
253            }
254    
255            public int toFlags(E value) {
256                return getIntValue(value) << offset;
257            }
258    
259            protected abstract int getIntValue(E value);
260    
261        }
262    
263        private static class BooleanFlagField extends FlagField<Boolean> {
264            private static final Boolean[] BOOLEAN = { false, true };
265    
266            public BooleanFlagField(int offset) {
267                super(offset, BOOLEAN);
268            }
269    
270            @Override
271            protected int getIntValue(Boolean value) {
272                return value ? 1 : 0;
273            }
274        }
275    
276        private static class EnumLiteFlagField<E extends Internal.EnumLite> extends FlagField<E> {
277            public EnumLiteFlagField(int offset, E[] values) {
278                super(offset, values);
279            }
280    
281            @Override
282            protected int getIntValue(E value) {
283                return value.getNumber();
284            }
285        }
286    
287    }