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