public void JsParseScriptParsesAndReturnsAFunction()
        {
            if (CommonWindowsEngine == null)
            {
                return;
            }

            var    script    = "(()=>{return 6*7;})()";
            string sourceUrl = "[eval code]";

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

                    var resultHandle = CommonWindowsEngine.JsParseScript(script, JavaScriptSourceContext.GetNextSourceContext(), sourceUrl);

                    Assert.True(resultHandle != JavaScriptValueSafeHandle.Invalid);

                    var handleType = Engine.JsGetValueType(resultHandle);
                    Assert.True(handleType == JsValueType.Function);

                    resultHandle.Dispose();
                }
            }
        }
Beispiel #2
0
        protected JsValue(IJavaScriptEngine engine, BaristaContext context, JavaScriptValueSafeHandle valueHandle)
        {
            m_javaScriptEngine    = engine ?? throw new ArgumentNullException(nameof(engine));
            m_context             = context ?? throw new ArgumentNullException(nameof(context));
            m_javaScriptReference = valueHandle ?? throw new ArgumentNullException(nameof(valueHandle));

            //Let's just make sure that we're the correct type.
            var reportedType = engine.JsGetValueType(valueHandle);

            //if (reportedType != Type)
            //    throw new InvalidOperationException($"The underlying type ({reportedType}) does not match the type that is being created ({Type}). Ensure that correct value is being created.");

            //Set the event that will be called prior to the engine collecting the value.
            if ((this is JsNumber) == false)
            {
                //Set the event that will be called prior to the engine collecting the value.
                JavaScriptObjectBeforeCollectCallback beforeCollectCallback = (IntPtr handle, IntPtr callbackState) =>
                {
                    try
                    {
                        OnBeforeCollect(handle, callbackState);
                    }
                    catch
                    {
                        //Do Nothing.
                    }
                };

                m_beforeCollectCallbackDelegateHandle = GCHandle.Alloc(beforeCollectCallback);
                //An exception thrown here is usually due to trying to associate a callback to a JsNumber.
                Engine.JsSetObjectBeforeCollectCallback(valueHandle, IntPtr.Zero, beforeCollectCallback);
            }
        }
Beispiel #3
0
        public static string GetStringUtf8(this IJavaScriptEngine jsrt, JavaScriptValueSafeHandle stringHandle, bool autoConvert = false)
        {
            bool stringHandleWasCreated = false;

            if (stringHandle == null || stringHandle == JavaScriptValueSafeHandle.Invalid)
            {
                throw new ArgumentNullException(nameof(stringHandle));
            }

            //If Auto Convert is specified, ensure that the type is a string, otherwise convert it.
            if (autoConvert)
            {
                var handleValueType = jsrt.JsGetValueType(stringHandle);

                if (handleValueType != JsValueType.String)
                {
                    var convertedToStringHandle = jsrt.JsConvertValueToString(stringHandle);

                    stringHandle           = convertedToStringHandle;
                    stringHandleWasCreated = true;
                }
            }

            //Get the size
            var size = jsrt.JsCopyString(stringHandle, null, 0);

            if ((int)size > int.MaxValue)
            {
                throw new OutOfMemoryException("Exceeded maximum string length.");
            }

            byte[] result  = new byte[(int)size];
            var    written = jsrt.JsCopyString(stringHandle, result, (ulong)result.Length);

            var strResult = Encoding.UTF8.GetString(result, 0, result.Length);

            if (stringHandleWasCreated)
            {
                stringHandle.Dispose();
            }

            return(strResult);
        }
        /// <summary>
        /// Returns a new JavaScriptValue for the specified handle querying for the handle's value type.
        /// </summary>
        /// <remarks>
        /// Use the valueType parameter carefully. If the resulting type does not match the handle type unexpected issues may occur.
        /// </remarks>
        /// <returns>The JavaScript Value that represents the handle</returns>
        public JsValue CreateValue(JavaScriptValueSafeHandle valueHandle, JsValueType?valueType = null)
        {
            if (valueHandle == JavaScriptValueSafeHandle.Invalid)
            {
                return(null);
            }

            return(m_valuePool.GetOrAdd(valueHandle, () =>
            {
                if (valueType.HasValue == false)
                {
                    valueType = m_engine.JsGetValueType(valueHandle);
                }

                JsValue result;
                switch (valueType.Value)
                {
                case JsValueType.Array:
                    result = new JsArray(m_engine, Context, valueHandle);
                    break;

                case JsValueType.ArrayBuffer:
                    result = new JsArrayBuffer(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Boolean:
                    result = new JsBoolean(m_engine, Context, valueHandle);
                    break;

                case JsValueType.DataView:
                    result = new JsDataView(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Error:
                    result = new JsError(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Function:
                    result = new JsFunction(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Null:
                    result = new JsNull(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Number:
                    result = new JsNumber(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Object:
                    result = new JsObject(m_engine, Context, valueHandle);
                    break;

                case JsValueType.String:
                    result = new JsString(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Symbol:
                    result = new JsSymbol(m_engine, Context, valueHandle);
                    break;

                case JsValueType.TypedArray:
                    result = new JsTypedArray(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Undefined:
                    result = new JsUndefined(m_engine, Context, valueHandle);
                    break;

                default:
                    throw new NotImplementedException($"Error Creating JavaScript Value: The JavaScript Value Type '{valueType}' is unknown, invalid, or has not been implemented.");
                }

                result.BeforeCollect += JsValueBeforeCollectCallback;

                return result;
            }));
        }
        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 #6
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();
                    }
                }
            }
        }
Beispiel #7
0
        public void JsCreateStringTest()
        {
            var str = "Hello, World!";

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

                    var stringHandle = Engine.JsCreateString(str, (ulong)str.Length);

                    Assert.True(stringHandle != JavaScriptValueSafeHandle.Invalid);

                    var handleType = Engine.JsGetValueType(stringHandle);
                    Assert.True(handleType == JsValueType.String);

                    stringHandle.Dispose();
                }
            }
        }