void V8InjectedScriptHost::functionDetailsCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
{
    if (info.Length() < 1 || !info[0]->IsFunction())
        return;

    v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(info[0]);
    int lineNumber = function->GetScriptLineNumber();
    int columnNumber = function->GetScriptColumnNumber();

    v8::Isolate* isolate = info.GetIsolate();
    v8::Local<v8::Object> location = v8::Object::New(isolate);
    location->Set(v8AtomicString(isolate, "lineNumber"), v8::Integer::New(isolate, lineNumber));
    location->Set(v8AtomicString(isolate, "columnNumber"), v8::Integer::New(isolate, columnNumber));
    location->Set(v8AtomicString(isolate, "scriptId"), v8::Integer::New(isolate, function->ScriptId())->ToString(isolate));

    v8::Local<v8::Object> result = v8::Object::New(isolate);
    result->Set(v8AtomicString(isolate, "location"), location);

    v8::Local<v8::String> name = functionDisplayName(function);
    result->Set(v8AtomicString(isolate, "functionName"), name.IsEmpty() ? v8AtomicString(isolate, "") : name);

    result->Set(v8AtomicString(isolate, "isGenerator"), v8::Boolean::New(isolate, function->IsGeneratorFunction()));

    InjectedScriptHost* host = V8InjectedScriptHost::unwrap(info.GetIsolate()->GetCurrentContext(), info.Holder());
    V8DebuggerImpl& debugger = static_cast<V8DebuggerImpl&>(host->debugger());
    v8::MaybeLocal<v8::Value> scopes = debugger.functionScopes(function);
    if (!scopes.IsEmpty() && scopes.ToLocalChecked()->IsArray())
        result->Set(v8AtomicString(isolate, "rawScopes"), scopes.ToLocalChecked());

    v8SetReturnValue(info, result);
}
void V8InjectedScriptHost::collectionEntriesCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
{
    if (info.Length() < 1 || !info[0]->IsObject())
        return;

    v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(info[0]);

    InjectedScriptHost* host = V8InjectedScriptHost::unwrap(info.GetIsolate()->GetCurrentContext(), info.Holder());
    V8DebuggerImpl& debugger = static_cast<V8DebuggerImpl&>(host->debugger());
    v8SetReturnValue(info, debugger.collectionEntries(object));
}
void V8InjectedScriptHost::setFunctionVariableValueCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
{
    if (info.Length() < 4 || !info[0]->IsFunction() || !info[1]->IsInt32() || !info[2]->IsString())
        return;

    v8::Local<v8::Value> functionValue = info[0];
    int scopeIndex = info[1].As<v8::Int32>()->Value();
    String variableName = toCoreStringWithUndefinedOrNullCheck(info[2]);
    v8::Local<v8::Value> newValue = info[3];

    InjectedScriptHost* host = V8InjectedScriptHost::unwrap(info.GetIsolate()->GetCurrentContext(), info.Holder());
    V8DebuggerImpl& debugger = static_cast<V8DebuggerImpl&>(host->debugger());
    v8SetReturnValue(info, debugger.setFunctionVariableValue(functionValue, scopeIndex, variableName, newValue));
}
void V8InjectedScriptHost::getEventListenersCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
{
    if (info.Length() < 1)
        return;

    InjectedScriptHost* host = V8InjectedScriptHost::unwrap(info.GetIsolate()->GetCurrentContext(), info.Holder());
    V8DebuggerClient* client = static_cast<V8DebuggerImpl&>(host->debugger()).client();
    EventListenerInfoMap listenerInfo;
    client->eventListeners(info[0], listenerInfo);

    v8::Local<v8::Object> result = v8::Object::New(info.GetIsolate());
    Vector<String> types;
    for (auto& it : listenerInfo)
        types.append(it.key);
    nonCopyingSort(types.begin(), types.end(), WTF::codePointCompareLessThan);
    for (const String& type : types) {
        v8::Local<v8::Array> listeners = wrapListenerFunctions(info.GetIsolate(), *listenerInfo.get(type));
        if (!listeners->Length())
            continue;
        result->Set(v8String(info.GetIsolate(), type), listeners);
    }

    v8SetReturnValue(info, result);
}