presto-main/src/main/java/io/prestosql/sql/gen/BytecodeUtils.java/BytecodeUtils/generateInvocation
class BytecodeUtils:    public static BytecodeNode generateInvocation(Scope scope, String name, ScalarFunctionImplementation function, Optional<BytecodeNode> instance, List<BytecodeNode> arguments, CallSiteBinder binder)
    {
        LabelNode end = new LabelNode("end");
        BytecodeBlock block = new BytecodeBlock()
                .setDescription("invoke " + name);

        List<Class<?>> stackTypes = new ArrayList<>();
        if (function.getInstanceFactory().isPresent()) {
            checkArgument(instance.isPresent());
        }

        // Index of current parameter in the MethodHandle
        int currentParameterIndex = 0;

        // Index of parameter (without @IsNull) in Presto function
        int realParameterIndex = 0;

        // Go through all the choices in the function and then pick the best one
        List<ScalarImplementationChoice> choices = function.getAllChoices();
        ScalarImplementationChoice bestChoice = null;
        for (ScalarImplementationChoice currentChoice : choices) {
            boolean isValid = true;
            for (int i = 0; i < arguments.size(); i++) {
                if (currentChoice.getArgumentProperty(i).getArgumentType() != VALUE_TYPE) {
                    continue;
                }
                if (!(arguments.get(i) instanceof InputReferenceNode) && currentChoice.getArgumentProperty(i).getNullConvention() == NullConvention.BLOCK_AND_POSITION) {
                    isValid = false;
                    break;
                }
            }
            if (isValid) {
                bestChoice = currentChoice;
            }
        }

        checkState(bestChoice != null, "None of the scalar function implementation choices are valid");
        Binding binding = binder.bind(bestChoice.getMethodHandle());

        MethodType methodType = binding.getType();
        Class<?> returnType = methodType.returnType();
        Class<?> unboxedReturnType = Primitives.unwrap(returnType);

        boolean boundInstance = false;
        while (currentParameterIndex < methodType.parameterArray().length) {
            Class<?> type = methodType.parameterArray()[currentParameterIndex];
            stackTypes.add(type);
            if (bestChoice.getInstanceFactory().isPresent() && !boundInstance) {
                checkState(type.equals(bestChoice.getInstanceFactory().get().type().returnType()), "Mismatched type for instance parameter");
                block.append(instance.get());
                boundInstance = true;
            }
            else if (type == ConnectorSession.class) {
                block.append(scope.getVariable("session"));
            }
            else {
                ArgumentProperty argumentProperty = bestChoice.getArgumentProperty(realParameterIndex);
                switch (argumentProperty.getArgumentType()) {
                    case VALUE_TYPE:
                        // Apply null convention for value type argument
                        switch (argumentProperty.getNullConvention()) {
                            case RETURN_NULL_ON_NULL:
                                block.append(arguments.get(realParameterIndex));
                                checkArgument(!Primitives.isWrapperType(type), "Non-nullable argument must not be primitive wrapper type");
                                block.append(ifWasNullPopAndGoto(scope, end, unboxedReturnType, Lists.reverse(stackTypes)));
                                break;
                            case USE_NULL_FLAG:
                                block.append(arguments.get(realParameterIndex));
                                block.append(scope.getVariable("wasNull"));
                                block.append(scope.getVariable("wasNull").set(constantFalse()));
                                stackTypes.add(boolean.class);
                                currentParameterIndex++;
                                break;
                            case USE_BOXED_TYPE:
                                block.append(arguments.get(realParameterIndex));
                                block.append(boxPrimitiveIfNecessary(scope, type));
                                block.append(scope.getVariable("wasNull").set(constantFalse()));
                                break;
                            case BLOCK_AND_POSITION:
                                InputReferenceNode inputReferenceNode = (InputReferenceNode) arguments.get(realParameterIndex);
                                block.append(inputReferenceNode.produceBlockAndPosition());
                                stackTypes.add(int.class);
                                currentParameterIndex++;
                                break;
                            default:
                                throw new UnsupportedOperationException(format("Unsupported null convention: %s", argumentProperty.getNullConvention()));
                        }
                        break;
                    case FUNCTION_TYPE:
                        block.append(arguments.get(realParameterIndex));
                        break;
                    default:
                        throw new UnsupportedOperationException(format("Unsupported argument type: %s", argumentProperty.getArgumentType()));
                }
                realParameterIndex++;
            }
            currentParameterIndex++;
        }
        block.append(invoke(binding, name));

        if (function.isNullable()) {
            block.append(unboxPrimitiveIfNecessary(scope, returnType));
        }
        block.visitLabel(end);

        return block;
    }