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');
        }};
        ";
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions