Clearscript: using import modules v8

Created on 28 Nov 2019  路  5Comments  路  Source: microsoft/ClearScript

Hi,

I'm currently looking into the import module thing for v8 you added.
So it starts to work only when I execute like this with the DocumentInfo parameter (as in you unit tests):

engine. Execute(new DocumentInfo { Category = ModuleCategory.Standard }, theScript);

I used in the past always just:

engine.Execute(theScript)

The problem then is that I can't invoke any of the methods declared within my script from "native" code:

-----snip----
import * as Arithmetic from 'd:/Arithmetic2.js';
Console.WriteLine("Hello " + Arithmetic.Add(123, 456));

function OnInit()
{
Console.WriteLine("Hello " + Arithmetic.Add(123, 456));
}
----snip---

The first WriteLine works, but when I try to call OnInit using engine.Invoke it simply fails throwing "TypeError: Method or property not found". I understand the module thing is brandnew, so perhaps I ran into a bug here?

question

All 5 comments

Hi @flat-eric147,

A standard JavaScript module runs in a sandbox that prevents it from "polluting" the global object. This is by design. In your example above, OnInit is internal to the module and inaccessible to anyone else.

To work around this, you could expose the global object so that modules can modify it:

``` C#
engine.Script.Global = engine.Script;
engine.Execute(new DocumentInfo { Category = ModuleCategory.Standard }, @"
import * as Arithmetic from 'd:/Arithmetic2.js';
Global.OnInit = function () {
Console.WriteLine('Hello ' + Arithmetic.Add(123, 456));
}
");
engine.Invoke("OnInit");


Another possibility is to use the script evaluation result value:

``` C#
engine.Script.OnInit = engine.Evaluate(new DocumentInfo { Category = ModuleCategory.Standard }, @"
    import * as Arithmetic from 'd:/Arithmetic2.js';
    (function () {
        Console.WriteLine('Hello ' + Arithmetic.Add(123, 456));
    })
");
engine.Invoke("OnInit");

Good luck!

It makes sense that any modules that get imported run in a sandbox. But is it possible to specify that the global context of a ScriptEngine be allowed to import standard modules without being a standard module itself?

Similarly (and maybe this should be a different question), is it possible to add host objects to a module's sandbox instead of the global one?

Hi @BrianHanechak,

But is it possible to specify that the global context of a ScriptEngine be allowed to import standard modules without being a standard module itself?

In a normal (non-module) script, the appearance of the _static_ import statement is a syntax error. ClearScript can't change that. However, V8 and ClearScript also support the proposed but not yet standardized _dynamic_ function-like import(), which _can_ be used in a normal script:

``` C#
using (var engine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDynamicModuleImports)) {
engine.AddHostType(typeof(Console));
engine.DocumentSettings.AccessFlags = DocumentAccessFlags.EnableFileLoading;
engine.Execute(@"
(async function () {
let Arithmetic = await import('Arithmetic.js');
Console.WriteLine(Arithmetic.Add(123, 456));
})();
");
}


Note that the `import()` pseudo-function returns a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) whose fulfillment value contains all of the module's exports.

>is it possible to add host objects to a module's sandbox instead of the global one?

The host can pass data to a module privately via a [callback](https://microsoft.github.io/ClearScript/Reference/html/T_Microsoft_ClearScript_DocumentContextCallback.htm) mechanism. Standard modules have access to a special [`import.meta`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import.meta) object (also proposed but not yet standardized). On first access, ClearScript populates this object by invoking a host callback if one has been specified:

``` C#
engine.DocumentSettings.ContextCallback = documentInfo => {
    if (documentInfo.Name == "Logging.js") {
        return new Dictionary<string, object> {
            { "Console", typeof(Console).ToHostType() }
        };
    }
    return null;
};

With this setup, the Logging.js module acquires exclusive access to the system console via import.meta.Console.

Good luck!

Please reopen this issue if you have additional questions on this topic.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Bobris picture Bobris  路  6Comments

drearyrainDeng picture drearyrainDeng  路  3Comments

bhaeussermann picture bhaeussermann  路  6Comments

JohnLudlow picture JohnLudlow  路  4Comments

AllNamesRTaken picture AllNamesRTaken  路  7Comments