ClearScript Documentation

repository·master·Indexed 24 days ago

https://github.com/clearfoundry/clearscript

A library that enables .NET applications to host and interact with scripting engines such as V8, JScript, and VBScript. It provides a bridge between .NET types and script code, supporting .NET 5.0+, .NET Framework 4.6.2+, .NET Core 3.1, and .NET Standard 2.1 across Windows, Linux, and macOS. Key features include full generics support, collection iteration, custom attribute loaders for member exposure, and ES6/CommonJS module interoperability.

Tokens
4.4K
Snippets
6
Records
23
Agent score
83%

What's inside ClearScript

  1. Overview of ClearScript

    master

    ClearScript is a library designed to integrate scripting capabilities into .NET applications. It allows you to create a script engine, expose .NET objects or types to that engine, and execute scripts that can interact with your .NET code.

    Supported Script Engines:

    • JavaScript: via Google's V8 engine or Microsoft's JScript.
    • VBScript

    Key Capabilities:

    • Seamless Integration: Exposed .NET resources require no special modification or decoration. Scripts gain access to methods, properties, fields, events, indexers, extension methods, and more.
    • Advanced Type Support: Full support for generic types/methods (including C#-like type inference), constructors, and nested types.
    • Collection Interop: .NET collections work with native script iteration (e.g., for...of in V8, Enumerator in JScript, or For Each...Next in VBScript).
    • Bidirectional Communication: Scripts can invoke .NET methods (including those with out parameters, optional parameters, and params arrays), and the host can invoke script functions or access script objects directly.
    • Modern JavaScript Features (V8): Supports JavaScript modules, typed arrays for fast data transfer, and automatic conversions for BigInt, Date, and Promise (from .NET Task).
  2. Understanding Top-Level Await behavior in ClearScript 7.2.2+

    master

    In ClearScript 7.2.2 (paired with V8 9.8), Top-Level Await is always enabled by the underlying V8 engine and cannot be disabled by the embedder.

    To maintain compatibility with hosts that expect immediate module evaluation results, ClearScript uses a mechanism to detect if a loaded module requires async evaluation (i.e., it uses await or for await...of).

    • For synchronous modules: ClearScript tracks the evaluation via a promise and returns the result directly to the host, preserving original behavior.
    • For asynchronous modules: While ClearScript correctly identifies that async evaluation is required, there is a known limitation due to a V8 bug where the evaluation result of an async module is returned as undefined.
  3. How Custom Attribute Loaders work

    master

    ClearScript uses attributes (like ScriptMemberAttribute) to control how .NET resources are exposed to scripts, including renaming members, restricting access, or adjusting marshaling.

    Because you cannot add attributes to pre-compiled external libraries or platform components, ClearScript provides a global facility called the CustomAttributeLoader. By overriding the CustomAttributeLoader.LoadCustomAttributes<T> method, a host can "virtualize" attribute retrieval. This allows you to inject new attributes into any .NET resource or modify/hide existing ones globally without changing the original source code.

  4. Choose a ClearScript package type

    master

    ClearScript offers three main packaging strategies:

    1. Complete package: An all-in-one package that supports all platforms.
    2. Composite packages: Packages containing everything needed for a specific platform (Windows, Linux, or macOS).
    3. Component packages: Granular libraries and data, such as the Core library or specific engine support like JScript/VBScript or V8.
  5. Understand Document Categories in ClearScript

    master

    ClearScript uses DocumentCategory to distinguish how scripts should be executed and how they interact with the module system. Because ClearScript cannot automatically detect the category of a document, the host must provide this context.

    Categories

    • ModuleCategory.Standard: Represents a standard ES6 JavaScript module.
    • ModuleCategory.CommonJS: Represents a CommonJS module.
    • DocumentCategory.Script: The default category used if no category is provided. This is for normal scripts that are not part of a module system.

    Behavior Rules

    • Explicit Execution: When calling engine.Execute(), you should pass a DocumentInfo object specifying the Category to ensure the entry point is treated correctly.
    • Inheritance: When a module is loaded by another module, it inherits the category of the requesting module. For example, if a Standard module imports a file, that file is treated as Standard unless the LoadCallback overrides it.
    • Detection: Use engine.DocumentSettings.LoadCallback to intercept document loading and manually set info.Category for specific files.
  6. Features of ClearScript

    master

    ClearScript provides a robust bridge between .NET and scripting environments with the following features:

    .NET Integration

    • Zero Modification: Exposed resources require no special decoration or coding.
    • Rich Access: Scripts can access methods, properties, fields, events, indexers, extension methods, and constructors.
    • Generics Support: Full support for generic types and methods with C#-like type inference.
    • Collection Iteration: .NET collections support native iteration mechanisms:
      • V8: for...of and for await...of
      • JScript: Enumerator
      • VBScript: For Each...Next
    • Advanced Method Invocation: Supports output parameters, optional parameters, and parameter arrays.
    • Callbacks: Script delegates enable calling back into script code from .NET.
    • Assembly Exposure: Ability to expose all types in one or more assemblies in a single step.

    Engine-Specific Features

    • V8: Supports JavaScript typed arrays (fast data transfer), JavaScript modules, BigInt conversion, Date conversion, and Promise conversion.
    • JScript/V8: Supports CommonJS modules.

    Platform Support

    • Runtimes: .NET 5.0+, .NET Framework 4.6.2+, .NET Core 3.1, and .NET Standard 2.1.
    • OS: Windows (x86/x64/arm64), Linux (x64/arm/arm64), and macOS (x64/arm64).
  7. Enable the Performance API in V8ScriptEngine

    master

    By default, ClearScript provides a bare scripting environment. To access high-resolution timing facilities in the V8-based engine, you must explicitly enable the Performance object by passing the V8ScriptEngineFlags.AddPerformanceObject flag during the construction of your V8ScriptEngine instance.

    var engine = new V8ScriptEngine(V8ScriptEngineFlags.AddPerformanceObject);
  8. Install ClearScript via NuGet

    master
    ClearScript can be installed using NuGet packages. Depending on your requirements, you can choose between a single complete package containing all supported platforms or specific composite/component packages tailored to your target platform and engine requirements.
  9. Increase timer resolution with SetTimerResolution

    master

    To request that native timers be set to the highest available resolution while the script engine is active, use the V8ScriptEngineFlags.SetTimerResolution flag.

    Warning: This flag is a hint and may be ignored on some systems. Where supported, it can degrade overall system performance or power efficiency. Use with caution.

    To use both the Performance API and high-resolution timers, combine the flags using a bitwise OR:

    var flags = V8ScriptEngineFlags.AddPerformanceObject | V8ScriptEngineFlags.SetTimerResolution;
    var engine = new V8ScriptEngine(flags);
  10. Enable Module Interoperability (ES6 importing CommonJS)

    master

    In ClearScript 7.3.7+, you can allow standard ES6 modules to import CommonJS modules. This requires three specific configuration steps:

    1. Enable File Loading: Set DocumentAccessFlags.EnableFileLoading so the engine can resolve module URIs.
    2. Allow Category Mismatch: Set DocumentAccessFlags.AllowCategoryMismatch. By default, the loader throws an exception if a loaded module's category (e.g., CommonJS) differs from the requesting module's category (e.g., Standard). This flag relaxes that restriction.
    3. Specify Categories via LoadCallback: Since ClearScript cannot automatically detect if a file is ES6 or CommonJS, you must use the DocumentSettings.LoadCallback to manually assign the correct ModuleCategory based on the file's URI or name.

    Note: Reverse interoperability (CommonJS importing ES6) is not supported because CommonJS execution is synchronous and cannot handle the potentially asynchronous nature of ES6 modules.

    // 1. Configure access flags
    engine.DocumentSettings.AccessFlags = DocumentAccessFlags.EnableFileLoading | DocumentAccessFlags.AllowCategoryMismatch;
    
    // 2. Define how to categorize files (e.g., identifying CommonJS files)
    engine.DocumentSettings.LoadCallback = (ref DocumentInfo info) => {
        if (Path.GetFileNameWithoutExtension(info.Uri.AbsolutePath) == "Geometry") {
            info.Category = ModuleCategory.CommonJS;
        }
    };
    
    // 3. Execute the entry point as a Standard module
    engine.Execute(new DocumentInfo { Category = ModuleCategory.Standard }, @"
        import { Rectangle } from 'Geometry';
        Console.WriteLine('The area is {0}.', new Rectangle(3, 4).Area);
    ");
  11. Implement a Custom Attribute Loader

    master

    To customize how attributes are applied to .NET types, inherit from CustomAttributeLoader and override the LoadCustomAttributes<T> method.

    Inside the override, you can:

    1. Call base.LoadCustomAttributes<T>(resource, inherit) to get the attributes actually declared in the code.
    2. Inspect the resource (which can be cast to MemberInfo to access names) and the type T being requested.
    3. Return a new array of attributes to effectively "inject" them into the resource for the script engine.
    class MyAttributeLoader : CustomAttributeLoader {
        public override T[] LoadCustomAttributes<T>(ICustomAttributeProvider resource, bool inherit) {
            var declaredAttributes = base.LoadCustomAttributes<T>(resource, inherit);
            // Logic to inject or modify attributes goes here
            return declaredAttributes;
        }
    }