All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.PrimitiveUnaryOperationFIF Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2017 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
import org.jetbrains.kotlin.js.patterns.NamePredicate;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed;
import org.jetbrains.kotlin.js.translate.operation.OperatorTable;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.AstUtilsKt;
import org.jetbrains.kotlin.lexer.KtToken;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
import org.jetbrains.kotlin.util.OperatorNameConventions;

import java.util.List;
import java.util.function.Predicate;

import static org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern;

public enum PrimitiveUnaryOperationFIF implements FunctionIntrinsicFactory {

    INSTANCE;

    private static final NamePredicate UNARY_OPERATIONS = new NamePredicate(OperatorNameConventions.UNARY_OPERATION_NAMES);
    @NotNull
    private static final DescriptorPredicate UNARY_OPERATION_FOR_PRIMITIVE_NUMBER =
            pattern(NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS, UNARY_OPERATIONS);
    @NotNull
    private static final Predicate PRIMITIVE_UNARY_OPERATION_NAMES =
            UNARY_OPERATION_FOR_PRIMITIVE_NUMBER.or(pattern("Boolean.not")).or(pattern("Int|Short|Byte.inv"));
    @NotNull
    private static final DescriptorPredicate NO_PARAMETERS = descriptor -> !JsDescriptorUtils.hasParameters(descriptor);
    @NotNull
    private static final Predicate PATTERN = PRIMITIVE_UNARY_OPERATION_NAMES.and(NO_PARAMETERS);

    private static final DescriptorPredicate INC_OPERATION_FOR_INT = pattern("Int.inc");
    private static final DescriptorPredicate DEC_OPERATION_FOR_INT = pattern("Int.dec");
    private static final DescriptorPredicate INC_OPERATION_FOR_BYTE = pattern("Byte.inc");
    private static final DescriptorPredicate DEC_OPERATION_FOR_BYTE = pattern("Byte.dec");
    private static final DescriptorPredicate INC_OPERATION_FOR_SHORT = pattern("Short.inc");
    private static final DescriptorPredicate DEC_OPERATION_FOR_SHORT = pattern("Short.dec");
    private static final DescriptorPredicate NEG_OPERATION_FOR_INT = pattern("Int.unaryMinus");

    @NotNull
    private static final DescriptorPredicate INC_OPERATION_FOR_PRIMITIVE_NUMBER = pattern("Float|Double.inc()");

    @NotNull
    private static final DescriptorPredicate DEC_OPERATION_FOR_PRIMITIVE_NUMBER = pattern("Float|Double.dec()");

    private static class IntOverflowIntrinsic extends FunctionIntrinsicWithReceiverComputed {
        private final FunctionIntrinsicWithReceiverComputed underlyingIntrinsic;

        public IntOverflowIntrinsic(FunctionIntrinsicWithReceiverComputed underlyingIntrinsic) {
            this.underlyingIntrinsic = underlyingIntrinsic;
        }

        @NotNull
        @Override
        public JsExpression apply(
                @Nullable JsExpression receiver,
                @NotNull List arguments,
                @NotNull TranslationContext context
        ) {
            return JsAstUtils.toInt32(underlyingIntrinsic.apply(receiver, arguments, context));
        }
    }

    private static class ShortOverflowIntrinsic extends FunctionIntrinsicWithReceiverComputed {
        private final FunctionIntrinsicWithReceiverComputed underlyingIntrinsic;

        public ShortOverflowIntrinsic(FunctionIntrinsicWithReceiverComputed underlyingIntrinsic) {
            this.underlyingIntrinsic = underlyingIntrinsic;
        }

        @NotNull
        @Override
        public JsExpression apply(
                @Nullable JsExpression receiver,
                @NotNull List arguments,
                @NotNull TranslationContext context
        ) {
            return AstUtilsKt.toShort(context, underlyingIntrinsic.apply(receiver, arguments, context));
        }
    }

    private static class ByteOverflowIntrinsic extends FunctionIntrinsicWithReceiverComputed {
        private final FunctionIntrinsicWithReceiverComputed underlyingIntrinsic;

        public ByteOverflowIntrinsic(FunctionIntrinsicWithReceiverComputed underlyingIntrinsic) {
            this.underlyingIntrinsic = underlyingIntrinsic;
        }

        @NotNull
        @Override
        public JsExpression apply(
                @Nullable JsExpression receiver,
                @NotNull List arguments,
                @NotNull TranslationContext context
        ) {
            return AstUtilsKt.toByte(context, underlyingIntrinsic.apply(receiver, arguments, context));
        }
    }

    @NotNull
    private static final FunctionIntrinsicWithReceiverComputed NUMBER_INC_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() {
        @NotNull
        @Override
        public JsExpression apply(
                @Nullable JsExpression receiver,
                @NotNull List arguments,
                @NotNull TranslationContext context
        ) {
            assert receiver != null;
            assert arguments.size() == 0;
            return new JsBinaryOperation(JsBinaryOperator.ADD, receiver, new JsIntLiteral(1));
        }
    };

    @NotNull
    private static final FunctionIntrinsicWithReceiverComputed NUMBER_DEC_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() {
        @NotNull
        @Override
        public JsExpression apply(
                @Nullable JsExpression receiver,
                @NotNull List arguments,
                @NotNull TranslationContext context
        ) {
            assert receiver != null;
            assert arguments.size() == 0;
            return new JsBinaryOperation(JsBinaryOperator.SUB, receiver, new JsIntLiteral(1));
        }
    };

    private static final FunctionIntrinsicWithReceiverComputed NUMBER_NEG_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() {
        @NotNull
        @Override
        public JsExpression apply(
                @Nullable JsExpression receiver,
                @NotNull List arguments,
                @NotNull TranslationContext context
        ) {
            assert receiver != null;
            assert arguments.size() == 0;
            return new JsPrefixOperation(JsUnaryOperator.NEG, receiver);
        }
    };

    private static abstract class UnaryOperationInstrinsicBase extends FunctionIntrinsicWithReceiverComputed {
        @NotNull
        public abstract JsExpression doApply(@NotNull JsExpression receiver, @NotNull TranslationContext context);

        @NotNull
        @Override
        public JsExpression apply(
                @Nullable JsExpression receiver,
                @NotNull List arguments,
                @NotNull TranslationContext context
        ) {
            assert receiver != null;
            assert arguments.size() == 0;
            return doApply(receiver, context);
        }
    }

    @NotNull
    private static final FunctionIntrinsic CHAR_PLUS = new UnaryOperationInstrinsicBase() {
        @NotNull
        @Override
        public JsExpression doApply(
                @NotNull JsExpression receiver, @NotNull TranslationContext context
        ) {
            return JsAstUtils.charToInt(receiver);
        }
    };

    @NotNull
    private static final FunctionIntrinsic CHAR_MINUS = new UnaryOperationInstrinsicBase() {
        @NotNull
        @Override
        public JsExpression doApply(
                @NotNull JsExpression receiver, @NotNull TranslationContext context
        ) {
            return new JsPrefixOperation(JsUnaryOperator.NEG, JsAstUtils.charToInt(receiver));
        }
    };

    @NotNull
    private static final FunctionIntrinsic CHAR_INC = new UnaryOperationInstrinsicBase() {
        @NotNull
        @Override
        public JsExpression doApply(
                @NotNull JsExpression receiver, @NotNull TranslationContext context
        ) {
            return JsAstUtils.invokeKotlinFunction("charInc", receiver);
        }
    };

    @NotNull
    private static final FunctionIntrinsic CHAR_DEC = new UnaryOperationInstrinsicBase() {
        @NotNull
        @Override
        public JsExpression doApply(
                @NotNull JsExpression receiver, @NotNull TranslationContext context
        ) {
            return JsAstUtils.invokeKotlinFunction("charDec", receiver);
        }
    };

    @Nullable
    @Override
    public FunctionIntrinsic getIntrinsic(@NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
        if (!PATTERN.test(descriptor)) {
            return null;
        }

        if (pattern("Char.unaryPlus()").test(descriptor)) {
            return CHAR_PLUS;
        }
        if (pattern("Char.unaryMinus()").test(descriptor)) {
            return CHAR_MINUS;
        }
        if (pattern("Char.plus()").test(descriptor)) {
            return CHAR_PLUS;
        }
        if (pattern("Char.minus()").test(descriptor)) {
            return CHAR_MINUS;
        }
        if (pattern("Char.inc()").test(descriptor)) {
            return CHAR_INC;
        }
        if (pattern("Char.dec()").test(descriptor)) {
            return CHAR_DEC;
        }

        if (INC_OPERATION_FOR_INT.test(descriptor)) {
            return new IntOverflowIntrinsic(NUMBER_INC_INTRINSIC);
        }
        if (DEC_OPERATION_FOR_INT.test(descriptor)) {
            return new IntOverflowIntrinsic(NUMBER_DEC_INTRINSIC);
        }
        if (INC_OPERATION_FOR_SHORT.test(descriptor)) {
            return new ShortOverflowIntrinsic(NUMBER_INC_INTRINSIC);
        }
        if (DEC_OPERATION_FOR_SHORT.test(descriptor)) {
            return new ShortOverflowIntrinsic(NUMBER_DEC_INTRINSIC);
        }
        if (INC_OPERATION_FOR_BYTE.test(descriptor)) {
            return new ByteOverflowIntrinsic(NUMBER_INC_INTRINSIC);
        }
        if (DEC_OPERATION_FOR_BYTE.test(descriptor)) {
            return new ByteOverflowIntrinsic(NUMBER_DEC_INTRINSIC);
        }

        if (INC_OPERATION_FOR_PRIMITIVE_NUMBER.test(descriptor)) {
            return NUMBER_INC_INTRINSIC;
        }
        if (DEC_OPERATION_FOR_PRIMITIVE_NUMBER.test(descriptor)) {
            return NUMBER_DEC_INTRINSIC;
        }

        if (NEG_OPERATION_FOR_INT.test(descriptor)) {
            return new IntOverflowIntrinsic(NUMBER_NEG_INTRINSIC);
        }

        Name name = descriptor.getName();

        JsUnaryOperator jsOperator;
        if ("inv".equals(name.asString())) {
            jsOperator = JsUnaryOperator.BIT_NOT;
        }
        else {
            KtToken jetToken = OperatorConventions.UNARY_OPERATION_NAMES.inverse().get(name);
            jsOperator = OperatorTable.getUnaryOperator(jetToken);
        }

        JsUnaryOperator finalJsOperator = jsOperator;
        return new FunctionIntrinsicWithReceiverComputed() {
            @NotNull
            @Override
            public JsExpression apply(@Nullable JsExpression receiver,
                    @NotNull List arguments,
                    @NotNull TranslationContext context) {
                assert receiver != null;
                assert arguments.size() == 0 : "Unary operator should not have arguments.";
                //NOTE: cannot use this for increment/decrement
                return new JsPrefixOperation(finalJsOperator, receiver);
            }
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy