前几天用VS2017编译好了V8库,今天尝试编译Demo程序的时候遇到了一些问题:
V8的坑真的不是一般的多
#include "AppConfig.h"
#include "libplatform/libplatform.h"
#include "v8.h"
AppConfig* AppConfig::_ins = nullptr;
bool AppConfig::loadFile(std::string fileName)
{
lua_State* L;
L = luaL_newstate();
v8::V8::InitializeICUDefaultLocation(".");
v8::V8::InitializeExternalStartupData(".");
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
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);
// 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::NewFromUtf8(isolate, "'Hello' + ', World!'",
v8::NewStringType::kNormal)
.ToLocalChecked();
// 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::Utf8Value utf8(isolate, result);
printf("%s\n", *utf8);
}
// Dispose the isolate and tear down V8.
isolate->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete create_params.array_buffer_allocator;
return false;
}
AppConfig::AppConfig()
{
}
AppConfig::~AppConfig()
{
}
1>AppConfig.obj : error LNK2019: 无法解析的外部符号 "class std::unique_ptr > __cdecl v8::platform::NewDefaultPlatform(int,enum v8::platform::IdleTaskSupport,enum v8::platform::InProcessStackDumping,class std::unique_ptr >)" (?NewDefaultPlatform@platform@v8@@YA?AV?$unique_ptr@VPlatform@v8@@U?$default_delete@VPlatform@v8@@@std@@@std@@HW4IdleTaskSupport@12@W4InProcessStackDumping@12@V?$unique_ptr@VTracingController@v8@@U?$default_delete@VTracingController@v8@@@std@@@4@@Z),该符号在函数 "public: bool __cdecl AppConfig::loadFile(class std::basic_string,class std::allocator >)" (?loadFile@AppConfig@@QEAA_NV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) 中被引用
1>D:\projects\aps2\x64\Debug\aps.exe : fatal error LNK1120: 1 个无法解析的外部命令
demagle出错的符号
class std::unique_ptr<class v8::Platform,struct std::default_delete<class v8::Platform> > __cdecl v8::platform::NewDefaultPlatform(int,enum v8::platform::IdleTaskSupport,enum v8::platform::InProcessStackDumping,class std::unique_ptr<class v8::TracingController,struct std::default_delete<class v8::TracingController> >)" (class std::unique_ptr<class v8::Platform,struct std::default_delete<class v8::Platform> > __cdecl v8::platform::NewDefaultPlatform(int,enum v8::platform::IdleTaskSupport,enum v8::platform::InProcessStackDumping,class std::unique_ptr<class v8::TracingController,struct std::default_delete<class v8::TracingController> >)
似乎在链接阶段发现NewDefaultPlatform这个函数没有实现?这可是官网的Demo程序呀,不可能没实现的
于是赶紧dumpbin一下v8_libplatform.dll.lib,结果如下:
E:\projects\v8\v8\out\x64.debug>dumpbin /EXPORTS v8_libplatform.dll.lib > 1.txt
//1.txt
?NewDefaultPlatform@platform@v8@@YA?AV?$unique_ptr@VPlatform@v8@@U?$default_delete@VPlatform@v8@@@__1@std@@@__1@std@@HW4IdleTaskSupport@12@W4InProcessStackDumping@12@V?$unique_ptr@VTracingController@v8@@U?$default_delete@VTracingController@v8@@@__1@std@@@45@@Z (class std::__1::unique_ptr<class v8::Platform,struct std::__1::default_delete<class v8::Platform> > __cdecl v8::platform::NewDefaultPlatform(int,enum v8::platform::IdleTaskSupport,enum v8::platform::InProcessStackDumping,class std::__1::unique_ptr<class v8::TracingController,struct std::__1::default_delete<class v8::TracingController> >))
前后对比
class std::unique_ptr<class v8::Platform,struct std::default_delete<class v8::Platform> > __cdecl v8::platform::NewDefaultPlatform(int,enum v8::platform::IdleTaskSupport,enum v8::platform::InProcessStackDumping,class std::unique_ptr<class v8::TracingController,struct std::default_delete<class v8::TracingController> >)" (class std::unique_ptr<class v8::Platform,struct std::default_delete<class v8::Platform> > __cdecl v8::platform::NewDefaultPlatform(int,enum v8::platform::IdleTaskSupport,enum v8::platform::InProcessStackDumping,class std::unique_ptr<class v8::TracingController,struct std::default_delete<class v8::TracingController> >)
class std::__1::unique_ptr<class v8::Platform,struct std::__1::default_delete<class v8::Platform> > __cdecl v8::platform::NewDefaultPlatform(int,enum v8::platform::IdleTaskSupport,enum v8::platform::InProcessStackDumping,class std::__1::unique_ptr<class v8::TracingController,struct std::__1::default_delete<class v8::TracingController> >) (class std::__1::unique_ptr<class v8::Platform,struct std::__1::default_delete<class v8::Platform> > __cdecl v8::platform::NewDefaultPlatform(int,enum v8::platform::IdleTaskSupport,enum v8::platform::InProcessStackDumping,class std::__1::unique_ptr<class v8::TracingController,struct std::__1::default_delete<class v8::TracingController> >))
不难看出lib库里的确导出了NewDefaultPlatform符号,但有些不同的是导出库里unique_ptr这种类型都是在std::__1命名空间下,而VS工程里预期导入的符号确是在std命名空间下,问题就在这里。
以前在扒libc++和libstdc++的历史的时候就发现了类似的问题,果然这里出现了。
libc++的库在实现STL的时候使用了inline namespace __1,而libstdc++则没有。好吧,看来V8引擎的编译默认使用了libc++,难怪会生成一个libc++.dll,当时还并未在意来着。
找了找发现libc++的头文件就在\v8\buildtools\third_party\libc++\trunk\include目录下,试着直接把libc++的头文件引入进来,结果编译时报错
1>d:\projects\aps2\aps\c++\stddef.h(45): fatal error C1021: 无效的预处理器命令“include_next”
看来必须得换clang编译器了,可以按照llvm官网指南,一步步来 https://llvm.org/docs/GettingStartedVS.html
稍微看了下比较麻烦,索性换成VS2019,自带clang编译器,然后把将工程转换为CMake工程这样比较简单。
cmake_minimum_required (VERSION 3.8)
project ("test-cmake")
include_directories(./include/c++)
include_directories(./include)
include_directories(./include/v8)
link_directories(./libs)
link_libraries("opengl32.lib" "glew32d" "glfw3" "icui18n.dll.lib" "icuuc.dll.lib" "libglew32d.lib" "lua" "torque" "v8.dll.lib" "v8_libbase.dll.lib" "v8_libplatform.dll.lib" "zlib.dll.lib")
add_executable (test-cmake "appconfig.cpp" "appconfig.h" "main.cpp")
记得引入libc++的头文件,这下编译终于通过,程序也可以正确执行了
2020/3/6 更新
发现在Javascript中调用C++函数时,在C++函数中使用IsConstructCall()方法来判断是否构造函数时,出现了指针错误。
void MyObject::New(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.IsConstructCall()) {
...
}
跟踪调试发现
template <typename T>
V8_INLINE static T ReadRawField(internal::Address heap_object_ptr,
int offset) {
internal::Address addr = heap_object_ptr + offset - kHeapObjectTag;
#ifdef V8_COMPRESS_POINTERS
if (sizeof(T) > kApiTaggedSize) {
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
// fields (external pointers, doubles and BigInt data) are only
// kTaggedSize aligned so we have to use unaligned pointer friendly way of
// accessing them in order to avoid undefined behavior in C++ code.
T r;
memcpy(&r, reinterpret_cast<void*>(addr), sizeof(T));
return r;
}
#endif
return *reinterpret_cast<const T*>(addr);
}
TODO里写得也比较清楚,编译期间宏定义V8_COMPRESS_POINTERS是开启的,然后在工程里加上这个宏,程序就能正常运行了。