public CompletableFuture handle()

in com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java [71:332]


    public CompletableFuture<Response> handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) {
        IVariableFormatter variableFormatter = context.getVariableFormatter();
        VariablesArguments varArgs = (VariablesArguments) arguments;

        boolean showStaticVariables = DebugSettings.getCurrent().showStaticVariables;

        Map<String, Object> options = variableFormatter.getDefaultOptions();
        VariableUtils.applyFormatterOptions(options, varArgs.format != null && varArgs.format.hex);
        IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class);

        List<Types.Variable> list = new ArrayList<>();
        Object container = context.getRecyclableIdPool().getObjectById(varArgs.variablesReference);
        // vscode will always send variables request to a staled scope, return the empty list is ok since the next
        // variable request will contain the right variablesReference.
        if (container == null) {
            response.body = new Responses.VariablesResponseBody(list);
            return CompletableFuture.completedFuture(response);
        }

        if (!(container instanceof VariableProxy)) {
            throw AdapterUtils.createCompletionException(
                String.format("VariablesRequest: Invalid variablesReference %d.", varArgs.variablesReference),
                ErrorCode.GET_VARIABLE_FAILURE);
        }

        VariableProxy containerNode = (VariableProxy) container;
        List<Variable> childrenList = new ArrayList<>();
        IStackFrameManager stackFrameManager = context.getStackFrameManager();
        String containerEvaluateName = containerNode.getEvaluateName();
        boolean isUnboundedTypeContainer = containerNode.isUnboundedType();
        if (containerNode.getProxiedVariable() instanceof StackFrameReference) {
            StackFrameReference stackFrameReference = (StackFrameReference) containerNode.getProxiedVariable();
            StackFrame frame = stackFrameManager.getStackFrame(stackFrameReference);
            if (frame == null) {
                throw AdapterUtils.createCompletionException(
                    String.format("Invalid stackframe id %d to get variables.", varArgs.variablesReference),
                    ErrorCode.GET_VARIABLE_FAILURE);
            }
            try {
                long threadId = stackFrameReference.getThread().uniqueID();
                JdiMethodResult result = context.getStepResultManager().getMethodResult(threadId);
                if (result != null) {
                    String returnIcon = (AdapterUtils.isWin || AdapterUtils.isMac) ? "⎯►" : "->";
                    childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value, null));
                }
                childrenList.addAll(VariableUtils.listLocalVariables(frame));
                Variable thisVariable = VariableUtils.getThisVariable(frame);
                if (thisVariable != null) {
                    childrenList.add(thisVariable);
                }
                if (showStaticVariables && frame.location().method().isStatic()) {
                    childrenList.addAll(VariableUtils.listStaticVariables(frame));
                }
            } catch (AbsentInformationException | InternalException | InvalidStackFrameException e) {
                throw AdapterUtils.createCompletionException(
                    String.format("Failed to get variables. Reason: %s", e.toString()),
                    ErrorCode.GET_VARIABLE_FAILURE,
                    e);
            }
        } else {
            try {
                ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable();
                if (DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) {
                    JavaLogicalStructure logicalStructure = null;
                    try {
                        logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj);
                    } catch (Exception e) {
                        logger.log(Level.WARNING, "Failed to get the logical structure for the variable, fall back to the Object view.", e);
                    }
                    if (isUnboundedTypeContainer && logicalStructure != null && containerEvaluateName != null) {
                        containerEvaluateName = "((" + logicalStructure.getFullyQualifiedName() + ")" + containerEvaluateName + ")";
                        isUnboundedTypeContainer = false;
                    }
                    while (logicalStructure != null) {
                        LogicalStructureExpression valueExpression = logicalStructure.getValueExpression();
                        LogicalVariable[] logicalVariables = logicalStructure.getVariables();
                        try {
                            if (valueExpression != null) {
                                containerEvaluateName = containerEvaluateName == null ? null : containerEvaluateName + "." + valueExpression.evaluateName;
                                isUnboundedTypeContainer = valueExpression.returnUnboundedType;
                                Value value = logicalStructure.getValue(containerObj, containerNode.getThread(), evaluationEngine);
                                if (value instanceof ObjectReference) {
                                    containerObj = (ObjectReference) value;
                                    logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj);
                                    continue;
                                } else {
                                    childrenList = Arrays.asList(new Variable("logical structure", value));
                                }
                            } else if (logicalVariables != null && logicalVariables.length > 0) {
                                for (LogicalVariable logicalVariable : logicalVariables) {
                                    String name = logicalVariable.getName();
                                    Value value = logicalVariable.getValue(containerObj, containerNode.getThread(), evaluationEngine);
                                    Variable variable = new Variable(name, value, logicalVariable.getEvaluateName());
                                    variable.setUnboundedType(logicalVariable.returnUnboundedType());
                                    childrenList.add(variable);
                                }
                            }
                        } catch (Exception e) {
                            logger.log(Level.WARNING, "Failed to get the logical structure for the variable, fall back to the Object view.", e);
                        }

                        logicalStructure = null;
                    }
                }

                if (childrenList.isEmpty() && VariableUtils.hasChildren(containerObj, showStaticVariables)) {
                    if (varArgs.count > 0) {
                        childrenList = VariableUtils.listFieldVariables(containerObj, varArgs.start, varArgs.count);
                    } else {
                        childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables);
                    }
                }
            } catch (AbsentInformationException e) {
                throw AdapterUtils.createCompletionException(
                    String.format("Failed to get variables. Reason: %s", e.toString()),
                    ErrorCode.GET_VARIABLE_FAILURE,
                    e);
            }
        }

        // Find variable name duplicates
        Set<String> duplicateNames = getDuplicateNames(childrenList.stream().map(var -> var.name).collect(Collectors.toList()));
        Map<Variable, String> variableNameMap = new HashMap<>();
        if (!duplicateNames.isEmpty()) {
            Map<String, List<Variable>> duplicateVars = childrenList.stream()
                    .filter(var -> duplicateNames.contains(var.name)).collect(Collectors.groupingBy(var -> var.name, Collectors.toList()));

            duplicateVars.forEach((k, duplicateVariables) -> {
                Set<String> declarationTypeNames = new HashSet<>();
                boolean declarationTypeNameConflict = false;
                // try use type formatter to resolve name conflict
                for (Variable javaVariable : duplicateVariables) {
                    Type declarationType = javaVariable.getDeclaringType();
                    if (declarationType != null) {
                        String declarationTypeName = variableFormatter.typeToString(declarationType, options);
                        String compositeName = String.format("%s (%s)", javaVariable.name, declarationTypeName);
                        if (!declarationTypeNames.add(compositeName)) {
                            declarationTypeNameConflict = true;
                            break;
                        }
                        variableNameMap.put(javaVariable, compositeName);
                    }
                }
                // If there are duplicate names on declaration types, use fully qualified name
                if (declarationTypeNameConflict) {
                    for (Variable javaVariable : duplicateVariables) {
                        Type declarationType = javaVariable.getDeclaringType();
                        if (declarationType != null) {
                            variableNameMap.put(javaVariable, String.format("%s (%s)", javaVariable.name, declarationType.name()));
                        }
                    }
                }
            });
        }
        for (Variable javaVariable : childrenList) {
            Value value = javaVariable.value;
            String name = javaVariable.name;
            if (variableNameMap.containsKey(javaVariable)) {
                name = variableNameMap.get(javaVariable);
            }
            int indexedVariables = -1;
            Value sizeValue = null;
            if (value instanceof ArrayReference) {
                indexedVariables = ((ArrayReference) value).length();
            } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) {
                try {
                    JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value);
                    if (structure != null && structure.getSizeExpression() != null) {
                        sizeValue = structure.getSize((ObjectReference) value, containerNode.getThread(), evaluationEngine);
                        if (sizeValue != null && sizeValue instanceof IntegerValue) {
                            indexedVariables = ((IntegerValue) sizeValue).value();
                        }
                    }
                } catch (Exception e) {
                    logger.log(Level.INFO, "Failed to get the logical size of the variable", e);
                }
            }

            String evaluateName = null;
            if (javaVariable.evaluateName == null || (containerEvaluateName == null && containerNode.getProxiedVariable() instanceof ObjectReference)) {
                // Disable evaluate on the method return value.
                evaluateName = null;
            } else if (isUnboundedTypeContainer && !containerNode.isIndexedVariable()) {
                // The type name returned by JDI is the binary name, which uses '$' as the separator of
                // inner class e.g. Foo$Bar. But the evaluation expression only accepts using '.' as the class
                // name separator.
                String typeName = ((ObjectReference) containerNode.getProxiedVariable()).referenceType().name();
                // TODO: This replacement will possibly change the $ in the class name itself.
                typeName = typeName.replaceAll("\\$", ".");
                evaluateName = VariableUtils.getEvaluateName(javaVariable.evaluateName, "((" + typeName + ")" + containerEvaluateName + ")", false);
            } else {
                if (containerEvaluateName != null && containerEvaluateName.contains("%s")) {
                    evaluateName = String.format(containerEvaluateName, javaVariable.evaluateName);
                } else {
                    evaluateName = VariableUtils.getEvaluateName(javaVariable.evaluateName, containerEvaluateName, containerNode.isIndexedVariable());
                }
            }

            int referenceId = 0;
            if (indexedVariables > 0 || (indexedVariables < 0 && value instanceof ObjectReference)) {
                VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName);
                referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy);
                varProxy.setIndexedVariable(indexedVariables >= 0);
                varProxy.setUnboundedType(javaVariable.isUnboundedType());
            }

            boolean hasErrors = false;
            String valueString = null;
            try {
                valueString = variableFormatter.valueToString(value, options);
            } catch (OutOfMemoryError e) {
                hasErrors = true;
                logger.log(Level.SEVERE, "Failed to convert the value of a large object to a string", e);
                valueString = "<Unable to display the value of a large object>";
            } catch (Exception e) {
                hasErrors = true;
                logger.log(Level.SEVERE, "Failed to resolve the variable value", e);
                valueString = "<Failed to resolve the variable value due to \"" + e.getMessage() + "\">";
            }

            String typeString = "";
            try {
                typeString = variableFormatter.typeToString(value == null ? null : value.type(), options);
            } catch (Exception e) {
                logger.log(Level.SEVERE, "Failed to resolve the variable type", e);
                typeString = "";
            }

            Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName);
            typedVariables.indexedVariables = Math.max(indexedVariables, 0);

            String detailsValue = null;
            if (hasErrors) {
                // If failed to resolve the variable value, skip the details info as well.
            } else if (sizeValue != null) {
                detailsValue = "size=" + variableFormatter.valueToString(sizeValue, options);
            } else if (DebugSettings.getCurrent().showToString) {
                try {
                    detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine);
                } catch (OutOfMemoryError e) {
                    logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e);
                    detailsValue = "<Unable to display the details of a large object>";
                } catch (Exception e) {
                    logger.log(Level.SEVERE, "Failed to compute the toString() value", e);
                    detailsValue = "<Failed to resolve the variable details due to \"" + e.getMessage() + "\">";
                }
            }

            if (detailsValue != null) {
                typedVariables.value = typedVariables.value + " " + detailsValue;
            }
            list.add(typedVariables);
        }

        if (list.isEmpty() && containerNode.getProxiedVariable() instanceof ObjectReference) {
            list.add(new Types.Variable("Class has no fields", "", null, 0, null));
        }

        response.body = new Responses.VariablesResponseBody(list);

        return CompletableFuture.completedFuture(response);
    }