001    /*
002     * Copyright 2010-2015 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 com.google.common.base.Predicate;
020    import com.google.common.base.Predicates;
021    import com.google.dart.compiler.backend.js.ast.*;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
025    import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
026    import org.jetbrains.kotlin.js.patterns.NamePredicate;
027    import org.jetbrains.kotlin.js.translate.context.TranslationContext;
028    import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
029    import org.jetbrains.kotlin.js.translate.operation.OperatorTable;
030    import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
031    import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
032    import org.jetbrains.kotlin.lexer.JetToken;
033    import org.jetbrains.kotlin.name.Name;
034    import org.jetbrains.kotlin.types.expressions.OperatorConventions;
035    
036    import java.util.List;
037    
038    import static org.jetbrains.kotlin.js.patterns.NamePredicate.PRIMITIVE_NUMBERS;
039    import static org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern;
040    
041    public enum PrimitiveUnaryOperationFIF implements FunctionIntrinsicFactory {
042    
043        INSTANCE;
044    
045        private static final NamePredicate UNARY_OPERATIONS = new NamePredicate(OperatorConventions.UNARY_OPERATION_NAMES.values());
046        @NotNull
047        private static final DescriptorPredicate UNARY_OPERATION_FOR_PRIMITIVE_NUMBER =
048                pattern(NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS, UNARY_OPERATIONS);
049        @NotNull
050        private static final Predicate<FunctionDescriptor> PRIMITIVE_UNARY_OPERATION_NAMES =
051                Predicates.or(UNARY_OPERATION_FOR_PRIMITIVE_NUMBER, pattern("Boolean.not"), pattern("Int.inv"));
052        @NotNull
053        private static final DescriptorPredicate NO_PARAMETERS = new DescriptorPredicate() {
054            @Override
055            public boolean apply(@Nullable FunctionDescriptor descriptor) {
056                assert descriptor != null : "argument for DescriptorPredicate.apply should not be null";
057                return !JsDescriptorUtils.hasParameters(descriptor);
058            }
059        };
060        @NotNull
061        private static final Predicate<FunctionDescriptor> PATTERN = Predicates.and(PRIMITIVE_UNARY_OPERATION_NAMES, NO_PARAMETERS);
062    
063        @NotNull
064        private static final DescriptorPredicate INC_OPERATION_FOR_PRIMITIVE_NUMBER = pattern(PRIMITIVE_NUMBERS, "inc");
065    
066        @NotNull
067        private static final DescriptorPredicate DEC_OPERATION_FOR_PRIMITIVE_NUMBER = pattern(PRIMITIVE_NUMBERS, "dec");
068    
069        @NotNull
070        private static final FunctionIntrinsic NUMBER_INC_INTRINSIC = new FunctionIntrinsic() {
071            @NotNull
072            @Override
073            public JsExpression apply(
074                    @Nullable JsExpression receiver,
075                    @NotNull List<JsExpression> arguments,
076                    @NotNull TranslationContext context
077            ) {
078                assert receiver != null;
079                assert arguments.size() == 0;
080                return new JsBinaryOperation(JsBinaryOperator.ADD, receiver, context.program().getNumberLiteral(1));
081            }
082        };
083    
084        @NotNull
085        private static final FunctionIntrinsic NUMBER_DEC_INTRINSIC = new FunctionIntrinsic() {
086            @NotNull
087            @Override
088            public JsExpression apply(
089                    @Nullable JsExpression receiver,
090                    @NotNull List<JsExpression> arguments,
091                    @NotNull TranslationContext context
092            ) {
093                assert receiver != null;
094                assert arguments.size() == 0;
095                return new JsBinaryOperation(JsBinaryOperator.SUB, receiver, context.program().getNumberLiteral(1));
096            }
097        };
098    
099        private static abstract class UnaryOperationInstrinsicBase extends FunctionIntrinsic {
100            @NotNull
101            public abstract JsExpression doApply(@NotNull JsExpression receiver, @NotNull TranslationContext context);
102    
103            @NotNull
104            @Override
105            public JsExpression apply(
106                    @Nullable JsExpression receiver,
107                    @NotNull List<JsExpression> arguments,
108                    @NotNull TranslationContext context
109            ) {
110                assert receiver != null;
111                assert arguments.size() == 0;
112                return doApply(receiver, context);
113            }
114        }
115    
116        @NotNull
117        private static final FunctionIntrinsic CHAR_PLUS = new UnaryOperationInstrinsicBase() {
118            @NotNull
119            @Override
120            public JsExpression doApply(
121                    @NotNull JsExpression receiver, @NotNull TranslationContext context
122            ) {
123                return JsAstUtils.charToInt(receiver);
124            }
125        };
126    
127        @NotNull
128        private static final FunctionIntrinsic CHAR_MINUS = new UnaryOperationInstrinsicBase() {
129            @NotNull
130            @Override
131            public JsExpression doApply(
132                    @NotNull JsExpression receiver, @NotNull TranslationContext context
133            ) {
134                return new JsPrefixOperation(JsUnaryOperator.NEG, JsAstUtils.charToInt(receiver));
135            }
136        };
137    
138        @NotNull
139        private static final FunctionIntrinsic CHAR_INC = new UnaryOperationInstrinsicBase() {
140            @NotNull
141            @Override
142            public JsExpression doApply(
143                    @NotNull JsExpression receiver, @NotNull TranslationContext context
144            ) {
145                return JsAstUtils.invokeKotlinFunction("charInc", receiver);
146            }
147        };
148    
149        @NotNull
150        private static final FunctionIntrinsic CHAR_DEC = new UnaryOperationInstrinsicBase() {
151            @NotNull
152            @Override
153            public JsExpression doApply(
154                    @NotNull JsExpression receiver, @NotNull TranslationContext context
155            ) {
156                return JsAstUtils.invokeKotlinFunction("charDec", receiver);
157            }
158        };
159    
160        @Nullable
161        @Override
162        public FunctionIntrinsic getIntrinsic(@NotNull FunctionDescriptor descriptor) {
163            if (!PATTERN.apply(descriptor)) {
164                return null;
165            }
166    
167            if (pattern("Char.plus()").apply(descriptor)) {
168                return CHAR_PLUS;
169            }
170            if (pattern("Char.minus()").apply(descriptor)) {
171                return CHAR_MINUS;
172            }
173            if (pattern("Char.inc()").apply(descriptor)) {
174                return CHAR_INC;
175            }
176            if (pattern("Char.dec()").apply(descriptor)) {
177                return CHAR_DEC;
178            }
179    
180            if (INC_OPERATION_FOR_PRIMITIVE_NUMBER.apply(descriptor)) {
181                return NUMBER_INC_INTRINSIC;
182            }
183            if (DEC_OPERATION_FOR_PRIMITIVE_NUMBER.apply(descriptor)) {
184                return NUMBER_DEC_INTRINSIC;
185            }
186    
187    
188            Name name = descriptor.getName();
189    
190            JsUnaryOperator jsOperator;
191            if ("inv".equals(name.asString())) {
192                jsOperator = JsUnaryOperator.BIT_NOT;
193            }
194            else {
195                JetToken jetToken = OperatorConventions.UNARY_OPERATION_NAMES.inverse().get(name);
196                jsOperator = OperatorTable.getUnaryOperator(jetToken);
197            }
198    
199            final JsUnaryOperator finalJsOperator = jsOperator;
200            return new FunctionIntrinsic() {
201                @NotNull
202                @Override
203                public JsExpression apply(@Nullable JsExpression receiver,
204                        @NotNull List<JsExpression> arguments,
205                        @NotNull TranslationContext context) {
206                    assert receiver != null;
207                    assert arguments.size() == 0 : "Unary operator should not have arguments.";
208                    //NOTE: cannot use this for increment/decrement
209                    return new JsPrefixOperation(finalJsOperator, receiver);
210                }
211            };
212        }
213    }