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(); } } }
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); } }
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(); } } } }
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(); } } } }
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(); } } }