What is the best practice to reuse compiled script?

Jan 30, 2015 at 5:59 AM
Edited Jan 30, 2015 at 6:03 AM
Generally we first compile the script then execute it.
using (V8Engine engine = new V8Engine())
{
    Handle script = engine.Compile(scriptContent);
    Handle result = engine.Execute(script);
}
In my case the same script will be executed many times in a single thread with different parameter.
So, I wanna cache the compiled script and reuse it for subsequent executions to avoid unnecessary compilation.

Certainly I can use the same V8Engine instance for all executions but I am afraid the context may be modified by previous executions.

So, I suppose I can compile the script and save its Handle.
And for every execution there is a new V8Engine instance created.

code goes like below
using (V8Engine engine = new V8Engine())
{
    Handle script = engine.Compile(scriptContent);

    for( int i = 0; i < 200; i + + )
    {
        using (V8Engine engine2 = new V8Engine())
        {
            Handle result = engine2.Execute(script);
        }
    }
}
But from my test, it crashes the process by a System.AccessViolationException
Attempted to read or write protected memory. This is often an indication that other memory is corrupt
What is the best practise in this occation?
Coordinator
Jan 30, 2015 at 3:54 PM
Edited Jan 30, 2015 at 3:55 PM
It should work, but to be honest, I only tested with a small few engines instances at the same time (test can be run via the console project). There's a lot of threads at play I think (between V8 and V8.net). It's probably safer to use a single context and try to clean up at the end of the script. One other thing you can try is to execute the scripts in a JS worker thread context instead.
Jan 31, 2015 at 6:06 AM
Edited Jan 31, 2015 at 7:29 AM
I can reproduce the problem via following code in a Console application
        static void Main(string[] args)
        {
            using( V8Engine engine1 = new V8Engine() )
            {
                Handle script = engine1.Compile("var a = 1; a;");

                using (V8Engine engine2 = new V8Engine())
                {
                    using (Handle result = engine2.Execute(script, true))
                    {
                        Console.WriteLine(result.AsString);
                    }
                }
            }
        }
It fails at engine2.Execute(script, true))
Unhandled System.AccessViolationException
  HResult=-2147467261
  Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
  Source=V8.Net.Proxy.Interface.x86
  StackTrace:
       at V8.Net.V8NetProxy.V8ExecuteCompiledScript(NativeV8EngineProxy* engine, HandleProxy* script)
       at V8.Net.V8Engine.Execute(Handle script, Boolean throwExceptionOnError) in c:\Data\Visual Studio\Projects\V8.NET\Source\V8.Net\V8Engine.cs:line 306
       at myv8.Program.Main(String[] args) in g:\Test V8.NET\myv8\Program.cs:line 21
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
is there something wrong?

First, according to V8 doc, Script::Compile compiles context-bound script; Script::New compiles context-independent script.

So I assume V8.NET takes use of Script::New, yep?


Second, even if the compiled script is context-independent, it is still isolate-bound. I assume V8Engine's share the same isolate?
It's probably safer to use a single context and try to clean up at the end of the script.
How to do clean up? it seems that many V8 objects like returned value won't be collected until I dispose V8Engine instance, there is no way to destrop the handle scope and so on.
Coordinator
Jan 31, 2015 at 1:38 PM
You cannot use a compiled script from one engine in another.
Jan 31, 2015 at 3:48 PM
Ok :) thanks for the answer

On 2014 Mar, V8 is updated with a new API ScriptCompiler::CompileUnbound(), doc here.

It can be used to compile context-independent script. and then bind to specific context to execute
I can fork and try to modify.

However, it seems that the contexts must be from the same isolate
currently each V8Engine instance creates its own isolate? or share
Coordinator
Jan 31, 2015 at 5:20 PM
That's correct, each engine wrapper wraps a V8 instance, an isolate, and default context. I have plans to make this more flexible given recent changes, and better understanding of V8 in general.