Search code examples
c++v8

Google V8: access local variables in C++


Does someone know how I could look up local variables in a nested function call from C++? Consider the following example:

// e.g. a global variable in the browser
var global = "global_value";

function foo(){
    var global = "local_value";
    myCppFunction("global", global);
}

foo();

My question now is how in the implementation of myCppFunction I could access the function local variable "global" (NOT value, this would be given by the 2nd parameter) from 'foo'?

Handle<Value> MyCppFunction(const Arguments& args){
    Local<String> varName = args[0]->ToString();
    Local<String> varValue = args[1]->ToString(); // this holds "local_value"
    // how to access the variable "global" in the scope of 'foo' ?
}

Solution

  • I managed to find it out by myself. See the example below for how to find the value on the stack (and also replace it - here by the example of a string variable).

    Two remarks beforehand:

    • I have not tested this for undesired behavior except from my very use cases where I use this in my master thesis - there (may) be dragons.
    • I don't know exactly why in my tests sfl.FindJavaScriptFrame(0) yields the desired stack frame - but, as it works independently of the calling depth, I suspect the stack frame indexed by 0 to always be the immediate caller's frame (in my case I know that I want exactly that).

    And the code:

    // Prepare identification of the variable,assuming varName as in the question
    // More convenient conversions would be appreciated, at least by me
    Local<String> varName = args[0]->ToString();        
    std::string varStr = *String::AsciiValue(varName);
    // I'm using 'namespace i = internal;' (within v8-namespace)
    i::Vector<const char> tmpVar(varStr.data(), varStr.length());
    i::Handle<i::String> varIStr = i::Isolate::Current()->factory()->NewStringFromAscii(tmpVar, i::TENURED);
    
    // Now hunt down the stack frame of interest, be sure to consider my remarks above
    i::StackFrameLocator sfl;
    // Comment from the code: The caller must guarantee that such a frame exists.
    i::JavaScriptFrame* jsf = sfl.FindJavaScriptFrame(0);
    // create some replacement
    i::Vector<const char> tmp("there you go", 12);
    i::Handle<i::String> insert = i::Isolate::Current()->factory()->NewStringFromAscii(tmp, i::TENURED);
    i::Object* insertObj = insert->ToObjectChecked();
    
    // by the help of JavaScriptFrame::Print I came up with this:
    i::Object* fun = jsf->function();
    if (fun->IsJSFunction()){
        i::Handle<i::ScopeInfo> scope_info(i::ScopeInfo::Empty());
        i::Handle<i::SharedFunctionInfo> shared((i::JSFunction::cast(fun))->shared());
        scope_info = i::Handle<i::ScopeInfo>(shared->scope_info());
        i::Object* script_obj = shared->script();
        if (script_obj->IsScript()) {
            int stack_locals_count = scope_info->StackLocalCount();
            for (int i = 0; i < stack_locals_count; i++) {
                if (scope_info->StackLocalName(i)->Equals(*varIStr)){
                    // replace the value on the stack
                    jsf->SetExpression(i,insertObj);
                }
            }
        }
    }