public void JsCanRetrieveBreakpoints()
        {
            using (var runtimeHandle = Engine.JsCreateRuntime(JavaScriptRuntimeAttributes.None, null))
            {
                using (var contextHandle = Engine.JsCreateContext(runtimeHandle))
                {
                    Engine.JsSetCurrentContext(contextHandle);

                    bool called = false;
                    JavaScriptDiagDebugEventCallback callback = (JavaScriptDiagDebugEventType eventType, IntPtr eventData, IntPtr callbackState) =>
                    {
                        called = true;
                        return(true);
                    };

                    Engine.JsDiagStartDebugging(runtimeHandle, callback, IntPtr.Zero);

                    var breakpoints = Engine.JsDiagGetBreakpoints();
                    Assert.True(breakpoints != JavaScriptValueSafeHandle.Invalid);

                    Engine.JsDiagStopDebugging(runtimeHandle);

                    //We didn't specify any breakpoints so...
                    Assert.False(called);
                }
            }
        }
Beispiel #2
0
 internal static extern JavaScriptErrorCode JsDiagStartDebugging(
     JavaScriptRuntime handle,
     JavaScriptDiagDebugEventCallback debugEventCallback,
     IntPtr callbackState);
        public void JsCanBeDebugged()
        {
            string fibonacci = @"
function fibonacci(num){
  var a = 1, b = 0, temp;

  while (num >= 0){
    temp = a;
    a = a + b;
    b = temp;
    num--;
  }

  return b;
};

fibonacci(50);
";

            using (var runtimeHandle = Engine.JsCreateRuntime(JavaScriptRuntimeAttributes.None, null))
            {
                using (var contextHandle = Engine.JsCreateContext(runtimeHandle))
                {
                    Engine.JsSetCurrentContext(contextHandle);

                    //Get the JSON.stringify function for later...
                    var globalObjectHandle      = Engine.JsGetGlobalObject();
                    var jsonPropertyHandle      = Engine.JsCreatePropertyId("JSON", (ulong)"JSON".Length);
                    var stringifyPropertyHandle = Engine.JsCreatePropertyId("stringify", (ulong)"stringify".Length);
                    var jsonObjectHandle        = Engine.JsGetProperty(globalObjectHandle, jsonPropertyHandle);
                    var fnStringifyHandle       = Engine.JsGetProperty(jsonObjectHandle, stringifyPropertyHandle);

                    bool   firstLineHit = false;
                    string fibonacciFunctionPosition = "";
                    string firstFrameProperties      = "";
                    string firstFrameChildren        = "";
                    string firstStackObjectName      = "";

                    var numDecrementing = new List <int>();
                    int calledCount     = 0;

                    //Callback that is run for each breakpoint.
                    JavaScriptDiagDebugEventCallback callback = (JavaScriptDiagDebugEventType eventType, IntPtr eventData, IntPtr callbackState) =>
                    {
                        if (eventType == JavaScriptDiagDebugEventType.JsDiagDebugEventRuntimeException)
                        {
                            //When a problem comes along you must skip it
                            //Before the cream sits out too long you must skip it
                            Engine.JsDiagSetStepType(JavaScriptDiagStepType.StepIn);
                        }

                        var stackTrace = Engine.JsDiagGetStackTrace();

                        //Get the first frame in the stack.
                        var ix             = Engine.JsIntToNumber(0);
                        var objFrameHandle = Engine.JsGetIndexedProperty(stackTrace, ix);

                        //Get the line and the source
                        var linePropertyHandle = Engine.JsCreatePropertyId("line", (ulong)"line".Length);
                        var lineHandle         = Engine.JsGetProperty(objFrameHandle, linePropertyHandle);
                        var line = Engine.JsNumberToInt(lineHandle);

                        var functionHandlePropertyHandle = Engine.JsCreatePropertyId("functionHandle", (ulong)"functionHandle".Length);
                        var functionHandleHandle         = Engine.JsGetProperty(objFrameHandle, functionHandlePropertyHandle);
                        var functionHandle = Engine.JsNumberToInt(functionHandleHandle);

                        var sourceTextPropertyHandle = Engine.JsCreatePropertyId("sourceText", (ulong)"sourceText".Length);
                        var sourceTextHandle         = Engine.JsGetProperty(objFrameHandle, sourceTextPropertyHandle);
                        var sourceLength             = Engine.JsGetStringLength(sourceTextHandle);

                        byte[] buffer     = new byte[sourceLength];
                        var    written    = Engine.JsCopyString(sourceTextHandle, buffer, (ulong)sourceLength);
                        var    sourceText = Encoding.UTF8.GetString(buffer);

                        //If we hit the first line...
                        if (sourceText == "fibonacci(50)")
                        {
                            firstLineHit = true;
                            //Get some information about the stack frame.
                            var stackProperties = Engine.JsDiagGetStackProperties(0);

                            //Stringify the propertyHandle.
                            var stringifiedPropertyHandle = Engine.JsCallFunction(fnStringifyHandle, new IntPtr[] { globalObjectHandle.DangerousGetHandle(), stackProperties.DangerousGetHandle() }, 2);
                            var stringifiedPropertyLength = Engine.JsGetStringLength(stringifiedPropertyHandle);

                            byte[] serializedPropertyBuffer   = new byte[stringifiedPropertyLength];
                            var    stringifiedPropertyWritten = Engine.JsCopyString(stringifiedPropertyHandle, serializedPropertyBuffer, (ulong)stringifiedPropertyLength);
                            firstFrameProperties = Encoding.UTF8.GetString(serializedPropertyBuffer);

                            //Get information about the 'fibonacci' local -- which should be our function.
                            var localsPropertyHandle = Engine.JsCreatePropertyId("locals", (ulong)"locals".Length);
                            var localsHandle         = Engine.JsGetProperty(stackProperties, localsPropertyHandle);
                            var objLocalHandle       = Engine.JsGetIndexedProperty(localsHandle, ix);

                            //I'm not impressed with the naming of these... ;)
                            var handlePropertyHandle = Engine.JsCreatePropertyId("handle", (ulong)"handle".Length);
                            var handleHandle         = Engine.JsGetProperty(objLocalHandle, handlePropertyHandle);
                            var handle = Engine.JsNumberToInt(handleHandle);

                            var objectHandle       = Engine.JsDiagGetObjectFromHandle((uint)handle);
                            var namePropertyHandle = Engine.JsCreatePropertyId("name", (ulong)"name".Length);
                            var nameHandle         = Engine.JsGetProperty(objectHandle, namePropertyHandle);
                            var nameLength         = Engine.JsGetStringLength(nameHandle);

                            byte[] nameBuffer  = new byte[nameLength];
                            var    nameWritten = Engine.JsCopyString(nameHandle, nameBuffer, (ulong)nameLength);
                            firstStackObjectName = Encoding.UTF8.GetString(nameBuffer);

                            //Get the child properties
                            var objectChildrenHandle = Engine.JsDiagGetProperties((uint)handle, 0, 6);

                            //Stringify the children (muahaha).
                            var stringifiedChildrenHandle = Engine.JsCallFunction(fnStringifyHandle, new IntPtr[] { globalObjectHandle.DangerousGetHandle(), objectChildrenHandle.DangerousGetHandle() }, 2);
                            var stringifiedChildrenLength = Engine.JsGetStringLength(stringifiedChildrenHandle);

                            byte[] serializedChildrenBuffer   = new byte[stringifiedChildrenLength];
                            var    stringifiedChildrenWritten = Engine.JsCopyString(stringifiedChildrenHandle, serializedChildrenBuffer, (ulong)stringifiedChildrenLength);
                            firstFrameChildren = Encoding.UTF8.GetString(serializedChildrenBuffer);

                            //Set the step
                            Engine.JsDiagSetStepType(JavaScriptDiagStepType.StepIn);
                        }
                        else if (sourceText == "var a = 1")
                        {
                            //this is our step.
                            //do naught.
                        }
                        else if (sourceText == "throw new Error('we did it jim!')")
                        {
                            //This is the exception we're skipping.
                            //do naught.
                        }
                        //Otherwise...
                        else
                        {
                            var evalScript = Engine.JsCreateString("num", (ulong)"num".Length);
                            var numHandle  = Engine.JsDiagEvaluate(evalScript, 0, JavaScriptParseScriptAttributes.None, false);

                            var valuePropertyHandle = Engine.JsCreatePropertyId("value", (ulong)"value".Length);
                            var valueHandle         = Engine.JsGetProperty(numHandle, valuePropertyHandle);
                            numDecrementing.Add(Engine.JsNumberToInt(valueHandle));
                        }

                        //Increment our call counter (that we're asserting against) and continue.
                        calledCount++;
                        return(true);
                    };

                    var callbackHandle = GCHandle.Alloc(callback);
                    try
                    {
                        using (var ss = new ScriptSource(Engine, fibonacci))
                        {
                            Engine.JsDiagStartDebugging(runtimeHandle, callback, IntPtr.Zero);

                            var scripts = Engine.JsDiagGetScripts();
                            Assert.NotEqual(JavaScriptValueSafeHandle.Invalid, scripts);

                            var ix = Engine.JsIntToNumber(0);
                            var objScriptHandle = Engine.JsGetIndexedProperty(scripts, ix);

                            var handleType = Engine.JsGetValueType(objScriptHandle);
                            Assert.True(handleType == JsValueType.Object);

                            //Not sure if the ScriptId varies independently of the ScriptContext cookie
                            var scriptIdPropertyHandle = Engine.JsCreatePropertyId("scriptId", (ulong)"scriptId".Length);
                            var scriptIdHandle         = Engine.JsGetProperty(objScriptHandle, scriptIdPropertyHandle);

                            var scriptId = Engine.JsNumberToInt(scriptIdHandle);

                            //Assert that we can get the source for the script id.
                            var objSourceHandle      = Engine.JsDiagGetSource((uint)scriptId);
                            var sourcePropertyHandle = Engine.JsCreatePropertyId("source", (ulong)"source".Length);
                            var sourceHandle         = Engine.JsGetProperty(objSourceHandle, sourcePropertyHandle);

                            handleType = Engine.JsGetValueType(sourceHandle);
                            Assert.True(handleType == JsValueType.String);
                            var sourceLength = Engine.JsGetStringLength(sourceHandle);

                            byte[] buffer  = new byte[sourceLength];
                            var    written = Engine.JsCopyString(sourceHandle, buffer, (ulong)sourceLength);
                            var    source  = Encoding.UTF8.GetString(buffer);
                            Assert.Equal(fibonacci, source);


                            //Set a breakpoint with a knkown position
                            var breakPointHandle = Engine.JsDiagSetBreakpoint((uint)scriptId, 5, 0);

                            //Assert that the breakpoint has been set
                            var breakpointsHandle          = Engine.JsDiagGetBreakpoints();
                            var objBreakpointHandle        = Engine.JsGetIndexedProperty(breakpointsHandle, ix);
                            var breakpointIdPropertyHandle = Engine.JsCreatePropertyId("breakpointId", (ulong)"breakpointId".Length);
                            var breakpointIdHandle         = Engine.JsGetProperty(objBreakpointHandle, breakpointIdPropertyHandle);
                            var linePropertyHandle         = Engine.JsCreatePropertyId("line", (ulong)"line".Length);
                            var lineHandle = Engine.JsGetProperty(objBreakpointHandle, linePropertyHandle);

                            var line         = Engine.JsNumberToInt(lineHandle);
                            var breakPointId = Engine.JsNumberToInt(breakpointIdHandle);

                            Assert.Equal(5, line);
                            Assert.Equal(1, breakPointId);

                            //Get/set the break on exception setting.
                            var breakOnExceptionSetting = Engine.JsDiagGetBreakOnException(runtimeHandle);
                            Assert.True(breakOnExceptionSetting == JavaScriptDiagBreakOnExceptionAttributes.Uncaught);

                            Engine.JsDiagSetBreakOnException(runtimeHandle, JavaScriptDiagBreakOnExceptionAttributes.None);
                            breakOnExceptionSetting = Engine.JsDiagGetBreakOnException(runtimeHandle);
                            Assert.True(breakOnExceptionSetting == JavaScriptDiagBreakOnExceptionAttributes.None);

                            //Get the function position
                            var fibonacciFunctionPositionHandle = Engine.JsDiagGetFunctionPosition(ss.FunctionHandle);

                            //Stringify the function position.
                            var stringifiedFibonacciHandle = Engine.JsCallFunction(fnStringifyHandle, new IntPtr[] { globalObjectHandle.DangerousGetHandle(), fibonacciFunctionPositionHandle.DangerousGetHandle() }, 2);
                            var stringifiedFibonacciLength = Engine.JsGetStringLength(stringifiedFibonacciHandle);

                            byte[] serializedFibonacciBuffer   = new byte[stringifiedFibonacciLength];
                            var    stringifiedFibonacciWritten = Engine.JsCopyString(stringifiedFibonacciHandle, serializedFibonacciBuffer, (ulong)stringifiedFibonacciLength);
                            fibonacciFunctionPosition = Encoding.UTF8.GetString(serializedFibonacciBuffer);

                            //Break on the first line
                            Engine.JsDiagRequestAsyncBreak(runtimeHandle);

                            var finalResult = Engine.JsCallFunction(ss.FunctionHandle, new IntPtr[] { ss.FunctionHandle.DangerousGetHandle() }, 1);
                            handleType = Engine.JsGetValueType(finalResult);

                            //Fib = 51, first break = 1, step = 1. 51+1+1 = 53
                            Assert.Equal(53, calledCount);

                            //Remove our previous breakpoint
                            Engine.JsDiagRemoveBreakpoint((uint)breakPointId);

                            //Assert the breakpoint has been removed.
                            breakpointsHandle   = Engine.JsDiagGetBreakpoints();
                            objBreakpointHandle = Engine.JsGetIndexedProperty(breakpointsHandle, ix);
                            handleType          = Engine.JsGetValueType(objBreakpointHandle);
                            Assert.True(handleType == JsValueType.Undefined);

                            Engine.JsDiagStopDebugging(runtimeHandle);
                        }

                        Assert.True(firstLineHit);
                        Assert.False(string.IsNullOrWhiteSpace(fibonacciFunctionPosition));
                        Assert.False(string.IsNullOrWhiteSpace(firstFrameProperties));
                        Assert.False(string.IsNullOrWhiteSpace(firstFrameChildren));
                        Assert.Equal("fibonacci", firstStackObjectName);
                        Assert.True(numDecrementing.Count == 51);
                    }
                    finally
                    {
                        callbackHandle.Free();
                    }
                }
            }
        }
Beispiel #4
0
        public void JsDiagEvaluateReturnsAValue()
        {
            if (DebugWindowsEngine == null)
            {
                return;
            }

            string iterate = @"
var moose = 0;
for(var i = 0; i < 50; i++)
{
    moose++;
}

moose;
";
            int    iPod    = 0;

            using (var runtimeHandle = Engine.JsCreateRuntime(JavaScriptRuntimeAttributes.None, null))
            {
                using (var contextHandle = Engine.JsCreateContext(runtimeHandle))
                {
                    Engine.JsSetCurrentContext(contextHandle);

                    //Callback that is run for each breakpoint.
                    JavaScriptDiagDebugEventCallback callback = (JavaScriptDiagDebugEventType eventType, IntPtr eventData, IntPtr callbackState) =>
                    {
                        var evalScript       = Engine.JsCreateString("i", (ulong)"i".Length);
                        var evalResultHandle = Engine.JsDiagEvaluate(evalScript, 0, JavaScriptParseScriptAttributes.None, false);

                        var handleType = Engine.JsGetValueType(evalResultHandle);
                        Assert.Equal(JsValueType.Object, handleType);

                        var valuePropertyHandle = CommonWindowsEngine.JsGetPropertyIdFromName("value");
                        var valueHandle         = Engine.JsGetProperty(evalResultHandle, valuePropertyHandle);
                        iPod = Engine.JsNumberToInt(valueHandle);
                        evalScript.Dispose();
                        return(true);
                    };

                    var callbackHandle = GCHandle.Alloc(callback);
                    try
                    {
                        using (var ss = new ScriptSource(Engine, iterate))
                        {
                            Engine.JsDiagStartDebugging(runtimeHandle, callback, IntPtr.Zero);

                            var scripts = Engine.JsDiagGetScripts();
                            Assert.NotEqual(JavaScriptValueSafeHandle.Invalid, scripts);

                            var ix = Engine.JsIntToNumber(0);
                            var objScriptHandle        = Engine.JsGetIndexedProperty(scripts, ix);
                            var scriptIdPropertyHandle = CommonWindowsEngine.JsGetPropertyIdFromName("scriptId");
                            var scriptIdHandle         = Engine.JsGetProperty(objScriptHandle, scriptIdPropertyHandle);

                            var scriptId = Engine.JsNumberToInt(scriptIdHandle);

                            //Set a breakpoint with a knkown position
                            var breakPointHandle = Engine.JsDiagSetBreakpoint((uint)scriptId, 4, 0);
                            Assert.NotEqual(JavaScriptValueSafeHandle.Invalid, breakPointHandle);

                            var finalResult = Engine.JsCallFunction(ss.FunctionHandle, new IntPtr[] { ss.FunctionHandle.DangerousGetHandle() }, 1);
                            var handleType  = Engine.JsGetValueType(finalResult);
                            Assert.Equal(JsValueType.Number, handleType);

                            Engine.JsDiagStopDebugging(runtimeHandle);
                        }

                        Assert.Equal(49, iPod);
                    }
                    finally
                    {
                        callbackHandle.Free();
                    }
                }
            }
        }