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.codegen.intrinsics;
018    
019    import com.google.common.collect.ImmutableList;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.codegen.RangeCodegenUtil;
023    import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
024    import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
025    import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
026    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027    import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
028    import org.jetbrains.jet.lang.resolve.name.FqName;
029    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
030    import org.jetbrains.jet.lang.resolve.name.Name;
031    import org.jetbrains.jet.lang.resolve.name.SpecialNames;
032    import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
033    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
034    import org.jetbrains.jet.lang.types.lang.PrimitiveType;
035    
036    import javax.annotation.PostConstruct;
037    import java.util.HashMap;
038    import java.util.List;
039    import java.util.Map;
040    
041    import static org.jetbrains.asm4.Opcodes.*;
042    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
043    
044    public class IntrinsicMethods {
045        private static final IntrinsicMethod UNARY_MINUS = new UnaryMinus();
046        private static final IntrinsicMethod UNARY_PLUS = new UnaryPlus();
047        private static final IntrinsicMethod NUMBER_CAST = new NumberCast();
048        private static final IntrinsicMethod INV = new Inv();
049        private static final IntrinsicMethod RANGE_TO = new RangeTo();
050        private static final IntrinsicMethod INC = new Increment(1);
051        private static final IntrinsicMethod DEC = new Increment(-1);
052        private static final IntrinsicMethod HASH_CODE = new HashCode();
053    
054        private static final IntrinsicMethod ARRAY_SIZE = new ArraySize();
055        private static final IntrinsicMethod ARRAY_INDICES = new ArrayIndices();
056        private static final Equals EQUALS = new Equals();
057        private static final IdentityEquals IDENTITY_EQUALS = new IdentityEquals();
058        private static final IteratorNext ITERATOR_NEXT = new IteratorNext();
059        private static final ArraySet ARRAY_SET = new ArraySet();
060        private static final ArrayGet ARRAY_GET = new ArrayGet();
061        private static final StringPlus STRING_PLUS = new StringPlus();
062        public static final String KOTLIN_JAVA_CLASS_FUNCTION = "kotlin.javaClass.function";
063        private static final String KOTLIN_JAVA_CLASS_PROPERTY = "kotlin.javaClass.property";
064        public static final String KOTLIN_ARRAYS_ARRAY = "kotlin.arrays.array";
065        public static final String KOTLIN_COPY_TO_ARRAY = "kotlin.collections.copyToArray";
066        private static final String KOTLIN_TO_STRING = "kotlin.toString";
067        private static final String KOTLIN_HASH_CODE = "kotlin.hashCode";
068        private static final EnumValues ENUM_VALUES = new EnumValues();
069        private static final EnumValueOf ENUM_VALUE_OF = new EnumValueOf();
070        private static final ToString TO_STRING = new ToString();
071    
072        private final Map<String, IntrinsicMethod> namedMethods = new HashMap<String, IntrinsicMethod>();
073        private static final IntrinsicMethod ARRAY_ITERATOR = new ArrayIterator();
074        private final IntrinsicsMap intrinsicsMap = new IntrinsicsMap();
075    
076    
077        @PostConstruct
078        public void init() {
079            namedMethods.put(KOTLIN_JAVA_CLASS_FUNCTION, new JavaClassFunction());
080            namedMethods.put(KOTLIN_JAVA_CLASS_PROPERTY, new JavaClassProperty());
081            namedMethods.put(KOTLIN_ARRAYS_ARRAY, new JavaClassArray());
082            namedMethods.put(KOTLIN_COPY_TO_ARRAY, new CopyToArray());
083            namedMethods.put(KOTLIN_HASH_CODE, HASH_CODE);
084            namedMethods.put(KOTLIN_TO_STRING, TO_STRING);
085    
086            ImmutableList<Name> primitiveCastMethods = OperatorConventions.NUMBER_CONVERSIONS.asList();
087            for (Name method : primitiveCastMethods) {
088                declareIntrinsicFunction(Name.identifier("Number"), method, 0, NUMBER_CAST);
089                for (PrimitiveType type : PrimitiveType.NUMBER_TYPES) {
090                    declareIntrinsicFunction(type.getTypeName(), method, 0, NUMBER_CAST);
091                }
092            }
093    
094            for (PrimitiveType type : PrimitiveType.NUMBER_TYPES) {
095                Name typeName = type.getTypeName();
096                declareIntrinsicFunction(typeName, Name.identifier("plus"), 0, UNARY_PLUS);
097                declareIntrinsicFunction(typeName, Name.identifier("minus"), 0, UNARY_MINUS);
098                declareIntrinsicFunction(typeName, Name.identifier("inv"), 0, INV);
099                declareIntrinsicFunction(typeName, Name.identifier("rangeTo"), 1, RANGE_TO);
100                declareIntrinsicFunction(typeName, Name.identifier("inc"), 0, INC);
101                declareIntrinsicFunction(typeName, Name.identifier("dec"), 0, DEC);
102                declareIntrinsicFunction(typeName, Name.identifier("hashCode"), 0, HASH_CODE);
103                declareIntrinsicFunction(typeName, Name.identifier("equals"), 1, EQUALS);
104            }
105    
106            declareBinaryOp(Name.identifier("plus"), IADD);
107            declareBinaryOp(Name.identifier("minus"), ISUB);
108            declareBinaryOp(Name.identifier("times"), IMUL);
109            declareBinaryOp(Name.identifier("div"), IDIV);
110            declareBinaryOp(Name.identifier("mod"), IREM);
111            declareBinaryOp(Name.identifier("shl"), ISHL);
112            declareBinaryOp(Name.identifier("shr"), ISHR);
113            declareBinaryOp(Name.identifier("ushr"), IUSHR);
114            declareBinaryOp(Name.identifier("and"), IAND);
115            declareBinaryOp(Name.identifier("or"), IOR);
116            declareBinaryOp(Name.identifier("xor"), IXOR);
117    
118            declareIntrinsicFunction(Name.identifier("Boolean"), Name.identifier("not"), 0, new Not());
119    
120            declareIntrinsicFunction(Name.identifier("String"), Name.identifier("plus"), 1, new Concat());
121            declareIntrinsicFunction(Name.identifier("CharSequence"), Name.identifier("get"), 1, new StringGetChar());
122            declareIntrinsicFunction(Name.identifier("String"), Name.identifier("get"), 1, new StringGetChar());
123    
124            FqName builtInsPackageFqName = KotlinBuiltIns.getInstance().getBuiltInsPackageFqName();
125            intrinsicsMap.registerIntrinsic(builtInsPackageFqName, Name.identifier("name"), 0, new EnumName());
126            intrinsicsMap.registerIntrinsic(builtInsPackageFqName, Name.identifier("ordinal"), 0, new EnumOrdinal());
127    
128            intrinsicsMap.registerIntrinsic(builtInsPackageFqName, Name.identifier("toString"), 0, TO_STRING);
129            intrinsicsMap.registerIntrinsic(builtInsPackageFqName, Name.identifier("equals"), 1, EQUALS);
130            intrinsicsMap.registerIntrinsic(builtInsPackageFqName, Name.identifier("identityEquals"), 1, IDENTITY_EQUALS);
131            intrinsicsMap.registerIntrinsic(builtInsPackageFqName, Name.identifier("plus"), 1, STRING_PLUS);
132            intrinsicsMap.registerIntrinsic(builtInsPackageFqName, Name.identifier("arrayOfNulls"), 1, new NewArray());
133            intrinsicsMap.registerIntrinsic(builtInsPackageFqName, Name.identifier("synchronized"), 2, new StupidSync());
134            intrinsicsMap.registerIntrinsic(builtInsPackageFqName, Name.identifier("iterator"), 0, new IteratorIterator());
135    
136    
137            declareIntrinsicFunction(Name.identifier("ByteIterator"), Name.identifier("next"), 0, ITERATOR_NEXT);
138            declareIntrinsicFunction(Name.identifier("ShortIterator"), Name.identifier("next"), 0, ITERATOR_NEXT);
139            declareIntrinsicFunction(Name.identifier("IntIterator"), Name.identifier("next"), 0, ITERATOR_NEXT);
140            declareIntrinsicFunction(Name.identifier("LongIterator"), Name.identifier("next"), 0, ITERATOR_NEXT);
141            declareIntrinsicFunction(Name.identifier("CharIterator"), Name.identifier("next"), 0, ITERATOR_NEXT);
142            declareIntrinsicFunction(Name.identifier("BooleanIterator"), Name.identifier("next"), 0, ITERATOR_NEXT);
143            declareIntrinsicFunction(Name.identifier("FloatIterator"), Name.identifier("next"), 0, ITERATOR_NEXT);
144            declareIntrinsicFunction(Name.identifier("DoubleIterator"), Name.identifier("next"), 0, ITERATOR_NEXT);
145    
146            for (PrimitiveType type : PrimitiveType.values()) {
147                declareIntrinsicFunction(type.getTypeName(), Name.identifier("compareTo"), 1, new CompareTo());
148            }
149            //        declareIntrinsicFunction("Any", "equals", 1, new Equals());
150            //
151            declareIntrinsicProperty(Name.identifier("CharSequence"), Name.identifier("length"), new StringLength());
152            declareIntrinsicProperty(Name.identifier("String"), Name.identifier("length"), new StringLength());
153    
154            registerStaticField(getFQName(KotlinBuiltIns.getInstance().getUnit()).toSafe(), Name.identifier("VALUE"));
155    
156            for (PrimitiveType type : PrimitiveType.NUMBER_TYPES) {
157                FqName rangeClassFqName = RangeCodegenUtil.getRangeClassFqName(type);
158                FqName progressionClassFqName = RangeCodegenUtil.getProgressionClassFqName(type);
159    
160                registerStaticField(rangeClassFqName, Name.identifier("EMPTY"));
161    
162                registerRangeOrProgressionProperty(rangeClassFqName, Name.identifier("start"));
163                registerRangeOrProgressionProperty(rangeClassFqName, Name.identifier("end"));
164    
165                registerRangeOrProgressionProperty(progressionClassFqName, Name.identifier("start"));
166                registerRangeOrProgressionProperty(progressionClassFqName, Name.identifier("end"));
167                registerRangeOrProgressionProperty(progressionClassFqName, Name.identifier("increment"));
168            }
169    
170            declareArrayMethods();
171        }
172    
173        private void registerStaticField(@NotNull FqName classFqName, @NotNull Name propertyName) {
174            FqNameUnsafe classObjectFqName = classFqName.toUnsafe().child(SpecialNames.getClassObjectName(classFqName.shortName()));
175            intrinsicsMap.registerIntrinsic(classObjectFqName, propertyName, -1, new StaticField(classFqName, propertyName));
176        }
177    
178        private void registerRangeOrProgressionProperty(@NotNull FqName ownerClass, @NotNull Name propertyName) {
179            intrinsicsMap.registerIntrinsic(ownerClass, propertyName, -1, new PropertyOfProgressionOrRange(ownerClass, propertyName));
180        }
181    
182        private void declareArrayMethods() {
183    
184            for (JvmPrimitiveType jvmPrimitiveType : JvmPrimitiveType.values()) {
185                declareArrayMethodsForPrimitive(jvmPrimitiveType);
186            }
187    
188            declareIntrinsicProperty(Name.identifier("Array"), Name.identifier("size"), ARRAY_SIZE);
189            declareIntrinsicProperty(Name.identifier("Array"), Name.identifier("indices"), ARRAY_INDICES);
190            declareIntrinsicFunction(Name.identifier("Array"), Name.identifier("set"), 2, ARRAY_SET);
191            declareIntrinsicFunction(Name.identifier("Array"), Name.identifier("get"), 1, ARRAY_GET);
192            declareIterator(Name.identifier("Array"));
193        }
194    
195        private void declareArrayMethodsForPrimitive(JvmPrimitiveType jvmPrimitiveType) {
196            PrimitiveType primitiveType = jvmPrimitiveType.getPrimitiveType();
197            declareIntrinsicProperty(primitiveType.getArrayTypeName(), Name.identifier("size"), ARRAY_SIZE);
198            declareIntrinsicProperty(primitiveType.getArrayTypeName(), Name.identifier("indices"), ARRAY_INDICES);
199            declareIntrinsicFunction(primitiveType.getArrayTypeName(), Name.identifier("set"), 2, ARRAY_SET);
200            declareIntrinsicFunction(primitiveType.getArrayTypeName(), Name.identifier("get"), 1, ARRAY_GET);
201            declareIterator(primitiveType.getArrayTypeName());
202        }
203    
204        private void declareIterator(@NotNull Name arrayClassName) {
205            declareIntrinsicFunction(arrayClassName, Name.identifier("iterator"), 0, ARRAY_ITERATOR);
206        }
207    
208        private void declareBinaryOp(Name methodName, int opcode) {
209            BinaryOp op = new BinaryOp(opcode);
210            for (PrimitiveType type : PrimitiveType.values()) {
211                declareIntrinsicFunction(type.getTypeName(), methodName, 1, op);
212            }
213        }
214    
215        private void declareIntrinsicProperty(Name className, Name methodName, IntrinsicMethod implementation) {
216            intrinsicsMap.registerIntrinsic(KotlinBuiltIns.getInstance().getBuiltInsPackageFqName().child(className), methodName, -1, implementation);
217        }
218    
219        private void declareIntrinsicFunction(Name className, Name functionName, int arity, IntrinsicMethod implementation) {
220            intrinsicsMap.registerIntrinsic(KotlinBuiltIns.getInstance().getBuiltInsPackageFqName().child(className), functionName, arity, implementation);
221        }
222    
223        @Nullable
224        public IntrinsicMethod getIntrinsic(@NotNull CallableMemberDescriptor descriptor) {
225            IntrinsicMethod intrinsicMethod = intrinsicsMap.getIntrinsic(descriptor);
226            if (intrinsicMethod != null) {
227                return intrinsicMethod;
228            }
229    
230            if (descriptor instanceof SimpleFunctionDescriptor) {
231                SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) descriptor;
232    
233                if (isEnumClassObject(functionDescriptor.getContainingDeclaration())) {
234                    if (isEnumValuesMethod(functionDescriptor)) {
235                        return ENUM_VALUES;
236                    }
237    
238                    if (isEnumValueOfMethod(functionDescriptor)) {
239                        return ENUM_VALUE_OF;
240                    }
241                }
242            }
243    
244            List<AnnotationDescriptor> annotations = descriptor.getAnnotations();
245            if (annotations != null) {
246                for (AnnotationDescriptor annotation : annotations) {
247                    ClassifierDescriptor classifierDescriptor = annotation.getType().getConstructor().getDeclarationDescriptor();
248                    assert classifierDescriptor != null;
249                    if ("Intrinsic".equals(classifierDescriptor.getName().asString())) {
250                        String value = (String) annotation.getAllValueArguments().values().iterator().next().getValue();
251                        intrinsicMethod = namedMethods.get(value);
252                        if (intrinsicMethod != null) {
253                            break;
254                        }
255                    }
256                }
257            }
258            return intrinsicMethod;
259        }
260    }