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

com.oracle.truffle.js.nodes.binary.JSOverloadedBinaryNode Maven / Gradle / Ivy

/*
 * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.js.nodes.binary;

import static com.oracle.truffle.js.nodes.JSGuards.isString;

import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Idempotent;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.cast.JSToNumericNode;
import com.oracle.truffle.js.nodes.cast.JSToOperandNode;
import com.oracle.truffle.js.nodes.cast.JSToPrimitiveNode.Hint;
import com.oracle.truffle.js.nodes.cast.JSToStringNode;
import com.oracle.truffle.js.nodes.function.JSFunctionCallNode;
import com.oracle.truffle.js.runtime.BigInt;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSArguments;
import com.oracle.truffle.js.runtime.Strings;
import com.oracle.truffle.js.runtime.builtins.JSOverloadedOperatorsObject;
import com.oracle.truffle.js.runtime.objects.OperatorSet;
import com.oracle.truffle.js.runtime.objects.Undefined;

/**
 * This node implements the semantics of a binary operator in the case when one of the two operands
 * features overloaded operators. Its job is to call ToOperand on its arguments (converting objects
 * which don't overload operators to primitives). The bulk of the work is then delegated to the
 * {@link DispatchBinaryOperatorNode}.
 * 

* Check {@link JSAddNode} for an example of using this node. *

*/ public abstract class JSOverloadedBinaryNode extends JavaScriptBaseNode { /** * The name of the overloaded operator, used to lookup its definition in the user-provided table * of overloaded operators. */ private final TruffleString overloadedOperatorName; /** * Whether operands should be converted using ToNumericOperand (i.e. ToNumeric) or ToOperand * (i.e. ToPrimitive). */ private final boolean numeric; /** * Which hint should be passed to ToOperand (and consequently to ToPrimitive). Only applies when * {@link #numeric} is {@code false}. */ private final Hint hint; /** * Whether the two operands should be evaluated (converted using ToOperand) from left-to-right * or right-to-left. This is needed for comparison operators, which can swap the order of the * operands. */ private final boolean leftToRight; protected JSOverloadedBinaryNode(TruffleString overloadedOperatorName, boolean numeric, Hint hint, boolean leftToRight) { this.overloadedOperatorName = overloadedOperatorName; this.numeric = numeric; this.hint = hint; this.leftToRight = leftToRight; } @NeverDefault public static JSOverloadedBinaryNode create(TruffleString overloadedOperatorName, Hint hint) { return JSOverloadedBinaryNodeGen.create(overloadedOperatorName, false, hint, true); } @NeverDefault public static JSOverloadedBinaryNode createHintDefault(TruffleString overloadedOperatorName) { return JSOverloadedBinaryNodeGen.create(overloadedOperatorName, false, Hint.Default, true); } @NeverDefault public static JSOverloadedBinaryNode createHintNumber(TruffleString overloadedOperatorName) { return JSOverloadedBinaryNodeGen.create(overloadedOperatorName, false, Hint.Number, true); } @NeverDefault public static JSOverloadedBinaryNode createHintNumberLeftToRight(TruffleString overloadedOperatorName) { return JSOverloadedBinaryNodeGen.create(overloadedOperatorName, false, Hint.Number, true); } @NeverDefault public static JSOverloadedBinaryNode createHintNumberRightToLeft(TruffleString overloadedOperatorName) { return JSOverloadedBinaryNodeGen.create(overloadedOperatorName, false, Hint.Number, false); } @NeverDefault public static JSOverloadedBinaryNode createHintString(TruffleString overloadedOperatorName) { return JSOverloadedBinaryNodeGen.create(overloadedOperatorName, false, Hint.String, true); } @NeverDefault public static JSOverloadedBinaryNode createNumeric(TruffleString overloadedOperatorName) { return JSOverloadedBinaryNodeGen.create(overloadedOperatorName, true, null, true); } public abstract Object execute(Object left, Object right); @Specialization(guards = {"!isNumeric()", "!isAddition()"}) protected Object doToOperandGeneric(Object left, Object right, @Cached("create(getHint(), !isEquality())") JSToOperandNode toOperandLeftNode, @Cached("create(getHint(), !isEquality())") JSToOperandNode toOperandRightNode, @Cached("create(getOverloadedOperatorName())") @Shared DispatchBinaryOperatorNode dispatchBinaryOperatorNode) { Object leftOperand; Object rightOperand; if (leftToRight) { leftOperand = toOperandLeftNode.execute(left); rightOperand = toOperandRightNode.execute(right); } else { rightOperand = toOperandRightNode.execute(right); leftOperand = toOperandLeftNode.execute(left); } return dispatchBinaryOperatorNode.execute(leftOperand, rightOperand); } @SuppressWarnings("truffle-static-method") @Specialization(guards = {"!isNumeric()", "isAddition()"}) protected Object doToOperandAddition(Object left, Object right, @Bind("this") Node node, @Cached("create(getHint())") JSToOperandNode toOperandLeftNode, @Cached("create(getHint())") JSToOperandNode toOperandRightNode, @Cached("create(getOverloadedOperatorName())") @Shared DispatchBinaryOperatorNode dispatchBinaryOperatorNode, @Cached JSToStringNode toStringLeftNode, @Cached JSToStringNode toStringRightNode, @Cached InlinedConditionProfile leftStringProfile, @Cached InlinedConditionProfile rightStringProfile, @Cached("createUnoptimized()") JSAddNode addNode) { Object leftOperand; Object rightOperand; if (leftToRight) { leftOperand = toOperandLeftNode.execute(left); rightOperand = toOperandRightNode.execute(right); } else { rightOperand = toOperandRightNode.execute(right); leftOperand = toOperandLeftNode.execute(left); } // Addition with Strings cannot be overloaded. If either operand of + is a String, the // result is always the concatenation of their String values. if (leftStringProfile.profile(node, isString(leftOperand))) { return addNode.execute(leftOperand, toStringRightNode.executeString(rightOperand)); } else if (rightStringProfile.profile(node, isString(rightOperand))) { return addNode.execute(toStringLeftNode.executeString(leftOperand), rightOperand); } else { return dispatchBinaryOperatorNode.execute(leftOperand, rightOperand); } } @Specialization(guards = {"isNumeric()"}) protected Object doToNumericOperand(Object left, Object right, @Cached("create(true)") JSToNumericNode toNumericOperandLeftNode, @Cached("create(true)") JSToNumericNode toNumericOperandRightNode, @Cached("create(getOverloadedOperatorName())") @Shared DispatchBinaryOperatorNode dispatchBinaryOperatorNode) { Object leftOperand; Object rightOperand; if (leftToRight) { leftOperand = toNumericOperandLeftNode.execute(left); rightOperand = toNumericOperandRightNode.execute(right); } else { rightOperand = toNumericOperandRightNode.execute(right); leftOperand = toNumericOperandLeftNode.execute(left); } return dispatchBinaryOperatorNode.execute(leftOperand, rightOperand); } protected TruffleString getOverloadedOperatorName() { return overloadedOperatorName; } @Idempotent protected final boolean isNumeric() { return numeric; } protected Hint getHint() { return hint; } @Idempotent protected boolean isAddition() { return Strings.equals(Strings.SYMBOL_PLUS, overloadedOperatorName); } protected boolean isEquality() { return Strings.equals(Strings.SYMBOL_EQUALS_EQUALS, overloadedOperatorName); } /** * This class implements the {@code DispatchBinaryOperator} spec function. Its responsibility is * to call the correct operator overload. This node aims to perform the overload lookup at * compile-time and then use direct, inlinable function calls, guarded by shape checks and type * checks. */ @ImportStatic(OperatorSet.class) public abstract static class DispatchBinaryOperatorNode extends JavaScriptBaseNode { static final int LIMIT = 3; private final TruffleString overloadedOperatorName; protected DispatchBinaryOperatorNode(TruffleString overloadedOperatorName) { this.overloadedOperatorName = overloadedOperatorName; } protected abstract Object execute(Object left, Object right); @Specialization(guards = {"left.matchesOperatorCounter(leftOperatorCounter)", "right.matchesOperatorCounter(rightOperatorCounter)"}, limit = "LIMIT") protected Object doOverloadedOverloaded(JSOverloadedOperatorsObject left, JSOverloadedOperatorsObject right, @Cached("left.getOperatorCounter()") @SuppressWarnings("unused") int leftOperatorCounter, @Cached("right.getOperatorCounter()") @SuppressWarnings("unused") int rightOperatorCounter, @Cached("getOperatorImplementation(left, right, getOverloadedOperatorName())") Object operatorImplementation, @Cached("createCall()") @Exclusive JSFunctionCallNode callNode) { return performOverloaded(callNode, operatorImplementation, left, right); } @Specialization(guards = {"left.matchesOperatorCounter(leftOperatorCounter)", "isNumber(right)"}, limit = "LIMIT") protected Object doOverloadedNumber(JSOverloadedOperatorsObject left, Object right, @Cached("left.getOperatorCounter()") @SuppressWarnings("unused") int leftOperatorCounter, @Cached("getOperatorImplementation(left, right, getOverloadedOperatorName())") Object operatorImplementation, @Cached("createCall()") @Exclusive JSFunctionCallNode callNode) { return performOverloaded(callNode, operatorImplementation, left, right); } @Specialization(guards = {"left.matchesOperatorCounter(leftOperatorCounter)"}, limit = "LIMIT") protected Object doOverloadedBigInt(JSOverloadedOperatorsObject left, BigInt right, @Cached("left.getOperatorCounter()") @SuppressWarnings("unused") int leftOperatorCounter, @Cached("getOperatorImplementation(left, right, getOverloadedOperatorName())") Object operatorImplementation, @Cached("createCall()") @Exclusive JSFunctionCallNode callNode) { return performOverloaded(callNode, operatorImplementation, left, right); } @Specialization(guards = {"left.matchesOperatorCounter(leftOperatorCounter)", "isString(right)", "!isAddition()"}, limit = "LIMIT") protected Object doOverloadedString(JSOverloadedOperatorsObject left, Object right, @Cached("left.getOperatorCounter()") @SuppressWarnings("unused") int leftOperatorCounter, @Cached("getOperatorImplementation(left, right, getOverloadedOperatorName())") Object operatorImplementation, @Cached("createCall()") @Exclusive JSFunctionCallNode callNode) { return performOverloaded(callNode, operatorImplementation, left, right); } @Specialization(guards = "isNullOrUndefined(right)") protected Object doOverloadedNullish(@SuppressWarnings("unused") JSOverloadedOperatorsObject left, @SuppressWarnings("unused") Object right) { return missingImplementation(); } @Specialization(guards = {"right.matchesOperatorCounter(rightOperatorCounter)", "isNumber(left)"}, limit = "LIMIT") protected Object doNumberOverloaded(Object left, JSOverloadedOperatorsObject right, @Cached("right.getOperatorCounter()") @SuppressWarnings("unused") int rightOperatorCounter, @Cached("getOperatorImplementation(left, right, getOverloadedOperatorName())") Object operatorImplementation, @Cached("createCall()") @Exclusive JSFunctionCallNode callNode) { return performOverloaded(callNode, operatorImplementation, left, right); } @Specialization(guards = {"right.matchesOperatorCounter(rightOperatorCounter)"}, limit = "LIMIT") protected Object doBigIntOverloaded(BigInt left, JSOverloadedOperatorsObject right, @Cached("right.getOperatorCounter()") @SuppressWarnings("unused") int rightOperatorCounter, @Cached("getOperatorImplementation(left, right, getOverloadedOperatorName())") Object operatorImplementation, @Cached("createCall()") @Exclusive JSFunctionCallNode callNode) { return performOverloaded(callNode, operatorImplementation, left, right); } @Specialization(guards = {"right.matchesOperatorCounter(rightOperatorCounter)", "isString(left)", "!isAddition()"}, limit = "LIMIT") protected Object doStringOverloaded(Object left, JSOverloadedOperatorsObject right, @Cached("right.getOperatorCounter()") @SuppressWarnings("unused") int rightOperatorCounter, @Cached("getOperatorImplementation(left, right, getOverloadedOperatorName())") Object operatorImplementation, @Cached("createCall()") @Exclusive JSFunctionCallNode callNode) { return performOverloaded(callNode, operatorImplementation, left, right); } @Specialization(guards = "isNullOrUndefined(left)") protected Object doNullishOverloaded(@SuppressWarnings("unused") Object left, @SuppressWarnings("unused") JSOverloadedOperatorsObject right) { return missingImplementation(); } @ReportPolymorphism.Megamorphic @Specialization(replaces = {"doOverloadedOverloaded", "doOverloadedNumber", "doOverloadedBigInt", "doOverloadedString", "doNumberOverloaded", "doBigIntOverloaded", "doStringOverloaded"}) protected Object doGeneric(Object left, Object right, @Cached("createCall()") @Exclusive JSFunctionCallNode callNode) { Object operatorImplementation = OperatorSet.getOperatorImplementation(left, right, getOverloadedOperatorName()); return performOverloaded(callNode, operatorImplementation, left, right); } private boolean missingImplementation() { if (isEquality()) { return false; } else { throw Errors.createTypeErrorNoOverloadFound(getOverloadedOperatorName(), this); } } private Object performOverloaded(JSFunctionCallNode callNode, Object operatorImplementation, Object left, Object right) { if (operatorImplementation == null) { return missingImplementation(); } // What should be the value of 'this' when invoking overloaded operators? // Currently, we set it to 'undefined'. return callNode.executeCall(JSArguments.create(Undefined.instance, operatorImplementation, left, right)); } protected TruffleString getOverloadedOperatorName() { return overloadedOperatorName; } @Idempotent protected boolean isAddition() { return Strings.equals(Strings.SYMBOL_PLUS, overloadedOperatorName); } protected boolean isEquality() { return Strings.equals(Strings.SYMBOL_EQUALS_EQUALS, overloadedOperatorName); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy