Search code examples
javascriptandroidc++v8

How to run V8 evaluation multiple times?


Maybe it is stupid question (I am newbie to C++, just wanted to use it as library for android), but I am not able to run evaluation of some JS multiple times.

I have started with "hello world" tutorial. But then I have wanted simple thing, re-run main (just wrap content of tutorial code into function and run it twice in newly empty main.

This is what I got:

#
# Fatal error in ../src/isolate.cc, line 1868
# Check failed: thread_data_table_.
#

==== C stack trace ===============================

1: 0xa890b9
2: 0x6a22fc
3: 0x42694f
4: 0x405f66
5: 0x405ec7
6: __libc_start_main
7: 0x405dc9
Illegal instruction (core dumped)

This cames after creating new isolate

Isolate* isolate = Isolate::New(create_params);

Well, what I should do? Am I using wrong construct or so? Should I close/delete/clear something more?

In bigger view I just want to do evaluate function, that can be triggered multiple times, and beside that also run multiple js snipets in same context (how to split this function?).

Any idea?


UPDATE:

Ok, lets say that the main can be split into three logical parts:

init

int main(int argc, char* argv[]) {
// Initialize V8.
V8::InitializeICU();
V8::InitializeExternalStartupData(argv[0]);
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();

// Create a new Isolate and make it the current one.
ArrayBufferAllocator allocator;
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = &allocator;

evaluation

Isolate* isolate = Isolate::New(create_params);
{
Isolate::Scope isolate_scope(isolate);

// Create a stack-allocated handle scope.
HandleScope handle_scope(isolate);

// Create a new context.
Local<Context> context = Context::New(isolate);

// Enter the context for compiling and running the hello world script.
Context::Scope context_scope(context);

// Create a string containing the JavaScript source code.
Local<String> source =
    String::NewFromUtf8(isolate, "'Hello' + ', World!'",
                        NewStringType::kNormal).ToLocalChecked();

// Compile the source code.
Local<Script> script = Script::Compile(context, source).ToLocalChecked();

// Run the script to get the result.
Local<Value> result = script->Run(context).ToLocalChecked();

// Convert the result to an UTF8 string and print it.
String::Utf8Value utf8(result);
printf("%s\n", *utf8);
}
isolate->Dispose();

and clean

// Dispose and tear down V8.
V8::Dispose();
V8::ShutdownPlatform();
delete platform;
return 0;

Now as I said before if I run main consists of init->evaluation->clean twice, that mean init->evaluation->clean->init->evaluation->clean, then the error occurs. I have figured out, that if I extract evaluation part into separate function I can run it multiple times e.g. as init->(evaluation){2}->clean

Is that how should it work? Next step is to divide this main into tree separate function that mean I have to have static member with platform? Could it cause leak somehow?

NOTE: that I want to run it from android, that mean e.g. click in UI, propagate js source to C via JNI and then call c++ V8, which is already initialized or not. hm?

Prefered way is to have "blackbox", but if I have to hold platform, so be it. It maybe could be also faster without re-initialization of V8, right?


UPDATE 2:

Well, still have problems with splitting evaluation part to achieve multiple runs in same isolate/context.

I have splitted it after creating context with stored isolate and context, but with no luck. When in second part try to create source string it fails, probably because of using stored isolate (something with isolate scope I guess).

:(


Solution

  • My assumption as I introduced in UPDATE1 was correct. That part works well.

    According to UPDATE2 I have splitted evaluation part into two.

    First for initialize isolate and context:

    mIsolate = Isolate::New(mCreate_params);
    Isolate::Scope isolate_scope(mIsolate);
    {
        // Create a stack-allocated handle scope.
        HandleScope handle_scope(mIsolate);
    
        v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(mIsolate);
        // Bind the global 'print' function to the C++ Print callback.
        global->Set(v8::String::NewFromUtf8(mIsolate, "print"), v8::FunctionTemplate::New(mIsolate, Print));
    
        // Create a new context.
        mContext = Context::New(mIsolate, NULL, global);
        Persistent<Context, CopyablePersistentTraits<Context>> persistent(mIsolate, mContext);
        mContext_persistent = persistent;
    }
    

    and second that will run js in same context:

    Isolate::Scope isolate_scope(mIsolate);
    {
        HandleScope handle_scope(mIsolate);
        mContext = Local<Context>::New(mIsolate, mContext_persistent);
        // Enter the context for compiling and running the hello world script.
        Context::Scope context_scope(mContext);
        {
            // Create a string containing the JavaScript source code.
            Local<String> source =
                String::NewFromUtf8(mIsolate, js_source, NewStringType::kNormal).ToLocalChecked();
    
            // Compile the source code.
            Local<Script> script = Script::Compile(mContext, source).ToLocalChecked();
    
            TryCatch trycatch(mIsolate);
    
            // Run the script to get the result.
            v8::Local<v8::Value> result;
            if(!script->Run(mContext).ToLocal(&result)){
                v8::String::Utf8Value exception_str(trycatch.Exception());
                dprint(*exception_str);
            }else{
                if(!result->IsUndefined()){
                    String::Utf8Value utf8(result);
                    dprint(*utf8);
                }
            }
        }
    } 
    

    Well the code works very well on linux, but I still have some issues when I try to run first part for the second time (create new context) on android:

     A/art: art/runtime/thread.cc:986] pthread_getschedparam failed for DumpState: No such process
     A/art: art/runtime/base/mutex.cc:485] Unexpected state_ 0 in unlock for logging lock
    

    But that's another question I guess. Peace.