I want to make a log of every function called when i run a js script.
So i want to make a callback for all the functions in javascript like this:
global->Set(v8::String::NewFromUtf8(isolate, "print"), v8::FunctionTemplate::New(isolate, LogName));
global->Set(v8::String::NewFromUtf8(isolate, "eval"), v8::FunctionTemplate::New(isolate, LogName));
global->Set(v8::String::NewFromUtf8(isolate, "unescape"), v8::FunctionTemplate::New(isolate, LogName));
I define my function like this:
void LogName(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::String::Utf8Value str_caller(args.Callee());
printf("%s", str_caller);
}
This is printed when unescape is called: function unescape() { [native code] }
But if do:
object = v8::Handle<v8::Object>::Cast(context->Global()->Get(v8::String::NewFromUtf8(isolate, "String")));
object->Set(v8::String::NewFromUtf8(isolate, "fromCharCode"), v8::FunctionTemplate::New(isolate, LogName)->GetFunction());
This is printed when String.fromCharCode is called: function () { [native code] }
Why in the second example i don't have the functions name, like for example "fromCharCode" ?
I'm still pretty new to V8 but have run into this exact same problem. I've found only one solution so far. I'm not sure if it is ideal, but there are no other solutions so here goes...
Notice the functions where getting the name works are where you are adding a FunctionTemplate value to an ObjectTemplate (that is presumably then used as the global template parameter when you create the Context). Also notice in the ones that don't work you are trying to add a Function to the global Object for that existing Context, and this is when getting the Callee name fails (returns a blank string).
The only solution I've found so far is to keep a persistent handle to the ObjectTemplate you create for global scope, add the FunctionTemplate to that when you go to register your new function, and then create a new Context that uses that modified ObjectTemplate. After this then calls to the function will return the Callee name as desired.
To try to illustrate this with some code:
Isolate *gIsolate;
Persistent<ObjectTemplate> gTemplate;
Persistent<Context> gContext;
// Adds a new function to the global object template
void AddFunction(const char *name, FunctionCallback func)
{
// Get global template
Local<ObjectTemplate> global = ObjectTemplate::New(gIsolate, gTemplate);
// Add new function to it
global->Set(String::NewFromUtf8(gIsolate, name), FunctionTemplate::New(gIsolate, func));
}
void FirstTimeInit()
{
gIsolate = Isolate::New();
HandleScope handle_scope(gIsolate);
Handle<ObjectTemplate> global = ObjectTemplate::New(gIsolate);
// Store global template in persistent handle
gTemplate.Reset(gIsolate, global);
// Register starting functions
AddFunction( "print", LogName );
AddFunction( "eval", LogName );
AddFunction( "unescape", LogName );
// Create context
Handle<Context> context = Context::New(gIsolate, NULL, global);
gContext.Reset(gIsolate, context);
}
void AddOtherFunction()
{
AddFunction( "fromCharCode", LogName );
Local<ObjectTemplate> global = ObjectTemplate::New(gIsolate, gTemplate);
// Create a new context from the modified global template
Local<Context> newContext = Context::New(gIsolate, nil, global);
// Update persistent context handle to reference the new one
gContext.Reset(gIsolate, newContext);
}
Related
cy.visit('somesite1.com');
....
....
const args = {
testObject: myObject
};
cy.origin( 'somesite2.com', { args }, ({testObject}) => {
console.log('print out my test obj', testObject);
someFunction(testObject);}
`
I am trying to use the cross domain feature. I have Cypress 10.3.1. I am able to visit the cross domain but I have trouble passing the variable for further testing. The error I see is
ReferenceError
_actions4 is not defined
Variables must either be defined within the cy.origin() command or passed in using the args option.
myObject is a json object.
Can someone help please?
Krithika
The problem is someFunction is defined outside the cy.origin(), and cannot be accessed.
function someFunction() {
...
}
const args = {
testObject: myObject
};
cy.origin( 'somesite2.com', { args }, ({testObject}) => {
someFunction(testObject); // cannot be accessed here
}
There are severe limitations on using executable code inside the origin callback.
See Arguments
The args object is the only mechanism via which data may be injected into the callback, the callback is not a closure and does not retain access to the JavaScript context in which it was declared. Values passed into args must be serializable.
not a closure means you can't reference things outside the origin in the same way a simple function can.
must be serializable means you cannot pass functions into the origin.
Essentially, it seems only cy. commands can be be used inside the origin.
This post is about exposing C++ objects to the v8 javascript engine. To attach a C++ object to a javascript object, I make use of the GetInternalField() and External APIs. Before you can set or get any internal field, you have to call SetInternalFieldCount() on the corresponding ObjectTemplate. Since I want to expose a constructor function to the JS, I created a FunctionTemplate, set a C++ function that attache the native object to the JS object to that template, and finally SetInternalCount() on the InstanceTemplate() of that function template. Too much words for the description, here is what I did:
struct Point {
int x, y;
Local<FunctionTemplate> CreatePointContext(Isolate* isolate) {
Local<FunctionTemplate> constructor = FunctionTemplate::New(isolate, &ConstructorCallback);
constructor->InstanceTemplate()->SetInternalFieldCount(1); // I set internal field count here.
constructor->SetClassName(String::NewFromUtf8(isolate, "Point", NewStringType::kInternalized).ToLocalChecked());
auto prototype_t = constructor->PrototypeTemplate();
prototype_t->SetAccessor(String::NewFromUtf8(isolate, "x", NewStringType::kInternalized).ToLocalChecked(),
XGetterCallback);
return constructor;
};
// This callback is bound to the constructor to attach a C++ Point instance to js object.
static void ConstructorCallback(const FunctionCallbackInfo<Value>& args) {
auto isolate = args.GetIsolate();
Local<External> external = External::New(isolate, new Point);
args.Holder()->SetInternalField(0, external);
}
// This callback retrieves the C++ object and extract its 'x' field.
static void XGetterCallback(Local<String> property, const PropertyCallbackInfo<Value>& info) {
auto external = Local<External>::Cast(info.Holder()->GetInternalField(0)); // This line triggers an out-of-bound error.
auto point = reinterpret_cast<Point*>(external->Value());
info.GetReturnValue().Set(static_cast< double>(point->x));
}
// This function creates a context that install the Point function template.
Local<Context> CreatePointContext(Isolate* isolate) {
auto global = ObjectTemplate::New(isolate);
auto point_ctor = Point::CreateClassTemplate(isolate);
global->Set(isolate, "Point", point_ctor);
return Context::New(isolate, nullptr, global);
}
When I tried to run the following JS code with the exposed C++ object, I got Internal field out of bounds error.
var p = new Point();
p.x;
I wonder setting internal field count on the instance template of a function template has nothing to do with the object created by the new expression. If so, what is the correct way to set the internal field count of the object created by new while exposing the constructor function to javascript? I want to achieve the following 2 things:
In javascript, a Point function is avaible so we can var p = new Point;.
In C++ I can make sure the JS object has 1 internal field for our C++ Point to live in.
Edit: As #snek pointed out, I changed Holder() to This() and everything started to work. But later When I changed SetAccessor to SetAccessorProperty, it worked even with Holder.
Although the behaviour are very confusing, I think the major problem may not lie in the difference between Holder and This, but rather in SetAccessor and SetAccessorProperty. Why? Because in many docs I have read, Holder should be identical to This in most cases and I believe without using Signature and given that my testing js code is so simple (not with any magic property moving), in my case This should just be Holder.
Thus I decided to post another question about SetAccessor and SetAccessorProperty and leave this post as a reference.
For why I am so sure about in my case This() == Holder() should hold, here are some old threads:
https://groups.google.com/forum/#!topic/v8-users/fK9PBWxJxtQ
https://groups.google.com/forum/#!topic/v8-users/Axf4hF_RfZo
And what does the docs say?
/**
* If the callback was created without a Signature, this is the same
* value as This(). If there is a signature, and the signature didn't match
* This() but one of its hidden prototypes, this will be the respective
* hidden prototype.
*
* Note that this is not the prototype of This() on which the accessor
* referencing this callback was found (which in V8 internally is often
* referred to as holder [sic]).
*/
V8_INLINE Local<Object> Holder() const;
Note in my code there is not Signature, literally. So This and Holder should make no difference, but with SetAccessor, they made a difference.
This is my Callback function:
void VideoCapturerSampleCallback(struct LmiVideoCapturer_* capturer, const LmiVideoFrame* videoFrame, LmiVoidPtr userData)
{
//TODO will have to be on ui thread;
Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(0, ref new Windows::UI::Core::DispatchedHandler([this]()
{
Windows::UI::Xaml::Media::Imaging::BitmapImage^ bitmapImage =
ref new Windows::UI::Xaml::Media::Imaging::BitmapImage();
auto uri = ref new Windows::Foundation::Uri("ms-appx:///Assets/1.jpg");
if (first){
first = false;
}
else{
uri = ref new Windows::Foundation::Uri("ms-appx:///Assets/1.jpg");
first = true;
}
bitmapImage->UriSource = uri;
media->Source = bitmapImage;
}));
}
Which will basically change the picture every time the callback will be called, for now, I just change 2 pictures from Assets, in order to see if it works.
This is the error I get in the logs:
Error 17 error C1903: unable to recover from previous error(s); stopping compilation C:\Users\Alin Rosu\Downloads\App2\App2\MainPage.xaml.cpp 109 1 App2
Error 16 error C3482: 'this' can only be used as a lambda capture within a non-static member function C:\Users\Alin Rosu\Downloads\App2\App2\MainPage.xaml.cpp 93 1 App2
19 IntelliSense: 'this' may only be used inside a nonstatic member function c:\Users\Alin Rosu\Downloads\App2\App2\MainPage.xaml.cpp 93 144 App2
18 IntelliSense: function "Windows::UI::Core::CoreDispatcher::RunAsync" cannot be called with the given argument list
argument types are: (int, Windows::UI::Core::DispatchedHandler ^)
object type is: Windows::UI::Core::CoreDispatcher ^ c:\Users\Alin Rosu\Downloads\App2\App2\MainPage.xaml.cpp 93 84 App2
Now is it possible to use that [this] inside the callback, without getting an error?
I am a Android developer, so for me the logic response to this, was creating a global variable App2::MainPage, and then in the constructor, save "this" in it, so that I would have the context over there, and try to call RunAsync with that, but it does not work.
Is it possible to instantiate the DispatchedHandler another way?
Declared the context like this:
App2::MainPage^ context;
Then set it in the MainPage function:
MainPage::MainPage()
{
InitializeComponent();
media = this->player;
context = this;
}
And then I did it this way inside the VideoCapturerSampleCallback:
void VideoCapturerSampleCallback(struct LmiVideoCapturer_* capturer, const LmiVideoFrame* videoFrame, LmiVoidPtr userData)
{
App2::MainPage^ ctx = context;
Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([ctx]()
{
//TODO will need to set in here
Windows::UI::Xaml::Media::Imaging::BitmapImage^ bitmapImage =
ref new Windows::UI::Xaml::Media::Imaging::BitmapImage();
auto uri = ref new Windows::Foundation::Uri("ms-appx:///Assets/1.jpg");
if (first){
XTRACE(L"===================FIIIIIIIIIIIIRST =======================\n");
first = false;
}
else{
XTRACE(L"===================FIIIIIIIIIIIIRST FAAAALSEEEEE=======================\n");
uri = ref new Windows::Foundation::Uri("ms-appx:///Assets/2.jpg");
first = true;
}
bitmapImage->UriSource = uri;
media->Source = bitmapImage;
}));
}
This resolved my issues and I am able to modify the UI, being on the UI Thread.
Is it possible to set a fallback callback which is called when the user wants to call a function which does not exists? E.g.
my_object.ThisFunctionDoesNotExists(2, 4);
Now I want that a function is getting called where the first parameter is the name and a stack (or something like that) with the arguments passed. To clarify, the fallback callback should be a C++ function.
Assuming your question is about embedded V8 engine which is inferred from tags, you can use harmony Proxies feature:
var A = Proxy.create({
get: function (proxy, name) {
return function (param) {
console.log(name, param);
}
}
});
A.hello('world'); // hello world
Use --harmony_proxies param to enable this feature. From C++ code:
static const char v8_flags[] = "--harmony_proxies";
v8::V8::SetFlagsFromString(v8_flags, sizeof(v8_flags) - 1);
Other way:
There is a method on v8::ObjectTemplate called SetNamedPropertyHandler so you can intercept property access. For example:
void GetterCallback(v8::Local<v8::String> property,
const v8::PropertyCallbackInfo<v8::Value>& info)
{
// This will be called on property read
// You can return function here to call it
}
...
object_template->SetNamedPropertyHandler(GetterCallback);
OK, I have a function in C++ that I need to call from JavaScript, and one of the parameters is a JavaScript object. The JavaScript looks like this:
var message = {
fieldA: 42,
fieldB: "moo"
};
myObj.send(message, function (err) { console.log("Result: " + err); });
In the send() routine I need to call a native function in another C library that may block. All functions in this library may block so I've been using uv_queue_work extensively.
This routine is the first time I've hit an issue and it is because of the JavaScript object. The C++ code looks like this:
struct SendMessageRequest
{
Persistent<Object> message;
Persistent<Function> callback;
int result;
};
Handle<Value> MyObj::Send(const Arguments& args)
{
HandleScope scope;
// Parameter checking done but not included here
Local<Object> message = Local<Object>::Cast(args[0]);
Local<Function> callback = Local<Function>::Cast(args[1]);
// Send data to worker thread
SendMessageRequest* request = new SendMessageRequest;
request->message = Persistent<Object>::New(message);
request->callback = Persistent<Function>::New(callback);
uv_work_t* req = new uv_work_t();
req->data = request;
uv_queue_work(uv_default_loop(), req, SendMessageWorker, SendMessageWorkerComplete);
return scope.Close(Undefined());
}
This is all fine, the problem comes when I try to access request->message in the SendMessageWorker function.
void SendMessageWorker(uv_work_t* req)
{
SendMessageRequest* request = (SendMessageRequest*)req->data;
Local<Array> names = request->message->GetPropertyNames();
// CRASH
It seems that calling methods off of request->message causes an Access Violation on a really small address (probably a NULL pointer reference somewhere in V8/node). So using request->message directly must be wrong. I know to access the callback function I need to do this:
request->callback->Call(Context::GetCurrent()->Global(), 1, argv);
Do I need to use Context::GetCurrent()->Global() in order to access methods off of the Object class that is wrapped by the Persistent template? If so how do I do that?
The code in SendMessageWorker is not executed on the JavaScript - what uv_queue_work does is execute your SendMessageWorker in a separate thread, so it can let the node.js code run as well, and when it's ready, SendMessageWorkerComplete is executed back on the JavaScript thread.
So you can't use JavaScript variables in SendMessageWorker - if you really need to, you'd have to convert them to e.g. C++ string before calling uv_queue_work.