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