001 /*
002 * Copyright 2010-2013 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.k2js.translate.intrinsic.functions.factories;
018
019 import com.google.common.base.Predicate;
020 import com.google.common.base.Predicates;
021 import com.google.common.collect.ImmutableMap;
022 import com.google.dart.compiler.backend.js.ast.*;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
026 import org.jetbrains.jet.lang.resolve.name.Name;
027 import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
028 import org.jetbrains.jet.lexer.JetToken;
029 import org.jetbrains.k2js.translate.context.TranslationContext;
030 import org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic;
031 import org.jetbrains.k2js.translate.intrinsic.functions.patterns.DescriptorPredicate;
032 import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate;
033 import org.jetbrains.k2js.translate.operation.OperatorTable;
034
035 import java.util.List;
036
037 import static org.jetbrains.k2js.translate.intrinsic.functions.factories.NumberConversionFIF.INTEGER_NUMBER_TYPES;
038 import static org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder.pattern;
039 import static org.jetbrains.k2js.translate.utils.JsAstUtils.setArguments;
040
041 public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
042 INSTANCE;
043
044 @NotNull
045 private static final FunctionIntrinsic RANGE_TO_INTRINSIC = new FunctionIntrinsic() {
046
047 @NotNull
048 @Override
049 public JsExpression apply(@Nullable JsExpression rangeStart, @NotNull List<JsExpression> arguments,
050 @NotNull TranslationContext context) {
051 assert arguments.size() == 1 : "RangeTo must have one argument.";
052 assert rangeStart != null;
053 JsExpression rangeEnd = arguments.get(0);
054 JsNameRef expr = new JsNameRef("NumberRange", "Kotlin");
055 HasArguments numberRangeConstructorInvocation = context.isEcma5() ? new JsInvocation(expr) : new JsNew(expr);
056 //TODO: add tests and correct expression for reversed ranges.
057 setArguments(numberRangeConstructorInvocation, rangeStart, rangeEnd);
058 return numberRangeConstructorInvocation;
059 }
060 };
061
062 @NotNull
063 private static final FunctionIntrinsic INTEGER_DIVISION_INTRINSIC = new FunctionIntrinsic() {
064 @NotNull
065 @Override
066 public JsExpression apply(@Nullable JsExpression receiver,
067 @NotNull List<JsExpression> arguments,
068 @NotNull TranslationContext context) {
069 assert receiver != null;
070 assert arguments.size() == 1;
071 JsBinaryOperation div = new JsBinaryOperation(JsBinaryOperator.DIV, receiver, arguments.get(0));
072 JsBinaryOperation toInt32 = new JsBinaryOperation(JsBinaryOperator.BIT_OR, div, context.program().getNumberLiteral(0));
073 return toInt32;
074 }
075 };
076
077 @NotNull
078 private static final NamePredicate BINARY_OPERATIONS = new NamePredicate(OperatorConventions.BINARY_OPERATION_NAMES.values());
079 private static final DescriptorPredicate PRIMITIVE_NUMBERS_BINARY_OPERATIONS = pattern(NamePredicate.PRIMITIVE_NUMBERS, BINARY_OPERATIONS);
080 private static final DescriptorPredicate INT_WITH_BIT_OPERATIONS = pattern("Int.or|and|xor|shl|shr|ushr");
081 private static final DescriptorPredicate BOOLEAN_OPERATIONS = pattern("Boolean.or|and|xor");
082 private static final DescriptorPredicate STRING_PLUS = pattern("String.plus");
083
084 private static final ImmutableMap<String, JsBinaryOperator> BINARY_BITWISE_OPERATIONS = ImmutableMap.<String, JsBinaryOperator>builder()
085 .put("or", JsBinaryOperator.BIT_OR)
086 .put("and", JsBinaryOperator.BIT_AND)
087 .put("xor", JsBinaryOperator.BIT_XOR)
088 .put("shl", JsBinaryOperator.SHL)
089 .put("shr", JsBinaryOperator.SHR)
090 .put("ushr", JsBinaryOperator.SHRU)
091 .build();
092
093 private static final Predicate<FunctionDescriptor> PREDICATE = Predicates.or(PRIMITIVE_NUMBERS_BINARY_OPERATIONS, BOOLEAN_OPERATIONS,
094 STRING_PLUS, INT_WITH_BIT_OPERATIONS);
095
096 @Nullable
097 @Override
098 public FunctionIntrinsic getIntrinsic(@NotNull FunctionDescriptor descriptor) {
099 if (!PREDICATE.apply(descriptor)) {
100 return null;
101 }
102
103 if (pattern(INTEGER_NUMBER_TYPES + ".div").apply(descriptor)) {
104 return INTEGER_DIVISION_INTRINSIC;
105 }
106 if (descriptor.getName().equals(Name.identifier("rangeTo"))) {
107 return RANGE_TO_INTRINSIC;
108 }
109 if (INT_WITH_BIT_OPERATIONS.apply(descriptor)) {
110 JsBinaryOperator op = BINARY_BITWISE_OPERATIONS.get(descriptor.getName().asString());
111 if (op != null) {
112 return new PrimitiveBinaryOperationFunctionIntrinsic(op);
113 }
114 }
115 JsBinaryOperator operator = getOperator(descriptor);
116 return new PrimitiveBinaryOperationFunctionIntrinsic(operator);
117 }
118
119 @NotNull
120 private static JsBinaryOperator getOperator(@NotNull FunctionDescriptor descriptor) {
121 JetToken token = OperatorConventions.BINARY_OPERATION_NAMES.inverse().get(descriptor.getName());
122 if (token == null) {
123 token = OperatorConventions.BOOLEAN_OPERATIONS.inverse().get(descriptor.getName());
124 }
125 if (token == null) {
126 assert descriptor.getName().asString().equals("xor");
127 return JsBinaryOperator.BIT_XOR;
128 }
129 return OperatorTable.getBinaryOperator(token);
130 }
131
132 private static class PrimitiveBinaryOperationFunctionIntrinsic extends FunctionIntrinsic {
133
134 @NotNull
135 private final JsBinaryOperator operator;
136
137 private PrimitiveBinaryOperationFunctionIntrinsic(@NotNull JsBinaryOperator operator) {
138 this.operator = operator;
139 }
140
141 @NotNull
142 @Override
143 public JsExpression apply(@Nullable JsExpression receiver,
144 @NotNull List<JsExpression> arguments,
145 @NotNull TranslationContext context) {
146 assert receiver != null;
147 assert arguments.size() == 1 : "Binary operator should have a receiver and one argument";
148 return new JsBinaryOperation(operator, receiver, arguments.get(0));
149 }
150 }
151 }