├── src ├── ConsoleHost │ ├── project.json │ ├── app.config │ ├── packages.config.ignore │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Toaster.cs │ ├── ConsoleHost.csproj │ └── Program.cs ├── Microsoft.Scripting.Tests │ ├── project.json │ ├── UnitTest1.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── Microsoft.Scripting.Tests.csproj ├── Microsoft.Scripting │ ├── project.json │ ├── JavaScript │ │ ├── JavaScriptMemoryAllocationEventType.cs │ │ ├── JavaScriptTypedArrayType.cs │ │ ├── JavaScriptValueType.cs │ │ ├── JavaScriptSymbol.cs │ │ ├── JavaScriptExecutionContext.cs │ │ ├── JavaScriptMemoryAllocationEventArgs.cs │ │ ├── SafeHandles │ │ │ ├── JavaScriptEngineSafeHandle.cs │ │ │ ├── JavaScriptRuntimeSafeHandle.cs │ │ │ └── JavaScriptValueSafeHandle.cs │ │ ├── JavaScriptArrayBuffer.cs │ │ ├── JavaScriptTypedArray.cs │ │ ├── JavaScriptRuntimeSettings.cs │ │ ├── JavaScriptFunction.cs │ │ ├── JavaScriptValue.cs │ │ ├── JavaScriptRuntime.cs │ │ ├── JavaScriptArray.cs │ │ ├── JavaScriptDataView.cs │ │ ├── JavaScriptObject.cs │ │ ├── JavaScriptEngine.cs │ │ └── ChakraApi.cs │ ├── InternalExtensions.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── ScriptSource.cs │ ├── Microsoft.Scripting.csproj │ ├── Errors.cs │ └── NativeMethods.cs └── Microsoft.Scripting.sln ├── LICENSE ├── .gitignore └── readme.md /src/ConsoleHost/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "frameworks": { 3 | "net46": {} 4 | }, 5 | "runtimes": { 6 | "win": {} 7 | } 8 | } -------------------------------------------------------------------------------- /src/Microsoft.Scripting.Tests/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "frameworks": { 3 | "net46": {} 4 | }, 5 | "runtimes": { 6 | "win": {} 7 | } 8 | } -------------------------------------------------------------------------------- /src/ConsoleHost/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "supports": { 3 | "net46.app": {}, 4 | "dnxcore50.app": {} 5 | }, 6 | "dependencies": { 7 | "Microsoft.NETCore": "5.0.0", 8 | "Microsoft.NETCore.Portable.Compatibility": "1.0.0" 9 | }, 10 | "frameworks": { 11 | "dotnet": { 12 | "imports": "portable-net452" 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptMemoryAllocationEventType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Scripting.JavaScript 8 | { 9 | public enum JavaScriptMemoryAllocationEventType 10 | { 11 | AllocationRequest, 12 | Free, 13 | Failure, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptTypedArrayType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Scripting.JavaScript 8 | { 9 | public enum JavaScriptTypedArrayType 10 | { 11 | Int8, 12 | Uint8, 13 | Uint8Clamped, 14 | Int16, 15 | Uint16, 16 | Int32, 17 | Uint32, 18 | Float32, 19 | Float64, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptValueType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Scripting.JavaScript 8 | { 9 | public enum JavaScriptValueType 10 | { 11 | Object, 12 | Array, 13 | Boolean, 14 | Date, 15 | Function, 16 | Number, 17 | String, 18 | Undefined, 19 | Symbol, 20 | ArrayBuffer, 21 | TypedArray, 22 | DataView, 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/ConsoleHost/packages.config.ignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/InternalExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Scripting 8 | { 9 | internal static class InternalExtensions 10 | { 11 | public static IEnumerable PrependWith(this IEnumerable sequence, params T[] itemsToPrepend) 12 | { 13 | foreach (var item in itemsToPrepend) 14 | yield return item; 15 | 16 | foreach (var item in sequence) 17 | yield return item; 18 | } 19 | 20 | public static IEnumerable PrependWith(this IEnumerable sequence, T itemToPrepend) 21 | { 22 | yield return itemToPrepend; 23 | 24 | foreach (var item in sequence) 25 | yield return item; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptSymbol.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Scripting.JavaScript 9 | { 10 | public class JavaScriptSymbol : JavaScriptValue 11 | { 12 | internal JavaScriptSymbol(JavaScriptValueSafeHandle handle, JavaScriptValueType type, JavaScriptEngine engine): 13 | base(handle, type, engine) 14 | { 15 | 16 | } 17 | 18 | public string Description 19 | { 20 | get 21 | { 22 | throw new NotImplementedException("Converting a symbol to a string in the host is not currently supported. To get the Symbol's description, if there is one, request it via toString in script."); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Rob Paveza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("Microsoft.Scripting")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("Microsoft.Scripting")] 14 | [assembly: AssemblyCopyright("Copyright © 2015")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: NeutralResourcesLanguage("en")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.0.0")] 30 | [assembly: AssemblyFileVersion("1.0.0.0")] 31 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptExecutionContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Scripting.JavaScript 9 | { 10 | public sealed class JavaScriptExecutionContext : IDisposable 11 | { 12 | private JavaScriptEngine engine_; 13 | private Action release_; 14 | 15 | internal JavaScriptExecutionContext(JavaScriptEngine engine, Action release) 16 | { 17 | Debug.Assert(engine != null); 18 | Debug.Assert(release != null); 19 | 20 | engine_ = engine; 21 | release_ = release; 22 | } 23 | 24 | public void Dispose() 25 | { 26 | Dispose(true); 27 | } 28 | 29 | ~JavaScriptExecutionContext() 30 | { 31 | Dispose(false); 32 | } 33 | 34 | private void Dispose(bool disposing) 35 | { 36 | if (release_ != null) 37 | release_(); 38 | 39 | if (disposing) 40 | { 41 | engine_ = null; 42 | release_ = null; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Microsoft.Scripting.JavaScript; 4 | 5 | namespace Microsoft.Scripting.Tests 6 | { 7 | [TestClass] 8 | public class UnitTest1 9 | { 10 | [TestMethod] 11 | public void TestMethod1() 12 | { 13 | bool ok = false; 14 | JavaScriptCallableFunction callback = (eng, construct, thisObj, args) => 15 | { 16 | ok = true; 17 | return eng.UndefinedValue; 18 | }; 19 | 20 | using (var rt = new JavaScriptRuntime()) 21 | { 22 | rt.MemoryChanging += Rt_MemoryChanging; 23 | using (var eng = rt.CreateEngine()) 24 | { 25 | eng.SetGlobalFunction("hitIt", callback); 26 | 27 | eng.Execute(new ScriptSource("[eval code]", "hitIt();")); 28 | } 29 | } 30 | Assert.IsTrue(ok); 31 | } 32 | 33 | private void Rt_MemoryChanging(object sender, JavaScriptMemoryAllocationEventArgs e) 34 | { 35 | System.Diagnostics.Debugger.Log(0, "Log", $"Allocation/Change: {e.Type} :: {e.Amount}"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptMemoryAllocationEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Scripting.JavaScript 8 | { 9 | public sealed class JavaScriptMemoryAllocationEventArgs 10 | { 11 | private bool cancelled_; 12 | 13 | internal JavaScriptMemoryAllocationEventArgs(UIntPtr amount, JavaScriptMemoryAllocationEventType type) 14 | { 15 | Amount = amount; 16 | Type = type; 17 | } 18 | 19 | public UIntPtr Amount 20 | { 21 | get; 22 | private set; 23 | } 24 | 25 | public JavaScriptMemoryAllocationEventType Type 26 | { 27 | get; 28 | private set; 29 | } 30 | 31 | public bool Cancel 32 | { 33 | get { return cancelled_; } 34 | set 35 | { 36 | // once one event listener cancels, it's cancelled. 37 | cancelled_ |= value; 38 | } 39 | } 40 | 41 | public bool IsCancelable 42 | { 43 | get { return Type == JavaScriptMemoryAllocationEventType.AllocationRequest; } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/SafeHandles/JavaScriptEngineSafeHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Scripting.JavaScript.SafeHandles 10 | { 11 | internal class JavaScriptEngineSafeHandle : SafeHandle 12 | { 13 | public JavaScriptEngineSafeHandle() : 14 | base(IntPtr.Zero, ownsHandle: true) 15 | { 16 | 17 | } 18 | 19 | public JavaScriptEngineSafeHandle(IntPtr handle): 20 | base(handle, true) 21 | { 22 | 23 | } 24 | 25 | public override bool IsInvalid 26 | { 27 | get 28 | { 29 | return handle == IntPtr.Zero; 30 | } 31 | } 32 | 33 | protected override bool ReleaseHandle() 34 | { 35 | if (IsInvalid) 36 | return false; 37 | 38 | uint count; 39 | var error = ChakraApi.Instance.JsRelease(handle, out count); 40 | 41 | Debug.Assert(error == JsErrorCode.JsNoError); 42 | return true; 43 | } 44 | 45 | public static readonly JavaScriptEngineSafeHandle Invalid = new JavaScriptEngineSafeHandle(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/SafeHandles/JavaScriptRuntimeSafeHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Scripting.JavaScript.SafeHandles 10 | { 11 | internal sealed class JavaScriptRuntimeSafeHandle : SafeHandle 12 | { 13 | public JavaScriptRuntimeSafeHandle(): 14 | base(IntPtr.Zero, ownsHandle: true) 15 | { 16 | 17 | } 18 | 19 | public JavaScriptRuntimeSafeHandle(IntPtr handle): 20 | base(handle, true) 21 | { 22 | 23 | } 24 | 25 | public override bool IsInvalid 26 | { 27 | get 28 | { 29 | return handle == IntPtr.Zero; 30 | } 31 | } 32 | 33 | protected override bool ReleaseHandle() 34 | { 35 | if (IsInvalid) 36 | return false; 37 | 38 | var toRelease = this.handle; 39 | 40 | var error = ChakraApi.Instance.JsReleaseCurrentContext(); 41 | Debug.Assert(error == JsErrorCode.JsNoError); 42 | 43 | error = ChakraApi.Instance.JsDisposeRuntime(toRelease); 44 | Debug.Assert(error == JsErrorCode.JsNoError); 45 | return true; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/ConsoleHost/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ConsoleHost")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ConsoleHost")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9cdb2c68-d1e1-419e-90bc-b7d219ddf69b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/SafeHandles/JavaScriptValueSafeHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Scripting.JavaScript.SafeHandles 10 | { 11 | internal class JavaScriptValueSafeHandle : SafeHandle 12 | { 13 | private WeakReference engine_; 14 | 15 | public JavaScriptValueSafeHandle(): 16 | base(IntPtr.Zero, ownsHandle: true) 17 | { 18 | 19 | } 20 | 21 | public JavaScriptValueSafeHandle(IntPtr handle): 22 | base(handle, true) 23 | { 24 | 25 | } 26 | 27 | internal void SetEngine(JavaScriptEngine engine) 28 | { 29 | Debug.Assert(engine != null); 30 | 31 | engine_ = new WeakReference(engine); 32 | } 33 | 34 | public override bool IsInvalid 35 | { 36 | get 37 | { 38 | return handle == IntPtr.Zero; 39 | } 40 | } 41 | 42 | protected override bool ReleaseHandle() 43 | { 44 | if (IsInvalid || engine_ == null) 45 | return false; 46 | 47 | JavaScriptEngine eng; 48 | if (engine_.TryGetTarget(out eng)) 49 | { 50 | eng.EnqueueRelease(handle); 51 | return true; 52 | } 53 | 54 | return false; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Microsoft.Scripting.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Microsoft.Scripting.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("51a6516b-762f-4a4d-a06d-47b328db9054")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/ScriptSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Scripting 9 | { 10 | public class ScriptSource 11 | { 12 | private static IntPtr sourceContextId = IntPtr.Zero; 13 | 14 | public ScriptSource(string sourceLocation, string sourceText) 15 | { 16 | if (null == sourceLocation) 17 | throw new ArgumentNullException(nameof(sourceLocation)); 18 | if (null == sourceText) 19 | throw new ArgumentNullException(nameof(sourceText)); 20 | 21 | SourceLocation = sourceLocation; 22 | SourceText = sourceText; 23 | 24 | while (true) 25 | { 26 | IntPtr mySrcContextId = sourceContextId; 27 | IntPtr incremented = (sourceContextId + 1); 28 | 29 | Interlocked.CompareExchange(ref sourceContextId, incremented, mySrcContextId); 30 | if (sourceContextId == incremented) 31 | { 32 | SourceContextId = mySrcContextId; 33 | break; 34 | } 35 | } 36 | } 37 | 38 | public string SourceLocation 39 | { 40 | get; 41 | private set; 42 | } 43 | 44 | public string SourceText 45 | { 46 | get; 47 | private set; 48 | } 49 | 50 | internal IntPtr SourceContextId 51 | { 52 | get; 53 | private set; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptArrayBuffer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Scripting.JavaScript 10 | { 11 | public sealed class JavaScriptArrayBuffer : JavaScriptObject 12 | { 13 | private Lazy len_; 14 | 15 | internal JavaScriptArrayBuffer(JavaScriptValueSafeHandle handle, JavaScriptValueType type, JavaScriptEngine engine): 16 | base(handle, type, engine) 17 | { 18 | len_ = new Lazy(GetLength); 19 | } 20 | 21 | private uint GetLength() 22 | { 23 | var eng = GetEngine(); 24 | IntPtr buffer; 25 | uint len; 26 | Errors.ThrowIfIs(api_.JsGetArrayBufferStorage(handle_, out buffer, out len)); 27 | 28 | return len; 29 | } 30 | 31 | public uint ByteLength 32 | { 33 | get 34 | { 35 | return len_.Value; 36 | } 37 | } 38 | 39 | public unsafe Stream GetUnderlyingMemory() 40 | { 41 | var eng = GetEngine(); 42 | IntPtr buffer; 43 | uint len; 44 | Errors.ThrowIfIs(api_.JsGetArrayBufferStorage(handle_, out buffer, out len)); 45 | 46 | return new UnmanagedMemoryStream((byte*)buffer.ToPointer(), len); 47 | } 48 | 49 | internal unsafe Tuple GetUnderlyingMemoryInfo() 50 | { 51 | var eng = GetEngine(); 52 | IntPtr buffer; 53 | uint len; 54 | Errors.ThrowIfIs(api_.JsGetArrayBufferStorage(handle_, out buffer, out len)); 55 | 56 | return Tuple.Create(buffer, len); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting", "Microsoft.Scripting\Microsoft.Scripting.csproj", "{1BB54949-D911-4A97-9715-608EA27FDC93}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting.Tests", "Microsoft.Scripting.Tests\Microsoft.Scripting.Tests.csproj", "{51A6516B-762F-4A4D-A06D-47B328DB9054}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleHost", "ConsoleHost\ConsoleHost.csproj", "{9CDB2C68-D1E1-419E-90BC-B7D219DDF69B}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {1BB54949-D911-4A97-9715-608EA27FDC93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {1BB54949-D911-4A97-9715-608EA27FDC93}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {1BB54949-D911-4A97-9715-608EA27FDC93}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {1BB54949-D911-4A97-9715-608EA27FDC93}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {51A6516B-762F-4A4D-A06D-47B328DB9054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {51A6516B-762F-4A4D-A06D-47B328DB9054}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {51A6516B-762F-4A4D-A06D-47B328DB9054}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {51A6516B-762F-4A4D-A06D-47B328DB9054}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {9CDB2C68-D1E1-419E-90BC-B7D219DDF69B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {9CDB2C68-D1E1-419E-90BC-B7D219DDF69B}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {9CDB2C68-D1E1-419E-90BC-B7D219DDF69B}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {9CDB2C68-D1E1-419E-90BC-B7D219DDF69B}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /src/ConsoleHost/Toaster.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace ConsoleHost 9 | { 10 | public class Toaster 11 | { 12 | public virtual void StartToasting() 13 | { 14 | Timer t = null; 15 | t = new Timer((s) => 16 | { 17 | OnToastCompleted(); 18 | t.Dispose(); 19 | }, null, 1500, 1500); 20 | } 21 | 22 | public event EventHandler ToastCompleted; 23 | protected virtual void OnToastCompleted() 24 | { 25 | var tc = ToastCompleted; 26 | if (tc != null) 27 | { 28 | tc(this, EventArgs.Empty); 29 | } 30 | } 31 | } 32 | 33 | public class ToasterOven : Toaster 34 | { 35 | private int count_; 36 | private Timer t_; 37 | public override void StartToasting() 38 | { 39 | if (t_ != null) 40 | throw new Exception("Already toasting."); 41 | 42 | t_ = new Timer(ServiceTimer, null, 1000, 1000); 43 | } 44 | 45 | private bool servicing_ = false; 46 | private void ServiceTimer(object state) 47 | { 48 | if (servicing_) 49 | return; 50 | 51 | servicing_ = true; 52 | OnToastCompleted(); 53 | count_++; 54 | if (count_ % 10 == 0) 55 | { 56 | OnLoafToasted(new Loaf { PiecesCookied = 10 }); 57 | } 58 | servicing_ = false; 59 | } 60 | 61 | public void StopToasting() 62 | { 63 | t_.Dispose(); 64 | t_ = null; 65 | } 66 | 67 | public event EventHandler LoafToasted; 68 | protected virtual void OnLoafToasted(Loaf loaf) 69 | { 70 | var eh = LoafToasted; 71 | if (eh != null) 72 | eh(this, loaf); 73 | } 74 | } 75 | 76 | public class Loaf 77 | { 78 | public int PiecesCookied { get; set; } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptTypedArray.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.Scripting.JavaScript 11 | { 12 | public sealed class JavaScriptTypedArray : JavaScriptObject 13 | { 14 | private Lazy arrayType_; 15 | internal JavaScriptTypedArray(JavaScriptValueSafeHandle handle, JavaScriptValueType type, JavaScriptEngine engine): 16 | base(handle, type, engine) 17 | { 18 | arrayType_ = new Lazy(GetArrayType); 19 | } 20 | 21 | public JavaScriptArrayBuffer Buffer 22 | { 23 | get 24 | { 25 | return GetPropertyByName("buffer") as JavaScriptArrayBuffer; 26 | } 27 | } 28 | 29 | public uint ByteLength 30 | { 31 | get 32 | { 33 | var eng = GetEngine(); 34 | var val = GetPropertyByName("byteLength"); 35 | return (uint)eng.Converter.ToDouble(val); 36 | } 37 | } 38 | 39 | public uint ByteOffset 40 | { 41 | get 42 | { 43 | var eng = GetEngine(); 44 | var val = GetPropertyByName("byteOffset"); 45 | return (uint)eng.Converter.ToDouble(val); 46 | } 47 | } 48 | 49 | public unsafe Stream GetUnderlyingMemory() 50 | { 51 | var buf = Buffer; 52 | Debug.Assert(buf != null); 53 | 54 | var mem = buf.GetUnderlyingMemoryInfo(); 55 | byte* pMem = (byte*)mem.Item1.ToPointer(); 56 | 57 | return new UnmanagedMemoryStream(pMem + ByteOffset, ByteLength); 58 | } 59 | 60 | public uint Length 61 | { 62 | get 63 | { 64 | var eng = GetEngine(); 65 | var val = GetPropertyByName("length"); 66 | return (uint)eng.Converter.ToDouble(val); 67 | } 68 | } 69 | 70 | public JavaScriptTypedArrayType ArrayType 71 | { 72 | get 73 | { 74 | return arrayType_.Value; 75 | } 76 | } 77 | 78 | private JavaScriptTypedArrayType GetArrayType() 79 | { 80 | GetEngine(); 81 | IntPtr buf; 82 | uint len; 83 | JavaScriptTypedArrayType type; 84 | int elemSize; 85 | 86 | Errors.ThrowIfIs(api_.JsGetTypedArrayStorage(handle_, out buf, out len, out type, out elemSize)); 87 | 88 | return type; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptRuntimeSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Scripting.JavaScript 8 | { 9 | public sealed class JavaScriptRuntimeSettings 10 | { 11 | private bool backgroundWork_; 12 | private bool allowScriptInterrupt_; 13 | private bool enableIdle_; 14 | private bool disableNativeCode_; 15 | private bool disableEval_; 16 | private bool used_; 17 | 18 | 19 | public JavaScriptRuntimeSettings() 20 | { 21 | 22 | } 23 | 24 | public bool DisableBackgroundWork 25 | { 26 | get { return backgroundWork_; } 27 | set 28 | { 29 | if (used_) 30 | throw new InvalidOperationException(Errors.NoMutateJsRuntimeSettings); 31 | 32 | backgroundWork_ = value; 33 | } 34 | } 35 | 36 | public bool AllowScriptInterrupt 37 | { 38 | get { return allowScriptInterrupt_; } 39 | set 40 | { 41 | if (used_) 42 | throw new InvalidOperationException(Errors.NoMutateJsRuntimeSettings); 43 | 44 | allowScriptInterrupt_ = value; 45 | } 46 | } 47 | 48 | public bool EnableIdle 49 | { 50 | get { return enableIdle_; } 51 | set 52 | { 53 | if (used_) 54 | throw new InvalidOperationException(Errors.NoMutateJsRuntimeSettings); 55 | 56 | enableIdle_ = value; 57 | } 58 | } 59 | 60 | public bool DisableNativeCode 61 | { 62 | get { return disableNativeCode_; } 63 | set 64 | { 65 | if (used_) 66 | throw new InvalidOperationException(Errors.NoMutateJsRuntimeSettings); 67 | 68 | disableNativeCode_ = value; 69 | } 70 | } 71 | 72 | public bool DisableEval 73 | { 74 | get { return disableEval_; } 75 | set 76 | { 77 | if (used_) 78 | throw new InvalidOperationException(Errors.NoMutateJsRuntimeSettings); 79 | 80 | disableEval_ = value; 81 | } 82 | } 83 | 84 | internal bool Used 85 | { 86 | get { return used_; } 87 | set 88 | { 89 | used_ = value; 90 | } 91 | } 92 | 93 | internal JsRuntimeAttributes GetRuntimeAttributes() 94 | { 95 | var result = JsRuntimeAttributes.None; 96 | if (backgroundWork_) 97 | result |= JsRuntimeAttributes.DisableBackgroundWork; 98 | if (allowScriptInterrupt_) 99 | result |= JsRuntimeAttributes.AllowScriptInterrupt; 100 | if (enableIdle_) 101 | result |= JsRuntimeAttributes.EnableIdleProcessing; 102 | if (disableNativeCode_) 103 | result |= JsRuntimeAttributes.DisableNativeCodeGeneration; 104 | if (disableEval_) 105 | result |= JsRuntimeAttributes.DisableEval; 106 | 107 | return result; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/Microsoft.Scripting.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14.0 6 | Debug 7 | AnyCPU 8 | {1BB54949-D911-4A97-9715-608EA27FDC93} 9 | Library 10 | Properties 11 | Microsoft.Scripting 12 | Microsoft.Scripting 13 | en-US 14 | 512 15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | 17 | 18 | v5.0 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | true 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 77 | -------------------------------------------------------------------------------- /src/ConsoleHost/ConsoleHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9CDB2C68-D1E1-419E-90BC-B7D219DDF69B} 8 | Exe 9 | Properties 10 | ConsoleHost 11 | ConsoleHost 12 | v4.6 13 | 512 14 | 15 | publish\ 16 | true 17 | Disk 18 | false 19 | Foreground 20 | 7 21 | Days 22 | false 23 | false 24 | true 25 | 0 26 | 1.0.0.%2a 27 | false 28 | false 29 | true 30 | 31 | 32 | AnyCPU 33 | true 34 | full 35 | false 36 | bin\Debug\ 37 | DEBUG;TRACE 38 | prompt 39 | 4 40 | false 41 | 42 | 43 | win 44 | 45 | $(NuGetRuntimeIdentifier) 46 | 47 | 48 | AnyCPU 49 | pdbonly 50 | true 51 | bin\Release\ 52 | TRACE 53 | prompt 54 | 4 55 | false 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | {1bb54949-d911-4a97-9715-608ea27fdc93} 74 | Microsoft.Scripting 75 | 76 | 77 | 78 | 79 | False 80 | Microsoft .NET Framework 4.6 %28x86 and x64%29 81 | true 82 | 83 | 84 | False 85 | .NET Framework 3.5 SP1 86 | false 87 | 88 | 89 | 90 | 97 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptFunction.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Dynamic; 8 | 9 | namespace Microsoft.Scripting.JavaScript 10 | { 11 | public sealed class JavaScriptFunction : JavaScriptObject 12 | { 13 | internal JavaScriptFunction(JavaScriptValueSafeHandle handle, JavaScriptValueType type, JavaScriptEngine engine): 14 | base(handle, type, engine) 15 | { 16 | 17 | } 18 | 19 | public JavaScriptValue Invoke(IEnumerable args) 20 | { 21 | var argsArray = args.PrependWith(this).Select(val => val.handle_.DangerousGetHandle()).ToArray(); 22 | if (argsArray.Length > ushort.MaxValue) 23 | throw new ArgumentOutOfRangeException(nameof(args)); 24 | 25 | var eng = GetEngine(); 26 | JavaScriptValueSafeHandle resultHandle; 27 | Errors.CheckForScriptExceptionOrThrow(api_.JsCallFunction(handle_, argsArray, (ushort)argsArray.Length, out resultHandle), eng); 28 | if (resultHandle.IsInvalid) 29 | return eng.UndefinedValue; 30 | 31 | return eng.CreateValueFromHandle(resultHandle); 32 | } 33 | 34 | public JavaScriptObject Construct(IEnumerable args) 35 | { 36 | var argsArray = args.PrependWith(this).Select(val => val.handle_.DangerousGetHandle()).ToArray(); 37 | if (argsArray.Length > ushort.MaxValue) 38 | throw new ArgumentOutOfRangeException(nameof(args)); 39 | 40 | var eng = GetEngine(); 41 | JavaScriptValueSafeHandle resultHandle; 42 | Errors.CheckForScriptExceptionOrThrow(api_.JsConstructObject(handle_, argsArray, (ushort)argsArray.Length, out resultHandle), eng); 43 | if (resultHandle.IsInvalid) 44 | return eng.NullValue; 45 | 46 | return eng.CreateObjectFromHandle(resultHandle); 47 | } 48 | 49 | public JavaScriptFunction Bind(JavaScriptObject thisObject, IEnumerable args) 50 | { 51 | var eng = GetEngine(); 52 | 53 | if (thisObject == null) 54 | thisObject = eng.NullValue; 55 | if (args == null) 56 | args = Enumerable.Empty(); 57 | 58 | var bindFn = GetBuiltinFunctionProperty("bind", "Function.prototype.bind"); 59 | return bindFn.Invoke(args.PrependWith(thisObject)) as JavaScriptFunction; 60 | } 61 | 62 | public JavaScriptValue Apply(JavaScriptObject thisObject, JavaScriptArray args = null) 63 | { 64 | var eng = GetEngine(); 65 | if (thisObject == null) 66 | thisObject = eng.NullValue; 67 | 68 | var applyFn = GetBuiltinFunctionProperty("apply", "Function.prototype.apply"); 69 | 70 | List resultList = new List(); 71 | resultList.Add(thisObject); 72 | if (args != null) 73 | resultList.Add(args); 74 | 75 | return applyFn.Invoke(resultList); 76 | } 77 | 78 | public JavaScriptValue Call(JavaScriptObject thisObject, IEnumerable args) 79 | { 80 | var eng = GetEngine(); 81 | if (thisObject == null) 82 | thisObject = eng.NullValue; 83 | 84 | if (args == null) 85 | args = Enumerable.Empty(); 86 | 87 | var argsArray = args.PrependWith(thisObject).Select(v => v.handle_.DangerousGetHandle()).ToArray(); 88 | JavaScriptValueSafeHandle result; 89 | Errors.CheckForScriptExceptionOrThrow(api_.JsCallFunction(handle_, argsArray, unchecked((ushort)argsArray.Length), out result), eng); 90 | return eng.CreateValueFromHandle(result); 91 | } 92 | 93 | #region DynamicObject overrides 94 | public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) 95 | { 96 | var e = GetEngine(); 97 | var c = e.Converter; 98 | result = Invoke(args.Select(a => c.FromObject(a))); 99 | 100 | return true; 101 | } 102 | 103 | public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result) 104 | { 105 | var e = GetEngine(); 106 | var c = e.Converter; 107 | result = Construct(args.Select(a => c.FromObject(a))); 108 | 109 | return true; 110 | } 111 | #endregion 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/ConsoleHost/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace ConsoleHost 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | using (var runtime = new JavaScriptRuntime()) 14 | { 15 | runtime.MemoryChanging += Runtime_MemoryChanging; 16 | 17 | using (var engine = runtime.CreateEngine()) 18 | { 19 | using (var context = engine.AcquireContext()) 20 | { 21 | engine.SetGlobalFunction("echo", Echo); 22 | engine.AddTypeToGlobal(); 23 | engine.AddTypeToGlobal(); 24 | engine.AddTypeToGlobal(); 25 | engine.AddTypeToGlobal(); 26 | var pt = new Point3D { X = 18, Y = 27, Z = -1 }; 27 | //engine.SetGlobalVariable("pt", engine.Converter.FromObject(pt)); 28 | engine.RuntimeExceptionRaised += (sender, e) => 29 | { 30 | dynamic error = engine.GetAndClearException(); 31 | dynamic glob = engine.GlobalObject; 32 | var color = Console.ForegroundColor; 33 | Console.ForegroundColor = ConsoleColor.Red; 34 | var err = glob.JSON.stringify(error); 35 | if ((string)err == "{}") 36 | err = engine.Converter.ToString(error); 37 | Console.WriteLine("Script error occurred: {0}", (string)err); 38 | Console.ForegroundColor = color; 39 | }; 40 | 41 | var fn = engine.EvaluateScriptText(@"(function() { 42 | var t = new ToasterOven(); 43 | t.addEventListener('toastcompleted', function(e) { 44 | echo('Toast is done!'); 45 | echo('{0}', JSON.stringify(e)); 46 | }); 47 | t.addEventListener('loaftoasted', function(e) { 48 | echo('Loaf is done!'); 49 | echo('{0}', JSON.stringify(e.e)); 50 | echo('Cooked {0} pieces', e.e.PiecesCookied); 51 | }); 52 | t.StartToasting(); 53 | 54 | var o = new Point3D(1, 2, 3); 55 | echo(o.toString()); 56 | o.X = 254; 57 | echo('{0}', o.X); 58 | o.Y = 189; 59 | o.Z = -254.341; 60 | echo('o after mutation? {0}', o.ToString()); 61 | echo('{0}, {1}!', 'Hello', 'world'); 62 | //echo('{0}', pt.X); 63 | //echo('{0}', pt.Y); 64 | //echo('{0}', pt.ToString()); 65 | //pt.Y = 207; 66 | //echo('{0}', pt.ToString()); 67 | })();"); 68 | fn.Invoke(Enumerable.Empty()); 69 | 70 | dynamic fnAsDynamic = fn; 71 | fnAsDynamic.foo = 24; 72 | dynamic global = engine.GlobalObject; 73 | global.echo("{0}, {1}, via dynamic!", "Hey there", "world"); 74 | 75 | dynamic echo = global.echo; 76 | echo("Whoa, {0}, that {1} {2}???", "world", "really", "worked"); 77 | 78 | foreach (dynamic name in global.Object.getOwnPropertyNames(global)) 79 | { 80 | echo(name); 81 | } 82 | 83 | } // release context 84 | 85 | Console.ReadLine(); 86 | } 87 | } 88 | } 89 | 90 | private static void Runtime_MemoryChanging(object sender, JavaScriptMemoryAllocationEventArgs e) 91 | { 92 | Console.WriteLine($"Allocation/Change: {e.Type} :: {e.Amount}"); 93 | } 94 | 95 | static JavaScriptValue Echo(JavaScriptEngine engine, bool construct, JavaScriptValue thisValue, IEnumerable arguments) 96 | { 97 | string fmt = arguments.First().ToString(); 98 | object[] args = (object[])arguments.Skip(1).ToArray(); 99 | Console.WriteLine(fmt, args); 100 | return engine.UndefinedValue; 101 | } 102 | } 103 | 104 | public class Point 105 | { 106 | public double X 107 | { 108 | get; 109 | set; 110 | } 111 | 112 | public double Y 113 | { 114 | get; 115 | set; 116 | } 117 | 118 | public override string ToString() 119 | { 120 | return $"({X}, {Y})"; 121 | } 122 | } 123 | 124 | public class Point3D : Point 125 | { 126 | public double Z 127 | { 128 | get; 129 | set; 130 | } 131 | 132 | public override string ToString() 133 | { 134 | return $"({X}, {Y}, {Z})"; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting.Tests/Microsoft.Scripting.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {51A6516B-762F-4A4D-A06D-47B328DB9054} 7 | Library 8 | Properties 9 | Microsoft.Scripting.Tests 10 | Microsoft.Scripting.Tests 11 | v4.6 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | false 30 | 31 | 32 | pdbonly 33 | true 34 | bin\Release\ 35 | TRACE 36 | prompt 37 | 4 38 | false 39 | 40 | 41 | win 42 | 43 | $(NuGetRuntimeIdentifier) 44 | 45 | 46 | 47 | 3.5 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {1bb54949-d911-4a97-9715-608ea27fdc93} 69 | Microsoft.Scripting 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | False 80 | 81 | 82 | False 83 | 84 | 85 | False 86 | 87 | 88 | False 89 | 90 | 91 | 92 | 93 | 94 | 95 | 102 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptValue.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Dynamic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Linq.Expressions; 10 | 11 | namespace Microsoft.Scripting.JavaScript 12 | { 13 | public class JavaScriptValue : DynamicObject, IDisposable 14 | { 15 | internal JavaScriptValueSafeHandle handle_; 16 | internal JavaScriptValueType type_; 17 | internal WeakReference engine_; 18 | internal ChakraApi api_; 19 | 20 | internal JavaScriptEngine GetEngine() 21 | { 22 | JavaScriptEngine result; 23 | if (!engine_.TryGetTarget(out result)) 24 | throw new ObjectDisposedException(nameof(JavaScriptEngine)); 25 | 26 | return result; 27 | } 28 | 29 | internal JavaScriptValue(JavaScriptValueSafeHandle handle, JavaScriptValueType type, JavaScriptEngine engine) 30 | { 31 | Debug.Assert(handle != null); 32 | Debug.Assert(engine != null); 33 | Debug.Assert(Enum.IsDefined(typeof(JavaScriptValueType), type)); 34 | handle.SetEngine(engine); 35 | api_ = engine.Api; 36 | 37 | uint count; 38 | Errors.ThrowIfIs(api_.JsAddRef(handle.DangerousGetHandle(), out count)); 39 | 40 | handle_ = handle; 41 | type_ = type; 42 | engine_ = new WeakReference(engine); 43 | } 44 | 45 | public override string ToString() 46 | { 47 | var engine = GetEngine(); 48 | return engine.Converter.ToString(this); 49 | } 50 | 51 | public JavaScriptValueType Type 52 | { 53 | get { return type_; } 54 | } 55 | 56 | public bool IsTruthy 57 | { 58 | get 59 | { 60 | var engine = GetEngine(); 61 | return engine.Converter.ToBoolean(this); 62 | } 63 | } 64 | 65 | public bool SimpleEquals(JavaScriptValue other) 66 | { 67 | var eng = GetEngine(); 68 | bool result; 69 | Errors.ThrowIfIs(api_.JsEquals(this.handle_, other.handle_, out result)); 70 | 71 | return result; 72 | } 73 | 74 | public bool StrictEquals(JavaScriptValue other) 75 | { 76 | var eng = GetEngine(); 77 | bool result; 78 | Errors.ThrowIfIs(api_.JsStrictEquals(this.handle_, other.handle_, out result)); 79 | 80 | return result; 81 | } 82 | 83 | #region DynamicObject overrides 84 | 85 | public override bool TryConvert(ConvertBinder binder, out object result) 86 | { 87 | var eng = GetEngine(); 88 | if (binder.Type == typeof(int)) 89 | { 90 | result = eng.Converter.ToInt32(this); 91 | return true; 92 | } 93 | else if (binder.Type == typeof(double)) 94 | { 95 | result = eng.Converter.ToDouble(this); 96 | return true; 97 | } 98 | else if (binder.Type == typeof(string)) 99 | { 100 | result = eng.Converter.ToString(this); 101 | return true; 102 | } 103 | else if (binder.Type == typeof(bool)) 104 | { 105 | result = eng.Converter.ToBoolean(this); 106 | return true; 107 | } 108 | 109 | return base.TryConvert(binder, out result); 110 | } 111 | 112 | public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result) 113 | { 114 | var eng = GetEngine(); 115 | 116 | switch (binder.Operation) 117 | { 118 | case ExpressionType.IsFalse: 119 | result = !IsTruthy; 120 | return true; 121 | case ExpressionType.IsTrue: 122 | result = IsTruthy; 123 | return true; 124 | 125 | case ExpressionType.Negate: 126 | case ExpressionType.NegateChecked: 127 | switch (Type) 128 | { 129 | case JavaScriptValueType.Number: 130 | double n = eng.Converter.ToDouble(this); 131 | result = -n; 132 | return true; 133 | 134 | case JavaScriptValueType.Boolean: 135 | if (IsTruthy) 136 | result = -1; 137 | else 138 | result = -0; 139 | return true; 140 | 141 | // TODO 142 | // case JavaScriptValueType.String: 143 | } 144 | 145 | result = double.NaN; 146 | return true; 147 | 148 | case ExpressionType.UnaryPlus: 149 | switch (Type) 150 | { 151 | case JavaScriptValueType.Number: 152 | result = eng.Converter.ToDouble(this); 153 | return true; 154 | 155 | case JavaScriptValueType.Boolean: 156 | if (IsTruthy) 157 | result = 1; 158 | else 159 | result = 0; 160 | 161 | return true; 162 | } 163 | 164 | result = double.NaN; 165 | return true; 166 | } 167 | 168 | return base.TryUnaryOperation(binder, out result); 169 | } 170 | #endregion 171 | 172 | #region IDisposable implementation 173 | public void Dispose() 174 | { 175 | Dispose(true); 176 | GC.SuppressFinalize(this); 177 | } 178 | 179 | protected virtual void Dispose(bool disposing) 180 | { 181 | if (disposing) 182 | { 183 | if (handle_ != null) 184 | { 185 | handle_.Dispose(); 186 | handle_ = null; 187 | } 188 | } 189 | } 190 | 191 | ~JavaScriptValue() 192 | { 193 | Dispose(false); 194 | } 195 | #endregion 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptRuntime.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.Scripting.JavaScript 11 | { 12 | public sealed class JavaScriptRuntime : IDisposable 13 | { 14 | private JavaScriptRuntimeSettings settings_; 15 | private JavaScriptRuntimeSafeHandle handle_; 16 | private ChakraApi api_ = ChakraApi.Instance; 17 | private List> childEngines_; 18 | 19 | public JavaScriptRuntime(JavaScriptRuntimeSettings settings = null) 20 | { 21 | if (settings == null) 22 | settings = new JavaScriptRuntimeSettings(); 23 | 24 | childEngines_ = new List>(); 25 | settings_ = settings; 26 | var attrs = settings.GetRuntimeAttributes(); 27 | 28 | var errorCode = api_.JsCreateRuntime(attrs, IntPtr.Zero, out handle_); 29 | if (errorCode != JsErrorCode.JsNoError) 30 | Errors.ThrowFor(errorCode); 31 | 32 | settings.Used = true; 33 | 34 | GCHandle handle = GCHandle.Alloc(this, GCHandleType.Weak); 35 | errorCode = api_.JsSetRuntimeMemoryAllocationCallback(handle_, GCHandle.ToIntPtr(handle), MemoryCallbackThunkPtr); 36 | if (errorCode != JsErrorCode.JsNoError) 37 | Errors.ThrowFor(errorCode); 38 | } 39 | 40 | public void CollectGarbage() 41 | { 42 | if (handle_ == null) 43 | throw new ObjectDisposedException(nameof(JavaScriptRuntime)); 44 | 45 | var error = api_.JsCollectGarbage(handle_); 46 | Errors.ThrowIfIs(error); 47 | } 48 | 49 | public JavaScriptEngine CreateEngine() 50 | { 51 | if (handle_ == null) 52 | throw new ObjectDisposedException(nameof(JavaScriptRuntime)); 53 | 54 | JavaScriptEngineSafeHandle engine; 55 | var error = api_.JsCreateContext(handle_, out engine); 56 | Errors.ThrowIfIs(error); 57 | 58 | return new JavaScriptEngine(engine, this, api_); 59 | } 60 | 61 | public void EnableExecution() 62 | { 63 | if (handle_ == null) 64 | throw new ObjectDisposedException(nameof(JavaScriptRuntime)); 65 | 66 | var error = api_.JsEnableRuntimeExecution(handle_); 67 | Errors.ThrowIfIs(error); 68 | } 69 | 70 | public void DisableExecution() 71 | { 72 | if (handle_ == null) 73 | throw new ObjectDisposedException(nameof(JavaScriptRuntime)); 74 | 75 | var error = api_.JsDisableRuntimeExecution(handle_); 76 | Errors.ThrowIfIs(error); 77 | } 78 | 79 | public ulong RuntimeMemoryUsage 80 | { 81 | get 82 | { 83 | if (handle_ == null) 84 | throw new ObjectDisposedException(nameof(JavaScriptRuntime)); 85 | 86 | ulong result; 87 | var error = api_.JsGetRuntimeMemoryUsage(handle_, out result); 88 | Errors.ThrowIfIs(error); 89 | 90 | return result; 91 | } 92 | } 93 | 94 | public bool IsExecutionEnabled 95 | { 96 | get 97 | { 98 | if (handle_ == null) 99 | throw new ObjectDisposedException(nameof(JavaScriptRuntime)); 100 | 101 | bool result; 102 | var error = api_.JsIsRuntimeExecutionDisabled(handle_, out result); 103 | Errors.ThrowIfIs(error); 104 | 105 | return !result; 106 | } 107 | } 108 | 109 | public event EventHandler MemoryChanging; 110 | private void OnMemoryChanging(JavaScriptMemoryAllocationEventArgs args) 111 | { 112 | var changing = MemoryChanging; 113 | if (changing != null) 114 | { 115 | changing(this, args); 116 | } 117 | } 118 | 119 | public JavaScriptRuntimeSettings Settings 120 | { 121 | get { return settings_; } 122 | } 123 | 124 | #region Disposable implementation 125 | public void Dispose() 126 | { 127 | Dispose(true); 128 | GC.SuppressFinalize(this); 129 | } 130 | 131 | private void Dispose(bool disposing) 132 | { 133 | if (disposing) 134 | { 135 | api_.JsSetCurrentContext(JavaScriptEngineSafeHandle.Invalid); 136 | api_.JsSetRuntimeMemoryAllocationCallback(this.handle_, IntPtr.Zero, IntPtr.Zero); 137 | if (childEngines_ != null) 138 | { 139 | foreach (var engineRef in childEngines_) 140 | { 141 | JavaScriptEngine engine; 142 | if (engineRef.TryGetTarget(out engine)) 143 | { 144 | engine.Dispose(); 145 | } 146 | } 147 | childEngines_ = null; 148 | } 149 | 150 | if (handle_ != null && !handle_.IsClosed) 151 | { 152 | handle_.Dispose(); 153 | handle_ = null; 154 | } 155 | } 156 | } 157 | 158 | ~JavaScriptRuntime() 159 | { 160 | Dispose(false); 161 | } 162 | #endregion 163 | 164 | #region Memory callback implementation 165 | static JavaScriptRuntime() 166 | { 167 | MemoryCallbackThunkDelegate = MemoryCallbackThunk; 168 | MemoryCallbackThunkPtr = Marshal.GetFunctionPointerForDelegate(MemoryCallbackThunkDelegate); 169 | } 170 | 171 | private static bool MemoryCallbackThunk(IntPtr callbackState, JavaScriptMemoryAllocationEventType allocationEvent, UIntPtr allocationSize) 172 | { 173 | GCHandle handle = GCHandle.FromIntPtr(callbackState); 174 | JavaScriptRuntime runtime = handle.Target as JavaScriptRuntime; 175 | if (runtime == null) 176 | { 177 | Debug.Assert(false, "Runtime has been freed."); 178 | return false; 179 | } 180 | 181 | var args = new JavaScriptMemoryAllocationEventArgs(allocationSize, allocationEvent); 182 | runtime.OnMemoryChanging(args); 183 | 184 | if (args.IsCancelable && args.Cancel) 185 | return false; 186 | 187 | return true; 188 | } 189 | private static IntPtr MemoryCallbackThunkPtr; 190 | private static MemoryCallbackThunkCallback MemoryCallbackThunkDelegate; 191 | #endregion 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/Errors.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Scripting 10 | { 11 | /* 12 | JsErrorHeapEnumInProgress We don't support profiling 13 | JsErrorInProfileCallback We don't support profiling currently 14 | JsErrorInThreadServiceCallback TODO: Not yet supported but will be 15 | JsErrorAlreadyProfilingContext We don't support profiling 16 | JsCannotSetProjectionEnqueueCa WinRT projection not supported for desktop apps 17 | JsErrorCannotStartProjection WinRT projection not supported for desktop apps 18 | JsErrorInObjectBeforeCollectCa TODO: Not yet supported but will be 19 | JsErrorObjectNotInspectable WinRT projection not supported for desktop apps 20 | 21 | JsErrorScriptException Turn into success case 22 | JsErrorScriptTerminated Turn into success case 23 | */ 24 | internal static class Errors 25 | { 26 | public const string NoMutateJsRuntimeSettings = "Can't change JavaScriptRuntimeSettings once it has been used to create a runtime."; 27 | public const string DefaultFnOverwritten = "The built-in function '{0}' has been overwritten and is no longer a function."; 28 | 29 | public const string ERROR_ENGINE_IN_EXCEPTION_STATE = "The script engine is in an exception state, and additional progress may not be made without clearing the exception."; 30 | public const string ERROR_WRONG_THREAD = "Could not acquire the runtime host on the current thread."; 31 | public const string ERROR_RUNTIME_IN_USE = "A runtime that is still in use cannot be disposed."; 32 | public const string ERROR_BAD_SERIALIZED_SCRIPT = "The serialized script is corrupt or incompatible with the current version."; 33 | public const string ERROR_DISABLED = "The runtime is disabled."; 34 | public const string ERROR_CONFIG_ERROR = "The runtime settings provided at initialization prevent the requested operation."; 35 | public const string ERROR_NOT_OBJECT = "An operation expected an object parameter but was provided a non-object value."; 36 | public const string ERROR_PROJECTION_NOT_STARTED = "The Windows Runtime projection could not be initialized."; 37 | public const string ERROR_ARG_NOT_INSPECTABLE = "Object cannot be projected into the script engine because it isn't a Windows Runtime object. Windows Runtime objects derive from IInspectable."; 38 | public const string ERROR_PROPERTY_NOT_SYMBOL = "Attempted to get a Symbol for a property name that is actually a string."; 39 | public const string ERROR_PROPERTY_NOT_STRING = "Attempted to get a property name that is actually a Symbol."; 40 | public const string ERROR_COMPILATION_FAILED = "A script failed to compile, probably due to a syntax error."; 41 | public const string ERROR_SCRIPT_ATTEMPTED_EVAL = "A script was terminated because it tried to use eval or Function() and eval was disabled."; 42 | public const string ERROR_ALREADY_DEBUGGING = "The script engine was already in debugging mode."; 43 | public const string ERROR_CANNOT_SERIALIZE_DEBUG_SCRIPT = "Can't serialize script while in debugging mode."; 44 | public const string ERROR_ENGINE_COLLECTING_GARBAGE = "The script engine is collecting garbage and is in a pre-collection callback. The requested operation may not be performed at this time."; 45 | public const string ERROR_NO_CURRENT_CONTEXT = "There is no current context set on the thread."; 46 | 47 | private static readonly Dictionary ErrorMap = new Dictionary() 48 | { 49 | { JsErrorCode.JsCannotSetProjectionEnqueueCallback, () => { Debug.Assert(false, "Should not occur, we don't support."); throw new Exception(); } }, 50 | { JsErrorCode.JsErrorAlreadyDebuggingContext, () => { throw new InvalidOperationException(ERROR_ALREADY_DEBUGGING); } }, 51 | { JsErrorCode.JsErrorAlreadyProfilingContext, () => { Debug.Assert(false, "Should not occur, we don't support."); throw new Exception(); } }, 52 | { JsErrorCode.JsErrorArgumentNotObject, () => { throw new ArgumentException(ERROR_NOT_OBJECT); } }, 53 | { JsErrorCode.JsErrorBadSerializedScript, () => { throw new ArgumentException(ERROR_BAD_SERIALIZED_SCRIPT); } }, 54 | { JsErrorCode.JsErrorCannotDisableExecution, () => { throw new InvalidOperationException(); } }, 55 | { JsErrorCode.JsErrorCannotSerializeDebugScript, () => { throw new InvalidOperationException(ERROR_CANNOT_SERIALIZE_DEBUG_SCRIPT); } }, 56 | { JsErrorCode.JsErrorCannotStartProjection, () => { Debug.Assert(false, "Should not occur, we don't support."); throw new InvalidOperationException(); } }, 57 | { JsErrorCode.JsErrorFatal, () => { throw new Exception("An unknown error occurred in the script engine."); } }, 58 | { JsErrorCode.JsErrorHeapEnumInProgress, () => { Debug.Assert(false, "Should not occur, we don't support."); throw new Exception(); } }, 59 | { JsErrorCode.JsErrorIdleNotEnabled, () => { throw new InvalidOperationException(ERROR_CONFIG_ERROR); } }, 60 | { JsErrorCode.JsErrorInDisabledState, () => { throw new InvalidOperationException(ERROR_DISABLED); } }, 61 | { JsErrorCode.JsErrorInExceptionState, () => { throw new InvalidOperationException(ERROR_ENGINE_IN_EXCEPTION_STATE); } }, 62 | { JsErrorCode.JsErrorInObjectBeforeCollectCallback, () => { throw new InvalidOperationException(ERROR_ENGINE_COLLECTING_GARBAGE); } }, 63 | { JsErrorCode.JsErrorInProfileCallback, () => { Debug.Assert(false, "Should not occur, we don't support."); throw new Exception(); } }, 64 | { JsErrorCode.JsErrorInThreadServiceCallback, () => { Debug.Assert(false, "Should not occur, we don't support."); throw new Exception(); } }, 65 | { JsErrorCode.JsErrorInvalidArgument, () => { throw new ArgumentException(); } }, 66 | { JsErrorCode.JsErrorNoCurrentContext, () => { throw new InvalidOperationException(ERROR_NO_CURRENT_CONTEXT); } }, 67 | { JsErrorCode.JsErrorNotImplemented, () => { throw new NotImplementedException(); } }, 68 | { JsErrorCode.JsErrorNullArgument, () => { throw new ArgumentNullException(); } }, 69 | { JsErrorCode.JsErrorObjectNotInspectable, () => { Debug.Assert(false, "Should not occur, we don't support."); throw new ArgumentException(ERROR_ARG_NOT_INSPECTABLE); } }, 70 | { JsErrorCode.JsErrorOutOfMemory, () => { throw new OutOfMemoryException(); } }, 71 | { JsErrorCode.JsErrorPropertyNotString, () => { throw new ArgumentException(ERROR_PROPERTY_NOT_STRING); } }, 72 | { JsErrorCode.JsErrorPropertyNotSymbol, () => { throw new ArgumentException(ERROR_PROPERTY_NOT_SYMBOL); } }, 73 | { JsErrorCode.JsErrorRuntimeInUse, () => { throw new InvalidOperationException(ERROR_RUNTIME_IN_USE); } }, 74 | { JsErrorCode.JsErrorScriptCompile, () => { throw new ArgumentException(ERROR_COMPILATION_FAILED); } }, 75 | { JsErrorCode.JsErrorScriptEvalDisabled, () => { throw new InvalidOperationException(ERROR_SCRIPT_ATTEMPTED_EVAL); } }, 76 | { JsErrorCode.JsErrorWrongThread, () => { throw new InvalidOperationException(ERROR_WRONG_THREAD); } }, 77 | }; 78 | 79 | [DebuggerStepThrough] 80 | public static void ThrowFor(JsErrorCode errorCode) 81 | { 82 | Debug.Assert(errorCode != JsErrorCode.JsNoError); 83 | 84 | Action throwAction; 85 | if (!ErrorMap.TryGetValue(errorCode, out throwAction)) 86 | { 87 | throwAction = () => { throw new Exception($"Unrecognized JavaScript error {errorCode} (0x{errorCode:x8})"); }; 88 | } 89 | 90 | throwAction(); 91 | } 92 | 93 | [DebuggerStepThrough] 94 | public static void ThrowIfIs(JsErrorCode errorCode) 95 | { 96 | Debug.Assert(errorCode != JsErrorCode.JsErrorScriptException); 97 | 98 | if (errorCode != JsErrorCode.JsNoError) 99 | throw new Exception(errorCode.ToString()); 100 | } 101 | 102 | [DebuggerStepThrough] 103 | public static void CheckForScriptExceptionOrThrow(JsErrorCode errorCode, JavaScriptEngine engine) 104 | { 105 | if (errorCode == JsErrorCode.JsErrorScriptException) 106 | { 107 | engine.OnRuntimeExceptionRaised(); 108 | return; 109 | } 110 | 111 | if (errorCode != JsErrorCode.JsNoError) 112 | ThrowFor(errorCode); 113 | } 114 | 115 | [DebuggerStepThrough] 116 | public static void ThrowIOEFmt(string formatStr, string param) 117 | { 118 | string result = string.Format(formatStr, param); 119 | throw new InvalidOperationException(result); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptArray.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Collections; 8 | 9 | namespace Microsoft.Scripting.JavaScript 10 | { 11 | public sealed class JavaScriptArray : JavaScriptObject, IEnumerable 12 | { 13 | internal JavaScriptArray(JavaScriptValueSafeHandle handle, JavaScriptValueType type, JavaScriptEngine engine): 14 | base(handle, type, engine) 15 | { 16 | 17 | } 18 | 19 | public int Length 20 | { 21 | get 22 | { 23 | var eng = GetEngine(); 24 | return eng.Converter.ToInt32(GetPropertyByName("length")); 25 | } 26 | } 27 | 28 | public JavaScriptValue this[int index] 29 | { 30 | get { return GetAt(index); } 31 | set { SetAt(index, value); } 32 | } 33 | 34 | public JavaScriptValue GetAt(int index) 35 | { 36 | var eng = GetEngine(); 37 | 38 | JavaScriptValueSafeHandle resultHandle; 39 | using (var temp = eng.Converter.FromInt32(index)) 40 | { 41 | Errors.ThrowIfIs(api_.JsGetIndexedProperty(handle_, temp.handle_, out resultHandle)); 42 | } 43 | return eng.CreateValueFromHandle(resultHandle); 44 | } 45 | 46 | public void SetAt(int index, JavaScriptValue value) 47 | { 48 | var eng = GetEngine(); 49 | 50 | using (var temp = eng.Converter.FromInt32(index)) 51 | { 52 | Errors.ThrowIfIs(api_.JsSetIndexedProperty(handle_, temp.handle_, value.handle_)); 53 | } 54 | } 55 | 56 | private JavaScriptFunction GetArrayBuiltin(string name) 57 | { 58 | var eng = GetEngine(); 59 | var arrayCtor = eng.GlobalObject.GetPropertyByName("Array") as JavaScriptFunction; 60 | if (arrayCtor == null) 61 | Errors.ThrowIOEFmt(Errors.DefaultFnOverwritten, "Array"); 62 | var arrayPrototype = arrayCtor.Prototype; 63 | if (arrayPrototype == null) 64 | Errors.ThrowIOEFmt(Errors.DefaultFnOverwritten, "Array.prototype"); 65 | var fn = arrayPrototype.GetPropertyByName(name) as JavaScriptFunction; 66 | if (fn == null) 67 | Errors.ThrowIOEFmt(Errors.DefaultFnOverwritten, "Array.prototype." + name); 68 | 69 | return fn; 70 | } 71 | 72 | public JavaScriptValue Pop() 73 | { 74 | var fn = GetArrayBuiltin("pop"); 75 | return fn.Invoke(new JavaScriptValue[] { this }); 76 | } 77 | public void Push(JavaScriptValue value) 78 | { 79 | var fn = GetArrayBuiltin("pop"); 80 | fn.Invoke(new JavaScriptValue[] { this, value }); 81 | } 82 | public void Reverse() 83 | { 84 | var fn = GetArrayBuiltin("reverse"); 85 | fn.Invoke(new JavaScriptValue[] { this }); 86 | } 87 | 88 | public JavaScriptValue Shift() 89 | { 90 | var fn = GetArrayBuiltin("shift"); 91 | return fn.Invoke(new JavaScriptValue[] { this }); 92 | } 93 | public int Unshift(IEnumerable valuesToInsert) 94 | { 95 | var eng = GetEngine(); 96 | var fn = GetArrayBuiltin("unshift"); 97 | return eng.Converter.ToInt32(fn.Invoke(valuesToInsert.PrependWith(this))); 98 | } 99 | public void Sort(JavaScriptFunction compareFunction = null) 100 | { 101 | var fn = GetArrayBuiltin("sort"); 102 | List args = new List(); 103 | args.Add(this); 104 | if (compareFunction != null) 105 | args.Add(compareFunction); 106 | 107 | fn.Invoke(args); 108 | } 109 | public JavaScriptArray Splice(uint index, uint numberToRemove, IEnumerable valuesToInsert) 110 | { 111 | if (valuesToInsert == null) 112 | valuesToInsert = Enumerable.Empty(); 113 | 114 | var eng = GetEngine(); 115 | var args = valuesToInsert.PrependWith(this, eng.Converter.FromDouble(index), eng.Converter.FromDouble(numberToRemove)); 116 | 117 | var fn = GetArrayBuiltin("splice"); 118 | return fn.Invoke(args) as JavaScriptArray; 119 | } 120 | public JavaScriptArray Concat(IEnumerable itemsToConcatenate) 121 | { 122 | JavaScriptArray otherIsArray = itemsToConcatenate as JavaScriptArray; 123 | List args = new List(); 124 | args.Add(this); 125 | if (otherIsArray != null) 126 | { 127 | args.Add(otherIsArray); 128 | } 129 | else 130 | { 131 | foreach (var item in itemsToConcatenate) 132 | args.Add(item); 133 | } 134 | 135 | var fn = GetArrayBuiltin("concat"); 136 | return fn.Invoke(args) as JavaScriptArray; 137 | } 138 | public string Join(string separator = "") 139 | { 140 | var eng = GetEngine(); 141 | List args = new List(); 142 | args.Add(this); 143 | if (!string.IsNullOrEmpty(separator)) 144 | args.Add(eng.Converter.FromString(separator)); 145 | 146 | var fn = GetArrayBuiltin("join"); 147 | return eng.Converter.ToString(fn.Invoke(args)); 148 | } 149 | public JavaScriptArray Slice(int beginning) 150 | { 151 | var args = new List(); 152 | args.Add(this); 153 | args.Add(GetEngine().Converter.FromInt32(beginning)); 154 | 155 | return GetArrayBuiltin("slice").Invoke(args) as JavaScriptArray; 156 | } 157 | public JavaScriptArray Slice(int beginning, int end) 158 | { 159 | var args = new List(); 160 | args.Add(this); 161 | args.Add(GetEngine().Converter.FromInt32(beginning)); 162 | args.Add(GetEngine().Converter.FromInt32(end)); 163 | 164 | return GetArrayBuiltin("slice").Invoke(args) as JavaScriptArray; 165 | } 166 | public int IndexOf(JavaScriptValue valueToFind) 167 | { 168 | var args = new List(); 169 | args.Add(this); 170 | args.Add(valueToFind); 171 | 172 | return GetEngine().Converter.ToInt32(GetArrayBuiltin("indexOf").Invoke(args)); 173 | } 174 | public int IndexOf(JavaScriptValue valueToFind, int startIndex) 175 | { 176 | var eng = GetEngine(); 177 | var args = new List(); 178 | args.Add(this); 179 | args.Add(valueToFind); 180 | args.Add(eng.Converter.FromInt32(startIndex)); 181 | 182 | return eng.Converter.ToInt32(GetArrayBuiltin("indexOf").Invoke(args)); 183 | } 184 | public int LastIndexOf(JavaScriptValue valueToFind) 185 | { 186 | var eng = GetEngine(); 187 | var args = new List(); 188 | args.Add(this); 189 | args.Add(valueToFind); 190 | 191 | return eng.Converter.ToInt32(GetArrayBuiltin("lastIndexOf").Invoke(args)); 192 | } 193 | public int LastIndexOf(JavaScriptValue valueToFind, int lastIndex) 194 | { 195 | var eng = GetEngine(); 196 | var args = new List(); 197 | args.Add(this); 198 | args.Add(valueToFind); 199 | args.Add(eng.Converter.FromInt32(lastIndex)); 200 | 201 | return eng.Converter.ToInt32(GetArrayBuiltin("lastIndexOf").Invoke(args)); 202 | } 203 | 204 | public void ForEach(JavaScriptFunction callee) 205 | { 206 | if (callee == null) 207 | throw new ArgumentNullException(nameof(callee)); 208 | 209 | var args = new List(); 210 | args.Add(this); 211 | args.Add(callee); 212 | 213 | GetArrayBuiltin("forEach").Invoke(args); 214 | } 215 | public bool Every(JavaScriptFunction predicate) 216 | { 217 | if (predicate == null) 218 | throw new ArgumentNullException(nameof(predicate)); 219 | 220 | var args = new List(); 221 | args.Add(this); 222 | args.Add(predicate); 223 | 224 | return GetEngine().Converter.ToBoolean(GetArrayBuiltin("every").Invoke(args)); 225 | } 226 | public bool Some(JavaScriptFunction predicate) 227 | { 228 | if (predicate == null) 229 | throw new ArgumentNullException(nameof(predicate)); 230 | 231 | var args = new List(); 232 | args.Add(this); 233 | args.Add(predicate); 234 | 235 | return GetEngine().Converter.ToBoolean(GetArrayBuiltin("some").Invoke(args)); 236 | } 237 | public JavaScriptArray Filter(JavaScriptFunction predicate) 238 | { 239 | if (predicate == null) 240 | throw new ArgumentNullException(nameof(predicate)); 241 | 242 | var args = new List(); 243 | args.Add(this); 244 | args.Add(predicate); 245 | 246 | return GetArrayBuiltin("filter").Invoke(args) as JavaScriptArray; 247 | } 248 | public JavaScriptArray Map(JavaScriptFunction converter) 249 | { 250 | if (converter == null) 251 | throw new ArgumentNullException(nameof(converter)); 252 | 253 | var args = new List(); 254 | args.Add(this); 255 | args.Add(converter); 256 | 257 | return GetArrayBuiltin("map").Invoke(args) as JavaScriptArray; 258 | } 259 | public JavaScriptValue Reduce(JavaScriptFunction aggregator) 260 | { 261 | if (aggregator == null) 262 | throw new ArgumentNullException(nameof(aggregator)); 263 | 264 | var args = new List(); 265 | args.Add(this); 266 | args.Add(aggregator); 267 | 268 | return GetArrayBuiltin("reduce").Invoke(args); 269 | } 270 | public JavaScriptValue Reduce(JavaScriptFunction aggregator, JavaScriptValue initialValue) 271 | { 272 | if (aggregator == null) 273 | throw new ArgumentNullException(nameof(aggregator)); 274 | 275 | var args = new List(); 276 | args.Add(this); 277 | args.Add(aggregator); 278 | args.Add(initialValue); 279 | 280 | return GetArrayBuiltin("reduce").Invoke(args); 281 | } 282 | public JavaScriptValue ReduceRight(JavaScriptFunction aggregator) 283 | { 284 | if (aggregator == null) 285 | throw new ArgumentNullException(nameof(aggregator)); 286 | 287 | var args = new List(); 288 | args.Add(this); 289 | args.Add(aggregator); 290 | 291 | return GetArrayBuiltin("reduceRight").Invoke(args); 292 | } 293 | public JavaScriptValue ReduceRight(JavaScriptFunction aggregator, JavaScriptValue initialValue) 294 | { 295 | if (aggregator == null) 296 | throw new ArgumentNullException(nameof(aggregator)); 297 | 298 | var args = new List(); 299 | args.Add(this); 300 | args.Add(aggregator); 301 | args.Add(initialValue); 302 | 303 | return GetArrayBuiltin("reduceRight").Invoke(args); 304 | } 305 | 306 | public IEnumerator GetEnumerator() 307 | { 308 | var len = this.Length; 309 | for (int i = 0; i < len; i++) 310 | { 311 | yield return GetAt(i); 312 | } 313 | } 314 | 315 | IEnumerator IEnumerable.GetEnumerator() 316 | { 317 | return this.GetEnumerator(); 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # jsrt-dotnet 2 | A library for accessing the Chakra and ChakraCore 3 | [JavaScript Runtime hosting](https://github.com/Microsoft/ChakraCore/wiki/JavaScript-Runtime-%28JSRT%29-Reference) 4 | interface from the .NET Framework. It is inspired by [jsrt-winrt](https://github.com/robpaveza/jsrt-winrt) but 5 | is not directly compatible. 6 | 7 | *Why do I care?* If you want to extend your .NET application, or allow your users to do 8 | so, by writing some JavaScript at runtime, this allows you to do so without paying the 9 | cost of loading an HTML engine with it. And, it's far more convenient than the programming 10 | model supported by the HTML engine. (Unless you're using HTML rendering within your app, 11 | in which case using the HTML engine is pretty efficient for that). 12 | 13 | ## What is special about jsrt-dotnet? 14 | 15 | This project aims to create as seamless a bridge between your JavaScript and .NET code as 16 | possible. .NET objects can be directly exposed to JavaScript, and JavaScript objects can 17 | be accessed from C# using `dynamic` or via normal early binding. It also aims to be an 18 | accurate representation of the JavaScript type system from .NET. 19 | 20 | ## Getting started 21 | 22 | As an example, let's create a simple host function called Echo: 23 | 24 | ```csharp 25 | static JavaScriptValue Echo(JavaScriptEngine engine, bool construct, JavaScriptValue thisValue, IEnumerable arguments) 26 | { 27 | string fmt = arguments.First().ToString(); 28 | object[] args = (object[])arguments.Skip(1).ToArray(); 29 | Console.WriteLine(fmt, args); 30 | return engine.UndefinedValue; 31 | } 32 | ``` 33 | 34 | The function must return a `JavaScriptValue` (because all JavaScript functions return 35 | something - even if that something is `undefined`). The parameters to the function 36 | represent the things that the JavaScript code is calling: 37 | 38 | - `engine` is an isolated collection of globals and code 39 | - `construct` indicates whether the function is being called with the `new` operator 40 | - `thisValue` is the ambient JavaScript `this` value (if one is available) 41 | - `arguments` are the remaining parameters actually passed in 42 | 43 | The function expects at least a single parameter to be passed, and will accept multiple 44 | other parameters. It then uses the `Console.WriteLine(string, params object[])` overload 45 | to write a formatted string. We then add this function to the global object: 46 | 47 | ```csharp 48 | using (var runtime = new JavaScriptRuntime()) 49 | using (var engine = runtime.CreateEngine()) 50 | using (var context = engine.AcquireContext()) 51 | { 52 | engine.SetGlobalFunction("echo", Echo); 53 | 54 | // TODO: Call echo 55 | } 56 | ``` 57 | 58 | So what we do here are: 59 | - Create a `JavaScriptRuntime`, which has global settings and a shared memory allocator 60 | - Create a `JavaScriptEngine`, which is that isolated collection of globals and code 61 | - Acquire an execution context from the engine, which means that the script engine is 62 | bound to the current thread while the context is held. The engine is released from 63 | the thread when the context is disposed. 64 | - We then create a global function, `echo`, corresponding to the `Echo` method earlier 65 | 66 | All that's left is to run some script that will call it: 67 | 68 | ```csharp 69 | using (var runtime = new JavaScriptRuntime()) 70 | using (var engine = runtime.CreateEngine()) 71 | using (var context = engine.AcquireContext()) 72 | { 73 | engine.SetGlobalFunction("echo", Echo); 74 | 75 | var fn = engine.EvaluateScriptText(@"(function() { 76 | echo('{0}, {1}!', 'Hello', 'World'); 77 | })();"); 78 | fn.Invoke(Enumerable.Empty()); 79 | } 80 | ``` 81 | 82 | If you run this from a console, you'll see 83 | 84 | Hello, World! 85 | 86 | output on the screen. 87 | 88 | ## Using dynamic 89 | 90 | Let's start getting crazy. 91 | 92 | ```csharp 93 | using (var runtime = new JavaScriptRuntime()) 94 | using (var engine = runtime.CreateEngine()) 95 | using (var context = engine.AcquireContext()) 96 | { 97 | engine.SetGlobalFunction("echo", Echo); 98 | dynamic global = engine.GlobalObject; 99 | global.hello = "Hello"; 100 | global.world = "world"; 101 | 102 | var fn = engine.EvaluateScriptText(@"(function() { 103 | echo('{0}, {1}!', hello, world); 104 | })();"); 105 | fn.Invoke(Enumerable.Empty()); 106 | } 107 | ``` 108 | 109 | What happened here? 110 | 111 | The `dynamic` keyword instructs the C# compiler to perform runtime late binding 112 | on things that are performed against the dynamic thing. The engine's 113 | `GlobalObject` property is a JavaScript `Object` (it's the thing that provides 114 | all of those handy things like `ArrayBuffer` and `Math`). That Object, like 115 | any other JavaScript object, has properties, and those properties are accessible 116 | via the [normal name resolution rules](http://es5.github.io/#x10.2) per normal 117 | JavaScript semantics. 118 | 119 | Because JavaScript Objects are property bags, we can assign anything to them. 120 | What happens under the covers is: 121 | 122 | 1. `JavaScriptObject` derives from `JavaScriptValue`, which in turn derives 123 | from `DynamicObject`, provided in the .NET base class library 124 | 2. The C# language bindings call `JavaScriptObject.TrySetMember`, passing 125 | information about the operation, namely, that the name is `hello` and 126 | that the set-member operation is case-sensitive. (Because JavaScript 127 | is case-sensitive, we ignore this flag, and always treat it as 128 | case-sensitive). 129 | 3. That operation converts the right-hand side value (in this case, a C# 130 | `string` of `"Hello"`) to its JavaScript equivalent, a `JavaScriptValue`, 131 | and calls `JavaScriptObject.SetPropertyByName(string, JavaScriptValue)` 132 | method. 133 | 4. When the script text is executed, the environment record has bindings for 134 | `hello` and `world` as properties of the global, so they're passed back 135 | out to C# as `JavaScriptValue`s in the arguments. They get converted 136 | back to strings, without changing the code. 137 | 138 | I can blow your mind even more. Without even calling script, I can call into 139 | the script engine: 140 | 141 | ```csharp 142 | using (var runtime = new JavaScriptRuntime()) 143 | using (var engine = runtime.CreateEngine()) 144 | using (var context = engine.AcquireContext()) 145 | { 146 | engine.SetGlobalFunction("echo", Echo); 147 | dynamic global = engine.GlobalObject; 148 | 149 | global.echo("{0}, {1}, from dynamic.", "Hello", "world"); 150 | } 151 | ``` 152 | 153 | Here, the C# dynamic binder calls into `JavaScriptObject.TryInvokeMember`, 154 | which resolves the property, casts it to a `JavaScriptFunction`, and then 155 | calls it. That function happens to be a host function, so it calls back 156 | into C#, using the same round-trip behaviors as shown previously. 157 | 158 | ## Accessing CLR objects from script 159 | 160 | CLR objects can be added to and accessed via script. Wherever possible, 161 | we try to preserve object behavioral semantics across the script-host 162 | boundary. That is, if you have a C# object that you add to the script 163 | engine, and then mutate that object from script, those changes will be 164 | reflected in the C# object. 165 | 166 | ** Important: ** _.NET objects to JavaScript are still an incomplete and 167 | experimental feature. The following outlines how this feature is intended 168 | to function, but may not be implemented as described._ 169 | 170 | To convert a host object to a JavaScript representation, call 171 | `myJavaScriptEngine.Converter.FromObject`, which will return a 172 | `JavaScriptValue`. 173 | 174 | var pt = new Point3D { X = 18, Y = 27, Z = -1 }; 175 | engine.SetGlobalVariable("pt", engine.Converter.FromObject(pt)); 176 | 177 | For any type that isn't just represented by a JavaScript primitive, we 178 | attempt to follow this algorithm: 179 | 180 | - If the Type of the value is a `struct` (`System.ValueType`), an 181 | `ArgumentException` is thrown. Because struct types can define methods 182 | and properties, but object identity isn't preserved, they are invalid 183 | types for projection across the boundary. Instead, use a JSON 184 | serializer to serialize the value, and then deserialize the value on 185 | the JavaScript side. 186 | - If the Type of the value is a `delegate` (derived from `System.Delegate`), 187 | an `ArgumentException` is thrown. Instead, use an overload of 188 | `engine.CreateFunction`. 189 | - If the Type of the value is a `Task`, a `Promise` is created. The 190 | Promise will be resolved or rejected once the Task has completed. 191 | - Otherwise, an object will be projected as follows: 192 | - Get the Type of the object being converted 193 | - Create the constructor function: 194 | - If the Type defines one or more public constructors, create a function 195 | named the full name of the Type. Only one overload of each arity can 196 | be supported. The function, when called with or without the `new` 197 | operator, will call the constructor. 198 | - If the Type does not define any public constructors, the function will 199 | still be created, but it will only return `undefined`. 200 | - Regardless of whether there are any constructors, the function will 201 | not be added to the global namespace. It will only be accessible via 202 | a `constructor` property. 203 | - Create the prototype object: 204 | - If the prototype object's base Type is not `null` (in other words, 205 | if the Type isn't `System.Object`), create a prototype chain and 206 | recycle this algorithm. 207 | - For each public method defined by this type, create a function that 208 | trampolines from JavaScript into .NET based on number of arguments 209 | passed from JavaScript, and add it to the prototype object. 210 | - For each public property defined by this type, create an accessor 211 | property on the prototype object, with get and/or set methods, which 212 | trampolines from JavaScript into .NET. Indexer properties are not 213 | supported. 214 | - If the type defines any events, create an `addEventListener` and 215 | `removeEventListener` method. If there are events in the type's 216 | inheritance chain, the first thing that these methods should do is 217 | invoke the parent prototype's corresponding add/remove method. The 218 | `addEventListener` method should add an event handler which, when 219 | called, converts the .NET parameters into a single object. The 220 | object passed into the JavaScript callback has one property for 221 | each named argument in the event delegate signature. These 222 | arguments are converted by .NET-to-JavaScript semantics. 223 | - For each event, an `on{eventname}` property will also be created. 224 | When the property is set, it will unregister any registered 225 | listeners, and set a new listener (or not if set to a non-Function). 226 | - Public static properties, methods, and events will be projected in 227 | the same way as instance properties, methods, and events; except 228 | that events will not call via the prototype chain (type-bound events 229 | belong to the type, and do not work against a prototype chain). 230 | 231 | 232 | In addition to providing these via objects sent into the engine directly, the 233 | developer can add a constructor to the global namespace via the 234 | `AddTypeToGlobal` function: 235 | 236 | ```csharp 237 | engine.AddTypeToGlobal(); 238 | ``` 239 | 240 | Instead of providing an instance of an object to the engine, this example 241 | creates a function named `Point3D` on the global object, representing the 242 | `Point3D` public constructors. 243 | 244 | ### Example 245 | 246 | Given the following type definition: 247 | 248 | ```csharp 249 | public class Point 250 | { 251 | public double X 252 | { 253 | get; 254 | set; 255 | } 256 | 257 | public double Y 258 | { 259 | get; 260 | set; 261 | } 262 | 263 | public override string ToString() 264 | { 265 | return $"({X}, {Y})"; 266 | } 267 | } 268 | 269 | public class Point3D : Point 270 | { 271 | public double Z 272 | { 273 | get; 274 | set; 275 | } 276 | 277 | public override string ToString() 278 | { 279 | return $"({X}, {Y}, {Z})"; 280 | } 281 | } 282 | ``` 283 | 284 | If the `Point3D` type is added to the global object, the equivalent code 285 | is executed: 286 | 287 | ```js 288 | (function(global, createPoint, getX, setX, getY, setY, pointToString, createPoint3D, getZ, setZ, point3DToString) { 289 | function Point() { 290 | if (!(this instanceof Point)) 291 | return new Point(arguments); 292 | 293 | createPoint.call(this); 294 | } 295 | Object.defineProperty(Point.prototype, 'X', { 296 | get: getX, 297 | set: setX, 298 | enumerable: true 299 | }); 300 | Object.defineProperty(Point.prototype, 'Y', { 301 | get: getY, 302 | set: setY, 303 | enumerable: true 304 | }); 305 | Point.prototype.toString = pointToString; 306 | 307 | function Point3D() { 308 | if (!(this instanceof Point3D)) 309 | return new Point3D(arguments); 310 | 311 | createPoint3D.call(this); 312 | } 313 | Point3D.prototype = Object.create(Point.prototype); 314 | Point3D.prototype.constructor = Point3D; 315 | Object.defineProperty(Point3D.prototype, 'Z', { 316 | get: getZ, 317 | set: setZ, 318 | enumerable: true 319 | }); 320 | Point3D.prototype.toString = point3DToString; 321 | 322 | global.Point3D = Point3D; 323 | })([native method representations]); 324 | ``` 325 | 326 | The work to project `Point` in this example is preserved, and reused, so 327 | if `Point3D` is added to the global before `Point`, adding `Point` will 328 | only need to add the identifier `Point` to the global object; the 329 | initialization of the prototype properties doesn't need to occur. 330 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptDataView.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.Scripting.JavaScript 11 | { 12 | public sealed class JavaScriptDataView : JavaScriptObject 13 | { 14 | internal JavaScriptDataView(JavaScriptValueSafeHandle handle, JavaScriptValueType type, JavaScriptEngine engine) : 15 | base(handle, type, engine) 16 | { 17 | 18 | } 19 | 20 | public JavaScriptArrayBuffer Buffer 21 | { 22 | get 23 | { 24 | return GetPropertyByName("buffer") as JavaScriptArrayBuffer; 25 | } 26 | } 27 | 28 | public uint ByteLength 29 | { 30 | get 31 | { 32 | var eng = GetEngine(); 33 | var val = GetPropertyByName("byteLength"); 34 | return (uint)eng.Converter.ToDouble(val); 35 | } 36 | } 37 | 38 | public uint ByteOffset 39 | { 40 | get 41 | { 42 | var eng = GetEngine(); 43 | var val = GetPropertyByName("byteOffset"); 44 | return (uint)eng.Converter.ToDouble(val); 45 | } 46 | } 47 | 48 | public unsafe Stream GetUnderlyingMemory() 49 | { 50 | var buf = Buffer; 51 | Debug.Assert(buf != null); 52 | 53 | var mem = buf.GetUnderlyingMemoryInfo(); 54 | byte* pMem = (byte*)mem.Item1.ToPointer(); 55 | 56 | return new UnmanagedMemoryStream(pMem + ByteOffset, ByteLength); 57 | } 58 | 59 | /// 60 | /// Gets a signed 8-bit integer (byte) at the specified byte offset from the start of the view. 61 | /// 62 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 63 | public short GetInt8(uint byteOffset) 64 | { 65 | var eng = GetEngine(); 66 | var fn = GetBuiltinFunctionProperty("getInt8", "DataView.prototype.getInt8"); 67 | return (short)eng.Converter.ToDouble(fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset) })); 68 | } 69 | /// 70 | /// Gets an unsigned 8-bit integer (unsigned byte) at the specified byte offset from the start of the view. 71 | /// 72 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 73 | public byte GetUint8(uint byteOffset) 74 | { 75 | var eng = GetEngine(); 76 | var fn = GetBuiltinFunctionProperty("getUint8", "DataView.prototype.getUint8"); 77 | return (byte)eng.Converter.ToDouble(fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset) })); 78 | } 79 | /// 80 | /// Gets a signed 16-bit integer (short) at the specified byte offset from the start of the view. 81 | /// 82 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 83 | /// True to read as little-endian; otherwise read as big-endian. 84 | public short GetInt16(uint byteOffset, bool littleEndian = false) 85 | { 86 | var eng = GetEngine(); 87 | var fn = GetBuiltinFunctionProperty("getInt16", "DataView.prototype.getInt16"); 88 | return (short)eng.Converter.ToDouble(fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian) })); 89 | } 90 | /// 91 | /// Gets an unsigned 16-bit integer (unsigned short) at the specified byte offset from the start of the view. 92 | /// 93 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 94 | /// True to read as little-endian; otherwise read as big-endian. 95 | public ushort GetUint16(uint byteOffset, bool littleEndian = false) 96 | { 97 | var eng = GetEngine(); 98 | var fn = GetBuiltinFunctionProperty("getUint16", "DataView.prototype.getUint16"); 99 | return (ushort)eng.Converter.ToDouble(fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian) })); 100 | } 101 | /// 102 | /// Gets a signed 32-bit integer (long) at the specified byte offset from the start of the view. 103 | /// 104 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 105 | /// True to read as little-endian; otherwise read as big-endian. 106 | public int GetInt32(uint byteOffset, bool littleEndian = false) 107 | { 108 | var eng = GetEngine(); 109 | var fn = GetBuiltinFunctionProperty("getInt32", "DataView.prototype.getInt32"); 110 | return eng.Converter.ToInt32(fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian) })); 111 | } 112 | /// 113 | /// Gets an unsigned 32-bit integer (unsigned long) at the specified byte offset from the start of the view. 114 | /// 115 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 116 | /// True to read as little-endian; otherwise read as big-endian. 117 | public uint GetUint32(uint byteOffset, bool littleEndian = false) 118 | { 119 | var eng = GetEngine(); 120 | var fn = GetBuiltinFunctionProperty("getUint32", "DataView.prototype.getUint32"); 121 | return (uint)eng.Converter.ToDouble(fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian) })); 122 | } 123 | /// 124 | /// Gets a signed 32-bit float (float) at the specified byte offset from the start of the view. 125 | /// 126 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 127 | /// True to read as little-endian; otherwise read as big-endian. 128 | public float GetFloat32(uint byteOffset, bool littleEndian = false) 129 | { 130 | var eng = GetEngine(); 131 | var fn = GetBuiltinFunctionProperty("getFloat32", "DataView.prototype.getFloat32"); 132 | return (float)eng.Converter.ToDouble(fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian) })); 133 | } 134 | /// 135 | /// Gets a signed 64-bit float (double) at the specified byte offset from the start of the view. 136 | /// 137 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 138 | /// True to read as little-endian; otherwise read as big-endian. 139 | public double GetFloat64(uint byteOffset, bool littleEndian = false) 140 | { 141 | var eng = GetEngine(); 142 | var fn = GetBuiltinFunctionProperty("getFloat64", "DataView.prototype.getFloat64"); 143 | return eng.Converter.ToDouble(fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian) })); 144 | } 145 | 146 | 147 | /// 148 | /// Stores a signed 8-bit integer (byte) value at the specified byte offset from the start of the view. 149 | /// 150 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 151 | /// The value to store. 152 | public void SetInt8(uint byteOffset, short value) 153 | { 154 | if (value < -128 || value > 127) 155 | throw new ArgumentOutOfRangeException(nameof(value)); 156 | 157 | var eng = GetEngine(); 158 | var fn = GetBuiltinFunctionProperty("setInt8", "DataView.prototype.setInt8"); 159 | fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromDouble(value) }); 160 | } 161 | /// 162 | /// Stores an unsigned 8-bit integer (unsigned byte) value at the specified byte offset from the start of the view. 163 | /// 164 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 165 | /// The value to store. 166 | public void SetUint8(uint byteOffset, byte value) 167 | { 168 | var eng = GetEngine(); 169 | var fn = GetBuiltinFunctionProperty("setUint8", "DataView.prototype.setUint8"); 170 | fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromDouble(value) }); 171 | } 172 | /// 173 | /// Stores a signed 16-bit integer (short) value at the specified byte offset from the start of the view. 174 | /// 175 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 176 | /// The value to store. 177 | /// True to store as little-endian; otherwise store as big-endian. 178 | public void SetInt16(uint byteOffset, short value, bool littleEndian = false) 179 | { 180 | var eng = GetEngine(); 181 | var fn = GetBuiltinFunctionProperty("setInt16", "DataView.prototype.setInt16"); 182 | fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian), eng.Converter.FromDouble(value) }); 183 | } 184 | /// 185 | /// Stores an unsigned 16-bit integer (ushort) value at the specified byte offset from the start of the view. 186 | /// 187 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 188 | /// The value to store. 189 | /// True to store as little-endian; otherwise store as big-endian. 190 | public void SetUint16(uint byteOffset, ushort value, bool littleEndian = false) 191 | { 192 | var eng = GetEngine(); 193 | var fn = GetBuiltinFunctionProperty("setUint16", "DataView.prototype.setUint16"); 194 | fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian), eng.Converter.FromDouble(value) }); 195 | } 196 | /// 197 | /// Stores a signed 32-bit integer (int) value at the specified byte offset from the start of the view. 198 | /// 199 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 200 | /// The value to store. 201 | /// True to store as little-endian; otherwise store as big-endian. 202 | public void SetInt32(uint byteOffset, int value, bool littleEndian = false) 203 | { 204 | var eng = GetEngine(); 205 | var fn = GetBuiltinFunctionProperty("setInt32", "DataView.prototype.setInt32"); 206 | fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian), eng.Converter.FromInt32(value) }); 207 | } 208 | /// 209 | /// Stores an unsigned 32-bit integer (uint) value at the specified byte offset from the start of the view. 210 | /// 211 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 212 | /// The value to store. 213 | /// True to store as little-endian; otherwise store as big-endian. 214 | public void SetUint32(uint byteOffset, uint value, bool littleEndian = false) 215 | { 216 | var eng = GetEngine(); 217 | var fn = GetBuiltinFunctionProperty("setUint32", "DataView.prototype.setUint32"); 218 | fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian), eng.Converter.FromDouble(value) }); 219 | } 220 | /// 221 | /// Stores a 32-bit float (float) value at the specified byte offset from the start of the view. 222 | /// 223 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 224 | /// The value to store. 225 | /// True to store as little-endian; otherwise store as big-endian. 226 | public void SetFloat32(uint byteOffset, float value, bool littleEndian = false) 227 | { 228 | var eng = GetEngine(); 229 | var fn = GetBuiltinFunctionProperty("setFloat32", "DataView.prototype.setFloat32"); 230 | fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian), eng.Converter.FromDouble(value) }); 231 | } 232 | /// 233 | /// Stores a 64-bit integer (double) value at the specified byte offset from the start of the view. 234 | /// 235 | /// The offset from the beginning of the DataView's view of the underlying ArrayBuffer. 236 | /// The value to store. 237 | /// True to store as little-endian; otherwise store as big-endian. 238 | public void SetFloat64(uint byteOffset, double value, bool littleEndian = false) 239 | { 240 | var eng = GetEngine(); 241 | var fn = GetBuiltinFunctionProperty("setFloat64", "DataView.prototype.setFloat64"); 242 | fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromDouble(byteOffset), eng.Converter.FromBoolean(littleEndian), eng.Converter.FromDouble(value) }); 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptObject.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Dynamic; 8 | using System.Runtime.InteropServices; 9 | using System.Diagnostics; 10 | 11 | namespace Microsoft.Scripting.JavaScript 12 | { 13 | public class JavaScriptObject : JavaScriptValue 14 | { 15 | internal JavaScriptObject(JavaScriptValueSafeHandle handle, JavaScriptValueType type, JavaScriptEngine engine): 16 | base(handle, type, engine) 17 | { 18 | 19 | } 20 | 21 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 22 | public JavaScriptArray Keys 23 | { 24 | get 25 | { 26 | var eng = GetEngine(); 27 | var fn = GetObjectBuiltinFunction("keys", "Object.keys"); 28 | return fn.Invoke(new JavaScriptValue[] { eng.UndefinedValue, this }) as JavaScriptArray; 29 | } 30 | } 31 | 32 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 33 | public bool IsExtensible 34 | { 35 | get 36 | { 37 | var eng = GetEngine(); 38 | 39 | bool result; 40 | Errors.ThrowIfIs(api_.JsGetExtensionAllowed(handle_, out result)); 41 | 42 | return result; 43 | } 44 | } 45 | 46 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 47 | public JavaScriptObject Prototype 48 | { 49 | get 50 | { 51 | var eng = GetEngine(); 52 | 53 | JavaScriptValueSafeHandle handle; 54 | Errors.ThrowIfIs(api_.JsGetPrototype(handle_, out handle)); 55 | 56 | return eng.CreateObjectFromHandle(handle); 57 | } 58 | set 59 | { 60 | var eng = GetEngine(); 61 | if (value == null) 62 | value = eng.NullValue; 63 | 64 | Errors.ThrowIfIs(api_.JsSetPrototype(handle_, value.handle_)); 65 | } 66 | } 67 | 68 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 69 | public object ExternalObject 70 | { 71 | get 72 | { 73 | var eng = GetEngine(); 74 | return eng.GetExternalObjectFrom(this); 75 | } 76 | } 77 | 78 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 79 | public bool IsSealed 80 | { 81 | get 82 | { 83 | var eng = GetEngine(); 84 | var fn = GetObjectBuiltinFunction("isSealed", "Object.isSealed"); 85 | 86 | return eng.Converter.ToBoolean(fn.Invoke(new JavaScriptValue[] { eng.UndefinedValue, this })); 87 | } 88 | } 89 | 90 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 91 | public bool IsFrozen 92 | { 93 | get 94 | { 95 | var eng = GetEngine(); 96 | var fn = GetObjectBuiltinFunction("isFrozen", "Object.isFrozen"); 97 | 98 | return eng.Converter.ToBoolean(fn.Invoke(new JavaScriptValue[] { eng.UndefinedValue, this })); 99 | } 100 | } 101 | 102 | internal JavaScriptFunction GetBuiltinFunctionProperty(string functionName, string nameIfNotFound) 103 | { 104 | var fn = GetPropertyByName(functionName) as JavaScriptFunction; 105 | if (fn == null) 106 | Errors.ThrowIOEFmt(Errors.DefaultFnOverwritten, nameIfNotFound); 107 | 108 | return fn; 109 | } 110 | 111 | internal JavaScriptFunction GetObjectBuiltinFunction(string functionName, string nameIfNotFound) 112 | { 113 | var eng = GetEngine(); 114 | var obj = eng.GlobalObject.GetPropertyByName("Object") as JavaScriptFunction; 115 | if (obj == null) 116 | Errors.ThrowIOEFmt(Errors.DefaultFnOverwritten, "Object"); 117 | var fn = obj.GetPropertyByName(functionName) as JavaScriptFunction; 118 | if (fn == null) 119 | Errors.ThrowIOEFmt(Errors.DefaultFnOverwritten, nameIfNotFound); 120 | 121 | return fn; 122 | } 123 | 124 | public bool IsPrototypeOf(JavaScriptObject other) 125 | { 126 | if (other == null) 127 | throw new ArgumentNullException(nameof(other)); 128 | 129 | var eng = GetEngine(); 130 | var fn = GetBuiltinFunctionProperty("isPrototypeOf", "Object.prototype.isPrototypeOf"); 131 | 132 | var args = new List() { this, other }; 133 | 134 | return eng.Converter.ToBoolean(fn.Invoke(args)); 135 | } 136 | 137 | public bool PropertyIsEnumerable(string propertyName) 138 | { 139 | var eng = GetEngine(); 140 | var fn = GetBuiltinFunctionProperty("propertyIsEnumerable", "Object.prototype.propertyIsEnumerable"); 141 | using (var jsPropName = eng.Converter.FromString(propertyName)) 142 | { 143 | var args = new List() { this, jsPropName }; 144 | return eng.Converter.ToBoolean(fn.Invoke(args)); 145 | } 146 | } 147 | 148 | public JavaScriptValue GetPropertyByName(string propertyName) 149 | { 150 | var eng = GetEngine(); 151 | 152 | IntPtr propId; 153 | Errors.ThrowIfIs(api_.JsGetPropertyIdFromName(propertyName, out propId)); 154 | 155 | JavaScriptValueSafeHandle resultHandle; 156 | Errors.ThrowIfIs(api_.JsGetProperty(handle_, propId, out resultHandle)); 157 | 158 | return eng.CreateValueFromHandle(resultHandle); 159 | } 160 | 161 | public void SetPropertyByName(string propertyName, JavaScriptValue value) 162 | { 163 | var eng = GetEngine(); 164 | 165 | IntPtr propId; 166 | Errors.ThrowIfIs(api_.JsGetPropertyIdFromName(propertyName, out propId)); 167 | Errors.ThrowIfIs(api_.JsSetProperty(handle_, propId, value.handle_, false)); 168 | } 169 | 170 | public void DeletePropertyByName(string propertyName) 171 | { 172 | var eng = GetEngine(); 173 | 174 | IntPtr propId; 175 | Errors.ThrowIfIs(api_.JsGetPropertyIdFromName(propertyName, out propId)); 176 | 177 | JavaScriptValueSafeHandle tmpResult; 178 | Errors.ThrowIfIs(api_.JsDeleteProperty(handle_, propId, false, out tmpResult)); 179 | tmpResult.Dispose(); 180 | } 181 | 182 | public JavaScriptValue this[string name] 183 | { 184 | get 185 | { 186 | return GetPropertyByName(name); 187 | } 188 | set 189 | { 190 | SetPropertyByName(name, value); 191 | } 192 | } 193 | 194 | public JavaScriptValue GetPropertyBySymbol(JavaScriptSymbol symbol) 195 | { 196 | var eng = GetEngine(); 197 | 198 | IntPtr propId; 199 | Errors.ThrowIfIs(api_.JsGetPropertyIdFromSymbol(symbol.handle_, out propId)); 200 | 201 | JavaScriptValueSafeHandle resultHandle; 202 | Errors.ThrowIfIs(api_.JsGetProperty(handle_, propId, out resultHandle)); 203 | 204 | return eng.CreateValueFromHandle(resultHandle); 205 | } 206 | 207 | public void SetPropertyBySymbol(JavaScriptSymbol symbol, JavaScriptValue value) 208 | { 209 | var eng = GetEngine(); 210 | 211 | IntPtr propId; 212 | Errors.ThrowIfIs(api_.JsGetPropertyIdFromSymbol(symbol.handle_, out propId)); 213 | Errors.ThrowIfIs(api_.JsSetProperty(handle_, propId, value.handle_, false)); 214 | } 215 | 216 | public void DeletePropertyBySymbol(JavaScriptSymbol symbol) 217 | { 218 | var eng = GetEngine(); 219 | 220 | IntPtr propId; 221 | Errors.ThrowIfIs(api_.JsGetPropertyIdFromSymbol(symbol.handle_, out propId)); 222 | 223 | JavaScriptValueSafeHandle tmpResult; 224 | Errors.ThrowIfIs(api_.JsDeleteProperty(handle_, propId, false, out tmpResult)); 225 | tmpResult.Dispose(); 226 | } 227 | 228 | public JavaScriptValue this[JavaScriptSymbol symbol] 229 | { 230 | get 231 | { 232 | return GetPropertyBySymbol(symbol); 233 | } 234 | set 235 | { 236 | SetPropertyBySymbol(symbol, value); 237 | } 238 | } 239 | 240 | public JavaScriptValue GetValueAtIndex(JavaScriptValue index) 241 | { 242 | if (index == null) 243 | throw new ArgumentNullException(nameof(index)); 244 | 245 | var eng = GetEngine(); 246 | JavaScriptValueSafeHandle result; 247 | Errors.ThrowIfIs(api_.JsGetIndexedProperty(handle_, index.handle_, out result)); 248 | 249 | return eng.CreateValueFromHandle(result); 250 | } 251 | 252 | public void SetValueAtIndex(JavaScriptValue index, JavaScriptValue value) 253 | { 254 | if (index == null) 255 | throw new ArgumentNullException(nameof(index)); 256 | 257 | var eng = GetEngine(); 258 | if (value == null) 259 | value = eng.NullValue; 260 | 261 | Errors.ThrowIfIs(api_.JsSetIndexedProperty(handle_, index.handle_, value.handle_)); 262 | } 263 | 264 | public void DeleteValueAtIndex(JavaScriptValue index) 265 | { 266 | if (index == null) 267 | throw new ArgumentNullException(nameof(index)); 268 | 269 | Errors.ThrowIfIs(api_.JsDeleteIndexedProperty(handle_, index.handle_)); 270 | } 271 | 272 | public JavaScriptValue this[JavaScriptValue index] 273 | { 274 | get 275 | { 276 | return GetValueAtIndex(index); 277 | } 278 | set 279 | { 280 | SetValueAtIndex(index, value); 281 | } 282 | } 283 | 284 | public bool HasOwnProperty(string propertyName) 285 | { 286 | var eng = GetEngine(); 287 | var fn = GetBuiltinFunctionProperty("hasOwnProperty", "Object.prototype.hasOwnProperty"); 288 | 289 | return eng.Converter.ToBoolean(fn.Invoke(new JavaScriptValue[] { this, eng.Converter.FromString(propertyName) })); 290 | } 291 | 292 | public bool HasProperty(string propertyName) 293 | { 294 | IntPtr propId; 295 | Errors.ThrowIfIs(api_.JsGetPropertyIdFromName(propertyName, out propId)); 296 | bool has; 297 | Errors.ThrowIfIs(api_.JsHasProperty(handle_, propId, out has)); 298 | 299 | return has; 300 | } 301 | 302 | public JavaScriptObject GetOwnPropertyDescriptor(string propertyName) 303 | { 304 | var eng = GetEngine(); 305 | IntPtr propId; 306 | Errors.ThrowIfIs(api_.JsGetPropertyIdFromName(propertyName, out propId)); 307 | JavaScriptValueSafeHandle resultHandle; 308 | Errors.ThrowIfIs(api_.JsGetOwnPropertyDescriptor(handle_, propId, out resultHandle)); 309 | 310 | return eng.CreateObjectFromHandle(resultHandle); 311 | } 312 | 313 | public void DefineProperty(string propertyName, JavaScriptObject descriptor) 314 | { 315 | if (descriptor == null) 316 | throw new ArgumentNullException(nameof(descriptor)); 317 | 318 | var eng = GetEngine(); 319 | 320 | IntPtr propId; 321 | Errors.ThrowIfIs(api_.JsGetPropertyIdFromName(propertyName, out propId)); 322 | 323 | bool wasSet; 324 | Errors.CheckForScriptExceptionOrThrow(api_.JsDefineProperty(handle_, propId, descriptor.handle_, out wasSet), eng); 325 | } 326 | 327 | public void DefineProperties(JavaScriptObject propertiesContainer) 328 | { 329 | var eng = GetEngine(); 330 | var fnDP = GetObjectBuiltinFunction("defineProperties", "Object.defineProperties"); 331 | 332 | fnDP.Invoke(new JavaScriptValue[] { eng.UndefinedValue, this, propertiesContainer }); 333 | } 334 | 335 | public JavaScriptArray GetOwnPropertyNames() 336 | { 337 | var eng = GetEngine(); 338 | 339 | JavaScriptValueSafeHandle resultHandle; 340 | Errors.ThrowIfIs(api_.JsGetOwnPropertyNames(handle_, out resultHandle)); 341 | 342 | return eng.CreateArrayFromHandle(resultHandle); 343 | } 344 | 345 | public JavaScriptArray GetOwnPropertySymbols() 346 | { 347 | var eng = GetEngine(); 348 | 349 | JavaScriptValueSafeHandle resultHandle; 350 | Errors.ThrowIfIs(api_.JsGetOwnPropertySymbols(handle_, out resultHandle)); 351 | 352 | return eng.CreateArrayFromHandle(resultHandle); 353 | } 354 | 355 | public void PreventExtensions() 356 | { 357 | Errors.ThrowIfIs(api_.JsPreventExtension(handle_)); 358 | } 359 | 360 | public void Seal() 361 | { 362 | var eng = GetEngine(); 363 | var fn = GetObjectBuiltinFunction("seal", "Object.seal"); 364 | 365 | fn.Invoke(new JavaScriptValue[] { eng.UndefinedValue, this }); 366 | } 367 | 368 | public void Freeze() 369 | { 370 | var eng = GetEngine(); 371 | var fn = GetObjectBuiltinFunction("freeze", "Object.freeze"); 372 | 373 | fn.Invoke(new JavaScriptValue[] { eng.UndefinedValue, this }); 374 | } 375 | 376 | #region DynamicObject overrides 377 | public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 378 | { 379 | var fn = GetPropertyByName(binder.Name) as JavaScriptFunction; 380 | var eng = GetEngine(); 381 | var c = eng.Converter; 382 | 383 | if (fn != null) 384 | { 385 | result = fn.Invoke(args.Select(a => c.FromObject(a))); 386 | return true; 387 | } 388 | return base.TryInvokeMember(binder, args, out result); 389 | } 390 | 391 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) 392 | { 393 | if (indexes.Length > 1) 394 | return base.TryGetIndex(binder, indexes, out result); 395 | 396 | var eng = GetEngine(); 397 | var jsIndex = eng.Converter.FromObject(indexes[0]); 398 | result = GetValueAtIndex(jsIndex); 399 | return true; 400 | } 401 | 402 | public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) 403 | { 404 | if (indexes.Length > 1) 405 | return base.TrySetIndex(binder, indexes, value); 406 | 407 | var eng = GetEngine(); 408 | var jsIndex = eng.Converter.FromObject(indexes[0]); 409 | var jsVal = eng.Converter.FromObject(value); 410 | SetValueAtIndex(jsIndex, jsVal); 411 | 412 | return true; 413 | } 414 | 415 | public override bool TryGetMember(GetMemberBinder binder, out object result) 416 | { 417 | result = GetPropertyByName(binder.Name); 418 | return true; 419 | } 420 | 421 | public override bool TrySetMember(SetMemberBinder binder, object value) 422 | { 423 | var jsVal = GetEngine().Converter.FromObject(value); 424 | SetPropertyByName(binder.Name, jsVal); 425 | 426 | return true; 427 | } 428 | 429 | public override bool TryDeleteMember(DeleteMemberBinder binder) 430 | { 431 | DeletePropertyByName(binder.Name); 432 | return true; 433 | } 434 | 435 | public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes) 436 | { 437 | if (indexes.Length > 1) 438 | return base.TryDeleteIndex(binder, indexes); 439 | 440 | var jsIndex = GetEngine().Converter.FromObject(indexes[0]); 441 | DeleteValueAtIndex(jsIndex); 442 | 443 | return true; 444 | } 445 | 446 | public override IEnumerable GetDynamicMemberNames() 447 | { 448 | return Keys.Select(v => v.ToString()); 449 | } 450 | #endregion 451 | 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript; 2 | using Microsoft.Scripting.JavaScript.SafeHandles; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.Scripting 11 | { 12 | /*internal static class NativeMethods 13 | { 14 | [DllImport("chakra")] 15 | internal static extern JsErrorCode JsAddRef(IntPtr @ref, out uint count); 16 | [DllImport("chakra")] 17 | internal static extern JsErrorCode JsRelease(IntPtr @ref, out uint count); 18 | 19 | #region Runtime functions 20 | [DllImport("chakra")] 21 | internal static extern JsErrorCode JsDisposeRuntime(IntPtr runtime); 22 | [DllImport("chakra")] 23 | internal static extern JsErrorCode JsCreateRuntime(JsRuntimeAttributes attributes, IntPtr threadService, out JavaScriptRuntimeSafeHandle runtime); 24 | [DllImport("chakra")] 25 | internal static extern JsErrorCode JsSetRuntimeMemoryAllocationCallback(JavaScriptRuntimeSafeHandle runtime, IntPtr extraInformation, IntPtr pfnCallback); 26 | [DllImport("chakra")] 27 | internal static extern JsErrorCode JsCollectGarbage(JavaScriptRuntimeSafeHandle runtime); 28 | [DllImport("chakra")] 29 | internal static extern JsErrorCode JsDisableRuntimeExecution(JavaScriptRuntimeSafeHandle runtime); 30 | [DllImport("chakra")] 31 | internal static extern JsErrorCode JsEnableRuntimeExecution(JavaScriptRuntimeSafeHandle runtime); 32 | [DllImport("chakra")] 33 | internal static extern JsErrorCode JsGetRuntimeMemoryUsage(JavaScriptRuntimeSafeHandle runtime, out ulong usage); 34 | [DllImport("chakra")] 35 | internal static extern JsErrorCode JsIsRuntimeExecutionDisabled(JavaScriptRuntimeSafeHandle runtime, out bool isDisabled); 36 | [DllImport("chakra")] 37 | internal static extern JsErrorCode JsCreateContext(JavaScriptRuntimeSafeHandle runtime, out JavaScriptEngineSafeHandle engine); 38 | #endregion 39 | 40 | #region Context functions 41 | [DllImport("chakra")] 42 | internal static extern JsErrorCode JsSetCurrentContext(JavaScriptEngineSafeHandle contextHandle); 43 | [DllImport("chakra")] 44 | internal static extern JsErrorCode JsBooleanToBool( 45 | JavaScriptValueSafeHandle valueRef, 46 | [MarshalAs(UnmanagedType.U1)] out bool result); 47 | [DllImport("chakra")] 48 | internal static extern JsErrorCode JsConvertValueToBoolean(JavaScriptValueSafeHandle valueToConvert, out JavaScriptValueSafeHandle resultHandle); 49 | 50 | [DllImport("chakra")] 51 | internal static extern JsErrorCode JsNumberToDouble(JavaScriptValueSafeHandle valueRef, out double result); 52 | [DllImport("chakra")] 53 | internal static extern JsErrorCode JsDoubleToNumber(double value, out JavaScriptValueSafeHandle result); 54 | [DllImport("chakra")] 55 | internal static extern JsErrorCode JsConvertValueToNumber(JavaScriptValueSafeHandle valueToConvert, out JavaScriptValueSafeHandle resultHandle); 56 | [DllImport("chakra")] 57 | internal static extern JsErrorCode JsGetValueType(JavaScriptValueSafeHandle value, out JsValueType kind); 58 | [DllImport("chakra")] 59 | internal static extern JsErrorCode JsNumberToInt(JavaScriptValueSafeHandle valueRef, out int result); 60 | [DllImport("chakra")] 61 | internal static extern JsErrorCode JsIntToNumber(int value, out JavaScriptValueSafeHandle result); 62 | [DllImport("chakra")] 63 | internal static unsafe extern JsErrorCode JsPointerToString(void* psz, int length, out JavaScriptValueSafeHandle result); 64 | [DllImport("chakra")] 65 | internal static unsafe extern JsErrorCode JsStringToPointer(JavaScriptValueSafeHandle str, out void* result, out uint strLen); 66 | [DllImport("chakra")] 67 | internal static extern JsErrorCode JsConvertValueToString(JavaScriptValueSafeHandle valueToConvert, out JavaScriptValueSafeHandle resultHandle); 68 | [DllImport("chakra")] 69 | internal static extern JsErrorCode JsEquals(JavaScriptValueSafeHandle obj1, JavaScriptValueSafeHandle obj2, [MarshalAs(UnmanagedType.U1)] out bool result); 70 | [DllImport("chakra")] 71 | internal static extern JsErrorCode JsStrictEquals(JavaScriptValueSafeHandle obj1, JavaScriptValueSafeHandle obj2, [MarshalAs(UnmanagedType.U1)] out bool result); 72 | [DllImport("chakra")] 73 | internal static extern JsErrorCode JsCallFunction( 74 | JavaScriptValueSafeHandle fnHandle, 75 | [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] JavaScriptValueSafeHandle[] arguments, 76 | ushort argCount, 77 | out JavaScriptValueSafeHandle result); 78 | [DllImport("chakra")] 79 | internal static extern JsErrorCode JsConstructObject( 80 | JavaScriptValueSafeHandle fnHandle, 81 | [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] JavaScriptValueSafeHandle[] arguments, 82 | ushort argCount, 83 | out JavaScriptValueSafeHandle result); 84 | 85 | [DllImport("chakra")] 86 | internal static extern JsErrorCode JsGetPropertyIdFromName( 87 | [MarshalAs(UnmanagedType.LPWStr)] string propertyName, 88 | out IntPtr propertyId); 89 | [DllImport("chakra")] 90 | internal static extern JsErrorCode JsGetPropertyIdFromSymbol(JavaScriptValueSafeHandle valueHandle, out IntPtr propertyId); 91 | 92 | [DllImport("chakra")] 93 | internal static extern JsErrorCode JsGetProperty(JavaScriptValueSafeHandle obj, IntPtr propertyId, out JavaScriptValueSafeHandle result); 94 | [DllImport("chakra")] 95 | internal static extern JsErrorCode JsSetProperty(JavaScriptValueSafeHandle obj, IntPtr propertyId, JavaScriptValueSafeHandle propertyValue, [MarshalAs(UnmanagedType.U1)] bool useStrictSemantics); 96 | [DllImport("chakra")] 97 | internal static extern JsErrorCode JsDeleteProperty(JavaScriptValueSafeHandle obj, IntPtr propertyId, [MarshalAs(UnmanagedType.U1)] bool useStrictSemantics, out JavaScriptValueSafeHandle result); 98 | [DllImport("chakra")] 99 | internal static extern JsErrorCode JsGetIndexedProperty(JavaScriptValueSafeHandle obj, JavaScriptValueSafeHandle index, out JavaScriptValueSafeHandle result); 100 | [DllImport("chakra")] 101 | internal static extern JsErrorCode JsSetIndexedProperty(JavaScriptValueSafeHandle obj, JavaScriptValueSafeHandle index, JavaScriptValueSafeHandle value); 102 | [DllImport("chakra")] 103 | internal static extern JsErrorCode JsDeleteIndexedProperty(JavaScriptValueSafeHandle obj, JavaScriptValueSafeHandle index); 104 | [DllImport("chakra")] 105 | internal static extern JsErrorCode JsHasProperty(JavaScriptValueSafeHandle obj, IntPtr propertyId, [MarshalAs(UnmanagedType.U1)] out bool has); 106 | [DllImport("chakra")] 107 | internal static extern JsErrorCode JsGetOwnPropertyDescriptor(JavaScriptValueSafeHandle obj, IntPtr propertyId, out JavaScriptValueSafeHandle descriptor); 108 | [DllImport("chakra")] 109 | internal static extern JsErrorCode JsDefineProperty(JavaScriptValueSafeHandle obj, IntPtr propId, JavaScriptValueSafeHandle descriptorRef, [MarshalAs(UnmanagedType.U1)] out bool wasSet); 110 | [DllImport("chakra")] 111 | internal static extern JsErrorCode JsGetOwnPropertyNames(JavaScriptValueSafeHandle obj, out JavaScriptValueSafeHandle result); 112 | [DllImport("chakra")] 113 | internal static extern JsErrorCode JsGetOwnPropertySymbols(JavaScriptValueSafeHandle obj, out JavaScriptValueSafeHandle result); 114 | 115 | 116 | [DllImport("chakra")] 117 | internal static extern JsErrorCode JsPreventExtension(JavaScriptValueSafeHandle obj); 118 | [DllImport("chakra")] 119 | internal static extern JsErrorCode JsGetExtensionAllowed(JavaScriptValueSafeHandle obj, [MarshalAs(UnmanagedType.U1)] out bool allowed); 120 | 121 | [DllImport("chakra")] 122 | internal static extern JsErrorCode JsGetPrototype(JavaScriptValueSafeHandle obj, out JavaScriptValueSafeHandle prototype); 123 | [DllImport("chakra")] 124 | internal static extern JsErrorCode JsSetPrototype(JavaScriptValueSafeHandle obj, JavaScriptValueSafeHandle prototype); 125 | 126 | [DllImport("chakra")] 127 | internal static extern JsErrorCode JsGetArrayBufferStorage(JavaScriptValueSafeHandle obj, out IntPtr buffer, out uint len); 128 | [DllImport("chakra")] 129 | internal static extern JsErrorCode JsGetTypedArrayStorage(JavaScriptValueSafeHandle obj, out IntPtr buf, out uint len, out JavaScriptTypedArrayType type, out int elemSize); 130 | 131 | [DllImport("chakra")] 132 | internal static extern JsErrorCode JsGetGlobalObject(out JavaScriptValueSafeHandle globalHandle); 133 | [DllImport("chakra")] 134 | internal static extern JsErrorCode JsGetUndefinedValue(out JavaScriptValueSafeHandle globalHandle); 135 | [DllImport("chakra")] 136 | internal static extern JsErrorCode JsGetNullValue(out JavaScriptValueSafeHandle globalHandle); 137 | [DllImport("chakra")] 138 | internal static extern JsErrorCode JsGetTrueValue(out JavaScriptValueSafeHandle globalHandle); 139 | [DllImport("chakra")] 140 | internal static extern JsErrorCode JsGetFalseValue(out JavaScriptValueSafeHandle globalHandle); 141 | [DllImport("chakra")] 142 | internal static extern JsErrorCode JsHasException([MarshalAs(UnmanagedType.U1)] out bool has); 143 | [DllImport("chakra")] 144 | internal static extern JsErrorCode JsCreateObject(out JavaScriptValueSafeHandle result); 145 | [DllImport("chakra")] 146 | internal static extern JsErrorCode JsCreateExternalObject(IntPtr data, IntPtr finalizeCallback, out JavaScriptValueSafeHandle handle); 147 | [DllImport("chakra")] 148 | internal static extern JsErrorCode JsCreateSymbol(JavaScriptValueSafeHandle descriptionHandle, out JavaScriptValueSafeHandle result); 149 | [DllImport("chakra")] 150 | internal static extern JsErrorCode JsCreateArray(uint length, out JavaScriptValueSafeHandle result); 151 | [DllImport("chakra")] 152 | internal static extern JsErrorCode JsCreateFunction(IntPtr callback, IntPtr extraData, out JavaScriptValueSafeHandle result); 153 | [DllImport("chakra")] 154 | internal static extern JsErrorCode JsCreateNamedFunction(JavaScriptValueSafeHandle nameHandle, IntPtr callback, IntPtr extraData, out JavaScriptValueSafeHandle result); 155 | 156 | [DllImport("chakra")] 157 | internal static extern JsErrorCode JsIdle(out uint nextTick); 158 | [DllImport("chakra")] 159 | internal static extern JsErrorCode JsGetAndClearException(out JavaScriptValueSafeHandle result); 160 | [DllImport("chakra")] 161 | internal static extern JsErrorCode JsSetException(JavaScriptValueSafeHandle exception); 162 | 163 | [DllImport("chakra")] 164 | internal static extern JsErrorCode JsCreateError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 165 | [DllImport("chakra")] 166 | internal static extern JsErrorCode JsCreateRangeError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 167 | [DllImport("chakra")] 168 | internal static extern JsErrorCode JsCreateReferenceError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 169 | [DllImport("chakra")] 170 | internal static extern JsErrorCode JsCreateSyntaxError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 171 | [DllImport("chakra")] 172 | internal static extern JsErrorCode JsCreateTypeError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 173 | [DllImport("chakra")] 174 | internal static extern JsErrorCode JsCreateURIError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 175 | [DllImport("chakra")] 176 | internal static extern JsErrorCode JsStartDebugging(); 177 | #endregion 178 | 179 | #region Execution functions 180 | [DllImport("chakra")] 181 | internal static extern JsErrorCode JsParseScript( 182 | [MarshalAs(UnmanagedType.LPWStr)] string script, 183 | long sourceContextId, 184 | [MarshalAs(UnmanagedType.LPWStr)] string sourceUrl, 185 | out JavaScriptValueSafeHandle handle); 186 | 187 | [DllImport("chakra")] 188 | internal static extern JsErrorCode JsParseSerializedScript( 189 | [MarshalAs(UnmanagedType.LPWStr)] string script, 190 | IntPtr buffer, 191 | long sourceContext, 192 | [MarshalAs(UnmanagedType.LPWStr)] string sourceUrl, 193 | out JavaScriptValueSafeHandle handle); 194 | 195 | [DllImport("chakra")] 196 | internal static extern JsErrorCode JsSerializeScript( 197 | [MarshalAs(UnmanagedType.LPWStr)] string script, 198 | IntPtr buffer, 199 | ref uint bufferSize); 200 | 201 | [DllImport("chakra")] 202 | internal static extern JsErrorCode JsRunScript( 203 | [MarshalAs(UnmanagedType.LPWStr)] string script, 204 | long sourceContextId, 205 | [MarshalAs(UnmanagedType.LPWStr)] string sourceUrl, 206 | out JavaScriptValueSafeHandle handle); 207 | 208 | [DllImport("chakra")] 209 | internal static extern JsErrorCode JsRunSerializedScript( 210 | [MarshalAs(UnmanagedType.LPWStr)] string script, 211 | IntPtr buffer, 212 | long sourceContext, 213 | [MarshalAs(UnmanagedType.LPWStr)] string sourceUrl, 214 | out JavaScriptValueSafeHandle handle); 215 | #endregion 216 | } */ 217 | 218 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 219 | internal delegate bool MemoryCallbackThunkCallback(IntPtr callbackState, JavaScriptMemoryAllocationEventType allocationEvent, UIntPtr allocationSize); 220 | 221 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 222 | internal delegate IntPtr NativeFunctionThunkCallback( 223 | IntPtr callee, 224 | [MarshalAs(UnmanagedType.U1)] bool asConstructor, 225 | [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] IntPtr[] args, 226 | ushort argCount, 227 | IntPtr data); 228 | 229 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] 230 | internal delegate void JsFinalizeCallback(IntPtr data); 231 | 232 | internal enum JsErrorCode 233 | { 234 | /// 235 | /// Success error code. 236 | /// 237 | JsNoError = 0, 238 | 239 | /// 240 | /// Category of errors that relates to incorrect usage of the API itself. 241 | /// 242 | JsErrorCategoryUsage = 0x10000, 243 | /// 244 | /// An argument to a hosting API was invalid. 245 | /// 246 | JsErrorInvalidArgument, 247 | /// 248 | /// An argument to a hosting API was null in a context where null is not allowed. 249 | /// 250 | JsErrorNullArgument, 251 | /// 252 | /// The hosting API requires that a context be current, but there is no current context. 253 | /// 254 | JsErrorNoCurrentContext, 255 | /// 256 | /// The engine is in an exception state and no APIs can be called until the exception is 257 | /// cleared. 258 | /// 259 | JsErrorInExceptionState, 260 | /// 261 | /// A hosting API is not yet implemented. 262 | /// 263 | JsErrorNotImplemented, 264 | /// 265 | /// A hosting API was called on the wrong thread. 266 | /// 267 | JsErrorWrongThread, 268 | /// 269 | /// A runtime that is still in use cannot be disposed. 270 | /// 271 | JsErrorRuntimeInUse, 272 | /// 273 | /// A bad serialized script was used, or the serialized script was serialized by a 274 | /// different version of the Chakra engine. 275 | /// 276 | JsErrorBadSerializedScript, 277 | /// 278 | /// The runtime is in a disabled state. 279 | /// 280 | JsErrorInDisabledState, 281 | /// 282 | /// Runtime does not support reliable script interruption. 283 | /// 284 | JsErrorCannotDisableExecution, 285 | /// 286 | /// A heap enumeration is currently underway in the script context. 287 | /// 288 | JsErrorHeapEnumInProgress, 289 | /// 290 | /// A hosting API that operates on object values was called with a non-object value. 291 | /// 292 | JsErrorArgumentNotObject, 293 | /// 294 | /// A script context is in the middle of a profile callback. 295 | /// 296 | JsErrorInProfileCallback, 297 | /// 298 | /// A thread service callback is currently underway. 299 | /// 300 | JsErrorInThreadServiceCallback, 301 | /// 302 | /// Scripts cannot be serialized in debug contexts. 303 | /// 304 | JsErrorCannotSerializeDebugScript, 305 | /// 306 | /// The context cannot be put into a debug state because it is already in a debug state. 307 | /// 308 | JsErrorAlreadyDebuggingContext, 309 | /// 310 | /// The context cannot start profiling because it is already profiling. 311 | /// 312 | JsErrorAlreadyProfilingContext, 313 | /// 314 | /// Idle notification given when the host did not enable idle processing. 315 | /// 316 | JsErrorIdleNotEnabled, 317 | /// 318 | /// The context did not accept the enqueue callback. 319 | /// 320 | JsCannotSetProjectionEnqueueCallback, 321 | /// 322 | /// Failed to start projection. 323 | /// 324 | JsErrorCannotStartProjection, 325 | /// 326 | /// The operation is not supported in an object before collect callback. 327 | /// 328 | JsErrorInObjectBeforeCollectCallback, 329 | /// 330 | /// Object cannot be unwrapped to IInspectable pointer. 331 | /// 332 | JsErrorObjectNotInspectable, 333 | /// 334 | /// A hosting API that operates on symbol property ids but was called with a non-symbol property id. 335 | /// The error code is returned by JsGetSymbolFromPropertyId if the function is called with non-symbol property id. 336 | /// 337 | JsErrorPropertyNotSymbol, 338 | /// 339 | /// A hosting API that operates on string property ids but was called with a non-string property id. 340 | /// The error code is returned by existing JsGetPropertyNamefromId if the function is called with non-string property id. 341 | /// 342 | JsErrorPropertyNotString, 343 | 344 | /// 345 | /// Category of errors that relates to errors occurring within the engine itself. 346 | /// 347 | JsErrorCategoryEngine = 0x20000, 348 | /// 349 | /// The Chakra engine has run out of memory. 350 | /// 351 | JsErrorOutOfMemory, 352 | 353 | /// 354 | /// Category of errors that relates to errors in a script. 355 | /// 356 | JsErrorCategoryScript = 0x30000, 357 | /// 358 | /// A JavaScript exception occurred while running a script. 359 | /// 360 | JsErrorScriptException, 361 | /// 362 | /// JavaScript failed to compile. 363 | /// 364 | JsErrorScriptCompile, 365 | /// 366 | /// A script was terminated due to a request to suspend a runtime. 367 | /// 368 | JsErrorScriptTerminated, 369 | /// 370 | /// A script was terminated because it tried to use eval or function and eval 371 | /// was disabled. 372 | /// 373 | JsErrorScriptEvalDisabled, 374 | 375 | /// 376 | /// Category of errors that are fatal and signify failure of the engine. 377 | /// 378 | JsErrorCategoryFatal = 0x40000, 379 | /// 380 | /// A fatal error in the engine has occurred. 381 | /// 382 | JsErrorFatal, 383 | } 384 | 385 | [Flags] 386 | internal enum JsRuntimeAttributes 387 | { 388 | /// 389 | /// No special attributes. 390 | /// 391 | None = 0x00000000, 392 | /// 393 | /// The runtime will not do any work (such as garbage collection) on background threads. 394 | /// 395 | DisableBackgroundWork = 0x00000001, 396 | /// 397 | /// The runtime should support reliable script interruption. This increases the number of 398 | /// places where the runtime will check for a script interrupt request at the cost of a 399 | /// small amount of runtime performance. 400 | /// 401 | AllowScriptInterrupt = 0x00000002, 402 | /// 403 | /// Host will call JsIdle, so enable idle processing. Otherwise, the runtime will 404 | /// manage memory slightly more aggressively. 405 | /// 406 | EnableIdleProcessing = 0x00000004, 407 | /// 408 | /// Runtime will not generate native code. 409 | /// 410 | DisableNativeCodeGeneration = 0x00000008, 411 | /// 412 | /// Using eval or function constructor will throw an exception. 413 | /// 414 | DisableEval = 0x00000010, 415 | } 416 | 417 | internal enum JsValueType 418 | { 419 | /// 420 | /// The value is the undefined value. 421 | /// 422 | JsUndefined = 0, 423 | /// 424 | /// The value is the null value. 425 | /// 426 | JsNull = 1, 427 | /// 428 | /// The value is a JavaScript number value. 429 | /// 430 | JsNumber = 2, 431 | /// 432 | /// The value is a JavaScript string value. 433 | /// 434 | JsString = 3, 435 | /// 436 | /// The value is a JavaScript Boolean value. 437 | /// 438 | JsBoolean = 4, 439 | /// 440 | /// The value is a JavaScript object value. 441 | /// 442 | JsObject = 5, 443 | /// 444 | /// The value is a JavaScript function object value. 445 | /// 446 | JsFunction = 6, 447 | /// 448 | /// The value is a JavaScript error object value. 449 | /// 450 | JsError = 7, 451 | /// 452 | /// The value is a JavaScript array object value. 453 | /// 454 | JsArray = 8, 455 | /// 456 | /// The value is a JavaScript symbol value. 457 | /// 458 | JsSymbol = 9, 459 | /// 460 | /// The value is a JavaScript ArrayBuffer object value. 461 | /// 462 | JsArrayBuffer = 10, 463 | /// 464 | /// The value is a JavaScript typed array object value. 465 | /// 466 | JsTypedArray = 11, 467 | /// 468 | /// The value is a JavaScript DataView object value. 469 | /// 470 | JsDataView = 12, 471 | } 472 | 473 | internal static class ConvertExtensions 474 | { 475 | public static JavaScriptValueType ToApiValueType(this JsValueType type) 476 | { 477 | switch (type) 478 | { 479 | case JsValueType.JsArray: 480 | return JavaScriptValueType.Array; 481 | 482 | case JsValueType.JsArrayBuffer: 483 | return JavaScriptValueType.ArrayBuffer; 484 | 485 | case JsValueType.JsBoolean: 486 | return JavaScriptValueType.Boolean; 487 | 488 | case JsValueType.JsDataView: 489 | return JavaScriptValueType.DataView; 490 | 491 | case JsValueType.JsFunction: 492 | return JavaScriptValueType.Function; 493 | 494 | case JsValueType.JsNumber: 495 | return JavaScriptValueType.Number; 496 | 497 | case JsValueType.JsError: 498 | case JsValueType.JsNull: 499 | case JsValueType.JsObject: 500 | return JavaScriptValueType.Object; 501 | 502 | case JsValueType.JsString: 503 | return JavaScriptValueType.String; 504 | 505 | case JsValueType.JsSymbol: 506 | return JavaScriptValueType.Symbol; 507 | 508 | case JsValueType.JsTypedArray: 509 | return JavaScriptValueType.TypedArray; 510 | 511 | case JsValueType.JsUndefined: 512 | default: 513 | return JavaScriptValueType.Undefined; 514 | } 515 | } 516 | } 517 | } 518 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/JavaScriptEngine.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Scripting.JavaScript.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Microsoft.Scripting.JavaScript 12 | { 13 | public delegate void JavaScriptExternalObjectFinalizeCallback(object additionalData); 14 | public delegate JavaScriptValue JavaScriptCallableFunction(JavaScriptEngine callingEngine, bool asConstructor, JavaScriptValue thisValue, IEnumerable arguments); 15 | 16 | public sealed class JavaScriptEngine : IDisposable 17 | { 18 | private class NativeFunctionThunkData 19 | { 20 | public JavaScriptCallableFunction callback; 21 | public WeakReference engine; 22 | } 23 | 24 | private class ExternalObjectThunkData 25 | { 26 | public WeakReference engine; 27 | public WeakReference userData; 28 | public JavaScriptExternalObjectFinalizeCallback callback; 29 | } 30 | 31 | private JavaScriptEngineSafeHandle handle_; 32 | private WeakReference runtime_; 33 | private JavaScriptConverter converter_; 34 | private List nativeFunctionThunks_; 35 | private static NativeFunctionThunkCallback NativeCallback; 36 | private static IntPtr NativeCallbackPtr; 37 | private static JsFinalizeCallback FinalizerCallback; 38 | private static IntPtr FinalizerCallbackPtr; 39 | private HashSet externalObjects_; 40 | private ChakraApi api_; 41 | 42 | private List handlesToRelease_; 43 | private object handleReleaseLock_; 44 | 45 | private JavaScriptValue undefined_, true_, false_; 46 | private JavaScriptObject global_, null_; 47 | 48 | static JavaScriptEngine() 49 | { 50 | NativeCallback = NativeCallbackThunk; 51 | NativeCallbackPtr = Marshal.GetFunctionPointerForDelegate(NativeCallback); 52 | 53 | FinalizerCallback = FinalizerCallbackThunk; 54 | FinalizerCallbackPtr = Marshal.GetFunctionPointerForDelegate(FinalizerCallback); 55 | } 56 | 57 | internal JavaScriptEngine(JavaScriptEngineSafeHandle handle, JavaScriptRuntime runtime, ChakraApi api) 58 | { 59 | Debug.Assert(handle != null); 60 | Debug.Assert(runtime != null); 61 | Debug.Assert(api != null); 62 | api_ = api; 63 | 64 | handle_ = handle; 65 | runtime_ = new WeakReference(runtime); 66 | converter_ = new JavaScriptConverter(this); 67 | nativeFunctionThunks_ = new List(); 68 | externalObjects_ = new HashSet(); 69 | 70 | handlesToRelease_ = new List(); 71 | handleReleaseLock_ = new object(); 72 | 73 | ClaimContextPrivate(); 74 | JavaScriptValueSafeHandle global; 75 | Errors.ThrowIfIs(api_.JsGetGlobalObject(out global)); 76 | global_ = CreateObjectFromHandle(global); 77 | JavaScriptValueSafeHandle undef; 78 | Errors.ThrowIfIs(api_.JsGetUndefinedValue(out undef)); 79 | undefined_ = CreateValueFromHandle(undef); 80 | JavaScriptValueSafeHandle @null; 81 | Errors.ThrowIfIs(api_.JsGetNullValue(out @null)); 82 | null_ = CreateObjectFromHandle(@null); 83 | JavaScriptValueSafeHandle @true; 84 | Errors.ThrowIfIs(api_.JsGetTrueValue(out @true)); 85 | true_ = CreateValueFromHandle(@true); 86 | JavaScriptValueSafeHandle @false; 87 | Errors.ThrowIfIs(api_.JsGetFalseValue(out @false)); 88 | false_ = CreateValueFromHandle(@false); 89 | ReleaseContextPrivate(); 90 | } 91 | 92 | public JavaScriptRuntime Runtime 93 | { 94 | get 95 | { 96 | JavaScriptRuntime result; 97 | if (!runtime_.TryGetTarget(out result)) 98 | throw new ObjectDisposedException(nameof(JavaScriptEngine)); 99 | 100 | return result; 101 | } 102 | } 103 | 104 | public JavaScriptConverter Converter 105 | { 106 | get 107 | { 108 | return converter_; 109 | } 110 | } 111 | 112 | #region Internal methods 113 | internal ChakraApi Api 114 | { 115 | get 116 | { 117 | return api_; 118 | } 119 | } 120 | 121 | internal void EnqueueRelease(IntPtr handle) 122 | { 123 | lock (handleReleaseLock_) 124 | { 125 | handlesToRelease_.Add(handle); 126 | } 127 | } 128 | 129 | public JavaScriptExecutionContext AcquireContext() 130 | { 131 | ClaimContextPrivate(); 132 | return new JavaScriptExecutionContext(this, ReleaseContextPrivate); 133 | } 134 | 135 | private void ClaimContextPrivate() 136 | { 137 | if (handle_ == null) 138 | throw new ObjectDisposedException(nameof(JavaScriptEngine)); 139 | 140 | Errors.ThrowIfIs(api_.JsSetCurrentContext(handle_)); 141 | 142 | if (handlesToRelease_.Count > 0) 143 | { 144 | lock (handleReleaseLock_) 145 | { 146 | foreach (IntPtr handle in handlesToRelease_) 147 | { 148 | uint count; 149 | var error = api_.JsRelease(handle, out count); 150 | Debug.Assert(error == JsErrorCode.JsNoError); 151 | } 152 | 153 | handlesToRelease_.Clear(); 154 | } 155 | } 156 | } 157 | 158 | private void ReleaseContextPrivate() 159 | { 160 | Errors.ThrowIfIs(api_.JsReleaseCurrentContext()); 161 | } 162 | 163 | internal JavaScriptValue CreateValueFromHandle(JavaScriptValueSafeHandle handle) 164 | { 165 | Debug.Assert(!(handle.IsClosed || handle.IsInvalid)); 166 | 167 | JsValueType kind; 168 | Errors.ThrowIfIs(api_.JsGetValueType(handle, out kind)); 169 | 170 | JavaScriptValue result = null; 171 | switch (kind) 172 | { 173 | case JsValueType.JsArray: 174 | result = new JavaScriptArray(handle, JavaScriptValueType.Array, this); 175 | break; 176 | 177 | case JsValueType.JsFunction: 178 | result =new JavaScriptFunction(handle, JavaScriptValueType.Function, this); 179 | break; 180 | 181 | case JsValueType.JsObject: 182 | case JsValueType.JsNull: 183 | case JsValueType.JsError: 184 | result = new JavaScriptObject(handle, JavaScriptValueType.Object, this); 185 | break; 186 | 187 | case JsValueType.JsSymbol: 188 | result = new JavaScriptSymbol(handle, JavaScriptValueType.Symbol, this); 189 | break; 190 | 191 | case JsValueType.JsArrayBuffer: 192 | result = new JavaScriptArrayBuffer(handle, JavaScriptValueType.ArrayBuffer, this); 193 | break; 194 | 195 | case JsValueType.JsTypedArray: 196 | result = new JavaScriptTypedArray(handle, JavaScriptValueType.TypedArray, this); 197 | break; 198 | 199 | case JsValueType.JsDataView: 200 | result = new JavaScriptDataView(handle, JavaScriptValueType.DataView, this); 201 | break; 202 | 203 | case JsValueType.JsBoolean: 204 | case JsValueType.JsNumber: 205 | case JsValueType.JsString: 206 | case JsValueType.JsUndefined: 207 | default: 208 | result = new JavaScriptValue(handle, kind.ToApiValueType(), this); 209 | break; 210 | } 211 | 212 | return result; 213 | } 214 | 215 | internal JavaScriptObject CreateObjectFromHandle(JavaScriptValueSafeHandle handle) 216 | { 217 | JsValueType kind; 218 | Errors.ThrowIfIs(api_.JsGetValueType(handle, out kind)); 219 | 220 | JavaScriptObject result = null; 221 | switch (kind) 222 | { 223 | case JsValueType.JsArray: 224 | result = new JavaScriptArray(handle, JavaScriptValueType.Array, this); 225 | break; 226 | 227 | case JsValueType.JsFunction: 228 | result = new JavaScriptFunction(handle, JavaScriptValueType.Function, this); 229 | break; 230 | 231 | case JsValueType.JsObject: 232 | case JsValueType.JsError: 233 | case JsValueType.JsNull: 234 | result = new JavaScriptObject(handle, JavaScriptValueType.Object, this); 235 | break; 236 | 237 | case JsValueType.JsArrayBuffer: 238 | result = new JavaScriptArrayBuffer(handle, JavaScriptValueType.ArrayBuffer, this); 239 | break; 240 | 241 | case JsValueType.JsTypedArray: 242 | result = new JavaScriptTypedArray(handle, JavaScriptValueType.TypedArray, this); 243 | break; 244 | 245 | case JsValueType.JsDataView: 246 | result = new JavaScriptDataView(handle, JavaScriptValueType.DataView, this); 247 | break; 248 | 249 | case JsValueType.JsBoolean: 250 | case JsValueType.JsNumber: 251 | case JsValueType.JsString: 252 | case JsValueType.JsUndefined: 253 | case JsValueType.JsSymbol: 254 | default: 255 | throw new ArgumentException(); 256 | } 257 | 258 | return result; 259 | } 260 | 261 | internal JavaScriptArray CreateArrayFromHandle(JavaScriptValueSafeHandle handle) 262 | { 263 | JsValueType kind; 264 | Errors.ThrowIfIs(api_.JsGetValueType(handle, out kind)); 265 | 266 | switch (kind) 267 | { 268 | case JsValueType.JsArray: 269 | var result = new JavaScriptArray(handle, JavaScriptValueType.Array, this); 270 | return result; 271 | 272 | case JsValueType.JsFunction: 273 | case JsValueType.JsObject: 274 | case JsValueType.JsError: 275 | case JsValueType.JsNull: 276 | case JsValueType.JsArrayBuffer: 277 | case JsValueType.JsTypedArray: 278 | case JsValueType.JsDataView: 279 | case JsValueType.JsBoolean: 280 | case JsValueType.JsNumber: 281 | case JsValueType.JsString: 282 | case JsValueType.JsUndefined: 283 | case JsValueType.JsSymbol: 284 | default: 285 | throw new ArgumentException(); 286 | } 287 | } 288 | #endregion 289 | 290 | #region Base properties 291 | public JavaScriptObject GlobalObject 292 | { 293 | get 294 | { 295 | return global_; 296 | } 297 | } 298 | 299 | public JavaScriptValue UndefinedValue 300 | { 301 | get 302 | { 303 | return undefined_; 304 | } 305 | } 306 | 307 | public JavaScriptObject NullValue 308 | { 309 | get 310 | { 311 | return null_; 312 | } 313 | } 314 | 315 | public JavaScriptValue TrueValue 316 | { 317 | get 318 | { 319 | return true_; 320 | } 321 | } 322 | 323 | public JavaScriptValue FalseValue 324 | { 325 | get 326 | { 327 | return false_; 328 | } 329 | } 330 | 331 | public bool HasException 332 | { 333 | get 334 | { 335 | bool has; 336 | Errors.ThrowIfIs(api_.JsHasException(out has)); 337 | 338 | return has; 339 | } 340 | } 341 | 342 | public event EventHandler RuntimeExceptionRaised; 343 | internal void OnRuntimeExceptionRaised() 344 | { 345 | var rer = RuntimeExceptionRaised; 346 | if (rer != null) 347 | rer(this, EventArgs.Empty); 348 | } 349 | #endregion 350 | 351 | #region Code execution 352 | public JavaScriptFunction EvaluateScriptText(string code) 353 | { 354 | return Evaluate(new ScriptSource("[eval code]", code)); 355 | } 356 | 357 | public JavaScriptFunction Evaluate(ScriptSource source) 358 | { 359 | if (source == null) 360 | throw new ArgumentNullException(nameof(source)); 361 | 362 | JavaScriptValueSafeHandle handle; 363 | Errors.ThrowIfIs(api_.JsParseScript(source.SourceText, source.SourceContextId, source.SourceLocation, out handle)); 364 | 365 | return CreateObjectFromHandle(handle) as JavaScriptFunction; 366 | } 367 | 368 | public JavaScriptFunction Evaluate(ScriptSource source, Stream compiledCode) 369 | { 370 | throw new NotSupportedException(); 371 | } 372 | 373 | public unsafe void Compile(ScriptSource source, Stream compiledCodeDestination) 374 | { 375 | uint bufferSize = 0; 376 | Errors.ThrowIfIs(api_.JsSerializeScript(source.SourceText, IntPtr.Zero, ref bufferSize)); 377 | if (bufferSize > int.MaxValue) 378 | throw new OutOfMemoryException(); 379 | 380 | IntPtr mem = Marshal.AllocCoTaskMem(unchecked((int)bufferSize)); 381 | var error = api_.JsSerializeScript(source.SourceText, mem, ref bufferSize); 382 | if (error != JsErrorCode.JsNoError) 383 | { 384 | Marshal.FreeCoTaskMem(mem); 385 | Errors.ThrowFor(error); 386 | } 387 | 388 | using (UnmanagedMemoryStream ums = new UnmanagedMemoryStream((byte*)mem.ToPointer(), bufferSize)) 389 | { 390 | ums.CopyTo(compiledCodeDestination); 391 | } 392 | Marshal.FreeCoTaskMem(mem); 393 | } 394 | 395 | public JavaScriptValue Execute(ScriptSource source) 396 | { 397 | if (source == null) 398 | throw new ArgumentNullException(nameof(source)); 399 | 400 | JavaScriptValueSafeHandle handle; 401 | Errors.CheckForScriptExceptionOrThrow(api_.JsRunScript(source.SourceText, source.SourceContextId, source.SourceLocation, out handle), this); 402 | if (handle.IsInvalid) 403 | return undefined_; 404 | 405 | return CreateValueFromHandle(handle); 406 | } 407 | 408 | public JavaScriptValue Execute(ScriptSource source, Stream compiledCode) 409 | { 410 | throw new NotSupportedException(); 411 | } 412 | #endregion 413 | 414 | #region Main interaction functions 415 | public JavaScriptObject CreateObject(JavaScriptObject prototype = null) 416 | { 417 | JavaScriptValueSafeHandle handle; 418 | Errors.ThrowIfIs(api_.JsCreateObject(out handle)); 419 | 420 | if (prototype != null) 421 | { 422 | Errors.ThrowIfIs(api_.JsSetPrototype(handle, prototype.handle_)); 423 | } 424 | 425 | return CreateObjectFromHandle(handle); 426 | } 427 | 428 | private static void FinalizerCallbackThunk(IntPtr externalData) 429 | { 430 | if (externalData == IntPtr.Zero) 431 | return; 432 | 433 | GCHandle handle = GCHandle.FromIntPtr(externalData); 434 | var thunkData = handle.Target as ExternalObjectThunkData; 435 | if (thunkData == null) 436 | return; 437 | 438 | var engine = thunkData.engine; 439 | var callback = thunkData.callback; 440 | object userData; 441 | thunkData.userData.TryGetTarget(out userData); 442 | 443 | if (callback != null) 444 | callback(userData); 445 | 446 | JavaScriptEngine eng; 447 | if (engine.TryGetTarget(out eng)) 448 | { 449 | eng.externalObjects_.Remove(thunkData); 450 | } 451 | } 452 | 453 | public JavaScriptObject CreateExternalObject(object externalData, JavaScriptExternalObjectFinalizeCallback finalizeCallback) 454 | { 455 | ExternalObjectThunkData thunk = new ExternalObjectThunkData() { callback = finalizeCallback, engine = new WeakReference(this), userData = new WeakReference(externalData), }; 456 | GCHandle handle = GCHandle.Alloc(thunk); 457 | externalObjects_.Add(thunk); 458 | 459 | JavaScriptValueSafeHandle result; 460 | Errors.ThrowIfIs(api_.JsCreateExternalObject(GCHandle.ToIntPtr(handle), FinalizerCallbackPtr, out result)); 461 | 462 | return CreateObjectFromHandle(result); 463 | } 464 | 465 | internal object GetExternalObjectFrom(JavaScriptValue value) 466 | { 467 | Debug.Assert(value != null); 468 | 469 | IntPtr handlePtr; 470 | Errors.ThrowIfIs(api_.JsGetExternalData(value.handle_, out handlePtr)); 471 | GCHandle gcHandle = GCHandle.FromIntPtr(handlePtr); 472 | ExternalObjectThunkData thunk = gcHandle.Target as ExternalObjectThunkData; 473 | if (thunk == null) 474 | return null; 475 | 476 | object result; 477 | thunk.userData.TryGetTarget(out result); 478 | return result; 479 | } 480 | 481 | public JavaScriptSymbol CreateSymbol(string description) 482 | { 483 | JavaScriptValueSafeHandle handle; 484 | using (var str = converter_.FromString(description)) 485 | { 486 | Errors.ThrowIfIs(api_.JsCreateSymbol(str.handle_, out handle)); 487 | } 488 | 489 | return CreateValueFromHandle(handle) as JavaScriptSymbol; 490 | } 491 | 492 | public TimeSpan RunIdleWork() 493 | { 494 | uint nextTick; 495 | Errors.ThrowIfIs(api_.JsIdle(out nextTick)); 496 | 497 | return TimeSpan.FromTicks(nextTick); 498 | } 499 | 500 | public bool HasGlobalVariable(string name) 501 | { 502 | return GlobalObject.HasOwnProperty(name); 503 | } 504 | 505 | public JavaScriptValue GetGlobalVariable(string name) 506 | { 507 | return GlobalObject.GetPropertyByName(name); 508 | } 509 | 510 | public void SetGlobalVariable(string name, JavaScriptValue value) 511 | { 512 | if (value == null) 513 | throw new ArgumentNullException(nameof(value)); 514 | 515 | GlobalObject.SetPropertyByName(name, value); 516 | } 517 | 518 | public JavaScriptValue CallGlobalFunction(string functionName) 519 | { 520 | return CallGlobalFunction(functionName, Enumerable.Empty()); 521 | } 522 | 523 | public JavaScriptValue CallGlobalFunction(string functionName, IEnumerable args) 524 | { 525 | var global = GlobalObject; 526 | var fn = global.GetPropertyByName(functionName) as JavaScriptFunction; 527 | return fn.Call(global, args); 528 | } 529 | 530 | public void SetGlobalFunction(string functionName, JavaScriptCallableFunction hostFunction) 531 | { 532 | if (hostFunction == null) 533 | throw new ArgumentNullException(nameof(hostFunction)); 534 | 535 | GlobalObject.SetPropertyByName(functionName, CreateFunction(hostFunction, functionName)); 536 | } 537 | 538 | public JavaScriptArray CreateArray(int length) 539 | { 540 | if (length < 0) 541 | throw new ArgumentOutOfRangeException(nameof(length)); 542 | 543 | JavaScriptValueSafeHandle handle; 544 | Errors.ThrowIfIs(api_.JsCreateArray(unchecked((uint)length), out handle)); 545 | 546 | return CreateArrayFromHandle(handle); 547 | } 548 | 549 | private static IntPtr NativeCallbackThunk( 550 | IntPtr callee, 551 | [MarshalAs(UnmanagedType.U1)] bool asConstructor, 552 | [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] IntPtr[] args, 553 | ushort argCount, 554 | IntPtr data) 555 | { 556 | // callee and args[0] are the same 557 | if (data == IntPtr.Zero) 558 | return IntPtr.Zero; 559 | 560 | try 561 | { 562 | GCHandle handle = GCHandle.FromIntPtr(data); 563 | var nativeThunk = handle.Target as NativeFunctionThunkData; 564 | JavaScriptEngine engine; 565 | if (!nativeThunk.engine.TryGetTarget(out engine)) 566 | return IntPtr.Zero; 567 | 568 | JavaScriptValue thisValue = null; 569 | if (argCount > 0) 570 | { 571 | thisValue = engine.CreateValueFromHandle(new JavaScriptValueSafeHandle(args[0])); 572 | } 573 | 574 | try 575 | { 576 | var result = nativeThunk.callback(engine, asConstructor, thisValue, args.Skip(1).Select(h => engine.CreateValueFromHandle(new JavaScriptValueSafeHandle(h)))); 577 | return result.handle_.DangerousGetHandle(); 578 | } 579 | catch (Exception ex) 580 | { 581 | var error = engine.CreateError(ex.Message); 582 | engine.SetException(error); 583 | 584 | return engine.UndefinedValue.handle_.DangerousGetHandle(); 585 | } 586 | } 587 | catch 588 | { 589 | return IntPtr.Zero; 590 | } 591 | } 592 | 593 | public JavaScriptFunction CreateFunction(JavaScriptCallableFunction hostFunction) 594 | { 595 | if (hostFunction == null) 596 | throw new ArgumentNullException(nameof(hostFunction)); 597 | 598 | NativeFunctionThunkData td = new NativeFunctionThunkData() { callback = hostFunction, engine = new WeakReference(this) }; 599 | GCHandle handle = GCHandle.Alloc(td, GCHandleType.Weak); 600 | nativeFunctionThunks_.Add(td); 601 | 602 | JavaScriptValueSafeHandle resultHandle; 603 | Errors.ThrowIfIs(api_.JsCreateFunction(NativeCallbackPtr, GCHandle.ToIntPtr(handle), out resultHandle)); 604 | 605 | return CreateObjectFromHandle(resultHandle) as JavaScriptFunction; 606 | } 607 | 608 | public JavaScriptFunction CreateFunction(JavaScriptCallableFunction hostFunction, string name) 609 | { 610 | if (hostFunction == null) 611 | throw new ArgumentNullException(nameof(hostFunction)); 612 | if (string.IsNullOrEmpty(name)) 613 | throw new ArgumentNullException(nameof(name)); 614 | 615 | var nameVal = Converter.FromString(name); 616 | 617 | NativeFunctionThunkData td = new NativeFunctionThunkData() { callback = hostFunction, engine = new WeakReference(this) }; 618 | GCHandle handle = GCHandle.Alloc(td, GCHandleType.Weak); 619 | nativeFunctionThunks_.Add(td); 620 | 621 | JavaScriptValueSafeHandle resultHandle; 622 | Errors.ThrowIfIs(api_.JsCreateNamedFunction(nameVal.handle_, NativeCallbackPtr, GCHandle.ToIntPtr(handle), out resultHandle)); 623 | 624 | return CreateObjectFromHandle(resultHandle) as JavaScriptFunction; 625 | } 626 | 627 | public JavaScriptValue GetAndClearException() 628 | { 629 | JavaScriptValueSafeHandle handle; 630 | Errors.ThrowIfIs(api_.JsGetAndClearException(out handle)); 631 | 632 | return CreateValueFromHandle(handle); 633 | } 634 | 635 | public void SetException(JavaScriptValue exception) 636 | { 637 | if (exception == null) 638 | throw new ArgumentNullException(nameof(exception)); 639 | 640 | Errors.ThrowIfIs(api_.JsSetException(exception.handle_)); 641 | } 642 | #endregion 643 | 644 | #region Errors 645 | public JavaScriptObject CreateError(string message = "") 646 | { 647 | var str = Converter.FromString(message ?? ""); 648 | JavaScriptValueSafeHandle handle; 649 | Errors.ThrowIfIs(api_.JsCreateError(str.handle_, out handle)); 650 | 651 | return CreateObjectFromHandle(handle); 652 | } 653 | 654 | public JavaScriptObject CreateRangeError(string message = "") 655 | { 656 | var str = Converter.FromString(message ?? ""); 657 | JavaScriptValueSafeHandle handle; 658 | Errors.ThrowIfIs(api_.JsCreateRangeError(str.handle_, out handle)); 659 | 660 | return CreateObjectFromHandle(handle); 661 | } 662 | 663 | public JavaScriptObject CreateReferenceError(string message = "") 664 | { 665 | var str = Converter.FromString(message ?? ""); 666 | JavaScriptValueSafeHandle handle; 667 | Errors.ThrowIfIs(api_.JsCreateReferenceError(str.handle_, out handle)); 668 | 669 | return CreateObjectFromHandle(handle); 670 | } 671 | 672 | public JavaScriptObject CreateSyntaxError(string message = "") 673 | { 674 | var str = Converter.FromString(message ?? ""); 675 | JavaScriptValueSafeHandle handle; 676 | Errors.ThrowIfIs(api_.JsCreateSyntaxError(str.handle_, out handle)); 677 | 678 | return CreateObjectFromHandle(handle); 679 | } 680 | 681 | public JavaScriptObject CreateTypeError(string message = "") 682 | { 683 | var str = Converter.FromString(message ?? ""); 684 | JavaScriptValueSafeHandle handle; 685 | Errors.ThrowIfIs(api_.JsCreateTypeError(str.handle_, out handle)); 686 | 687 | return CreateObjectFromHandle(handle); 688 | } 689 | 690 | public JavaScriptObject CreateUriError(string message = "") 691 | { 692 | var str = Converter.FromString(message ?? ""); 693 | JavaScriptValueSafeHandle handle; 694 | Errors.ThrowIfIs(api_.JsCreateURIError(str.handle_, out handle)); 695 | 696 | return CreateObjectFromHandle(handle); 697 | } 698 | #endregion 699 | 700 | public bool CanEnableDebugging 701 | { 702 | get 703 | { 704 | return api_.JsStartDebugging != null; 705 | } 706 | } 707 | 708 | public void EnableDebugging() 709 | { 710 | if (api_.JsStartDebugging == null) 711 | throw new NotSupportedException("Debugging is not supported with ChakraCore. Check the CanEnableDebugging property before attempting to enable debugging."); 712 | 713 | Errors.ThrowIfIs(api_.JsStartDebugging()); 714 | } 715 | 716 | public void AddTypeToGlobal(string name = null) 717 | { 718 | Type t = typeof(T); 719 | if (null == name) 720 | { 721 | name = t.Name; 722 | } 723 | 724 | var proj = Converter.GetProjectionPrototypeForType(t); 725 | SetGlobalVariable(name, proj.GetPropertyByName("constructor")); 726 | } 727 | 728 | #region IDisposable implementation 729 | public void Dispose() 730 | { 731 | Dispose(true); 732 | GC.SuppressFinalize(this); 733 | } 734 | 735 | private void Dispose(bool disposing) 736 | { 737 | if (disposing) 738 | { 739 | if (handle_ != null) 740 | { 741 | handle_.Dispose(); 742 | handle_ = null; 743 | } 744 | } 745 | } 746 | 747 | ~JavaScriptEngine() 748 | { 749 | Dispose(false); 750 | } 751 | #endregion 752 | } 753 | } 754 | -------------------------------------------------------------------------------- /src/Microsoft.Scripting/JavaScript/ChakraApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using Microsoft.Scripting.JavaScript.SafeHandles; 5 | 6 | namespace Microsoft.Scripting 7 | { 8 | internal sealed class ChakraApi 9 | { 10 | #region API method type definitions 11 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 12 | public delegate JsErrorCode FnJsGetFalseValue(out JavaScriptValueSafeHandle globalHandle); 13 | 14 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 15 | public delegate JsErrorCode FnJsHasException([MarshalAsAttribute(UnmanagedType.U1)] out bool has); 16 | 17 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 18 | public delegate JsErrorCode FnJsCreateObject(out JavaScriptValueSafeHandle result); 19 | 20 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 21 | public delegate JsErrorCode FnJsCreateExternalObject(IntPtr data, IntPtr finalizeCallback, out JavaScriptValueSafeHandle handle); 22 | 23 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 24 | public delegate JsErrorCode FnJsCreateSymbol(JavaScriptValueSafeHandle descriptionHandle, out JavaScriptValueSafeHandle result); 25 | 26 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 27 | public delegate JsErrorCode FnJsCreateArray(uint length, out JavaScriptValueSafeHandle result); 28 | 29 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 30 | public delegate JsErrorCode FnJsCreateFunction(IntPtr callback, IntPtr extraData, out JavaScriptValueSafeHandle result); 31 | 32 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 33 | public delegate JsErrorCode FnJsCreateNamedFunction(JavaScriptValueSafeHandle nameHandle, IntPtr callback, IntPtr extraData, out JavaScriptValueSafeHandle result); 34 | 35 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 36 | public delegate JsErrorCode FnJsIdle(out uint nextTick); 37 | 38 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 39 | public delegate JsErrorCode FnJsGetAndClearException(out JavaScriptValueSafeHandle result); 40 | 41 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 42 | public delegate JsErrorCode FnJsSetException(JavaScriptValueSafeHandle exception); 43 | 44 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 45 | public delegate JsErrorCode FnJsCreateError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 46 | 47 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 48 | public delegate JsErrorCode FnJsCreateRangeError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 49 | 50 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 51 | public delegate JsErrorCode FnJsCreateReferenceError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 52 | 53 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 54 | public delegate JsErrorCode FnJsCreateSyntaxError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 55 | 56 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 57 | public delegate JsErrorCode FnJsCreateTypeError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 58 | 59 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 60 | public delegate JsErrorCode FnJsCreateURIError(JavaScriptValueSafeHandle message, out JavaScriptValueSafeHandle result); 61 | 62 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 63 | public delegate JsErrorCode FnJsStartDebugging(); 64 | 65 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 66 | public delegate JsErrorCode FnJsParseScript([MarshalAsAttribute(UnmanagedType.LPWStr)] string script, IntPtr sourceContextId, [MarshalAsAttribute(UnmanagedType.LPWStr)] string sourceUrl, out JavaScriptValueSafeHandle handle); 67 | 68 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 69 | public delegate JsErrorCode FnJsParseSerializedScript([MarshalAsAttribute(UnmanagedType.LPWStr)] string script, IntPtr buffer, IntPtr sourceContext, [MarshalAsAttribute(UnmanagedType.LPWStr)] string sourceUrl, out JavaScriptValueSafeHandle handle); 70 | 71 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 72 | public delegate JsErrorCode FnJsSerializeScript([MarshalAsAttribute(UnmanagedType.LPWStr)] string script, IntPtr buffer, ref uint bufferSize); 73 | 74 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 75 | public delegate JsErrorCode FnJsRunScript([MarshalAsAttribute(UnmanagedType.LPWStr)] string script, IntPtr sourceContextId, [MarshalAsAttribute(UnmanagedType.LPWStr)] string sourceUrl, out JavaScriptValueSafeHandle handle); 76 | 77 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 78 | public delegate JsErrorCode FnJsRunSerializedScript([MarshalAsAttribute(UnmanagedType.LPWStr)] string script, IntPtr buffer, IntPtr sourceContext, [MarshalAsAttribute(UnmanagedType.LPWStr)] string sourceUrl, out JavaScriptValueSafeHandle handle); 79 | 80 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 81 | public delegate JsErrorCode FnJsCallFunction(JavaScriptValueSafeHandle fnHandle, [MarshalAsAttribute(UnmanagedType.LPArray, SizeParamIndex = 2)] IntPtr[] arguments, ushort argCount, out JavaScriptValueSafeHandle result); 82 | 83 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 84 | public delegate JsErrorCode FnJsConstructObject(JavaScriptValueSafeHandle fnHandle, [MarshalAsAttribute(UnmanagedType.LPArray, SizeParamIndex = 2)] IntPtr[] arguments, ushort argCount, out JavaScriptValueSafeHandle result); 85 | 86 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 87 | public delegate JsErrorCode FnJsGetPropertyIdFromName([MarshalAsAttribute(UnmanagedType.LPWStr)] string propertyName, out IntPtr propertyId); 88 | 89 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 90 | public delegate JsErrorCode FnJsGetPropertyIdFromSymbol(JavaScriptValueSafeHandle valueHandle, out IntPtr propertyId); 91 | 92 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 93 | public delegate JsErrorCode FnJsGetProperty(JavaScriptValueSafeHandle obj, IntPtr propertyId, out JavaScriptValueSafeHandle result); 94 | 95 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 96 | public delegate JsErrorCode FnJsSetProperty(JavaScriptValueSafeHandle obj, IntPtr propertyId, JavaScriptValueSafeHandle propertyValue, [MarshalAsAttribute(UnmanagedType.U1)] bool useStrictSemantics); 97 | 98 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 99 | public delegate JsErrorCode FnJsDeleteProperty(JavaScriptValueSafeHandle obj, IntPtr propertyId, [MarshalAsAttribute(UnmanagedType.U1)] bool useStrictSemantics, out JavaScriptValueSafeHandle result); 100 | 101 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 102 | public delegate JsErrorCode FnJsGetIndexedProperty(JavaScriptValueSafeHandle obj, JavaScriptValueSafeHandle index, out JavaScriptValueSafeHandle result); 103 | 104 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 105 | public delegate JsErrorCode FnJsSetIndexedProperty(JavaScriptValueSafeHandle obj, JavaScriptValueSafeHandle index, JavaScriptValueSafeHandle value); 106 | 107 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 108 | public delegate JsErrorCode FnJsDeleteIndexedProperty(JavaScriptValueSafeHandle obj, JavaScriptValueSafeHandle index); 109 | 110 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 111 | public delegate JsErrorCode FnJsHasProperty(JavaScriptValueSafeHandle obj, IntPtr propertyId, [MarshalAsAttribute(UnmanagedType.U1)] out bool has); 112 | 113 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 114 | public delegate JsErrorCode FnJsGetOwnPropertyDescriptor(JavaScriptValueSafeHandle obj, IntPtr propertyId, out JavaScriptValueSafeHandle descriptor); 115 | 116 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 117 | public delegate JsErrorCode FnJsDefineProperty(JavaScriptValueSafeHandle obj, IntPtr propId, JavaScriptValueSafeHandle descriptorRef, [MarshalAsAttribute(UnmanagedType.U1)] out bool wasSet); 118 | 119 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 120 | public delegate JsErrorCode FnJsGetOwnPropertyNames(JavaScriptValueSafeHandle obj, out JavaScriptValueSafeHandle result); 121 | 122 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 123 | public delegate JsErrorCode FnJsGetOwnPropertySymbols(JavaScriptValueSafeHandle obj, out JavaScriptValueSafeHandle result); 124 | 125 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 126 | public delegate JsErrorCode FnJsPreventExtension(JavaScriptValueSafeHandle obj); 127 | 128 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 129 | public delegate JsErrorCode FnJsGetExtensionAllowed(JavaScriptValueSafeHandle obj, [MarshalAsAttribute(UnmanagedType.U1)] out bool allowed); 130 | 131 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 132 | public delegate JsErrorCode FnJsGetPrototype(JavaScriptValueSafeHandle obj, out JavaScriptValueSafeHandle prototype); 133 | 134 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 135 | public delegate JsErrorCode FnJsSetPrototype(JavaScriptValueSafeHandle obj, JavaScriptValueSafeHandle prototype); 136 | 137 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 138 | public delegate JsErrorCode FnJsGetArrayBufferStorage(JavaScriptValueSafeHandle obj, out IntPtr buffer, out uint len); 139 | 140 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 141 | public delegate JsErrorCode FnJsGetTypedArrayStorage(JavaScriptValueSafeHandle obj, out IntPtr buf, out uint len, out JavaScript.JavaScriptTypedArrayType type, out int elemSize); 142 | 143 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 144 | public delegate JsErrorCode FnJsGetGlobalObject(out JavaScriptValueSafeHandle globalHandle); 145 | 146 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 147 | public delegate JsErrorCode FnJsGetUndefinedValue(out JavaScriptValueSafeHandle globalHandle); 148 | 149 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 150 | public delegate JsErrorCode FnJsGetNullValue(out JavaScriptValueSafeHandle globalHandle); 151 | 152 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 153 | public delegate JsErrorCode FnJsGetTrueValue(out JavaScriptValueSafeHandle globalHandle); 154 | 155 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 156 | public delegate JsErrorCode FnJsAddRef(IntPtr @ref, out uint count); 157 | 158 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 159 | public delegate JsErrorCode FnJsRelease(IntPtr @ref, out uint count); 160 | 161 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 162 | public delegate JsErrorCode FnJsDisposeRuntime(IntPtr runtime); 163 | 164 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 165 | public delegate JsErrorCode FnJsCreateRuntime(JsRuntimeAttributes attributes, IntPtr threadService, out JavaScriptRuntimeSafeHandle runtime); 166 | 167 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 168 | public delegate JsErrorCode FnJsSetRuntimeMemoryAllocationCallback(JavaScriptRuntimeSafeHandle runtime, IntPtr extraInformation, IntPtr pfnCallback); 169 | [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.Winapi)] 170 | public delegate Microsoft.Scripting.JsErrorCode FnJsSetRuntimeMemoryAllocationCallbackWithIntPtr(IntPtr runtime, System.IntPtr extraInformation, System.IntPtr pfnCallback); 171 | 172 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 173 | public delegate JsErrorCode FnJsCollectGarbage(JavaScriptRuntimeSafeHandle runtime); 174 | 175 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 176 | public delegate JsErrorCode FnJsDisableRuntimeExecution(JavaScriptRuntimeSafeHandle runtime); 177 | 178 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 179 | public delegate JsErrorCode FnJsEnableRuntimeExecution(JavaScriptRuntimeSafeHandle runtime); 180 | 181 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 182 | public delegate JsErrorCode FnJsGetRuntimeMemoryUsage(JavaScriptRuntimeSafeHandle runtime, out ulong usage); 183 | 184 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 185 | public delegate JsErrorCode FnJsIsRuntimeExecutionDisabled(JavaScriptRuntimeSafeHandle runtime, out bool isDisabled); 186 | 187 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 188 | public delegate JsErrorCode FnJsCreateContext(JavaScriptRuntimeSafeHandle runtime, out JavaScriptEngineSafeHandle engine); 189 | 190 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 191 | public delegate JsErrorCode FnJsSetCurrentContext(JavaScriptEngineSafeHandle contextHandle); 192 | 193 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 194 | public delegate JsErrorCode FnJsBooleanToBool(JavaScriptValueSafeHandle valueRef, [MarshalAsAttribute(UnmanagedType.U1)] out bool result); 195 | 196 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 197 | public delegate JsErrorCode FnJsConvertValueToBoolean(JavaScriptValueSafeHandle valueToConvert, out JavaScriptValueSafeHandle resultHandle); 198 | 199 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 200 | public delegate JsErrorCode FnJsNumberToDouble(JavaScriptValueSafeHandle valueRef, out double result); 201 | 202 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 203 | public delegate JsErrorCode FnJsDoubleToNumber(double value, out JavaScriptValueSafeHandle result); 204 | 205 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 206 | public delegate JsErrorCode FnJsConvertValueToNumber(JavaScriptValueSafeHandle valueToConvert, out JavaScriptValueSafeHandle resultHandle); 207 | 208 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 209 | public delegate JsErrorCode FnJsGetValueType(JavaScriptValueSafeHandle value, out JsValueType kind); 210 | 211 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 212 | public delegate JsErrorCode FnJsNumberToInt(JavaScriptValueSafeHandle valueRef, out int result); 213 | 214 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 215 | public delegate JsErrorCode FnJsIntToNumber(int value, out JavaScriptValueSafeHandle result); 216 | 217 | // ***unsafe*** 218 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 219 | public unsafe delegate JsErrorCode FnJsPointerToString(void* psz, int length, out JavaScriptValueSafeHandle result); 220 | 221 | // ***unsafe*** 222 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 223 | public unsafe delegate JsErrorCode FnJsStringToPointer(JavaScriptValueSafeHandle str, out void* result, out uint strLen); 224 | 225 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 226 | public delegate JsErrorCode FnJsConvertValueToString(JavaScriptValueSafeHandle valueToConvert, out JavaScriptValueSafeHandle resultHandle); 227 | 228 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 229 | public delegate JsErrorCode FnJsEquals(JavaScriptValueSafeHandle obj1, JavaScriptValueSafeHandle obj2, [MarshalAsAttribute(UnmanagedType.U1)] out bool result); 230 | 231 | [UnmanagedFunctionPointerAttribute(CallingConvention.Winapi)] 232 | public delegate JsErrorCode FnJsStrictEquals(JavaScriptValueSafeHandle obj1, JavaScriptValueSafeHandle obj2, [MarshalAsAttribute(UnmanagedType.U1)] out bool result); 233 | 234 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 235 | public delegate JsErrorCode FnJsGetExternalData(JavaScriptValueSafeHandle @ref, out IntPtr externalData); 236 | 237 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 238 | public delegate JsErrorCode FnJsSetExternalData(JavaScriptValueSafeHandle @ref, IntPtr externalData); 239 | #endregion 240 | 241 | #region Field definitions 242 | public readonly FnJsGetFalseValue JsGetFalseValue; 243 | 244 | public readonly FnJsHasException JsHasException; 245 | 246 | public readonly FnJsCreateObject JsCreateObject; 247 | 248 | public readonly FnJsCreateExternalObject JsCreateExternalObject; 249 | 250 | public readonly FnJsCreateSymbol JsCreateSymbol; 251 | 252 | public readonly FnJsCreateArray JsCreateArray; 253 | 254 | public readonly FnJsCreateFunction JsCreateFunction; 255 | 256 | public readonly FnJsCreateNamedFunction JsCreateNamedFunction; 257 | 258 | public readonly FnJsIdle JsIdle; 259 | 260 | public readonly FnJsGetAndClearException JsGetAndClearException; 261 | 262 | public readonly FnJsSetException JsSetException; 263 | 264 | public readonly FnJsCreateError JsCreateError; 265 | 266 | public readonly FnJsCreateRangeError JsCreateRangeError; 267 | 268 | public readonly FnJsCreateReferenceError JsCreateReferenceError; 269 | 270 | public readonly FnJsCreateSyntaxError JsCreateSyntaxError; 271 | 272 | public readonly FnJsCreateTypeError JsCreateTypeError; 273 | 274 | public readonly FnJsCreateURIError JsCreateURIError; 275 | 276 | public readonly FnJsStartDebugging JsStartDebugging; 277 | 278 | public readonly FnJsParseScript JsParseScript; 279 | 280 | public readonly FnJsParseSerializedScript JsParseSerializedScript; 281 | 282 | public readonly FnJsSerializeScript JsSerializeScript; 283 | 284 | public readonly FnJsRunScript JsRunScript; 285 | 286 | public readonly FnJsRunSerializedScript JsRunSerializedScript; 287 | 288 | public readonly FnJsCallFunction JsCallFunction; 289 | 290 | public readonly FnJsConstructObject JsConstructObject; 291 | 292 | public readonly FnJsGetPropertyIdFromName JsGetPropertyIdFromName; 293 | 294 | public readonly FnJsGetPropertyIdFromSymbol JsGetPropertyIdFromSymbol; 295 | 296 | public readonly FnJsGetProperty JsGetProperty; 297 | 298 | public readonly FnJsSetProperty JsSetProperty; 299 | 300 | public readonly FnJsDeleteProperty JsDeleteProperty; 301 | 302 | public readonly FnJsGetIndexedProperty JsGetIndexedProperty; 303 | 304 | public readonly FnJsSetIndexedProperty JsSetIndexedProperty; 305 | 306 | public readonly FnJsDeleteIndexedProperty JsDeleteIndexedProperty; 307 | 308 | public readonly FnJsHasProperty JsHasProperty; 309 | 310 | public readonly FnJsGetOwnPropertyDescriptor JsGetOwnPropertyDescriptor; 311 | 312 | public readonly FnJsDefineProperty JsDefineProperty; 313 | 314 | public readonly FnJsGetOwnPropertyNames JsGetOwnPropertyNames; 315 | 316 | public readonly FnJsGetOwnPropertySymbols JsGetOwnPropertySymbols; 317 | 318 | public readonly FnJsPreventExtension JsPreventExtension; 319 | 320 | public readonly FnJsGetExtensionAllowed JsGetExtensionAllowed; 321 | 322 | public readonly FnJsGetPrototype JsGetPrototype; 323 | 324 | public readonly FnJsSetPrototype JsSetPrototype; 325 | 326 | public readonly FnJsGetArrayBufferStorage JsGetArrayBufferStorage; 327 | 328 | public readonly FnJsGetTypedArrayStorage JsGetTypedArrayStorage; 329 | 330 | public readonly FnJsGetGlobalObject JsGetGlobalObject; 331 | 332 | public readonly FnJsGetUndefinedValue JsGetUndefinedValue; 333 | 334 | public readonly FnJsGetNullValue JsGetNullValue; 335 | 336 | public readonly FnJsGetTrueValue JsGetTrueValue; 337 | 338 | public readonly FnJsAddRef JsAddRef; 339 | 340 | public readonly FnJsRelease JsRelease; 341 | 342 | public readonly FnJsDisposeRuntime JsDisposeRuntime; 343 | 344 | public readonly FnJsCreateRuntime JsCreateRuntime; 345 | 346 | public readonly FnJsSetRuntimeMemoryAllocationCallback JsSetRuntimeMemoryAllocationCallback; 347 | public readonly FnJsSetRuntimeMemoryAllocationCallbackWithIntPtr JsSetRuntimeMemoryAllocationCallbackWithIntPtr; 348 | 349 | public readonly FnJsCollectGarbage JsCollectGarbage; 350 | 351 | public readonly FnJsDisableRuntimeExecution JsDisableRuntimeExecution; 352 | 353 | public readonly FnJsEnableRuntimeExecution JsEnableRuntimeExecution; 354 | 355 | public readonly FnJsGetRuntimeMemoryUsage JsGetRuntimeMemoryUsage; 356 | 357 | public readonly FnJsIsRuntimeExecutionDisabled JsIsRuntimeExecutionDisabled; 358 | 359 | public readonly FnJsCreateContext JsCreateContext; 360 | 361 | public readonly FnJsSetCurrentContext JsSetCurrentContext; 362 | 363 | public JsErrorCode JsReleaseCurrentContext() 364 | { 365 | return JsSetCurrentContext(new JavaScriptEngineSafeHandle(IntPtr.Zero)); 366 | } 367 | 368 | public readonly FnJsBooleanToBool JsBooleanToBool; 369 | 370 | public readonly FnJsConvertValueToBoolean JsConvertValueToBoolean; 371 | 372 | public readonly FnJsNumberToDouble JsNumberToDouble; 373 | 374 | public readonly FnJsDoubleToNumber JsDoubleToNumber; 375 | 376 | public readonly FnJsConvertValueToNumber JsConvertValueToNumber; 377 | 378 | public readonly FnJsGetValueType JsGetValueType; 379 | 380 | public readonly FnJsNumberToInt JsNumberToInt; 381 | 382 | public readonly FnJsIntToNumber JsIntToNumber; 383 | 384 | public readonly FnJsPointerToString JsPointerToString; 385 | 386 | public readonly FnJsStringToPointer JsStringToPointer; 387 | 388 | public readonly FnJsConvertValueToString JsConvertValueToString; 389 | 390 | public readonly FnJsEquals JsEquals; 391 | 392 | public readonly FnJsStrictEquals JsStrictEquals; 393 | 394 | public readonly FnJsGetExternalData JsGetExternalData; 395 | 396 | public readonly FnJsSetExternalData JsSetExternalData; 397 | #endregion 398 | 399 | private static System.Lazy sharedInstance_ = new System.Lazy(Load); 400 | 401 | private static class NativeMethods 402 | { 403 | [DllImport("kernel32", SetLastError = true)] 404 | public static extern IntPtr LoadLibrary(string lpFileName); 405 | 406 | [DllImport("kernel32", SetLastError = true)] 407 | public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); 408 | 409 | [DllImport("kernel32", SetLastError = true)] 410 | public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); 411 | } 412 | 413 | 414 | internal ChakraApi(IntPtr hModule) 415 | { 416 | SetFn(ref JsAddRef, hModule, "JsAddRef"); 417 | SetFn(ref JsBooleanToBool, hModule, "JsBooleanToBool"); 418 | SetFn(ref JsCallFunction, hModule, "JsCallFunction"); 419 | SetFn(ref JsCollectGarbage, hModule, "JsCollectGarbage"); 420 | SetFn(ref JsConstructObject, hModule, "JsConstructObject"); 421 | SetFn(ref JsConvertValueToBoolean, hModule, "JsConvertValueToBoolean"); 422 | SetFn(ref JsConvertValueToNumber, hModule, "JsConvertValueToNumber"); 423 | SetFn(ref JsConvertValueToString, hModule, "JsConvertValueToString"); 424 | SetFn(ref JsCreateArray, hModule, "JsCreateArray"); 425 | SetFn(ref JsCreateContext, hModule, "JsCreateContext"); 426 | SetFn(ref JsCreateError, hModule, "JsCreateError"); 427 | SetFn(ref JsCreateExternalObject, hModule, "JsCreateExternalObject"); 428 | SetFn(ref JsCreateFunction, hModule, "JsCreateFunction"); 429 | SetFn(ref JsCreateNamedFunction, hModule, "JsCreateNamedFunction"); 430 | SetFn(ref JsCreateObject, hModule, "JsCreateObject"); 431 | SetFn(ref JsCreateRangeError, hModule, "JsCreateRangeError"); 432 | SetFn(ref JsCreateReferenceError, hModule, "JsCreateReferenceError"); 433 | SetFn(ref JsCreateRuntime, hModule, "JsCreateRuntime"); 434 | SetFn(ref JsCreateSymbol, hModule, "JsCreateSymbol"); 435 | SetFn(ref JsCreateSyntaxError, hModule, "JsCreateSyntaxError"); 436 | SetFn(ref JsCreateTypeError, hModule, "JsCreateTypeError"); 437 | SetFn(ref JsCreateURIError, hModule, "JsCreateURIError"); 438 | SetFn(ref JsDefineProperty, hModule, "JsDefineProperty"); 439 | SetFn(ref JsDeleteIndexedProperty, hModule, "JsDeleteIndexedProperty"); 440 | SetFn(ref JsDeleteProperty, hModule, "JsDeleteProperty"); 441 | SetFn(ref JsDisableRuntimeExecution, hModule, "JsDisableRuntimeExecution"); 442 | SetFn(ref JsDisposeRuntime, hModule, "JsDisposeRuntime"); 443 | SetFn(ref JsDoubleToNumber, hModule, "JsDoubleToNumber"); 444 | SetFn(ref JsEnableRuntimeExecution, hModule, "JsEnableRuntimeExecution"); 445 | SetFn(ref JsEquals, hModule, "JsEquals"); 446 | SetFn(ref JsGetAndClearException, hModule, "JsGetAndClearException"); 447 | SetFn(ref JsGetArrayBufferStorage, hModule, "JsGetArrayBufferStorage"); 448 | SetFn(ref JsGetExtensionAllowed, hModule, "JsGetExtensionAllowed"); 449 | SetFn(ref JsGetFalseValue, hModule, "JsGetFalseValue"); 450 | SetFn(ref JsGetGlobalObject, hModule, "JsGetGlobalObject"); 451 | SetFn(ref JsGetIndexedProperty, hModule, "JsGetIndexedProperty"); 452 | SetFn(ref JsGetNullValue, hModule, "JsGetNullValue"); 453 | SetFn(ref JsGetOwnPropertyDescriptor, hModule, "JsGetOwnPropertyDescriptor"); 454 | SetFn(ref JsGetOwnPropertyNames, hModule, "JsGetOwnPropertyNames"); 455 | SetFn(ref JsGetOwnPropertySymbols, hModule, "JsGetOwnPropertySymbols"); 456 | SetFn(ref JsGetProperty, hModule, "JsGetProperty"); 457 | SetFn(ref JsGetPropertyIdFromName, hModule, "JsGetPropertyIdFromName"); 458 | SetFn(ref JsGetPropertyIdFromSymbol, hModule, "JsGetPropertyIdFromSymbol"); 459 | SetFn(ref JsGetPrototype, hModule, "JsGetPrototype"); 460 | SetFn(ref JsGetRuntimeMemoryUsage, hModule, "JsGetRuntimeMemoryUsage"); 461 | SetFn(ref JsGetTrueValue, hModule, "JsGetTrueValue"); 462 | SetFn(ref JsGetTypedArrayStorage, hModule, "JsGetTypedArrayStorage"); 463 | SetFn(ref JsGetUndefinedValue, hModule, "JsGetUndefinedValue"); 464 | SetFn(ref JsGetValueType, hModule, "JsGetValueType"); 465 | SetFn(ref JsHasException, hModule, "JsHasException"); 466 | SetFn(ref JsHasProperty, hModule, "JsHasProperty"); 467 | SetFn(ref JsIdle, hModule, "JsIdle"); 468 | SetFn(ref JsIntToNumber, hModule, "JsIntToNumber"); 469 | SetFn(ref JsIsRuntimeExecutionDisabled, hModule, "JsIsRuntimeExecutionDisabled"); 470 | SetFn(ref JsNumberToDouble, hModule, "JsNumberToDouble"); 471 | SetFn(ref JsNumberToInt, hModule, "JsNumberToInt"); 472 | SetFn(ref JsParseScript, hModule, "JsParseScript"); 473 | SetFn(ref JsParseSerializedScript, hModule, "JsParseSerializedScript"); 474 | SetFn(ref JsPointerToString, hModule, "JsPointerToString"); 475 | SetFn(ref JsPreventExtension, hModule, "JsPreventExtension"); 476 | SetFn(ref JsRelease, hModule, "JsRelease"); 477 | SetFn(ref JsRunScript, hModule, "JsRunScript"); 478 | SetFn(ref JsRunSerializedScript, hModule, "JsRunSerializedScript"); 479 | SetFn(ref JsSerializeScript, hModule, "JsSerializeScript"); 480 | SetFn(ref JsSetCurrentContext, hModule, "JsSetCurrentContext"); 481 | SetFn(ref JsSetException, hModule, "JsSetException"); 482 | SetFn(ref JsSetIndexedProperty, hModule, "JsSetIndexedProperty"); 483 | SetFn(ref JsSetProperty, hModule, "JsSetProperty"); 484 | SetFn(ref JsSetPrototype, hModule, "JsSetPrototype"); 485 | SetFn(ref JsSetRuntimeMemoryAllocationCallback, hModule, "JsSetRuntimeMemoryAllocationCallback"); 486 | SetFn(ref JsStartDebugging, hModule, "JsStartDebugging", optional: true); 487 | SetFn(ref JsStrictEquals, hModule, "JsStrictEquals"); 488 | SetFn(ref JsStringToPointer, hModule, "JsStringToPointer"); 489 | SetFn(ref JsGetExternalData, hModule, "JsGetExternalData"); 490 | SetFn(ref JsSetExternalData, hModule, "JsSetExternalData"); 491 | } 492 | 493 | public static ChakraApi Instance 494 | { 495 | get 496 | { 497 | return sharedInstance_.Value; 498 | } 499 | } 500 | 501 | private static void ThrowForNative() 502 | { 503 | int hr = Marshal.GetHRForLastWin32Error(); 504 | throw Marshal.GetExceptionForHR(hr); 505 | } 506 | 507 | private static void SetFn(ref TDelegate target, IntPtr hModule, string procName, bool optional = false) 508 | where TDelegate : class 509 | { 510 | IntPtr procAddr = NativeMethods.GetProcAddress(hModule, procName); 511 | if (IntPtr.Zero != procAddr) 512 | { 513 | target = Marshal.GetDelegateForFunctionPointer(procAddr); 514 | } 515 | else if (!optional) 516 | { 517 | ThrowForNative(); 518 | } 519 | } 520 | 521 | private static ChakraApi Load() 522 | { 523 | string myPath = "."; 524 | string directory = Path.GetDirectoryName(myPath); 525 | string arch = "x86"; 526 | if (IntPtr.Size == 8) 527 | { 528 | arch = "x64"; 529 | } 530 | 531 | #if DEBUG 532 | string build = "dbg"; 533 | #else 534 | string build = "fre"; 535 | #endif 536 | 537 | string mainPath = Path.Combine(directory, "ChakraCore", build, arch, "ChakraCore.dll"); 538 | if (File.Exists(mainPath)) 539 | { 540 | return FromFile(mainPath); 541 | } 542 | string alternatePath = Path.Combine(directory, "ChakraCore", arch, "ChakraCore.dll"); 543 | if (File.Exists(alternatePath)) 544 | { 545 | return FromFile(alternatePath); 546 | } 547 | string localPath = Path.Combine(directory, "ChakraCore.dll"); 548 | if (File.Exists(localPath)) 549 | { 550 | return FromFile(localPath); 551 | } 552 | 553 | IntPtr hMod = NativeMethods.LoadLibrary("chakra.dll"); 554 | if (hMod != IntPtr.Zero) 555 | { 556 | return new ChakraApi(hMod); 557 | } 558 | 559 | throw new FileNotFoundException(string.Format("Could not locate a copy of ChakraCore.dll to load. The following paths were trie" + 560 | "d:\n\t{0}\n\t{1}\n\t{2}\n\nEnsure that an architecture-appropriate copy of ChakraCore.dl" + 561 | "l is included in your project. Also tried to load the system-provided Chakra.dll.", mainPath, alternatePath, localPath)); 562 | } 563 | 564 | public static ChakraApi FromFile(string filePath) 565 | { 566 | if ((false == File.Exists(filePath))) 567 | { 568 | throw new FileNotFoundException("The library could not be located at the specified path.", filePath); 569 | } 570 | IntPtr hMod = NativeMethods.LoadLibraryEx(filePath, IntPtr.Zero, 0); 571 | if ((hMod == IntPtr.Zero)) 572 | { 573 | ThrowForNative(); 574 | } 575 | return new ChakraApi(hMod); 576 | } 577 | 578 | 579 | } 580 | } 581 | 582 | --------------------------------------------------------------------------------