Пример #1
0
 async Task OnResume(MessageId msd_id, CancellationToken token)
 {
     //discard managed frames
     GetContext(msd_id).ClearState();
     await Task.CompletedTask;
 }
Пример #2
0
        internal async Task <JToken> TryGetVariableValue(MessageId msg_id, int scope_id, string expression, bool only_search_on_this, CancellationToken token)
        {
            JToken thisValue = null;
            var    context   = GetContext(msg_id);

            if (context.CallStack == null)
            {
                return(null);
            }

            if (TryFindVariableValueInCache(context, expression, only_search_on_this, out JToken obj))
            {
                return(obj);
            }

            var scope = context.CallStack.FirstOrDefault(s => s.Id == scope_id);
            var vars  = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset);

            //get_this
            int [] var_ids = { };
            var    res     = await SendMonoCommand(msg_id, MonoCommands.GetScopeVariables(scope.Id, var_ids), token);

            var values = res.Value? ["result"]? ["value"]?.Values <JObject> ().ToArray();

            thisValue = values.FirstOrDefault(v => v ["name"].Value <string> () == "this");

            if (!only_search_on_this)
            {
                if (thisValue != null && expression == "this")
                {
                    return(thisValue);
                }
                //search in locals
                var var_id = vars.SingleOrDefault(v => v.Name == expression);
                if (var_id != null)
                {
                    res = await SendMonoCommand(msg_id, MonoCommands.GetScopeVariables(scope.Id, new int [] { var_id.Index }), token);

                    values = res.Value? ["result"]? ["value"]?.Values <JObject> ().ToArray();
                    return(values [0]);
                }
            }

            //search in scope
            if (thisValue != null)
            {
                var objectId = thisValue ["value"] ["objectId"].Value <string> ();
                var parts    = objectId.Split(new char [] { ':' });
                res = await SendMonoCommand(msg_id, MonoCommands.GetObjectProperties(int.Parse(parts [2]), expandValueTypes: false), token);

                values = res.Value? ["result"]? ["value"]?.Values <JObject> ().ToArray();
                var foundValue = values.FirstOrDefault(v => v ["name"].Value <string> () == expression);
                if (foundValue != null)
                {
                    foundValue["fromThis"] = true;
                    context.LocalsCache[foundValue ["name"].Value <string> ()] = foundValue;
                    return(foundValue);
                }
            }
            return(null);
        }
Пример #3
0
 void OnCompileDotnetScript(MessageId msg_id, CancellationToken token)
 {
     SendResponse(msg_id, Result.OkFromObject(new { }), token);
 }
Пример #4
0
        protected override async Task <bool> AcceptCommand(MessageId id, string method, JObject args, CancellationToken token)
        {
            // Inspector doesn't use the Target domain or sessions
            // so we try to init immediately
            if (hideWebDriver && id == SessionId.Null)
            {
                await DeleteWebDriver(id, token);
            }

            if (!contexts.TryGetValue(id, out var context))
            {
                return(false);
            }

            switch (method)
            {
            case "Target.attachToTarget": {
                    var resp = await SendCommand(id, method, args, token);
                    await DeleteWebDriver(new SessionId (resp.Value ["sessionId"]?.ToString()), token);

                    break;
            }

            case "Debugger.enable": {
                    var resp = await SendCommand(id, method, args, token);

                    context.DebuggerId = resp.Value ["debuggerId"]?.ToString();

                    if (await IsRuntimeAlreadyReadyAlready(id, token))
                    {
                        await RuntimeReady(id, token);
                    }

                    SendResponse(id, resp, token);
                    return(true);
            }

            case "Debugger.getScriptSource": {
                var script = args? ["scriptId"]?.Value <string> ();
                return(await OnGetScriptSource(id, script, token));
            }

            case "Runtime.compileScript": {
                var exp = args? ["expression"]?.Value <string> ();
                if (exp.StartsWith("//dotnet:", StringComparison.Ordinal))
                {
                    OnCompileDotnetScript(id, token);
                    return(true);
                }
                break;
            }

            case "Debugger.getPossibleBreakpoints": {
                var resp = await SendCommand(id, method, args, token);

                if (resp.IsOk && resp.Value["locations"].HasValues)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var start = SourceLocation.Parse(args? ["start"] as JObject);
                //FIXME support variant where restrictToFunction=true and end is omitted
                var end = SourceLocation.Parse(args? ["end"] as JObject);
                if (start != null && end != null && await GetPossibleBreakpoints(id, start, end, token))
                {
                    return(true);
                }

                SendResponse(id, resp, token);
                return(true);
            }

            case "Debugger.setBreakpoint": {
                break;
            }

            case "Debugger.setBreakpointByUrl": {
                var resp = await SendCommand(id, method, args, token);

                if (!resp.IsOk)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var bpid    = resp.Value["breakpointId"]?.ToString();
                var request = BreakpointRequest.Parse(bpid, args);
                context.BreakpointRequests[bpid] = request;
                if (await IsRuntimeAlreadyReadyAlready(id, token))
                {
                    var store = await RuntimeReady(id, token);

                    Log("verbose", $"BP req {args}");
                    await SetBreakpoint(id, store, request, token);
                }

                SendResponse(id, Result.OkFromObject(request.AsSetBreakpointByUrlResponse()), token);
                return(true);
            }

            case "Debugger.removeBreakpoint": {
                await RemoveBreakpoint(id, args, token);

                break;
            }

            case "Debugger.resume": {
                await OnResume(id, token);

                break;
            }

            case "Debugger.stepInto": {
                return(await Step(id, StepKind.Into, token));
            }

            case "Debugger.stepOut": {
                return(await Step(id, StepKind.Out, token));
            }

            case "Debugger.stepOver": {
                return(await Step(id, StepKind.Over, token));
            }

            case "Debugger.evaluateOnCallFrame": {
                var objId = args? ["callFrameId"]?.Value <string> ();
                if (objId.StartsWith("dotnet:", StringComparison.Ordinal))
                {
                    var parts = objId.Split(new char [] { ':' });
                    if (parts.Length < 3)
                    {
                        return(true);
                    }
                    switch (parts [1])
                    {
                    case "scope": {
                        await GetEvaluateOnCallFrame(id, int.Parse(parts [2]), args? ["expression"]?.Value <string> (), token);

                        break;
                    }
                    }
                    return(true);
                }
                return(false);
            }

            case "Runtime.getProperties": {
                var objId = args? ["objectId"]?.Value <string> ();
                if (objId.StartsWith("dotnet:", StringComparison.Ordinal))
                {
                    var parts = objId.Split(new char [] { ':' });
                    if (parts.Length < 3)
                    {
                        return(true);
                    }
                    switch (parts[1])
                    {
                    case "scope": {
                        await GetScopeProperties(id, int.Parse(parts[2]), token);

                        break;
                    }

                    case "object": {
                        await GetDetails(id, MonoCommands.GetObjectProperties(int.Parse(parts[2]), expandValueTypes: false), token);

                        break;
                    }

                    case "array": {
                        await GetArrayDetails(id, objId, parts, token);

                        break;
                    }

                    case "valuetype": {
                        await GetDetailsForValueType(id, objId,
                                                     get_props_cmd_fn : () => {
                                    if (parts.Length < 4)
                                    {
                                        return(null);
                                    }

                                    var containerObjId = int.Parse(parts[2]);
                                    return(MonoCommands.GetObjectProperties(containerObjId, expandValueTypes: true));
                                }, token);

                        break;
                    }
                    }

                    return(true);
                }
                break;
            }

            // Protocol extensions
            case "Dotnet-test.setBreakpointByMethod": {
                Console.WriteLine("set-breakpoint-by-method: " + id + " " + args);

                var store = await RuntimeReady(id, token);

                string aname      = args ["assemblyName"]?.Value <string> ();
                string typeName   = args ["typeName"]?.Value <string> ();
                string methodName = args ["methodName"]?.Value <string> ();
                if (aname == null || typeName == null || methodName == null)
                {
                    SendResponse(id, Result.Err("Invalid protocol message '" + args + "'."), token);
                    return(true);
                }

                // GetAssemblyByName seems to work on file names
                var assembly = store.GetAssemblyByName(aname);
                if (assembly == null)
                {
                    assembly = store.GetAssemblyByName(aname + ".exe");
                }
                if (assembly == null)
                {
                    assembly = store.GetAssemblyByName(aname + ".dll");
                }
                if (assembly == null)
                {
                    SendResponse(id, Result.Err("Assembly '" + aname + "' not found."), token);
                    return(true);
                }

                var type = assembly.GetTypeByName(typeName);
                if (type == null)
                {
                    SendResponse(id, Result.Err($"Type '{typeName}' not found."), token);
                    return(true);
                }

                var methodInfo = type.Methods.FirstOrDefault(m => m.Name == methodName);
                if (methodInfo == null)
                {
                    SendResponse(id, Result.Err($"Method '{typeName}:{methodName}' not found."), token);
                    return(true);
                }

                bpIdGenerator++;
                string bpid    = "by-method-" + bpIdGenerator.ToString();
                var    request = new BreakpointRequest(bpid, methodInfo);
                context.BreakpointRequests[bpid] = request;

                var loc = methodInfo.StartLocation;
                var bp  = await SetMonoBreakpoint(id, bpid, loc, token);

                if (bp.State != BreakpointState.Active)
                {
                    // FIXME:
                    throw new NotImplementedException();
                }

                var resolvedLocation = new {
                    breakpointId = bpid,
                    location     = loc.AsLocation()
                };

                SendEvent(id, "Debugger.breakpointResolved", JObject.FromObject(resolvedLocation), token);

                SendResponse(id, Result.OkFromObject(new {
                        result = new { breakpointId = bpid, locations = new object [] { loc.AsLocation() } }
                    }), token);

                return(true);
            }
            }

            return(false);
        }
Пример #5
0
 internal void SendResponse(MessageId id, Result result, CancellationToken token)
 {
     SendResponseInternal(id, result, token);
 }
Пример #6
0
 protected virtual Task <bool> AcceptCommand(MessageId id, string method, JObject args, CancellationToken token)
 {
     return(Task.FromResult(false));
 }
Пример #7
0
        async Task GetScopeProperties(MessageId msg_id, int scope_id, CancellationToken token)
        {
            try {
                var scope = GetContext(msg_id).CallStack.FirstOrDefault(s => s.Id == scope_id);
                var vars  = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset);

                var var_ids = vars.Select(v => v.Index).ToArray();
                var res     = await SendMonoCommand(msg_id, MonoCommands.GetScopeVariables(scope.Id, var_ids), token);

                //if we fail we just buble that to the IDE (and let it panic over it)
                if (res.IsErr)
                {
                    SendResponse(msg_id, res, token);
                    return;
                }

                var values = res.Value? ["result"]? ["value"]?.Values <JObject> ().ToArray();

                if (values == null)
                {
                    SendResponse(msg_id, Result.OkFromObject(new { result = Array.Empty <object> () }), token);
                    return;
                }

                var var_list = new List <object> ();
                int i        = 0;
                // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
                // results in a "Memory access out of bounds", causing 'values' to be null,
                // so skip returning variable values in that case.
                while (i < vars.Length && i < values.Length)
                {
                    var value = values [i] ["value"];
                    if (((string)value ["description"]) == null)
                    {
                        value ["description"] = value ["value"]?.ToString();
                    }

                    var_list.Add(new {
                        name = vars [i].Name,
                        value
                    });
                    i++;
                }
                //Async methods are special in the way that local variables can be lifted to generated class fields
                //value of "this" comes here either
                while (i < values.Length)
                {
                    String name = values [i] ["name"].ToString();

                    if (name.IndexOf(">", StringComparison.Ordinal) > 0)
                    {
                        name = name.Substring(1, name.IndexOf(">", StringComparison.Ordinal) - 1);
                    }

                    var value = values [i + 1] ["value"];
                    if (((string)value ["description"]) == null)
                    {
                        value ["description"] = value ["value"]?.ToString();
                    }

                    var_list.Add(new {
                        name,
                        value
                    });
                    i = i + 2;
                }

                SendResponse(msg_id, Result.OkFromObject(new { result = var_list }), token);
            } catch (Exception exception) {
                Log("verbose", $"Error resolving scope properties {exception.Message}");
                SendResponse(msg_id, Result.Exception(exception), token);
            }
        }
Пример #8
0
        protected override async Task <bool> AcceptCommand(MessageId id, string method, JObject args, CancellationToken token)
        {
            if (!contexts.TryGetValue(id, out var context))
            {
                return(false);
            }

            switch (method)
            {
            case "Debugger.enable": {
                    var resp = await SendCommand(id, method, args, token);

                    context.DebuggerId = resp.Value ["debuggerId"]?.ToString();

                    if (await IsRuntimeAlreadyReadyAlready(id, token))
                    {
                        await RuntimeReady(id, token);
                    }

                    SendResponse(id, resp, token);
                    return(true);
            }

            case "Debugger.getScriptSource": {
                    var script = args? ["scriptId"]?.Value <string> ();
                    return(await OnGetScriptSource(id, script, token));
            }

            case "Runtime.compileScript": {
                var exp = args? ["expression"]?.Value <string> ();
                if (exp.StartsWith("//dotnet:", StringComparison.Ordinal))
                {
                    OnCompileDotnetScript(id, token);
                    return(true);
                }
                break;
            }

            case "Debugger.getPossibleBreakpoints": {
                var resp = await SendCommand(id, method, args, token);

                if (resp.IsOk && resp.Value["locations"].HasValues)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var start = SourceLocation.Parse(args? ["start"] as JObject);
                //FIXME support variant where restrictToFunction=true and end is omitted
                var end = SourceLocation.Parse(args? ["end"] as JObject);
                if (start != null && end != null && await GetPossibleBreakpoints(id, start, end, token))
                {
                    return(true);
                }

                SendResponse(id, resp, token);
                return(true);
            }

            case "Debugger.setBreakpoint": {
                break;
            }

            case "Debugger.setBreakpointByUrl": {
                var resp = await SendCommand(id, method, args, token);

                if (!resp.IsOk)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var bpid    = resp.Value["breakpointId"]?.ToString();
                var request = BreakpointRequest.Parse(bpid, args);
                context.BreakpointRequests[bpid] = request;
                if (await IsRuntimeAlreadyReadyAlready(id, token))
                {
                    var store = await RuntimeReady(id, token);

                    Log("verbose", $"BP req {args}");
                    await SetBreakpoint(id, store, request, token);
                }

                SendResponse(id, Result.OkFromObject(request.AsSetBreakpointByUrlResponse()), token);
                return(true);
            }

            case "Debugger.removeBreakpoint": {
                return(await RemoveBreakpoint(id, args, token));
            }

            case "Debugger.resume": {
                await OnResume(id, token);

                break;
            }

            case "Debugger.stepInto": {
                return(await Step(id, StepKind.Into, token));
            }

            case "Debugger.stepOut": {
                return(await Step(id, StepKind.Out, token));
            }

            case "Debugger.stepOver": {
                return(await Step(id, StepKind.Over, token));
            }

            case "Debugger.evaluateOnCallFrame": {
                var objId = args? ["callFrameId"]?.Value <string> ();
                if (objId.StartsWith("dotnet:", StringComparison.Ordinal))
                {
                    var parts = objId.Split(new char [] { ':' });
                    if (parts.Length < 3)
                    {
                        return(true);
                    }
                    switch (parts [1])
                    {
                    case "scope": {
                        await GetEvaluateOnCallFrame(id, int.Parse(parts [2]), args? ["expression"]?.Value <string> (), token);

                        break;
                    }
                    }
                    return(true);
                }
                return(false);
            }

            case "Runtime.getProperties": {
                var objId = args? ["objectId"]?.Value <string> ();
                if (objId.StartsWith("dotnet:", StringComparison.Ordinal))
                {
                    var parts = objId.Split(new char [] { ':' });
                    if (parts.Length < 3)
                    {
                        return(true);
                    }
                    switch (parts[1])
                    {
                    case "scope": {
                        await GetScopeProperties(id, int.Parse(parts[2]), token);

                        break;
                    }

                    case "object": {
                        await GetDetails(id, MonoCommands.GetObjectProperties(int.Parse(parts[2]), expandValueTypes: false), token);

                        break;
                    }

                    case "array": {
                        await GetArrayDetails(id, objId, parts, token);

                        break;
                    }

                    case "valuetype": {
                        await GetDetailsForValueType(id, objId,
                                                     get_props_cmd_fn : () => {
                                    if (parts.Length < 4)
                                    {
                                        return(null);
                                    }

                                    var containerObjId = int.Parse(parts[2]);
                                    return(MonoCommands.GetObjectProperties(containerObjId, expandValueTypes: true));
                                }, token);

                        break;
                    }
                    }

                    return(true);
                }
                break;
            }
            }

            return(false);
        }
Пример #9
0
        async Task OnGetScriptSource(MessageId msg_id, string script_id, CancellationToken token)
        {
            var id       = new SourceId(script_id);
            var src_file = store.GetFileById(id);

            var res = new StringWriter();

            //res.WriteLine ($"//{id}");

            try {
                var uri = new Uri(src_file.Url);
                if (uri.IsFile && File.Exists(uri.LocalPath))
                {
                    using (var f = new StreamReader(File.Open(uri.LocalPath, FileMode.Open))) {
                        await res.WriteAsync(await f.ReadToEndAsync());
                    }

                    var o = JObject.FromObject(new {
                        scriptSource = res.ToString()
                    });

                    SendResponse(msg_id, Result.Ok(o), token);
                }
                else if (src_file.SourceUri.IsFile && File.Exists(src_file.SourceUri.LocalPath))
                {
                    using (var f = new StreamReader(File.Open(src_file.SourceUri.LocalPath, FileMode.Open))) {
                        await res.WriteAsync(await f.ReadToEndAsync());
                    }

                    var o = JObject.FromObject(new {
                        scriptSource = res.ToString()
                    });

                    SendResponse(msg_id, Result.Ok(o), token);
                }
                else if (src_file.SourceLinkUri != null)
                {
                    var doc = await new WebClient().DownloadStringTaskAsync(src_file.SourceLinkUri);
                    await res.WriteAsync(doc);

                    var o = JObject.FromObject(new {
                        scriptSource = res.ToString()
                    });

                    SendResponse(msg_id, Result.Ok(o), token);
                }
                else
                {
                    var o = JObject.FromObject(new {
                        scriptSource = $"// Unable to find document {src_file.SourceUri}"
                    });

                    SendResponse(msg_id, Result.Ok(o), token);
                }
            } catch (Exception e) {
                var o = JObject.FromObject(new {
                    scriptSource = $"// Unable to read document ({e.Message})\n" +
                                   $"Local path: {src_file?.SourceUri}\n" +
                                   $"SourceLink path: {src_file?.SourceLinkUri}\n"
                });

                SendResponse(msg_id, Result.Ok(o), token);
            }
        }
Пример #10
0
        async Task GetScopeProperties(MessageId msg_id, int scope_id, CancellationToken token)
        {
            var scope = this.current_callstack.FirstOrDefault(s => s.Id == scope_id);
            var vars  = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset);


            var var_ids = string.Join(",", vars.Select(v => v.Index));

            var o = JObject.FromObject(new {
                expression            = string.Format(MonoCommands.GET_SCOPE_VARIABLES, scope.Id, var_ids),
                objectGroup           = "mono_debugger",
                includeCommandLineAPI = false,
                silent        = false,
                returnByValue = true,
            });

            var res = await SendCommand(msg_id, "Runtime.evaluate", o, token);

            //if we fail we just buble that to the IDE (and let it panic over it)
            if (res.IsErr)
            {
                SendResponse(msg_id, res, token);
                return;
            }

            try {
                var values = res.Value? ["result"]? ["value"]?.Values <JObject> ().ToArray();

                var var_list = new List <JObject> ();
                int i        = 0;
                // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
                // results in a "Memory access out of bounds", causing 'values' to be null,
                // so skip returning variable values in that case.
                while (values != null && i < vars.Length && i < values.Length)
                {
                    var value = values [i] ["value"];
                    if (((string)value ["description"]) == null)
                    {
                        value ["description"] = value ["value"]?.ToString();
                    }

                    var_list.Add(JObject.FromObject(new {
                        name = vars [i].Name,
                        value
                    }));
                    i++;
                }
                //Async methods are special in the way that local variables can be lifted to generated class fields
                //value of "this" comes here either
                while (i < values.Length)
                {
                    String name = values [i] ["name"].ToString();

                    if (name.IndexOf(">", StringComparison.Ordinal) > 0)
                    {
                        name = name.Substring(1, name.IndexOf(">", StringComparison.Ordinal) - 1);
                    }

                    var value = values [i + 1] ["value"];
                    if (((string)value ["description"]) == null)
                    {
                        value ["description"] = value ["value"]?.ToString();
                    }

                    var_list.Add(JObject.FromObject(new {
                        name,
                        value
                    }));
                    i = i + 2;
                }
                o = JObject.FromObject(new {
                    result = var_list
                });
                SendResponse(msg_id, Result.Ok(o), token);
            } catch (Exception exception) {
                Log("verbose", $"Error resolving scope properties {exception.Message}");
                SendResponse(msg_id, res, token);
            }
        }
Пример #11
0
        protected override async Task <bool> AcceptCommand(MessageId id, string method, JObject args, CancellationToken token)
        {
            switch (method)
            {
            case "Target.attachToTarget": {
                break;
            }

            case "Target.attachToBrowserTarget": {
                break;
            }

            case "Debugger.getScriptSource": {
                var script_id = args? ["scriptId"]?.Value <string> ();
                if (script_id.StartsWith("dotnet://", StringComparison.InvariantCultureIgnoreCase))
                {
                    await OnGetScriptSource(id, script_id, token);

                    return(true);
                }
                break;
            }

            case "Runtime.compileScript": {
                var exp = args? ["expression"]?.Value <string> ();
                if (exp.StartsWith("//dotnet:", StringComparison.InvariantCultureIgnoreCase))
                {
                    OnCompileDotnetScript(id, token);
                    return(true);
                }
                break;
            }

            case "Debugger.getPossibleBreakpoints": {
                var start = SourceLocation.Parse(args? ["start"] as JObject);
                //FIXME support variant where restrictToFunction=true and end is omitted
                var end = SourceLocation.Parse(args? ["end"] as JObject);
                if (start != null && end != null)
                {
                    return(GetPossibleBreakpoints(id, start, end, token));
                }
                break;
            }

            case "Debugger.setBreakpointByUrl": {
                Log("info", $"BP req {args}");
                var bp_req = BreakPointRequest.Parse(args, store);
                if (bp_req != null)
                {
                    await SetBreakPoint(id, bp_req, token);

                    return(true);
                }
                break;
            }

            case "Debugger.removeBreakpoint": {
                return(await RemoveBreakpoint(id, args, token));
            }

            case "Debugger.resume": {
                await OnResume(token);

                break;
            }

            case "Debugger.stepInto": {
                if (this.current_callstack != null)
                {
                    await Step(id, StepKind.Into, token);

                    return(true);
                }
                break;
            }

            case "Debugger.stepOut": {
                if (this.current_callstack != null)
                {
                    await Step(id, StepKind.Out, token);

                    return(true);
                }
                break;
            }

            case "Debugger.stepOver": {
                if (this.current_callstack != null)
                {
                    await Step(id, StepKind.Over, token);

                    return(true);
                }
                break;
            }

            case "Runtime.getProperties": {
                var objId = args? ["objectId"]?.Value <string> ();
                if (objId.StartsWith("dotnet:"))
                {
                    var parts = objId.Split(new char [] { ':' });
                    if (parts.Length < 3)
                    {
                        return(true);
                    }
                    switch (parts[1])
                    {
                    case "scope": {
                        await GetScopeProperties(id, int.Parse(parts[2]), token);

                        break;
                    }

                    case "object": {
                        await GetDetails(id, int.Parse(parts[2]), token, MonoCommands.GET_OBJECT_PROPERTIES);

                        break;
                    }

                    case "array": {
                        await GetDetails(id, int.Parse(parts[2]), token, MonoCommands.GET_ARRAY_VALUES);

                        break;
                    }
                    }
                    return(true);
                }
                break;
            }
            }

            return(false);
        }
Пример #12
0
        protected override async Task <bool> AcceptCommand(MessageId id, string method, JObject args, CancellationToken token)
        {
            // Inspector doesn't use the Target domain or sessions
            // so we try to init immediately
            if (hideWebDriver && id == SessionId.Null)
            {
                await DeleteWebDriver(id, token);
            }

            if (!contexts.TryGetValue(id, out var context))
            {
                return(false);
            }

            switch (method)
            {
            case "Target.attachToTarget": {
                    var resp = await SendCommand(id, method, args, token);
                    await DeleteWebDriver(new SessionId (resp.Value ["sessionId"]?.ToString()), token);

                    break;
            }

            case "Debugger.enable": {
                    var resp = await SendCommand(id, method, args, token);

                    context.DebuggerId = resp.Value ["debuggerId"]?.ToString();

                    if (await IsRuntimeAlreadyReadyAlready(id, token))
                    {
                        await RuntimeReady(id, token);
                    }

                    SendResponse(id, resp, token);
                    return(true);
            }

            case "Debugger.getScriptSource": {
                var script = args? ["scriptId"]?.Value <string> ();
                return(await OnGetScriptSource(id, script, token));
            }

            case "Runtime.compileScript": {
                var exp = args? ["expression"]?.Value <string> ();
                if (exp.StartsWith("//dotnet:", StringComparison.Ordinal))
                {
                    OnCompileDotnetScript(id, token);
                    return(true);
                }
                break;
            }

            case "Debugger.getPossibleBreakpoints": {
                var resp = await SendCommand(id, method, args, token);

                if (resp.IsOk && resp.Value["locations"].HasValues)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var start = SourceLocation.Parse(args? ["start"] as JObject);
                //FIXME support variant where restrictToFunction=true and end is omitted
                var end = SourceLocation.Parse(args? ["end"] as JObject);
                if (start != null && end != null && await GetPossibleBreakpoints(id, start, end, token))
                {
                    return(true);
                }

                SendResponse(id, resp, token);
                return(true);
            }

            case "Debugger.setBreakpoint": {
                break;
            }

            case "Debugger.setBreakpointByUrl": {
                var resp = await SendCommand(id, method, args, token);

                if (!resp.IsOk)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var bpid      = resp.Value["breakpointId"]?.ToString();
                var locations = resp.Value["locations"]?.Values <object>();
                var request   = BreakpointRequest.Parse(bpid, args);

                // is the store done loading?
                var loaded = context.Source.Task.IsCompleted;
                if (!loaded)
                {
                    // Send and empty response immediately if not
                    // and register the breakpoint for resolution
                    context.BreakpointRequests [bpid] = request;
                    SendResponse(id, resp, token);
                }

                if (await IsRuntimeAlreadyReadyAlready(id, token))
                {
                    var store = await RuntimeReady(id, token);

                    Log("verbose", $"BP req {args}");
                    await SetBreakpoint(id, store, request, !loaded, token);
                }

                if (loaded)
                {
                    // we were already loaded so we should send a response
                    // with the locations included and register the request
                    context.BreakpointRequests [bpid] = request;
                    var result = Result.OkFromObject(request.AsSetBreakpointByUrlResponse(locations));
                    SendResponse(id, result, token);
                }
                return(true);
            }

            case "Debugger.removeBreakpoint": {
                await RemoveBreakpoint(id, args, token);

                break;
            }

            case "Debugger.resume": {
                await OnResume(id, token);

                break;
            }

            case "Debugger.stepInto": {
                return(await Step(id, StepKind.Into, token));
            }

            case "Debugger.stepOut": {
                return(await Step(id, StepKind.Out, token));
            }

            case "Debugger.stepOver": {
                return(await Step(id, StepKind.Over, token));
            }

            case "Debugger.evaluateOnCallFrame": {
                if (!DotnetObjectId.TryParse(args? ["callFrameId"], out var objectId))
                {
                    return(false);
                }

                switch (objectId.Scheme)
                {
                case "scope":
                    return(await OnEvaluateOnCallFrame(id,
                                                       int.Parse(objectId.Value),
                                                       args? ["expression"]?.Value <string> (), token));

                default:
                    return(false);
                }
            }

            case "Runtime.getProperties": {
                if (!DotnetObjectId.TryParse(args? ["objectId"], out var objectId))
                {
                    break;
                }

                var result = await RuntimeGetProperties(id, objectId, args, token);

                SendResponse(id, result, token);
                return(true);
            }

            case "Runtime.releaseObject": {
                if (!(DotnetObjectId.TryParse(args ["objectId"], out var objectId) && objectId.Scheme == "cfo_res"))
                {
                    break;
                }

                await SendMonoCommand(id, MonoCommands.ReleaseObject(objectId), token);

                SendResponse(id, Result.OkFromObject(new{}), token);
                return(true);
            }

            // Protocol extensions
            case "DotnetDebugger.getMethodLocation": {
                Console.WriteLine("set-breakpoint-by-method: " + id + " " + args);

                var store = await RuntimeReady(id, token);

                string aname      = args ["assemblyName"]?.Value <string> ();
                string typeName   = args ["typeName"]?.Value <string> ();
                string methodName = args ["methodName"]?.Value <string> ();
                if (aname == null || typeName == null || methodName == null)
                {
                    SendResponse(id, Result.Err("Invalid protocol message '" + args + "'."), token);
                    return(true);
                }

                // GetAssemblyByName seems to work on file names
                var assembly = store.GetAssemblyByName(aname);
                if (assembly == null)
                {
                    assembly = store.GetAssemblyByName(aname + ".exe");
                }
                if (assembly == null)
                {
                    assembly = store.GetAssemblyByName(aname + ".dll");
                }
                if (assembly == null)
                {
                    SendResponse(id, Result.Err("Assembly '" + aname + "' not found."), token);
                    return(true);
                }

                var type = assembly.GetTypeByName(typeName);
                if (type == null)
                {
                    SendResponse(id, Result.Err($"Type '{typeName}' not found."), token);
                    return(true);
                }

                var methodInfo = type.Methods.FirstOrDefault(m => m.Name == methodName);
                if (methodInfo == null)
                {
                    SendResponse(id, Result.Err($"Method '{typeName}:{methodName}' not found."), token);
                    return(true);
                }

                var src_url = methodInfo.Assembly.Sources.Single(sf => sf.SourceId == methodInfo.SourceId).Url;
                SendResponse(id, Result.OkFromObject(new {
                        result = new { line = methodInfo.StartLocation.Line, column = methodInfo.StartLocation.Column, url = src_url }
                    }), token);

                return(true);
            }

            case "Runtime.callFunctionOn": {
                if (!DotnetObjectId.TryParse(args ["objectId"], out var objectId))
                {
                    return(false);
                }

                var silent = args ["silent"]?.Value <bool> () ?? false;
                if (objectId.Scheme == "scope")
                {
                    var fail = silent ? Result.OkFromObject(new { result = new { } }) : Result.Exception(new ArgumentException($"Runtime.callFunctionOn not supported with scope ({objectId})."));

                    SendResponse(id, fail, token);
                    return(true);
                }

                var res = await SendMonoCommand(id, MonoCommands.CallFunctionOn(args), token);

                var res_value_type = res.Value? ["result"]? ["value"]?.Type;

                if (res.IsOk && res_value_type == JTokenType.Object || res_value_type == JTokenType.Object)
                {
                    res = Result.OkFromObject(new { result = res.Value ["result"]["value"] });
                }
                else if (res.IsErr && silent)
                {
                    res = Result.OkFromObject(new { result = new { } });
                }

                SendResponse(id, res, token);
                return(true);
            }
            }

            return(false);
        }
Пример #13
0
        protected override async Task <bool> AcceptCommand(MessageId id, string method, JObject args, CancellationToken token)
        {
            // Inspector doesn't use the Target domain or sessions
            // so we try to init immediately
            if (hideWebDriver && id == SessionId.Null)
            {
                await DeleteWebDriver(id, token);
            }

            if (!contexts.TryGetValue(id, out var context))
            {
                return(false);
            }

            switch (method)
            {
            case "Target.attachToTarget": {
                    var resp = await SendCommand(id, method, args, token);
                    await DeleteWebDriver(new SessionId (resp.Value ["sessionId"]?.ToString()), token);

                    break;
            }

            case "Debugger.enable": {
                    var resp = await SendCommand(id, method, args, token);

                    context.DebuggerId = resp.Value ["debuggerId"]?.ToString();

                    if (await IsRuntimeAlreadyReadyAlready(id, token))
                    {
                        await RuntimeReady(id, token);
                    }

                    SendResponse(id, resp, token);
                    return(true);
            }

            case "Debugger.getScriptSource": {
                var script = args? ["scriptId"]?.Value <string> ();
                return(await OnGetScriptSource(id, script, token));
            }

            case "Runtime.compileScript": {
                var exp = args? ["expression"]?.Value <string> ();
                if (exp.StartsWith("//dotnet:", StringComparison.Ordinal))
                {
                    OnCompileDotnetScript(id, token);
                    return(true);
                }
                break;
            }

            case "Debugger.getPossibleBreakpoints": {
                var resp = await SendCommand(id, method, args, token);

                if (resp.IsOk && resp.Value["locations"].HasValues)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var start = SourceLocation.Parse(args? ["start"] as JObject);
                //FIXME support variant where restrictToFunction=true and end is omitted
                var end = SourceLocation.Parse(args? ["end"] as JObject);
                if (start != null && end != null && await GetPossibleBreakpoints(id, start, end, token))
                {
                    return(true);
                }

                SendResponse(id, resp, token);
                return(true);
            }

            case "Debugger.setBreakpoint": {
                break;
            }

            case "Debugger.setBreakpointByUrl": {
                var resp = await SendCommand(id, method, args, token);

                if (!resp.IsOk)
                {
                    SendResponse(id, resp, token);
                    return(true);
                }

                var bpid    = resp.Value["breakpointId"]?.ToString();
                var request = BreakpointRequest.Parse(bpid, args);
                context.BreakpointRequests[bpid] = request;
                if (await IsRuntimeAlreadyReadyAlready(id, token))
                {
                    var store = await RuntimeReady(id, token);

                    Log("verbose", $"BP req {args}");
                    await SetBreakpoint(id, store, request, token);
                }

                SendResponse(id, Result.OkFromObject(request.AsSetBreakpointByUrlResponse()), token);
                return(true);
            }

            case "Debugger.removeBreakpoint": {
                await RemoveBreakpoint(id, args, token);

                break;
            }

            case "Debugger.resume": {
                await OnResume(id, token);

                break;
            }

            case "Debugger.stepInto": {
                return(await Step(id, StepKind.Into, token));
            }

            case "Debugger.stepOut": {
                return(await Step(id, StepKind.Out, token));
            }

            case "Debugger.stepOver": {
                return(await Step(id, StepKind.Over, token));
            }

            case "Debugger.evaluateOnCallFrame": {
                if (!DotnetObjectId.TryParse(args? ["callFrameId"], out var objectId))
                {
                    return(false);
                }

                switch (objectId.Scheme)
                {
                case "scope":
                    return(await OnEvaluateOnCallFrame(id,
                                                       int.Parse(objectId.Value),
                                                       args? ["expression"]?.Value <string> (), token));

                default:
                    return(false);
                }
            }

            case "Runtime.getProperties": {
                if (!DotnetObjectId.TryParse(args? ["objectId"], out var objectId))
                {
                    break;
                }

                var result = await RuntimeGetProperties(id, objectId, args, token);

                SendResponse(id, result, token);
                return(true);
            }

            case "Runtime.releaseObject": {
                if (!(DotnetObjectId.TryParse(args ["objectId"], out var objectId) && objectId.Scheme == "cfo_res"))
                {
                    break;
                }

                await SendMonoCommand(id, new MonoCommands ($"MONO.mono_wasm_release_object('{objectId}')"), token);

                SendResponse(id, Result.OkFromObject(new{}), token);
                return(true);
            }

            // Protocol extensions
            case "Dotnet-test.setBreakpointByMethod": {
                Console.WriteLine("set-breakpoint-by-method: " + id + " " + args);

                var store = await RuntimeReady(id, token);

                string aname      = args ["assemblyName"]?.Value <string> ();
                string typeName   = args ["typeName"]?.Value <string> ();
                string methodName = args ["methodName"]?.Value <string> ();
                if (aname == null || typeName == null || methodName == null)
                {
                    SendResponse(id, Result.Err("Invalid protocol message '" + args + "'."), token);
                    return(true);
                }

                // GetAssemblyByName seems to work on file names
                var assembly = store.GetAssemblyByName(aname);
                if (assembly == null)
                {
                    assembly = store.GetAssemblyByName(aname + ".exe");
                }
                if (assembly == null)
                {
                    assembly = store.GetAssemblyByName(aname + ".dll");
                }
                if (assembly == null)
                {
                    SendResponse(id, Result.Err("Assembly '" + aname + "' not found."), token);
                    return(true);
                }

                var type = assembly.GetTypeByName(typeName);
                if (type == null)
                {
                    SendResponse(id, Result.Err($"Type '{typeName}' not found."), token);
                    return(true);
                }

                var methodInfo = type.Methods.FirstOrDefault(m => m.Name == methodName);
                if (methodInfo == null)
                {
                    SendResponse(id, Result.Err($"Method '{typeName}:{methodName}' not found."), token);
                    return(true);
                }

                bpIdGenerator++;
                string bpid    = "by-method-" + bpIdGenerator.ToString();
                var    request = new BreakpointRequest(bpid, methodInfo);
                context.BreakpointRequests[bpid] = request;

                var loc = methodInfo.StartLocation;
                var bp  = await SetMonoBreakpoint(id, bpid, loc, token);

                if (bp.State != BreakpointState.Active)
                {
                    // FIXME:
                    throw new NotImplementedException();
                }

                var resolvedLocation = new {
                    breakpointId = bpid,
                    location     = loc.AsLocation()
                };

                SendEvent(id, "Debugger.breakpointResolved", JObject.FromObject(resolvedLocation), token);

                SendResponse(id, Result.OkFromObject(new {
                        result = new { breakpointId = bpid, locations = new object [] { loc.AsLocation() } }
                    }), token);

                return(true);
            }

            case "Runtime.callFunctionOn": {
                if (!DotnetObjectId.TryParse(args ["objectId"], out var objectId))
                {
                    return(false);
                }

                var silent = args ["silent"]?.Value <bool> () ?? false;
                if (objectId.Scheme == "scope")
                {
                    var fail = silent ? Result.OkFromObject(new { result = new { } }) : Result.Exception(new ArgumentException($"Runtime.callFunctionOn not supported with scope ({objectId})."));

                    SendResponse(id, fail, token);
                    return(true);
                }

                var returnByValue = args ["returnByValue"]?.Value <bool> () ?? false;
                var cmd           = new MonoCommands($"MONO.mono_wasm_call_function_on ({args.ToString ()}, {(returnByValue ? "true" : "false")})");

                var res = await SendMonoCommand(id, cmd, token);

                if (!returnByValue &&
                    DotnetObjectId.TryParse(res.Value?["result"]?["value"]?["objectId"], out var resultObjectId) &&
                    resultObjectId.Scheme == "cfo_res")
                {
                    res = Result.OkFromObject(new { result = res.Value ["result"]["value"] });
                }

                if (res.IsErr && silent)
                {
                    res = Result.OkFromObject(new { result = new { } });
                }

                SendResponse(id, res, token);
                return(true);
            }
            }

            return(false);
        }