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 }