001    /*
002     * Copyright 2010-2016 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.js.translate.intrinsic.functions.factories;
018    
019    import org.jetbrains.kotlin.js.backend.ast.JsExpression;
020    import org.jetbrains.kotlin.js.backend.ast.JsInvocation;
021    import org.jetbrains.kotlin.js.backend.ast.JsNameRef;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.descriptors.*;
025    import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
026    import org.jetbrains.kotlin.js.patterns.NamePredicate;
027    import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo;
028    import org.jetbrains.kotlin.js.translate.callTranslator.CallInfoExtensionsKt;
029    import org.jetbrains.kotlin.js.translate.context.TranslationContext;
030    import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
031    import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed;
032    import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
033    import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
034    import org.jetbrains.kotlin.resolve.DescriptorFactory;
035    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
036    import org.jetbrains.kotlin.types.KotlinType;
037    
038    import java.util.List;
039    import java.util.Map;
040    
041    import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES;
042    import static org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern;
043    
044    public final class TopLevelFIF extends CompositeFIF {
045        public static final DescriptorPredicate EQUALS_IN_ANY = pattern("kotlin", "Any", "equals");
046        @NotNull
047        private static final KotlinFunctionIntrinsic KOTLIN_ANY_EQUALS = new KotlinFunctionIntrinsic("equals") {
048            @NotNull
049            @Override
050            public JsExpression apply(
051                    @NotNull CallInfo callInfo,
052                    @NotNull List<? extends JsExpression> arguments, @NotNull TranslationContext context
053            ) {
054                if (CallInfoExtensionsKt.isSuperInvocation(callInfo)) {
055                    JsExpression dispatchReceiver = callInfo.getDispatchReceiver();
056                    assert arguments.size() == 1 && dispatchReceiver != null;
057                    return JsAstUtils.equality(dispatchReceiver, arguments.get(0));
058                }
059    
060                return super.apply(callInfo, arguments, context);
061            }
062        };
063    
064        @NotNull
065        public static final KotlinFunctionIntrinsic KOTLIN_EQUALS = new KotlinFunctionIntrinsic("equals");
066    
067        @NotNull
068        private static final KotlinFunctionIntrinsic KOTLIN_SUBSEQUENCE = new KotlinFunctionIntrinsic("subSequence");
069    
070        @NotNull
071        private static final DescriptorPredicate HASH_CODE_IN_ANY = pattern("kotlin", "Any", "hashCode");
072        @NotNull
073        private static final KotlinFunctionIntrinsic KOTLIN_HASH_CODE = new KotlinFunctionIntrinsic("hashCode");
074    
075        @NotNull
076        private static final FunctionIntrinsic RETURN_RECEIVER_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() {
077            @NotNull
078            @Override
079            public JsExpression apply(
080                    @Nullable JsExpression receiver,
081                    @NotNull List<? extends JsExpression> arguments,
082                    @NotNull TranslationContext context
083            ) {
084                assert receiver != null;
085                return receiver;
086            }
087        };
088    
089    
090        private static JsExpression getReferenceToOnlyTypeParameter(
091                @NotNull CallInfo callInfo, @NotNull TranslationContext context
092        ) {
093            ResolvedCall<? extends CallableDescriptor> resolvedCall = callInfo.getResolvedCall();
094            Map<TypeParameterDescriptor, KotlinType> typeArguments = resolvedCall.getTypeArguments();
095    
096            assert typeArguments.size() == 1;
097            KotlinType type = typeArguments.values().iterator().next();
098    
099            return UtilsKt.getReferenceToJsClass(type, context);
100        }
101    
102        private static final FunctionIntrinsic JS_CLASS_FUN_INTRINSIC = new FunctionIntrinsic() {
103            @NotNull
104            @Override
105            public JsExpression apply(
106                    @NotNull CallInfo callInfo,
107                    @NotNull List<? extends JsExpression> arguments,
108                    @NotNull TranslationContext context
109            ) {
110                return getReferenceToOnlyTypeParameter(callInfo, context);
111            }
112        };
113    
114    
115        private static final FunctionIntrinsic ENUM_VALUES_INTRINSIC = new FunctionIntrinsic() {
116            @NotNull
117            @Override
118            public JsExpression apply(
119                    @NotNull CallInfo callInfo,
120                    @NotNull List<? extends JsExpression> arguments,
121                    @NotNull TranslationContext context
122            ) {
123                JsExpression enumClassRef = getReferenceToOnlyTypeParameter(callInfo, context);
124    
125                FunctionDescriptor fd = DescriptorFactory.createEnumValuesMethod(context.getCurrentModule().getBuiltIns().getEnum());
126    
127                return new JsInvocation(new JsNameRef(context.getNameForDescriptor(fd), enumClassRef));
128            }
129        };
130    
131    
132        private static final FunctionIntrinsic ENUM_VALUE_OF_INTRINSIC = new FunctionIntrinsic() {
133            @NotNull
134            @Override
135            public JsExpression apply(
136                    @NotNull CallInfo callInfo,
137                    @NotNull List<? extends JsExpression> arguments,
138                    @NotNull TranslationContext context
139            ) {
140                JsExpression arg = arguments.get(2); // The first two are reified parameters
141    
142                JsExpression enumClassRef = getReferenceToOnlyTypeParameter(callInfo, context);
143    
144                FunctionDescriptor fd = DescriptorFactory.createEnumValueOfMethod(context.getCurrentModule().getBuiltIns().getEnum());
145    
146                return new JsInvocation(new JsNameRef(context.getNameForDescriptor(fd), enumClassRef), arg);
147            }
148        };
149    
150        private static final FunctionIntrinsic STRING_SUBSTRING = new FunctionIntrinsicWithReceiverComputed() {
151            @NotNull
152            @Override
153            public JsExpression apply(
154                    @Nullable JsExpression receiver,
155                   @NotNull List<? extends JsExpression> arguments,
156                   @NotNull TranslationContext context
157            ) {
158                return new JsInvocation(new JsNameRef("substring", receiver), arguments);
159            }
160        };
161    
162    
163        @NotNull
164        public static final KotlinFunctionIntrinsic TO_STRING = new KotlinFunctionIntrinsic("toString");
165    
166        @NotNull
167        public static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsic() {
168            @NotNull
169            @Override
170            public JsExpression apply(
171                    @NotNull CallInfo callInfo, @NotNull List<? extends JsExpression> arguments, @NotNull TranslationContext context
172            ) {
173                return JsAstUtils.charToString(callInfo.getDispatchReceiver());
174            }
175        };
176    
177    
178        @NotNull
179        public static final FunctionIntrinsicFactory INSTANCE = new TopLevelFIF();
180    
181        private TopLevelFIF() {
182            add(EQUALS_IN_ANY, KOTLIN_ANY_EQUALS);
183            add(pattern("Char.toString"), CHAR_TO_STRING);
184            add(pattern("kotlin", "toString").isExtensionOf(FQ_NAMES.any.asString()), TO_STRING);
185            add(pattern("kotlin", "equals").isExtensionOf(FQ_NAMES.any.asString()), KOTLIN_EQUALS);
186            add(HASH_CODE_IN_ANY, KOTLIN_HASH_CODE);
187            add(pattern(NamePredicate.PRIMITIVE_NUMBERS, "equals"), KOTLIN_EQUALS);
188            add(pattern("String|Boolean|Char|Number.equals"), KOTLIN_EQUALS);
189            add(pattern("String.subSequence"), STRING_SUBSTRING);
190            add(pattern("CharSequence.subSequence"), KOTLIN_SUBSEQUENCE);
191            add(pattern("kotlin", "iterator").isExtensionOf(FQ_NAMES.iterator.asString()), RETURN_RECEIVER_INTRINSIC);
192    
193            add(pattern("kotlin.js", "Json", "get"), ArrayFIF.GET_INTRINSIC);
194            add(pattern("kotlin.js", "Json", "set"), ArrayFIF.SET_INTRINSIC);
195    
196            add(pattern("kotlin.js", "jsClass"), JS_CLASS_FUN_INTRINSIC);
197    
198            add(pattern("kotlin", "enumValues"), ENUM_VALUES_INTRINSIC);
199            add(pattern("kotlin", "enumValueOf"), ENUM_VALUE_OF_INTRINSIC);
200        }
201    
202    }