Skip to content

Module evaluation stopped working at CS 7.5 #649

Description

@simontom

Hey guys, we've ran into the trouble after upgrading to the latest CS v7.5. Worked without a problem in CS v7.4.5.
MainScriptCode newly sees the ModuleRegistrationName to be undefined. The module registration/creation happens in the ScriptLoader. Previously, it returned the V8Object, however, after the upgrade, it returns undefined.

using Microsoft.ClearScript;
using Microsoft.ClearScript.JavaScript;
using Microsoft.ClearScript.V8;

public static class Repro
{
    const string scriptCode =
        //language=JavaScript
        $@"
        export function {ScriptExecutor.ExecuteFunctionName}() {{
            log('execution function returning a result');
            return 'script result';
        }}
    ";

    public static void Main()
    {
        var scriptResult = ScriptExecutor.Execute(scriptCode);
        Console.WriteLine("This is the result from the script: " + scriptResult);
    }
}

public sealed class EngineWrapper(V8ScriptEngine engine) : IDisposable
{
    public dynamic Script => engine.Script;

    public DocumentLoader DocumentLoader
    {
        get => engine.DocumentSettings.Loader;
        set => engine.DocumentSettings.Loader = value;
    }

    public void Execute(string code) => engine.Execute(code);

    public object EvaluateModule(string code) => engine.Evaluate(CreateModuleInfo(), code);

    public void Dispose()
    {
        engine.Dispose();
    }

    private static DocumentInfo CreateModuleInfo() => new() { Category = ModuleCategory.Standard };
}

public static class EngineFactory
{
    public static EngineWrapper CreateEngine()
    {
        var engine = new V8ScriptEngine(GetConstraints(), GetFlags());
        engine.Script.log = new Action<object>(Console.WriteLine);

        return new EngineWrapper(engine);
    }

    private static V8ScriptEngineFlags GetFlags() =>
        V8ScriptEngineFlags.EnableTaskPromiseConversion
        | V8ScriptEngineFlags.EnableValueTaskPromiseConversion
        | V8ScriptEngineFlags.EnableDateTimeConversion;

    private static V8RuntimeConstraints GetConstraints() => new() { MaxArrayBufferAllocation = 50 * 1024 * 1024 };
}

public class ScriptLoader(string scriptCode) : DocumentLoader
{
    public const string ModuleRegistrationName = nameof(ModuleRegistrationName);
    private const string CustomScriptSpecifier = "script.js";

    public void LoadScriptModule(EngineWrapper engineWrapper)
    {
        engineWrapper.DocumentLoader = this;
        engineWrapper.Script.ModuleRegistrationName = engineWrapper.EvaluateModule($@"
                import * as module from '{CustomScriptSpecifier}';
                module;
            ");
    }

    public override async Task<Document> LoadDocumentAsync(
        DocumentSettings settings,
        DocumentInfo? sourceInfo,
        string specifier,
        DocumentCategory category,
        DocumentContextCallback contextCallback
    )
    {
        if (specifier != CustomScriptSpecifier)
        {
            return await Default.LoadDocumentAsync(settings, sourceInfo, specifier, category, contextCallback);
        }

        var documentInfo = new DocumentInfo(specifier) { Category = category, ContextCallback = contextCallback };
        return new StringDocument(documentInfo, scriptCode);
    }
}

public static class ScriptExecutor
{
    public const string ExecuteFunctionName = "execute";
    public const string ScriptResultVariableName = "ScriptResult";

    public static dynamic Execute(string scriptCode)
    {
        using var engine = EngineFactory.CreateEngine();
        var scriptLoader = new ScriptLoader(scriptCode);
        scriptLoader.LoadScriptModule(engine);

        engine.Execute(MainScriptCode);
        return engine.Script.ScriptResult;
    }

    private const string MainScriptCode =
        //language=JavaScript
        $@"
        {{
            try {{
                log('started');

                log({ScriptLoader.ModuleRegistrationName});
                {ScriptResultVariableName} = {ScriptLoader.ModuleRegistrationName}.{ExecuteFunctionName}();

                log('finished execution');
            }} catch(exception) {{
                log('catch block');

                try {{
                    log('log exception');

                    const logInfo = exception.stack ? exception.stack : exception;
                    log('logInfo: ' + logInfo);

                    const message = exception instanceof Error
                        ? exception.message
                        : exception.toString();
                    log('exception message: ' + message);
                }} catch (e) {{
                    log('failed to process exception: ' + (e?.toString() ?? 'Unknown error'));

                    const message = exception instanceof Error
                        ? exception?.message ?? 'Unknown error message'
                        : exception?.toString() ?? 'Unknown error';
                    log('exception message 2: ' + message);
                }}
            }}

            log('finished');
        }};
        ";
}

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions