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.Map;
039    
040    import static org.jetbrains.asm4.Opcodes.*;
041    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
042    import static org.jetbrains.jet.lang.types.lang.KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME;
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            declareIntrinsicFunction(Name.identifier("Boolean"), Name.identifier("equals"), 1, EQUALS);
120    
121            declareIntrinsicFunction(Name.identifier("String"), Name.identifier("plus"), 1, new Concat());
122            declareIntrinsicFunction(Name.identifier("CharSequence"), Name.identifier("get"), 1, new StringGetChar());
123            declareIntrinsicFunction(Name.identifier("String"), Name.identifier("get"), 1, new StringGetChar());
124    
125            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("toString"), 0, TO_STRING);
126            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("equals"), 1, EQUALS);
127            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("identityEquals"), 1, IDENTITY_EQUALS);
128            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("plus"), 1, STRING_PLUS);
129            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("arrayOfNulls"), 1, new NewArray());
130            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("synchronized"), 2, new StupidSync());
131            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("iterator"), 0, new IteratorIterator());
132    
133            for (PrimitiveType type : PrimitiveType.values()) {
134                declareIntrinsicFunction(type.getTypeName(), Name.identifier("compareTo"), 1, new CompareTo());
135                declareIntrinsicFunction(Name.identifier(type.getTypeName() + "Iterator"), Name.identifier("next"), 0, ITERATOR_NEXT);
136            }
137    
138            declareIntrinsicProperty(Name.identifier("CharSequence"), Name.identifier("length"), new StringLength());
139            declareIntrinsicProperty(Name.identifier("String"), Name.identifier("length"), new StringLength());
140    
141            registerStaticField(getFqNameSafe(KotlinBuiltIns.getInstance().getUnit()), Name.identifier("VALUE"));
142    
143            for (PrimitiveType type : PrimitiveType.NUMBER_TYPES) {
144                FqName rangeClassFqName = RangeCodegenUtil.getRangeClassFqName(type);
145                FqName progressionClassFqName = RangeCodegenUtil.getProgressionClassFqName(type);
146    
147                registerStaticField(rangeClassFqName, Name.identifier("EMPTY"));
148    
149                registerRangeOrProgressionProperty(rangeClassFqName, Name.identifier("start"));
150                registerRangeOrProgressionProperty(rangeClassFqName, Name.identifier("end"));
151    
152                registerRangeOrProgressionProperty(progressionClassFqName, Name.identifier("start"));
153                registerRangeOrProgressionProperty(progressionClassFqName, Name.identifier("end"));
154                registerRangeOrProgressionProperty(progressionClassFqName, Name.identifier("increment"));
155            }
156    
157            declareArrayMethods();
158        }
159    
160        private void registerStaticField(@NotNull FqName classFqName, @NotNull Name propertyName) {
161            FqNameUnsafe classObjectFqName = classFqName.toUnsafe().child(SpecialNames.getClassObjectName(classFqName.shortName()));
162            intrinsicsMap.registerIntrinsic(classObjectFqName, propertyName, -1, new StaticField(classFqName, propertyName));
163        }
164    
165        private void registerRangeOrProgressionProperty(@NotNull FqName ownerClass, @NotNull Name propertyName) {
166            intrinsicsMap.registerIntrinsic(ownerClass, propertyName, -1, new PropertyOfProgressionOrRange(ownerClass, propertyName));
167        }
168    
169        private void declareArrayMethods() {
170    
171            for (JvmPrimitiveType jvmPrimitiveType : JvmPrimitiveType.values()) {
172                declareArrayMethodsForPrimitive(jvmPrimitiveType);
173            }
174    
175            declareIntrinsicProperty(Name.identifier("Array"), Name.identifier("size"), ARRAY_SIZE);
176            declareIntrinsicProperty(Name.identifier("Array"), Name.identifier("indices"), ARRAY_INDICES);
177            declareIntrinsicFunction(Name.identifier("Array"), Name.identifier("set"), 2, ARRAY_SET);
178            declareIntrinsicFunction(Name.identifier("Array"), Name.identifier("get"), 1, ARRAY_GET);
179            declareIterator(Name.identifier("Array"));
180        }
181    
182        private void declareArrayMethodsForPrimitive(JvmPrimitiveType jvmPrimitiveType) {
183            PrimitiveType primitiveType = jvmPrimitiveType.getPrimitiveType();
184            declareIntrinsicProperty(primitiveType.getArrayTypeName(), Name.identifier("size"), ARRAY_SIZE);
185            declareIntrinsicProperty(primitiveType.getArrayTypeName(), Name.identifier("indices"), ARRAY_INDICES);
186            declareIntrinsicFunction(primitiveType.getArrayTypeName(), Name.identifier("set"), 2, ARRAY_SET);
187            declareIntrinsicFunction(primitiveType.getArrayTypeName(), Name.identifier("get"), 1, ARRAY_GET);
188            declareIterator(primitiveType.getArrayTypeName());
189        }
190    
191        private void declareIterator(@NotNull Name arrayClassName) {
192            declareIntrinsicFunction(arrayClassName, Name.identifier("iterator"), 0, ARRAY_ITERATOR);
193        }
194    
195        private void declareBinaryOp(Name methodName, int opcode) {
196            BinaryOp op = new BinaryOp(opcode);
197            for (PrimitiveType type : PrimitiveType.values()) {
198                declareIntrinsicFunction(type.getTypeName(), methodName, 1, op);
199            }
200        }
201    
202        private void declareIntrinsicProperty(Name className, Name methodName, IntrinsicMethod implementation) {
203            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME.child(className), methodName, -1, implementation);
204        }
205    
206        private void declareIntrinsicFunction(Name className, Name functionName, int arity, IntrinsicMethod implementation) {
207            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME.child(className), functionName, arity, implementation);
208        }
209    
210        @Nullable
211        public IntrinsicMethod getIntrinsic(@NotNull CallableMemberDescriptor descriptor) {
212            IntrinsicMethod intrinsicMethod = intrinsicsMap.getIntrinsic(descriptor);
213            if (intrinsicMethod != null) {
214                return intrinsicMethod;
215            }
216    
217            if (descriptor instanceof SimpleFunctionDescriptor) {
218                SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) descriptor;
219    
220                if (isEnumClassObject(functionDescriptor.getContainingDeclaration())) {
221                    if (isEnumValuesMethod(functionDescriptor)) {
222                        return ENUM_VALUES;
223                    }
224    
225                    if (isEnumValueOfMethod(functionDescriptor)) {
226                        return ENUM_VALUE_OF;
227                    }
228                }
229            }
230    
231            for (AnnotationDescriptor annotation : descriptor.getAnnotations()) {
232                ClassifierDescriptor classifierDescriptor = annotation.getType().getConstructor().getDeclarationDescriptor();
233                assert classifierDescriptor != null;
234                if ("Intrinsic".equals(classifierDescriptor.getName().asString())) {
235                    String value = (String) annotation.getAllValueArguments().values().iterator().next().getValue();
236                    IntrinsicMethod result = namedMethods.get(value);
237                    if (result != null) return result;
238                }
239            }
240    
241            return null;
242        }
243    }