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.JvmCodegenUtil;
023    import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
024    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
025    import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
026    import org.jetbrains.jet.lang.resolve.CompileTimeConstantUtils;
027    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028    import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
029    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
030    import org.jetbrains.jet.lang.resolve.name.Name;
031    import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
032    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
033    import org.jetbrains.jet.lang.types.lang.PrimitiveType;
034    
035    import java.util.HashMap;
036    import java.util.Map;
037    
038    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject;
039    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumClass;
040    import static org.jetbrains.jet.lang.types.lang.KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME;
041    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
042    
043    public class IntrinsicMethods {
044        private static final IntrinsicMethod UNARY_MINUS = new UnaryMinus();
045        private static final IntrinsicMethod UNARY_PLUS = new UnaryPlus();
046        private static final IntrinsicMethod NUMBER_CAST = new NumberCast();
047        private static final IntrinsicMethod INV = new Inv();
048        private static final IntrinsicMethod RANGE_TO = new RangeTo();
049        private static final IntrinsicMethod INC = new Increment(1);
050        private static final IntrinsicMethod DEC = new Increment(-1);
051        private static final IntrinsicMethod HASH_CODE = new HashCode();
052    
053        private static final IntrinsicMethod ARRAY_SIZE = new ArraySize();
054        private static final IntrinsicMethod ARRAY_INDICES = new ArrayIndices();
055        private static final Equals EQUALS = new Equals();
056        private static final IdentityEquals IDENTITY_EQUALS = new IdentityEquals();
057        private static final IteratorNext ITERATOR_NEXT = new IteratorNext();
058        private static final ArraySet ARRAY_SET = new ArraySet();
059        private static final ArrayGet ARRAY_GET = new ArrayGet();
060        private static final StringPlus STRING_PLUS = new StringPlus();
061        private static final EnumValues ENUM_VALUES = new EnumValues();
062        private static final EnumValueOf ENUM_VALUE_OF = new EnumValueOf();
063        private static final ToString TO_STRING = new ToString();
064        private static final Clone CLONE = new Clone();
065    
066        private static final FqNameUnsafe KOTLIN_ANY_FQ_NAME = DescriptorUtils.getFqName(KotlinBuiltIns.getInstance().getAny());
067        private static final FqNameUnsafe KOTLIN_STRING_FQ_NAME = DescriptorUtils.getFqName(KotlinBuiltIns.getInstance().getString());
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        public IntrinsicMethods() {
074            namedMethods.put("kotlin.javaClass.function", new JavaClassFunction());
075            namedMethods.put("kotlin.javaClass.property", new JavaClassProperty());
076            namedMethods.put("kotlin.arrays.array", new JavaClassArray());
077            namedMethods.put("kotlin.collections.copyToArray", new CopyToArray());
078            namedMethods.put("kotlin.jvm.internal.unsafe.monitorEnter", MonitorInstruction.MONITOR_ENTER);
079            namedMethods.put("kotlin.jvm.internal.unsafe.monitorExit", MonitorInstruction.MONITOR_EXIT);
080    
081            ImmutableList<Name> primitiveCastMethods = OperatorConventions.NUMBER_CONVERSIONS.asList();
082            for (Name method : primitiveCastMethods) {
083                String methodName = method.asString();
084                declareIntrinsicFunction("Number", methodName, 0, NUMBER_CAST);
085                for (PrimitiveType type : PrimitiveType.NUMBER_TYPES) {
086                    declareIntrinsicFunction(type.getTypeName().asString(), methodName, 0, NUMBER_CAST);
087                }
088            }
089    
090            for (PrimitiveType type : PrimitiveType.NUMBER_TYPES) {
091                String typeName = type.getTypeName().asString();
092                declareIntrinsicFunction(typeName, "plus", 0, UNARY_PLUS);
093                declareIntrinsicFunction(typeName, "minus", 0, UNARY_MINUS);
094                declareIntrinsicFunction(typeName, "inv", 0, INV);
095                declareIntrinsicFunction(typeName, "rangeTo", 1, RANGE_TO);
096                declareIntrinsicFunction(typeName, "inc", 0, INC);
097                declareIntrinsicFunction(typeName, "dec", 0, DEC);
098            }
099    
100            for (PrimitiveType type : PrimitiveType.values()) {
101                String typeName = type.getTypeName().asString();
102                declareIntrinsicFunction(typeName, "equals", 1, EQUALS);
103                declareIntrinsicFunction(typeName, "hashCode", 0, HASH_CODE);
104                declareIntrinsicFunction(typeName, "toString", 0, TO_STRING);
105            }
106    
107            declareBinaryOp("plus", IADD);
108            declareBinaryOp("minus", ISUB);
109            declareBinaryOp("times", IMUL);
110            declareBinaryOp("div", IDIV);
111            declareBinaryOp("mod", IREM);
112            declareBinaryOp("shl", ISHL);
113            declareBinaryOp("shr", ISHR);
114            declareBinaryOp("ushr", IUSHR);
115            declareBinaryOp("and", IAND);
116            declareBinaryOp("or", IOR);
117            declareBinaryOp("xor", IXOR);
118    
119            declareIntrinsicFunction("Boolean", "not", 0, new Not());
120    
121            declareIntrinsicFunction("String", "plus", 1, new Concat());
122            declareIntrinsicFunction("CharSequence", "get", 1, new StringGetChar());
123            declareIntrinsicFunction("String", "get", 1, new StringGetChar());
124    
125            declareIntrinsicFunction("Cloneable", "clone", 0, CLONE);
126    
127            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, KOTLIN_ANY_FQ_NAME, "toString", 0, TO_STRING);
128            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, KOTLIN_ANY_FQ_NAME, "equals", 1, EQUALS);
129            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, KOTLIN_ANY_FQ_NAME, "identityEquals", 1, IDENTITY_EQUALS);
130            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, KOTLIN_STRING_FQ_NAME, "plus", 1, STRING_PLUS);
131            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, null, "arrayOfNulls", 1, new NewArray());
132    
133            for (PrimitiveType type : PrimitiveType.values()) {
134                String typeName = type.getTypeName().asString();
135                declareIntrinsicFunction(typeName, "compareTo", 1, new CompareTo());
136                declareIntrinsicFunction(typeName + "Iterator", "next", 0, ITERATOR_NEXT);
137            }
138    
139            declareIntrinsicProperty("CharSequence", "length", new StringLength());
140            declareIntrinsicProperty("String", "length", new StringLength());
141    
142            declareArrayMethods();
143        }
144    
145        private void declareArrayMethods() {
146            for (JvmPrimitiveType jvmPrimitiveType : JvmPrimitiveType.values()) {
147                declareArrayMethodsForPrimitive(jvmPrimitiveType);
148            }
149    
150            declareIntrinsicProperty("Array", "size", ARRAY_SIZE);
151            declareIntrinsicProperty("Array", "indices", ARRAY_INDICES);
152            declareIntrinsicFunction("Array", "set", 2, ARRAY_SET);
153            declareIntrinsicFunction("Array", "get", 1, ARRAY_GET);
154            declareIntrinsicFunction("Array", "clone", 0, CLONE);
155            declareIterator("Array");
156        }
157    
158        private void declareArrayMethodsForPrimitive(@NotNull JvmPrimitiveType jvmPrimitiveType) {
159            String arrayTypeName = jvmPrimitiveType.getPrimitiveType().getArrayTypeName().asString();
160            declareIntrinsicProperty(arrayTypeName, "size", ARRAY_SIZE);
161            declareIntrinsicProperty(arrayTypeName, "indices", ARRAY_INDICES);
162            declareIntrinsicFunction(arrayTypeName, "set", 2, ARRAY_SET);
163            declareIntrinsicFunction(arrayTypeName, "get", 1, ARRAY_GET);
164            declareIntrinsicFunction(arrayTypeName, "clone", 0, CLONE);
165            declareIterator(arrayTypeName);
166        }
167    
168        private void declareIterator(@NotNull String arrayClassName) {
169            declareIntrinsicFunction(arrayClassName, "iterator", 0, ARRAY_ITERATOR);
170        }
171    
172        private void declareBinaryOp(@NotNull String methodName, int opcode) {
173            BinaryOp op = new BinaryOp(opcode);
174            for (PrimitiveType type : PrimitiveType.values()) {
175                declareIntrinsicFunction(type.getTypeName().asString(), methodName, 1, op);
176            }
177        }
178    
179        private void declareIntrinsicProperty(@NotNull String className, @NotNull String methodName, @NotNull IntrinsicMethod implementation) {
180            declareIntrinsicFunction(className, methodName, -1, implementation);
181        }
182    
183        private void declareIntrinsicFunction(
184                @NotNull String className,
185                @NotNull String methodName,
186                int arity,
187                @NotNull IntrinsicMethod implementation
188        ) {
189            intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier(className)),
190                                            null, methodName, arity, implementation);
191        }
192    
193        @Nullable
194        public IntrinsicMethod getIntrinsic(@NotNull CallableMemberDescriptor descriptor) {
195            IntrinsicMethod intrinsicMethod = intrinsicsMap.getIntrinsic(descriptor);
196            if (intrinsicMethod != null) {
197                return intrinsicMethod;
198            }
199    
200            if (descriptor instanceof SimpleFunctionDescriptor) {
201                SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) descriptor;
202    
203                DeclarationDescriptor container = descriptor.getContainingDeclaration();
204                //noinspection ConstantConditions
205                if (isClassObject(container) && isEnumClass(container.getContainingDeclaration())) {
206                    if (JvmCodegenUtil.isEnumValuesMethod(functionDescriptor)) {
207                        return ENUM_VALUES;
208                    }
209    
210                    if (JvmCodegenUtil.isEnumValueOfMethod(functionDescriptor)) {
211                        return ENUM_VALUE_OF;
212                    }
213                }
214            }
215    
216            String value = CompileTimeConstantUtils.getIntrinsicAnnotationArgument(descriptor);
217            if (value == null) return null;
218    
219            return namedMethods.get(value);
220        }
221    }