| 754 | |
| 755 | OPCODE_SWITCH(_code_ptr[ip]) { |
| 756 | OPCODE(OPCODE_OPERATOR) { |
| 757 | constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr); |
| 758 | CHECK_SPACE(7 + _pointer_size); |
| 759 | |
| 760 | bool valid; |
| 761 | Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4]; |
| 762 | GD_ERR_BREAK(op >= Variant::OP_MAX); |
| 763 | |
| 764 | GET_VARIANT_PTR(a, 0); |
| 765 | GET_VARIANT_PTR(b, 1); |
| 766 | GET_VARIANT_PTR(dst, 2); |
| 767 | // Compute signatures (types of operands) so it can be optimized when matching. |
| 768 | uint32_t op_signature = _code_ptr[ip + 5]; |
| 769 | uint32_t actual_signature = (a->get_type() << 8) | (b->get_type()); |
| 770 | |
| 771 | #ifdef DEBUG_ENABLED |
| 772 | if (op == Variant::OP_DIVIDE || op == Variant::OP_MODULE) { |
| 773 | // Don't optimize division and modulo since there's not check for division by zero with validated calls. |
| 774 | op_signature = 0xFFFF; |
| 775 | _code_ptr[ip + 5] = op_signature; |
| 776 | } |
| 777 | #endif |
| 778 | |
| 779 | // Check if this is the first run. If so, store the current signature for the optimized path. |
| 780 | if (unlikely(op_signature == 0)) { |
| 781 | static Mutex initializer_mutex; |
| 782 | initializer_mutex.lock(); |
| 783 | Variant::Type a_type = (Variant::Type)((actual_signature >> 8) & 0xFF); |
| 784 | Variant::Type b_type = (Variant::Type)(actual_signature & 0xFF); |
| 785 | |
| 786 | Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(op, a_type, b_type); |
| 787 | |
| 788 | if (unlikely(!op_func)) { |
| 789 | #ifdef DEBUG_ENABLED |
| 790 | err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'."; |
| 791 | #endif |
| 792 | initializer_mutex.unlock(); |
| 793 | OPCODE_BREAK; |
| 794 | } else { |
| 795 | Variant::Type ret_type = Variant::get_operator_return_type(op, a_type, b_type); |
| 796 | VariantInternal::initialize(dst, ret_type); |
| 797 | op_func(a, b, dst); |
| 798 | |
| 799 | // Check again in case another thread already set it. |
| 800 | if (_code_ptr[ip + 5] == 0) { |
| 801 | _code_ptr[ip + 5] = actual_signature; |
| 802 | _code_ptr[ip + 6] = static_cast<int>(ret_type); |
| 803 | Variant::ValidatedOperatorEvaluator *tmp = reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]); |
| 804 | *tmp = op_func; |
| 805 | } |
| 806 | } |
| 807 | initializer_mutex.unlock(); |
| 808 | } else if (likely(op_signature == actual_signature)) { |
| 809 | // If the signature matches, we can use the optimized path. |
| 810 | Variant::Type ret_type = static_cast<Variant::Type>(_code_ptr[ip + 6]); |
| 811 | Variant::ValidatedOperatorEvaluator op_func = *reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]); |
| 812 | |
| 813 | // Make sure the return value has the correct type. |
no test coverage detected