4

When using V8 as a scripting engine I expose a C++ function called construct_with_ec6_syntax to Javascript. This function, when invoked, should simply return an instance of some_ec6_class.

This C++ function should basically do the following equivalent Javascript:

return new some_ec6_class(111, 222);

This class will be defined in Javascript as follows with EC6 syntax:

class some_ec6_class
{
    constructor(x, y) {
        this.result = x + y;
    }
}

My goal is to run the following in Javascript...

var the_instance = construct_with_ec6_syntax(111, 222);
the_instance.result ; // .. and get 333 here.

My current implementation for the C++ function is this:

void construct_with_ec6_syntax(const FunctionCallbackInfo<Value>& args) {
    Handle<Object> global = args.GetIsolate()->GetCurrentContext()->Global();
    std::string newvecfunc = "some_ec6_class";
    Handle<Value> value = global->Get(String::NewFromUtf8(args.GetIsolate(), newvecfunc.c_str(), String::kNormalString, newvecfunc.length()));
    Local<Value> result;
    if (value->IsFunction()) {
        Handle<Function> func = Handle<Function>::Cast(value);
        Handle<Value> args2[2];
        args2[0] = Number::New(args.GetIsolate(), 111);
        args2[1] = Number::New(args.GetIsolate(), 222);
        result = func->CallAsConstructor(args.GetIsolate()->GetCurrentContext(), 2, args2).ToLocalChecked();
    }   
    args.GetReturnValue().Set(result);
}   

Running this function from Javascript has it return undefined! Instead of the object I expect. As xaxxon pointed out to me, it's because value->IsFunction() returns false, but value->IsUndefined() returns true. If I had defined the class using the with non EC6 syntax, the above function does return an instance..

function some_non_ec6_class(x, y) // this guy would work with the above function
{
    this.result = x + y;
}

So I'm a bit confused! Do I somehow need to be more specific, like get the constructor function from the object first, and then invoke CallAsConstructor?

Any hint is appreciated!

(This question looks similar to Calling a v8 javascript function from c++ with an argument but is different.)

I'm using a V8 checkout from 22nd of October 2016. Complete testcase:

https://gist.github.com/rayburgemeestre/c0abd528f6f67edbfe686d484c45ddbb

Minor update:

As you can see in the comments, I also made a more specific test-case regarding "fetching" the class from the context here: https://gist.github.com/rayburgemeestre/df6193d532c7b7908fe27c89799bfa3a

Also I posted to the v8-users mailinglist: https://groups.google.com/forum/#!topic/v8-users/Hj2j4rJMwBw

9
  • Please update your question to concisely state your current question, as there are no responses to it yet. We don't need to know your process for getting to where you currently are. Or if you no longer need help, perhaps just delete the question altogether. Commented Jan 4, 2017 at 5:58
  • @xaxxon Yeah I still need help with this, I've removed the process part Commented Jan 11, 2017 at 8:16
  • You don't seem to be doing much error checking. Does the if(value->IsFunction()) part return true? If that if never runs, that would explain your return value Commented Jan 11, 2017 at 20:55
  • Yeah good point, it returns false, and if(value->IsUndefined()) returns true. I'll try some more and update the question again. I'm now figuring out if I can use something like v8::FunctionTemplate/NewInstance(). Though all snippets I can find seem to construct something entirely new, and I want to fetch the "class" from an existing context. Commented Jan 11, 2017 at 23:40
  • i glanced around to see what I could see about the object model for es6 classes and didn't see anything. Commented Jan 12, 2017 at 2:15

1 Answer 1

2

class is a way of creating a variable in javascript, similar to let. Variables created with class are block scoped and do not create global properties (again, similar to let). Therefor it is not available in context.global like a function is:

http://exploringjs.com/es6/ch_variables.html

function    Complete    Block   Yes

class       No          Block   No

You would need to explicitly create a global variable for your code to work:

some_es6_class = class{}; 

or explicitly pass in the class object to your c++ function after creating it with the 'traditional es6' syntax:

class some_es6_class{};
some_native_function(some_es6_class);

Edit: I've done some more digging and I believe that the context object has a LexicalEnvironment that is shared across its scripts but is distinct from the global object. When a name is looked up, it traverses a parent hierarchy of LexicalEnvironments looking for the name. These lexical environments are not exposed via the API (and may not actually exist - they are a construct used by the JS specification to define behavior, not a required part of an implementation)

Sign up to request clarification or add additional context in comments.

2 Comments

First of all thanks a bunch, I think I now understand why I couldn't refer to the class, and whenever I tried to create an instance in C++ I'd just get a new object with the same name :) The first approach, making a global variable for the class works for me and is more performant than the current workaround I was using until now (using a factory function)
@RayBurgemeestre glad we got it figured out and I learned quite a bit in the process.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.