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 org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
024 import org.jetbrains.kotlin.js.backend.ast.*;
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.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed;
030 import org.jetbrains.kotlin.js.translate.operation.OperatorTable;
031 import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
032 import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
033 import org.jetbrains.kotlin.lexer.KtToken;
034 import org.jetbrains.kotlin.name.Name;
035 import org.jetbrains.kotlin.types.expressions.OperatorConventions;
036 import org.jetbrains.kotlin.util.OperatorNameConventions;
037
038 import java.util.List;
039
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);
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|Short|Byte.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 private static final DescriptorPredicate INC_OPERATION_FOR_INT = pattern("Int.inc");
065 private static final DescriptorPredicate DEC_OPERATION_FOR_INT = pattern("Int.dec");
066 private static final DescriptorPredicate INC_OPERATION_FOR_BYTE = pattern("Byte.inc");
067 private static final DescriptorPredicate DEC_OPERATION_FOR_BYTE = pattern("Byte.dec");
068 private static final DescriptorPredicate INC_OPERATION_FOR_SHORT = pattern("Short.inc");
069 private static final DescriptorPredicate DEC_OPERATION_FOR_SHORT = pattern("Short.dec");
070
071 @NotNull
072 private static final DescriptorPredicate INC_OPERATION_FOR_PRIMITIVE_NUMBER = pattern("Float|Double.inc()");
073
074 @NotNull
075 private static final DescriptorPredicate DEC_OPERATION_FOR_PRIMITIVE_NUMBER = pattern("Float|Double.dec()");
076
077 private static class IntOverflowIntrinsic extends FunctionIntrinsicWithReceiverComputed {
078 private final FunctionIntrinsicWithReceiverComputed underlyingIntrinsic;
079
080 public IntOverflowIntrinsic(FunctionIntrinsicWithReceiverComputed underlyingIntrinsic) {
081 this.underlyingIntrinsic = underlyingIntrinsic;
082 }
083
084 @NotNull
085 @Override
086 public JsExpression apply(
087 @Nullable JsExpression receiver,
088 @NotNull List<? extends JsExpression> arguments,
089 @NotNull TranslationContext context
090 ) {
091 return JsAstUtils.toInt32(underlyingIntrinsic.apply(receiver, arguments, context));
092 }
093 }
094
095 private static class ShortOverflowIntrinsic extends FunctionIntrinsicWithReceiverComputed {
096 private final FunctionIntrinsicWithReceiverComputed underlyingIntrinsic;
097
098 public ShortOverflowIntrinsic(FunctionIntrinsicWithReceiverComputed underlyingIntrinsic) {
099 this.underlyingIntrinsic = underlyingIntrinsic;
100 }
101
102 @NotNull
103 @Override
104 public JsExpression apply(
105 @Nullable JsExpression receiver,
106 @NotNull List<? extends JsExpression> arguments,
107 @NotNull TranslationContext context
108 ) {
109 return JsAstUtils.toShort(underlyingIntrinsic.apply(receiver, arguments, context));
110 }
111 }
112
113 private static class ByteOverflowIntrinsic extends FunctionIntrinsicWithReceiverComputed {
114 private final FunctionIntrinsicWithReceiverComputed underlyingIntrinsic;
115
116 public ByteOverflowIntrinsic(FunctionIntrinsicWithReceiverComputed underlyingIntrinsic) {
117 this.underlyingIntrinsic = underlyingIntrinsic;
118 }
119
120 @NotNull
121 @Override
122 public JsExpression apply(
123 @Nullable JsExpression receiver,
124 @NotNull List<? extends JsExpression> arguments,
125 @NotNull TranslationContext context
126 ) {
127 return JsAstUtils.toByte(underlyingIntrinsic.apply(receiver, arguments, context));
128 }
129 }
130
131 @NotNull
132 private static final FunctionIntrinsicWithReceiverComputed NUMBER_INC_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() {
133 @NotNull
134 @Override
135 public JsExpression apply(
136 @Nullable JsExpression receiver,
137 @NotNull List<? extends JsExpression> arguments,
138 @NotNull TranslationContext context
139 ) {
140 assert receiver != null;
141 assert arguments.size() == 0;
142 return new JsBinaryOperation(JsBinaryOperator.ADD, receiver, context.program().getNumberLiteral(1));
143 }
144 };
145
146 @NotNull
147 private static final FunctionIntrinsicWithReceiverComputed NUMBER_DEC_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() {
148 @NotNull
149 @Override
150 public JsExpression apply(
151 @Nullable JsExpression receiver,
152 @NotNull List<? extends JsExpression> arguments,
153 @NotNull TranslationContext context
154 ) {
155 assert receiver != null;
156 assert arguments.size() == 0;
157 return new JsBinaryOperation(JsBinaryOperator.SUB, receiver, context.program().getNumberLiteral(1));
158 }
159 };
160
161 private static abstract class UnaryOperationInstrinsicBase extends FunctionIntrinsicWithReceiverComputed {
162 @NotNull
163 public abstract JsExpression doApply(@NotNull JsExpression receiver, @NotNull TranslationContext context);
164
165 @NotNull
166 @Override
167 public JsExpression apply(
168 @Nullable JsExpression receiver,
169 @NotNull List<? extends JsExpression> arguments,
170 @NotNull TranslationContext context
171 ) {
172 assert receiver != null;
173 assert arguments.size() == 0;
174 return doApply(receiver, context);
175 }
176 }
177
178 @NotNull
179 private static final FunctionIntrinsic CHAR_PLUS = new UnaryOperationInstrinsicBase() {
180 @NotNull
181 @Override
182 public JsExpression doApply(
183 @NotNull JsExpression receiver, @NotNull TranslationContext context
184 ) {
185 return JsAstUtils.charToInt(receiver);
186 }
187 };
188
189 @NotNull
190 private static final FunctionIntrinsic CHAR_MINUS = new UnaryOperationInstrinsicBase() {
191 @NotNull
192 @Override
193 public JsExpression doApply(
194 @NotNull JsExpression receiver, @NotNull TranslationContext context
195 ) {
196 return new JsPrefixOperation(JsUnaryOperator.NEG, JsAstUtils.charToInt(receiver));
197 }
198 };
199
200 @NotNull
201 private static final FunctionIntrinsic CHAR_INC = new UnaryOperationInstrinsicBase() {
202 @NotNull
203 @Override
204 public JsExpression doApply(
205 @NotNull JsExpression receiver, @NotNull TranslationContext context
206 ) {
207 return JsAstUtils.invokeKotlinFunction("charInc", receiver);
208 }
209 };
210
211 @NotNull
212 private static final FunctionIntrinsic CHAR_DEC = new UnaryOperationInstrinsicBase() {
213 @NotNull
214 @Override
215 public JsExpression doApply(
216 @NotNull JsExpression receiver, @NotNull TranslationContext context
217 ) {
218 return JsAstUtils.invokeKotlinFunction("charDec", receiver);
219 }
220 };
221
222 @Nullable
223 @Override
224 public FunctionIntrinsic getIntrinsic(@NotNull FunctionDescriptor descriptor) {
225 if (!PATTERN.apply(descriptor)) {
226 return null;
227 }
228
229 if (pattern("Char.unaryPlus()").apply(descriptor)) {
230 return CHAR_PLUS;
231 }
232 if (pattern("Char.unaryMinus()").apply(descriptor)) {
233 return CHAR_MINUS;
234 }
235 if (pattern("Char.plus()").apply(descriptor)) {
236 return CHAR_PLUS;
237 }
238 if (pattern("Char.minus()").apply(descriptor)) {
239 return CHAR_MINUS;
240 }
241 if (pattern("Char.inc()").apply(descriptor)) {
242 return CHAR_INC;
243 }
244 if (pattern("Char.dec()").apply(descriptor)) {
245 return CHAR_DEC;
246 }
247
248 if (INC_OPERATION_FOR_INT.apply(descriptor)) {
249 return new IntOverflowIntrinsic(NUMBER_INC_INTRINSIC);
250 }
251 if (DEC_OPERATION_FOR_INT.apply(descriptor)) {
252 return new IntOverflowIntrinsic(NUMBER_DEC_INTRINSIC);
253 }
254 if (INC_OPERATION_FOR_SHORT.apply(descriptor)) {
255 return new ShortOverflowIntrinsic(NUMBER_INC_INTRINSIC);
256 }
257 if (DEC_OPERATION_FOR_SHORT.apply(descriptor)) {
258 return new ShortOverflowIntrinsic(NUMBER_DEC_INTRINSIC);
259 }
260 if (INC_OPERATION_FOR_BYTE.apply(descriptor)) {
261 return new ByteOverflowIntrinsic(NUMBER_INC_INTRINSIC);
262 }
263 if (DEC_OPERATION_FOR_BYTE.apply(descriptor)) {
264 return new ByteOverflowIntrinsic(NUMBER_DEC_INTRINSIC);
265 }
266
267 if (INC_OPERATION_FOR_PRIMITIVE_NUMBER.apply(descriptor)) {
268 return NUMBER_INC_INTRINSIC;
269 }
270 if (DEC_OPERATION_FOR_PRIMITIVE_NUMBER.apply(descriptor)) {
271 return NUMBER_DEC_INTRINSIC;
272 }
273
274
275 Name name = descriptor.getName();
276
277 JsUnaryOperator jsOperator;
278 if ("inv".equals(name.asString())) {
279 jsOperator = JsUnaryOperator.BIT_NOT;
280 }
281 else {
282 KtToken jetToken = OperatorConventions.UNARY_OPERATION_NAMES.inverse().get(name);
283 jsOperator = OperatorTable.getUnaryOperator(jetToken);
284 }
285
286 final JsUnaryOperator finalJsOperator = jsOperator;
287 return new FunctionIntrinsicWithReceiverComputed() {
288 @NotNull
289 @Override
290 public JsExpression apply(@Nullable JsExpression receiver,
291 @NotNull List<? extends JsExpression> arguments,
292 @NotNull TranslationContext context) {
293 assert receiver != null;
294 assert arguments.size() == 0 : "Unary operator should not have arguments.";
295 //NOTE: cannot use this for increment/decrement
296 return new JsPrefixOperation(finalJsOperator, receiver);
297 }
298 };
299 }
300 }