16 #include <fmt/format.h> 17 #include <boost/format.hpp> 26 void*
operator new[](
size_t size,
const char* pName,
int flags,
unsigned debugFlags,
const char* file,
int line)
31 void*
operator new[](
size_t size,
size_t alignment,
size_t alignmentOffset,
const char* pName,
int flags,
unsigned debugFlags,
const char* file,
int line)
48 v8::V8::SetFlagsFromCommandLine(&argc, argv,
true);
54 static const char * EXPOSE_GC =
"--expose-gc";
55 v8::V8::SetFlagsFromString(EXPOSE_GC, strlen(EXPOSE_GC));
67 object_template->Set(isolate, name, template_to_attach);
73 auto isolate = context->GetIsolate();
74 (void)object->Set(context, v8::String::NewFromUtf8(isolate, name), value);
86 std::stringstream sstream;
89 if (args.Length() > 0) {
90 auto format = boost::format(*v8::String::Utf8Value(values[0]));
93 for (i = 1; format.remaining_args() > 0; i++) {
94 if (i < values.size()) {
95 format % *v8::String::Utf8Value(values[i]);
101 while (i < values.size()) {
102 sstream <<
" " << *v8::String::Utf8Value(values[i]);
106 if (append_newline) {
107 sstream << std::endl;
109 return sstream.str();
123 std::vector<v8::Local<v8::Value>>
get_all_values(
const v8::FunctionCallbackInfo<v8::Value>& args,
int depth) {
124 std::vector<v8::Local<v8::Value>> values;
126 auto isolate = args.GetIsolate();
127 auto context = isolate->GetCurrentContext();
129 for (
int i = 0; i < args.Length(); i++) {
130 if (args[i]->IsArray()) {
131 auto array = v8::Object::Cast(*args[i]);
133 while(array->Has(context, i).FromMaybe(
false)) {
134 values.push_back(array->Get(context, i).ToLocalChecked());
138 values.push_back(args[i]);
148 std::stringstream sstream;
151 for (
auto value : values) {
155 sstream << *v8::String::Utf8Value(value);
158 if (append_newline) {
159 sstream << std::endl;
161 return sstream.str();
167 add_function(isolate, object_template,
"printf", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){callback(
_printf_helper(info,
false));});
168 add_function(isolate, object_template,
"printfln", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){callback(
_printf_helper(info,
true));});
169 add_function(isolate, object_template,
"sprintf", [](
const v8::FunctionCallbackInfo<v8::Value>& info){
return _format_helper(info,
false);});
171 add_function(isolate, object_template,
"print", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){callback(
_print_helper(info,
false));});
172 add_function(isolate, object_template,
"println", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){callback(
_print_helper(info,
true));});
174 add_function(isolate, object_template,
"printobj", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){
175 auto isolate = info.GetIsolate();
176 for (
int i = 0; i < info.Length(); i++) {
180 add_function(isolate, object_template,
"printobjall", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){
181 auto isolate = info.GetIsolate();
182 for (
int i = 0; i < info.Length(); i++) {
190 add_function(context, context->Global(),
"printf", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){callback(
_printf_helper(info,
false));});
191 add_function(context, context->Global(),
"printfln", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){callback(
_printf_helper(info,
true));});
192 add_function(context, context->Global(),
"sprintf", [](
const v8::FunctionCallbackInfo<v8::Value>& info){
return _format_helper(info,
false);});
194 add_function(context, context->Global(),
"print", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){callback(
_print_helper(info,
false));});
195 add_function(context, context->Global(),
"println", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){callback(
_print_helper(info,
true));});
197 add_function(context, context->Global(),
"printobj", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){
198 auto isolate = info.GetIsolate();
199 for (
int i = 0; i < info.Length(); i++) {
203 add_function(context, context->Global(),
"printobjall", [callback](
const v8::FunctionCallbackInfo<v8::Value>& info){
204 auto isolate = info.GetIsolate();
205 for (
int i = 0; i < info.Length(); i++) {
214 add_function(isolate, object_template,
"assert", [](
const v8::FunctionCallbackInfo<v8::Value>& info) {
215 auto isolate = info.GetIsolate();
216 auto context = isolate->GetCurrentContext();
217 if (
V8_TOOLKIT_DEBUG) printf(
"Asserting: '%s'\n", *v8::String::Utf8Value(info[0]));
219 v8::TryCatch tc(isolate);
220 auto script_maybe = v8::Script::Compile(context, info[0]->ToString());
221 assert(!tc.HasCaught());
223 auto script = script_maybe.ToLocalChecked();
224 auto result_maybe = script->Run(context);
225 assert (!tc.HasCaught());
227 auto result = result_maybe.ToLocalChecked();
230 bool default_value =
false;
231 bool assert_result = result->BooleanValue(context).FromMaybe(default_value);
233 assert(assert_result);
252 auto file_handle = CreateFile(filename.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr);
253 if (file_handle == INVALID_HANDLE_VALUE) {
257 FILETIME creationTime,
260 bool err = GetFileTime(file_handle, &creationTime, &lpLastAccessTime, &lastWriteTime);
262 auto file_size =
GetFileSize(file_handle,
nullptr);
263 file_contents.resize(file_size);
265 ReadFile(file_handle, &file_contents[0], file_size,
nullptr,
nullptr);
272 int fd = open(filename.c_str(), O_RDONLY);
276 struct stat file_stat;
277 if (fstat(fd, &file_stat) == -1) {
281 file_modification_time = file_stat.st_mtime;
282 auto file_size = file_stat.st_size;
284 file_contents.resize(file_size);
286 auto bytes_remaining = file_size;
288 while (bytes_remaining > 0) {
289 auto bytes_read = read(fd, &file_contents[file_size - bytes_remaining], bytes_remaining);
290 bytes_remaining -= bytes_read;
301 auto file_handle = CreateFile(filename.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr);
302 if (file_handle == INVALID_HANDLE_VALUE) {
306 FILETIME creationTime,
309 bool err = GetFileTime(file_handle, &creationTime, &lpLastAccessTime, &lastWriteTime);
311 modification_time = *(time_t*)&lastWriteTime;
316 int fd = open(filename.c_str(), O_RDONLY);
320 struct stat file_stat;
321 if (fstat(fd, &file_stat) == -1) {
325 modification_time = file_stat.st_mtime;
341 std::lock_guard<std::mutex> l(require_results_mutex);
342 require_results.erase(isolate);
347 std::lock_guard<std::mutex> l(require_results_mutex);
348 return require_results[isolate];
358 auto isolate = context->GetIsolate();
359 v8::TryCatch try_catch(isolate);
360 auto local_source = v8::String::NewFromUtf8(isolate, source.c_str());
362 auto script_maybe = v8::Script::Compile(context, local_source, script_origin);
364 if (try_catch.HasCaught()) {
369 error = try_catch.Exception();
370 printf(
"%s\n",
stringify_value(isolate, try_catch.Exception()).c_str());
371 if (
V8_TOOLKIT_DEBUG) printf(
"Failed to compile: %s\n", *v8::String::Utf8Value(try_catch.Exception()));
375 script = script_maybe.ToLocalChecked();
382 v8::Isolate * isolate = context->GetIsolate();
385 v8::TryCatch try_catch(isolate);
387 auto maybe_result = script->Run(context);
388 if (try_catch.HasCaught()) {
395 if(maybe_result.IsEmpty()) {
402 if(e->IsExternal()) {
403 auto anybase = (
AnyBase *)v8::External::Cast(*e)->Value();
405 assert(anyptr_exception_ptr);
408 std::rethrow_exception(anyptr_exception_ptr->get());
410 printf(
"v8 internal exception thrown: %s\n", *v8::String::Utf8Value(e));
411 throw V8Exception(isolate, v8::Global<v8::Value>(isolate, e));
429 const v8::ScriptOrigin & script_origin,
432 auto isolate = context->GetIsolate();
440 v8::ScriptCompiler::Source source(v8::String::NewFromUtf8(isolate, module_source.c_str()), script_origin);
442 "module"_v8,
"exports"_v8
444 v8::TryCatch try_catch(isolate);
445 auto maybe_module_function =
446 v8::ScriptCompiler::CompileFunctionInContext(context, &source, 2, ¶meter_names[0], 0,
nullptr);
447 if (try_catch.HasCaught()) {
455 assert(!maybe_module_function.IsEmpty());
456 compiled_function = maybe_module_function.ToLocalChecked();
466 module_params[0] = v8::Object::New(isolate);
467 auto exports_object = v8::Object::New(isolate);
468 module_params[1] = exports_object;
469 add_variable(context, module_params[0]->ToObject(),
"exports", exports_object);
471 (void)compiled_function->Call(context, context->Global(), 2, &module_params[0]);
473 return exports_object;
496 const std::vector<std::string> & paths,
497 bool track_file_modification_times,
503 auto isolate = context->GetIsolate();
504 v8::Locker l(isolate);
505 if (filename.find(
"..") != std::string::npos) {
506 if (REQUIRE_DEBUG_PRINTS) printf(
"require() attempted to use a path with more than one . in a row '%s' (disallowed as simple algorithm to stop tricky paths)", filename.c_str());
507 isolate->ThrowException(
"Cannot specify a file containing .."_v8);
511 for (
auto suffix : std::vector<std::string>{
"",
".js",
".json", }) {
512 for (
auto path : paths) {
515 auto complete_filename = path +
"\\" + filename + suffix;
517 auto complete_filename = path +
"/" + filename + suffix;
521 if (resource_name_callback) {
522 resource_name = resource_name_callback(complete_filename);
526 time_t file_modification_time = 0;
527 if (!
get_file_contents(complete_filename, file_contents, file_modification_time)) {
528 if (REQUIRE_DEBUG_PRINTS) printf(
"Module not found at %s\n", complete_filename.c_str());
535 std::lock_guard<std::mutex> l(require_results_mutex);
536 auto & isolate_require_results = require_results[isolate];
538 auto cached_require_results = isolate_require_results.find(complete_filename);
539 if (cached_require_results != isolate_require_results.end()) {
540 if (REQUIRE_DEBUG_PRINTS) printf(
"Found cached results, using cache instead of re-running module\n");
544 if (!track_file_modification_times || file_modification_time == cached_require_results->second.time) {
545 if (REQUIRE_DEBUG_PRINTS) printf(
"Returning cached results\n");
546 result = cached_require_results->second.result.Get(isolate);
549 if (REQUIRE_DEBUG_PRINTS) printf(
"Not returning cached results because modification time was no good\n");
552 if (REQUIRE_DEBUG_PRINTS) printf(
"Didn't find cached version for isolate %p %s\n", isolate, complete_filename.c_str());
562 if (std::regex_search(filename, std::regex(
".json$"))) {
564 v8::ScriptOrigin script_origin(v8::String::NewFromUtf8(isolate,
565 resource_name.c_str()),
566 v8::Integer::New(isolate, 0),
567 v8::Integer::New(isolate, 0)
571 v8::TryCatch try_catch(isolate);
573 if (REQUIRE_DEBUG_PRINTS) printf(
"About to try to parse json: %s\n", file_contents.c_str());
574 auto maybe_result = v8::JSON::Parse(isolate, v8::String::NewFromUtf8(isolate,file_contents.c_str()));
575 if (try_catch.HasCaught()) {
577 if (REQUIRE_DEBUG_PRINTS) printf(
"Couldn't run json for %s, error: %s\n", complete_filename.c_str(), *v8::String::Utf8Value(try_catch.Exception()));
580 result = maybe_result.ToLocalChecked();
584 std::lock_guard<std::mutex> l(require_results_mutex);
585 auto & isolate_require_results = require_results[isolate];
586 auto i = isolate_require_results.find(complete_filename);
587 if (i == isolate_require_results.end()) {
594 v8::ScriptOrigin script_origin(v8::String::NewFromUtf8(isolate, resource_name.c_str()),
602 result =
execute_module(context, file_contents, script_origin, module_function);
604 std::lock_guard<std::mutex> l(require_results_mutex);
605 auto & isolate_require_results = require_results[isolate];
606 isolate_require_results.emplace(complete_filename,
608 file_modification_time));
610 callback(isolate_require_results.find(complete_filename)->second);
626 if (REQUIRE_DEBUG_PRINTS) printf(
"Couldn't find any matches for %s\n", filename.c_str());
627 isolate->ThrowException(
"No such module found in any search path"_v8);
634 using ReturnType = std::map<std::string, v8::Global<v8::Value>&>;
638 [isolate]{
return scoped_run(isolate, [isolate]()->ReturnType {
640 auto & isolate_results = require_results[isolate];
641 for (
auto & result_pair : isolate_results) {
642 results.emplace(result_pair.first, result_pair.second.result);
654 std::lock_guard<std::mutex> guard(require_results_mutex);
655 if (require_results.find(isolate) == require_results.end()) {
662 auto context = isolate->GetCurrentContext();
667 if(
require(context, filename, result, paths)) {
682 auto full_directory_name = directory_name;
683 DIR * dir = opendir(full_directory_name.c_str());
685 if (
V8_TOOLKIT_DEBUG) printf(
"Could not open directory %s\n", full_directory_name.c_str());
690 auto require_path = std::vector<std::string>{directory_name};
691 while ((dp = readdir(dir)) != NULL) {
692 if (dp->d_type == DT_DIR) {
698 require(context, dp->d_name, result, require_path);
713 fprintf(stderr,
"Looking at prototype chain\n");
714 while (!object->IsNull()) {
723 fprintf(stderr,
"%s:\n", *v8::String::Utf8Value(
object));
728 fprintf(stderr,
"Done looking at prototype chain\n");
735 auto context = isolate->GetCurrentContext();
738 if (left->IsUndefined() || right->IsUndefined()) {
739 return left->IsUndefined() && right->IsUndefined();
746 if (left->IsBoolean()) {
748 return right->IsBoolean() && left->ToBoolean(context).ToLocalChecked()->Value() == right->ToBoolean(context).ToLocalChecked()->Value();
751 if (left->IsNumber()) {
756 return right->IsNumber() && left->ToNumber(context).ToLocalChecked()->Value() == right->ToNumber(context).ToLocalChecked()->Value();
759 if (left->IsString()) {
761 if (!right->IsString()) {
764 return !strcmp(*v8::String::Utf8Value(left), *v8::String::Utf8Value(right));
767 if (left->IsArray()) {
769 if (!right->IsArray()) {
778 if (left_length != right_length) {
783 for (
int i = 0; i < left_length; i++) {
784 auto left_value = array_left->Get(context, i);
785 auto right_value = array_right->Get(context, i);
786 if (!
compare_contents(isolate, left_value.ToLocalChecked(), right_value.ToLocalChecked())) {
797 if(!object_left.IsEmpty()) {
803 if (object_right.IsEmpty()) {
809 if (left_keys.size() != right_keys.size()) {
814 for(
auto left_key : left_keys) {
815 auto left_value = object_left->Get(context, v8::String::NewFromUtf8(isolate, left_key.c_str()));
816 auto right_value = object_right->Get(context, v8::String::NewFromUtf8(isolate, left_key.c_str()));
817 if (right_value.IsEmpty()) {
820 }
else if (!
compare_contents(isolate, left_value.ToLocalChecked(), right_value.ToLocalChecked())) {
845 auto isolate = context->GetIsolate();
846 auto names =
object->GetPropertyNames(context).ToLocalChecked();
853 v8::Isolate * isolate,
856 destructive(destructive)
859 this->
global.Reset(isolate, javascript_object);
871 callback_data->global.template SetWeak<SetWeakCallbackData>(callback_data,
872 [](
const v8::WeakCallbackInfo<SetWeakCallbackData> & info) {
875 callback_data->
global.Reset();
876 delete callback_data;
877 }, v8::WeakCallbackType::kParameter);
879 return callback_data;
886 const volatile bool files,
887 const volatile bool directories,
893 auto full_directory_name = directory_name;
894 DIR * dir = opendir(full_directory_name.c_str());
896 printf(
"Could not open directory %s\n", full_directory_name.c_str());
900 while ((dp = readdir(dir)) != NULL) {
902 if ((dp->d_type == DT_DIR && directories && strcmp(dp->d_name,
".") && strcmp(dp->d_name,
"..")) ||
903 (dp->d_type == DT_REG && files)) {
912 WIN32_FIND_DATA file_metadata;
913 HANDLE directory = FindFirstFile((directory_name +
"\\*").c_str(), &file_metadata);
915 if (directory == INVALID_HANDLE_VALUE) {
916 printf(
"Could not open directory %s\n", directory_name.c_str());
920 if (!strcmp(file_metadata.cFileName,
".") || !strcmp(file_metadata.cFileName,
"..")) {
924 ((file_metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && directories) ||
925 ((!(file_metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) && files)) {
928 printf(
"Skipping file %s because it's not of desired type file: %d directory: %d\n", file_metadata.cFileName, files, directories);
933 }
while(FindNextFile(directory, &file_metadata));
935 FindClose(directory);
GTEST_API_ size_t GetFileSize(FILE *file)