v8 version : 10.5.0
IDE : vs2022
I am trying to embed v8 in MFC. To test simply, I created a dialog project, initialized v8 on OnInitDialog(), disposed it OnDestroy(). And then, I wrote the code to create the context in BN_CLICKED event and run script("Hello World!)". So, when the button was pressed, I wanted to show "Hello World!". But the crash took place in the code that create the context(v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr);).
As a result of testing, this only happens in MFC. This crash does not happen in console programs.
Somebody, anybody help me~. there is my whole code below.
cpp
BOOL Cv8TestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
...
// Initialize V8.
v8::V8::InitializeICUDefaultLocation("");
v8::V8::InitializeExternalStartupData("");
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
// Create a new Isolate and make it the current one.
m_create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
m_isolate = v8::Isolate::New(m_create_params);
return TRUE;
}
void Cv8TestDlg::OnDestroy()
{
CDialogEx::OnDestroy();
m_isolate->Dispose();
v8::V8::Dispose();
v8::V8::DisposePlatform();
delete m_create_params.array_buffer_allocator;
}
void Cv8TestDlg::OnRun()
{
CString text;
v8::Isolate* isolate = m_isolate;
v8::Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
v8::HandleScope handle_scope(isolate);
// Create a new context.
v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr);
// Enter the context for compiling and running the hello world script.
v8::Context::Scope context_scope(context);
{
// Create a string containing the JavaScript source code.
v8::Local<v8::String> source = v8::String::NewFromUtf8Literal(isolate, "'Hello' + ', World!'");
// Compile the source code.
v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
// Run the script to get the result.
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
// Convert the result to an UTF8 string and print it.
v8::String::Value utf8(isolate, result);
text = (LPCWSTR)*utf8;
}
GetDlgItem(IDC_EDIT1)->SetWindowText(text);
}
h
class Cv8TestDlg : public CDialogEx
{
...
v8::Isolate::CreateParams m_create_params;
v8::Isolate* m_isolate = nullptr;
virtual BOOL OnInitDialog();
...
afx_msg void OnDestroy();
afx_msg void OnRun();
};
crash call stack
v8.dll!v8::internal::tracing::TraceEventHelper::GetTracingController()
v8.dll!v8::NewContext(v8::Isolate * external_isolate, v8::ExtensionConfiguration * extensions, v8::MaybeLocal<v8::ObjectTemplate> global_template, v8::MaybeLocal<v8::Value> global_object, unsigned __int64 context_snapshot_index, v8::DeserializeInternalFieldsCallback embedder_fields_deserializer, v8::MicrotaskQueue * microtask_queue) line 6391 C++
v8.dll!v8::Context::New(v8::Isolate * external_isolate, v8::ExtensionConfiguration * extensions, v8::MaybeLocal<v8::ObjectTemplate> global_template, v8::MaybeLocal<v8::Value> global_object, v8::DeserializeInternalFieldsCallback internal_fields_deserializer, v8::MicrotaskQueue * microtask_queue) line 6413 C++
v8Test.exe!Cv8TestDlg::OnRun() line 198 C++
exception message
(0x00007FFADBB43D53(v8.dll), v8Test.exe): 0xC0000005: 0x0000000000000088 access violation
break position(trace-event.cc)
v8::TracingController* TraceEventHelper::GetTracingController() {
-> return v8::internal::V8::GetCurrentPlatform()->GetTracingController();
}
I think the problem is std::unique_ptr<v8::Platform> platform in OnInitDialog. The purpose of a std::unique_ptr is to destroy the thing it points at when the pointer goes out of scope (i.e. in this case, at the end of the function). The v8::Platform should be long-lived, all the way until the v8::V8::DisposePlatform() call.
Related
I am trying to call some native api from node js and perform long running task. I am using libuv for that perposes, store js callback to perform some meaningful task in separate thread and call callback later. This is what I've done so far:
task.cpp
struct Work {
uv_work_t request;
Local<Function> callback;
};
static void WorkAsyncComplete(uv_work_t* req, int status)
{
Isolate* isolate = Isolate::GetCurrent();
v8::HandleScope handleScope(isolate);
Work* work = static_cast<Work*>(req->data);
const unsigned argc = 1;
Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "Hello world").ToLocalChecked() };
Local<Function> callback = Local<Function>::New(isolate, work->callback);
callback->Call(isolate->GetCurrentContext(), Null(isolate), argc, argv);
work->callback.Clear();
delete work;
}
static void WorkAsync(uv_work_t* req)
{
Work* work = static_cast<Work*>(req->data);
using namespace std::chrono_literals;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
void RunCallback(const FunctionCallbackInfo<Value>& args)
{
Isolate* isolate = args.GetIsolate();
Work* work = new Work();
work->request.data = work;
Local<Function> callback = Local<Function>::Cast(args[1]);
work->callback.New(isolate, callback);
uv_queue_work(uv_default_loop(), &work->request, WorkAsync, WorkAsyncComplete);
args.GetReturnValue().Set(Undefined(isolate));
}
void Init(Local<Object> exports, Local<Object> module)
{
//isolate = exports->GetIsolate();
NODE_SET_METHOD(module, "exports", RunCallback);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
task.js:
let task = require('./build/Release/task');
task((msg) => {
console.log(msg);
}
But I got crash in WorkAsyncComplete() when calling callback. Here is the error detail:
Debugger attached.
FATAL ERROR: v8::Function::Call Function to be called is a null pointer
1: 00007FF6579E03DF napi_wrap+109311
2: 00007FF657985096 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NumberOfElementsOffset+33302
3: 00007FF657985E66 node::OnFatalError+294
4: 00007FF65822A9BD v8::Function::Call+573
5: 00007FFA62FD1172 load_exe_hook+258
6: 00007FF657A37D90 uv_timer_stop+560
7: 00007FF657A37E67 uv_timer_stop+775
8: 00007FF657A3469B uv_async_send+331
9: 00007FF657A33E2C uv_loop_init+1292
10: 00007FF657A33FCA uv_run+202
11: 00007FF6579400A5 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NumberOfBucketsOffset+9365
12: 00007FF6579B3867 node::Start+311
13: 00007FF65781686C RC4_options+339820
14: 00007FF6587B523C v8::internal::compiler::RepresentationChanger::Uint32OverflowOperatorFor+153532
15: 00007FFA761A7C24 BaseThreadInitThunk+20
16: 00007FFA7694D4D1 RtlUserThreadStart+33
Please advice how to fix this issue. Thanks in advance.
Looks like the issue is that you're using a v8::Local<...> in a long-lived object. That doesn't work because the lifetime of a Local is tied to the surrounding HandleScope: the Local becomes invalid when the HandleScope dies. See https://v8.dev/docs/embed#handles-and-garbage-collection for details. The solution is to use a v8::Persistent<...> instead of a Local.
For the record, I'd also like to point out that while you can do C++ work in the background task, you won't be able to extend this example so that it takes a JavaScript function to execute concurrently. That would be multi-threaded programming, which JavaScript as a language doesn't support, and hence V8 as an implementation doesn't support either.
I'm a little bit desperate trying to get the result of a winrt asynchronous method with Windows::Foundation::IAsyncOperation interface. The project is a Visual Studio Community 2017 c++ project with winRT extension enabled.
I've tried using std::future / co_await functions and also task method but the call to GetResults() generated always "call at unexpected time" exception.
With the following code I don't get the exception but GetResults() returns nullptr. I have tried also to declare async_op as a shared_ptr auto async_op = std::make_shared<Windows::Foundation::IAsyncOperation<Windows::Devices::Bluetooth::BluetoothLEDevice^> ^> and access to it with *async_op inside the Handler code, but I get the same result.
Any ideas will be welcome.
Thanks in advance.
void BleEnumeration::PerformConnectDevice(std::string* bleDevId)
{
std::wstring bleDevId_w_str = std::wstring((*bleDevId).begin(), (*bleDevId).end());
const wchar_t* bleDevId_w_char = bleDevId_w_str.c_str();
Platform::String^ bleDevId_refStr = ref new Platform::String(bleDevId_w_char);
auto async_op = Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(bleDevId_refStr);
async_op->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<Windows::Devices::Bluetooth::BluetoothLEDevice^>(
[async_op] (Windows::Foundation::IAsyncOperation< Windows::Devices::Bluetooth::BluetoothLEDevice^ >^ operation, Windows::Foundation::AsyncStatus status)
{
if (async_op->Status == Windows::Foundation::AsyncStatus::Completed)
{
auto bleDevTest = async_op->GetResults();
}
});
}
I made it work:
Windows::Devices::Bluetooth::BluetoothLEDevice^ BleEnumeration::getBleDevice(Windows::Foundation::IAsyncOperation<Windows::Devices::Bluetooth::BluetoothLEDevice^>^ async)
{
if ((async)->Status != Windows::Foundation::AsyncStatus::Completed)
{
HANDLE signal = CreateEvent(nullptr, true, false, nullptr);
(async)->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<Windows::Devices::Bluetooth::BluetoothLEDevice^>(
[&signal](Windows::Foundation::IAsyncOperation< Windows::Devices::Bluetooth::BluetoothLEDevice^ >^ operation, Windows::Foundation::AsyncStatus status)
{
SetEvent(signal);
});
WaitForSingleObject(signal, INFINITE);
}
Windows::Devices::Bluetooth::BluetoothLEDevice^ device = async->GetResults();
return device;
}
bool BleEnumeration::PerformConnectDevice(std::string* bleDevId)
{
bool success = false;
Windows::Devices::Bluetooth::BluetoothLEDevice^ bleDevTest = nullptr;
std::wstring bleDevId_w_str = std::wstring((*bleDevId).begin(), (*bleDevId).end());
const wchar_t* bleDevId_w_char = bleDevId_w_str.c_str();
Platform::String^ bleDevId_refStr = ref new Platform::String(bleDevId_w_char);
Windows::Foundation::IAsyncOperation<Windows::Devices::Bluetooth::BluetoothLEDevice^>^ asyncOp = Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(bleDevId_refStr);
bleDevTest = getBleDevice(asyncOp);
if (bleDevTest != nullptr)
{
success = true;
}
return success;
}
And, the reason why GetResults() gave nullptr was this: Enable capability in appxmanifest
All needed capabilities must be enabled in Visual Studio project Package.appxmanifest file, in my case: "Bluetooth". I noticed I was getting error: onecoreuap\drivers\wdm\bluetooth\user\winrt\device\bluetoothledevice.cpp(728)\Windows.Devices.Bluetooth.dll!00007FFC1601AE2F: (caller: 00007FFC1602081A) Exception(1) tid(212c) 80070005 Access is denied.
in my MFC SDI application, i'm trying to override CDocument::DoSave to save my document. I'm using a third part component (TxTextControl) to build a text control. When i save the document, the file is created, but after about one minute my app crashes rising read access error 0xFEEEFEEE, in ole32.dll.
This is my code, txtCtrl is my component:
BOOL CEditorTxDoc::DoSave(LPCTSTR lpszPathName, BOOL bReplace)
{
CString path, nome;
VARIANT vt1, vt2, vt3;
POSITION pos = GetFirstViewPosition();
CEditorTxView *pView = (CEditorTxView*)this->GetNextView(pos);
VariantInit(&vt1);
vt1.vt = VT_INT;
vt1.intVal = -1;
VariantInit(&vt2);
vt2.vt = VT_INT;
vt2.intVal = 3;
VariantInit(&vt3);
vt3.vt = VT_BOOL;
vt3.boolVal = FALSE;
if (lpszPathName == NULL) {
CFileDialog fSaveDlg(FALSE);
fSaveDlg.m_pOFN->lpstrFilter = _T("File Tx (*.tx)");
fSaveDlg.m_pOFN->lpstrDefExt = _T("tx");
fSaveDlg.m_pOFN->lpstrTitle = _T("Salva documento");
fSaveDlg.m_pOFN->lpstrInitialDir = _T("c:");
if(fSaveDlg.DoModal()==IDOK)
{
path = fSaveDlg.GetPathName();
nome = fSaveDlg.GetFileName();
pView->txtCtrl.Save(path, vt1, vt2, vt3);
SetTitle(nome);
SetModifiedFlag(FALSE);
SetPathName(path);
}
} else {
pView->txtCtrl.Save(GetPathName(), vt1, vt2, vt3);
SetModifiedFlag(FALSE);
}
return TRUE;
}
Magic debug values:
FEEEFEEE Used by Microsoft's HeapFree() to mark freed heap memory
That is, the problem comes up from the fact that the code deals with released memory as if it is still alive. To isolate the issue to specific code fragment, debug and use call stack information at the time of exception.
I have a working Visual Studio project.
I've created a static library with the files of the original project (except main.cpp), also
I've created a "tester" project (with the static lib linked to it) with only a main.cpp file from the original project.
Both compiles without any relevant error.
And tester runs appropriately.
But! At testing the "tester" I am getting a heap allocation error at a (not the first)
new[] operator invoked in a constructor implemented in the library.
That line working fine in the original project without any error.
The "little" version of the code:
//the staticlib
void test() {
manager* m = new manager;
m->open();
}
//....
class manager {
public:
open() {
PRAWINPUTDEVICELIST lDevList;
UINT lDevCount;
GetRawInputDeviceList(NULL, &lDevCount, sizeof(RAWINPUTDEVICELIST));
lDevList = (PRAWINPUTDEVICELIST) malloc(sizeof(RAWINPUTDEVICELIST)*lDevCount);
GetRawInputDeviceList(lDevList, &lDevCount, sizeof(RAWINPUTDEVICELIST));
if(lDevCount) {
for(UINT i = 0; i < lDevCount; i++) {
HIDP_CAPS mCaps;
PHIDP_BUTTON_CAPS mButtonCaps;
PHIDP_VALUE_CAPS mValueCaps;
UINT size;
GetRawInputDeviceInfo(lDevList[i].hDevice, RIDI_DEVICENAME, NULL, &size);
char* name = new char[size+1];
//just to be sure
memset(name, 0, size+1);
//surely sure
name[size] = '\0';
GetRawInputDeviceInfo(lDevList[i].hDevice, RIDI_DEVICENAME, name, &size);
HANDLE lDev = CreateFile((LPCWSTR)name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);;
PHIDP_PREPARSED_DATA lPrep;
HidD_GetPreparsedData(lDev, &lPrep);
HidP_GetCaps(lPrep, &mCaps);
if(mCaps.NumberInputButtonCaps) {
//crash is here below
//mCaps.NumberInputButtonCaps ~1
mButtonCaps = new HIDP_BUTTON_CAPS[mCaps.NumberInputButtonCaps];
HidP_GetButtonCaps(HidP_Input, mButtonCaps, &mCaps.NumberInputButtonCaps, lPrep);
}
if(mCaps.NumberInputValueCaps) {
//if the first "crash-line" is commented, then
//the crash is here
mValueCaps = new HIDP_VALUE_CAPS[mCaps.NumberInputValueCaps];
HidP_GetValueCaps(HidP_Input, mValueCaps, &mCaps.NumberInputValueCaps, lPrep);
}
CloseHandle(lDev);
}
}
}
};
//the app
test();
Where I am wrong? Is it a typical novice commission I am not afraid of?
Sorry for my English, and thanks ahead for your time!
The error is that you should be allocating wide chars when you call GetRawInputDeviceInfo. From the manual
RIDI_DEVICENAME 0x20000007
pData points to a string that contains the device name.
For this uiCommand only, the value in pcbSize is the character count
(not the byte count).
In other words you should write
wchar_t* name = new wchar_t[size];
GetRawInputDeviceInfo(lDevList[i].hDevice, RIDI_DEVICENAME, name, &size);
Just from reading the manual, I have no actual experience with this API, but it seems a likely explanation.
Add logic that checks for error return states on every Win32 call you make. Possibly one of them is failing and when you remedy that, the rest will work. Always check for and handle errors when you are using Win32 APIs.
per Voo's advice on this thread:
How can I "hook into" Python from C++ when it executes a function? My goal is to profile
I have opened up a new thread for a new question, which is, in C++, how can I initialize a PythonInterpreter and then call a method from it. Specifically, I'd like to be able to call cProfile's methods and also get data from it.
Okay that'll be a bit longer. Note that I pretty much ignore all "usual" error checking - pretty much any python method may return NULL in which case you should handle that gracefully. I show the "unusual" part checking if the given object is callable. Note that PyDECREF fails if the object pointer is NULL, Py_XDECREF does not. And now to the code - there may be a better way to solve all this, but this works fine for me and sadly the documentation is extremely lacking.
C++ code:
#include <Python.h>
static PyThreadState *mainstate;
void initPython(){
PyEval_InitThreads();
Py_Initialize();
mainstate = PyThreadState_Swap(NULL);
PyEval_ReleaseLock();
}
void exitPython(){
PyEval_AcquireLock();
PyThreadState_Swap(mainstate);
Py_Finalize();
}
void callScript() {
PyGILState_STATE gstate = PyGILState_Ensure();
PyObject *pName = PyUnicode_FromString("Startup");
PyObject *pModule = PyImport_Import(pName);
Py_DECREF(pName);
PyObject *pFunc = PyObject_GetAttrString(pModule, "startup");
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *arglist = Py_BuildValue("(u)", "TestScript");
PyObject *result = PyObject_CallObject(pFunc, arglist);
Py_DECREF(arglist);
// Now you have the returned object of the function - do something with it.
// In our case that's None, but you should extend the python scrip to return
// whatever you need - see the profiler API.
Py_DECREF(result);
}
Py_XDECREF(pFunc); // XDECREF does not fail if pointer is NULL.
Py_DECREF(pModule);
PyGILState_Release(gstate);
}
int main() {
printf("Start.\n");
initPython();
callScript();
exitPython();
printf("Exit.\n");
return 0;
}
Your specific script that is always called, change this so that you return all the data you want in a useful manner - at the moment we just use cProfile.run() which just prints some information:
Startup.py
import cProfile
def startup(module_name):
print("Start script")
cProfile.run("import " + module_name)
print("Finished script")
Finally the trivial script that is executed:
TestScript.py
sum = 0
for i in range(10000):
sum += i