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.annotations.Nullable;
022    import org.jetbrains.kotlin.descriptors.*;
023    
024    public class Flags {
025        private Flags() {}
026    
027        // Common
028    
029        public static final BooleanFlagField HAS_ANNOTATIONS = FlagField.booleanFirst();
030        public static final FlagField<ProtoBuf.Visibility> VISIBILITY = FlagField.after(HAS_ANNOTATIONS, ProtoBuf.Visibility.values());
031        public static final FlagField<ProtoBuf.Modality> MODALITY = FlagField.after(VISIBILITY, ProtoBuf.Modality.values());
032    
033        // Class
034    
035        public static final FlagField<ProtoBuf.Class.Kind> CLASS_KIND = FlagField.after(MODALITY, ProtoBuf.Class.Kind.values());
036        public static final BooleanFlagField IS_INNER = FlagField.booleanAfter(CLASS_KIND);
037        public static final BooleanFlagField IS_DATA = FlagField.booleanAfter(IS_INNER);
038    
039        // Callables
040    
041        // TODO: use these flags
042        public static final BooleanFlagField RESERVED_1 = FlagField.booleanAfter(MODALITY);
043        public static final BooleanFlagField RESERVED_2 = FlagField.booleanAfter(RESERVED_1);
044    
045        public static final FlagField<ProtoBuf.MemberKind> MEMBER_KIND = FlagField.after(RESERVED_2, ProtoBuf.MemberKind.values());
046    
047        // Constructors
048    
049        public static final BooleanFlagField IS_SECONDARY = FlagField.booleanAfter(VISIBILITY);
050    
051        // Functions
052    
053        public static final BooleanFlagField IS_OPERATOR = FlagField.booleanAfter(MEMBER_KIND);
054        public static final BooleanFlagField IS_INFIX = FlagField.booleanAfter(IS_OPERATOR);
055        public static final BooleanFlagField IS_INLINE = FlagField.booleanAfter(IS_INFIX);
056        public static final BooleanFlagField IS_TAILREC = FlagField.booleanAfter(IS_INLINE);
057        public static final BooleanFlagField IS_EXTERNAL_FUNCTION = FlagField.booleanAfter(IS_TAILREC);
058    
059        // Properties
060    
061        public static final BooleanFlagField IS_VAR = FlagField.booleanAfter(MEMBER_KIND);
062        public static final BooleanFlagField HAS_GETTER = FlagField.booleanAfter(IS_VAR);
063        public static final BooleanFlagField HAS_SETTER = FlagField.booleanAfter(HAS_GETTER);
064        public static final BooleanFlagField IS_CONST = FlagField.booleanAfter(HAS_SETTER);
065        public static final BooleanFlagField IS_LATEINIT = FlagField.booleanAfter(IS_CONST);
066        public static final BooleanFlagField HAS_CONSTANT = FlagField.booleanAfter(IS_LATEINIT);
067    
068        // Parameters
069    
070        public static final BooleanFlagField DECLARES_DEFAULT_VALUE = FlagField.booleanAfter(HAS_ANNOTATIONS);
071        public static final BooleanFlagField IS_CROSSINLINE = FlagField.booleanAfter(DECLARES_DEFAULT_VALUE);
072        public static final BooleanFlagField IS_NOINLINE = FlagField.booleanAfter(IS_CROSSINLINE);
073    
074        // Accessors
075    
076        // It's important that this flag is negated: "is NOT default" instead of "is default"
077        public static final BooleanFlagField IS_NOT_DEFAULT = FlagField.booleanAfter(MODALITY);
078        public static final BooleanFlagField IS_EXTERNAL_ACCESSOR = FlagField.booleanAfter(IS_NOT_DEFAULT);
079    
080        // ---
081    
082        private static <E> int bitWidth(@NotNull E[] enumEntries) {
083            int length = enumEntries.length - 1;
084            if (length == 0) return 1;
085            for (int i = 31; i >= 0; i--) {
086                if ((length & (1 << i)) != 0) return i + 1;
087            }
088            throw new IllegalStateException("Empty enum: " + enumEntries.getClass());
089        }
090    
091        public static int getClassFlags(
092                boolean hasAnnotations,
093                Visibility visibility,
094                Modality modality,
095                ClassKind kind,
096                boolean inner,
097                boolean isCompanionObject,
098                boolean isData
099        ) {
100            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
101                   | MODALITY.toFlags(modality(modality))
102                   | VISIBILITY.toFlags(visibility(visibility))
103                   | CLASS_KIND.toFlags(classKind(kind, isCompanionObject))
104                   | IS_INNER.toFlags(inner)
105                   | IS_DATA.toFlags(isData)
106                   ;
107        }
108    
109        private static ProtoBuf.Class.Kind classKind(ClassKind kind, boolean isCompanionObject) {
110            if (isCompanionObject) return ProtoBuf.Class.Kind.COMPANION_OBJECT;
111    
112            switch (kind) {
113                case CLASS:
114                    return ProtoBuf.Class.Kind.CLASS;
115                case INTERFACE:
116                    return ProtoBuf.Class.Kind.INTERFACE;
117                case ENUM_CLASS:
118                    return ProtoBuf.Class.Kind.ENUM_CLASS;
119                case ENUM_ENTRY:
120                    return ProtoBuf.Class.Kind.ENUM_ENTRY;
121                case ANNOTATION_CLASS:
122                    return ProtoBuf.Class.Kind.ANNOTATION_CLASS;
123                case OBJECT:
124                    return ProtoBuf.Class.Kind.OBJECT;
125            }
126            throw new IllegalArgumentException("Unknown class kind: " + kind);
127        }
128    
129        public static int getConstructorFlags(
130                boolean hasAnnotations,
131                @NotNull Visibility visibility,
132                boolean isSecondary
133        ) {
134            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
135                   | VISIBILITY.toFlags(visibility(visibility))
136                   | IS_SECONDARY.toFlags(isSecondary)
137                    ;
138        }
139    
140        public static int getFunctionFlags(
141                boolean hasAnnotations,
142                @NotNull Visibility visibility,
143                @NotNull Modality modality,
144                @NotNull CallableMemberDescriptor.Kind memberKind,
145                boolean isOperator,
146                boolean isInfix,
147                boolean isInline,
148                boolean isTailrec,
149                boolean isExternal
150        ) {
151            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
152                   | VISIBILITY.toFlags(visibility(visibility))
153                   | MODALITY.toFlags(modality(modality))
154                   | MEMBER_KIND.toFlags(memberKind(memberKind))
155                   | IS_OPERATOR.toFlags(isOperator)
156                   | IS_INFIX.toFlags(isInfix)
157                   | IS_INLINE.toFlags(isInline)
158                   | IS_TAILREC.toFlags(isTailrec)
159                   | IS_EXTERNAL_FUNCTION.toFlags(isExternal)
160                    ;
161        }
162    
163        public static int getPropertyFlags(
164                boolean hasAnnotations,
165                @NotNull Visibility visibility,
166                @NotNull Modality modality,
167                @NotNull CallableMemberDescriptor.Kind memberKind,
168                boolean isVar,
169                boolean hasGetter,
170                boolean hasSetter,
171                boolean hasConstant,
172                boolean isConst,
173                boolean lateInit
174        ) {
175            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
176                   | VISIBILITY.toFlags(visibility(visibility))
177                   | MODALITY.toFlags(modality(modality))
178                   | MEMBER_KIND.toFlags(memberKind(memberKind))
179                   | IS_VAR.toFlags(isVar)
180                   | HAS_GETTER.toFlags(hasGetter)
181                   | HAS_SETTER.toFlags(hasSetter)
182                   | IS_CONST.toFlags(isConst)
183                   | IS_LATEINIT.toFlags(lateInit)
184                   | HAS_CONSTANT.toFlags(hasConstant)
185                    ;
186        }
187    
188        public static int getAccessorFlags(
189                boolean hasAnnotations,
190                @NotNull Visibility visibility,
191                @NotNull Modality modality,
192                boolean isNotDefault,
193                boolean isExternal
194        ) {
195            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
196                   | MODALITY.toFlags(modality(modality))
197                   | VISIBILITY.toFlags(visibility(visibility))
198                   | IS_NOT_DEFAULT.toFlags(isNotDefault)
199                   | IS_EXTERNAL_ACCESSOR.toFlags(isExternal)
200                   ;
201        }
202    
203        @NotNull
204        private static ProtoBuf.Visibility visibility(@NotNull Visibility visibility) {
205            if (visibility == Visibilities.INTERNAL) {
206                return ProtoBuf.Visibility.INTERNAL;
207            }
208            else if (visibility == Visibilities.PUBLIC) {
209                return ProtoBuf.Visibility.PUBLIC;
210            }
211            else if (visibility == Visibilities.PRIVATE) {
212                return ProtoBuf.Visibility.PRIVATE;
213            }
214            else if (visibility == Visibilities.PRIVATE_TO_THIS) {
215                return ProtoBuf.Visibility.PRIVATE_TO_THIS;
216            }
217            else if (visibility == Visibilities.PROTECTED) {
218                return ProtoBuf.Visibility.PROTECTED;
219            }
220            else if (visibility == Visibilities.LOCAL) {
221                return ProtoBuf.Visibility.LOCAL;
222            }
223            throw new IllegalArgumentException("Unknown visibility: " + visibility);
224        }
225    
226        @NotNull
227        private static ProtoBuf.Modality modality(@NotNull Modality modality) {
228            switch (modality) {
229                case FINAL:
230                    return ProtoBuf.Modality.FINAL;
231                case OPEN:
232                    return ProtoBuf.Modality.OPEN;
233                case ABSTRACT:
234                    return ProtoBuf.Modality.ABSTRACT;
235                case SEALED:
236                    return ProtoBuf.Modality.SEALED;
237            }
238            throw new IllegalArgumentException("Unknown modality: " + modality);
239        }
240    
241        @NotNull
242        private static ProtoBuf.MemberKind memberKind(@NotNull CallableMemberDescriptor.Kind kind) {
243            switch (kind) {
244                case DECLARATION:
245                    return ProtoBuf.MemberKind.DECLARATION;
246                case FAKE_OVERRIDE:
247                    return ProtoBuf.MemberKind.FAKE_OVERRIDE;
248                case DELEGATION:
249                    return ProtoBuf.MemberKind.DELEGATION;
250                case SYNTHESIZED:
251                    return ProtoBuf.MemberKind.SYNTHESIZED;
252            }
253            throw new IllegalArgumentException("Unknown member kind: " + kind);
254        }
255    
256        public static int getValueParameterFlags(
257                boolean hasAnnotations,
258                boolean declaresDefaultValue,
259                boolean isCrossinline,
260                boolean isNoinline
261        ) {
262            return HAS_ANNOTATIONS.toFlags(hasAnnotations)
263                   | DECLARES_DEFAULT_VALUE.toFlags(declaresDefaultValue)
264                   | IS_CROSSINLINE.toFlags(isCrossinline)
265                   | IS_NOINLINE.toFlags(isNoinline)
266                   ;
267        }
268    
269        // Infrastructure
270    
271        public static abstract class FlagField<E> {
272            public static <E extends Internal.EnumLite> FlagField<E> after(FlagField<?> previousField, E[] values) {
273                int offset = previousField.offset + previousField.bitWidth;
274                return new EnumLiteFlagField<E>(offset, values);
275            }
276    
277            public static <E extends Internal.EnumLite> FlagField<E> first(E[] values) {
278                return new EnumLiteFlagField<E>(0, values);
279            }
280    
281            public static BooleanFlagField booleanFirst() {
282                return new BooleanFlagField(0);
283            }
284    
285            public static BooleanFlagField booleanAfter(FlagField<?> previousField) {
286                int offset = previousField.offset + previousField.bitWidth;
287                return new BooleanFlagField(offset);
288            }
289    
290            private final int offset;
291            private final int bitWidth;
292            private final E[] values;
293    
294            private FlagField(int offset, E[] values) {
295                this.offset = offset;
296                this.bitWidth = bitWidth(values);
297                this.values = values;
298            }
299    
300            @Nullable
301            public E get(int flags) {
302                int maskUnshifted = (1 << bitWidth) - 1;
303                int mask = maskUnshifted << offset;
304                int value = (flags & mask) >> offset;
305                for (E e : values) {
306                    if (getIntValue(e) == value) {
307                        return e;
308                    }
309                }
310                return null;
311            }
312    
313            public int toFlags(E value) {
314                return getIntValue(value) << offset;
315            }
316    
317            protected abstract int getIntValue(E value);
318    
319        }
320    
321        public static class BooleanFlagField extends FlagField<Boolean> {
322            private static final Boolean[] BOOLEAN = { false, true };
323    
324            public BooleanFlagField(int offset) {
325                super(offset, BOOLEAN);
326            }
327    
328            @Override
329            protected int getIntValue(Boolean value) {
330                return value ? 1 : 0;
331            }
332    
333            @NotNull
334            @Override
335            public Boolean get(int flags) {
336                //noinspection ConstantConditions
337                return super.get(flags);
338            }
339        }
340    
341        private static class EnumLiteFlagField<E extends Internal.EnumLite> extends FlagField<E> {
342            public EnumLiteFlagField(int offset, E[] values) {
343                super(offset, values);
344            }
345    
346            @Override
347            protected int getIntValue(E value) {
348                return value.getNumber();
349            }
350        }
351    
352    }