Exemplo n.º 1
0
        public static void SetGlobalVariable(this IJavaScriptEngine jsrt, string name, JavaScriptValueSafeHandle value)
        {
            var globalObjectHandle = jsrt.JsGetGlobalObject();
            var propertyIdHandle   = default(JavaScriptPropertyIdSafeHandle);

            try
            {
                propertyIdHandle = jsrt.JsCreatePropertyId(name, (ulong)name.Length);
                jsrt.JsSetProperty(globalObjectHandle, propertyIdHandle, value, true);
            }
            finally
            {
                if (propertyIdHandle != default(JavaScriptPropertyIdSafeHandle))
                {
                    propertyIdHandle.Dispose();
                }
                globalObjectHandle.Dispose();
            }
        }
        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();
                    }
                }
            }
        }