| // Copyright 2017 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/script/v8c/v8c_global_environment.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "base/lazy_instance.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/trace_event/trace_event.h" |
| #include "cobalt/base/polymorphic_downcast.h" |
| #include "cobalt/script/javascript_engine.h" |
| #include "cobalt/script/v8c/embedded_resources.h" |
| #include "cobalt/script/v8c/entry_scope.h" |
| #include "cobalt/script/v8c/v8c_script_value_factory.h" |
| #include "cobalt/script/v8c/v8c_source_code.h" |
| #include "cobalt/script/v8c/v8c_user_object_holder.h" |
| #include "cobalt/script/v8c/v8c_value_handle.h" |
| #include "nb/memory_scope.h" |
| #include "starboard/common/murmurhash2.h" |
| |
| |
| namespace cobalt { |
| namespace script { |
| namespace v8c { |
| |
| namespace { |
| |
| std::string ExceptionToString(v8::Isolate* isolate, |
| const v8::TryCatch& try_catch) { |
| v8::HandleScope handle_scope(isolate); |
| v8::String::Utf8Value exception(isolate, try_catch.Exception()); |
| v8::Local<v8::Message> message(try_catch.Message()); |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| |
| std::string string; |
| if (message.IsEmpty()) { |
| string.append(base::StringPrintf("%s\n", *exception)); |
| } else { |
| v8::String::Utf8Value filename(isolate, |
| message->GetScriptOrigin().ResourceName()); |
| int linenum; |
| linenum = message->GetLineNumber(context).To(&linenum) ? linenum : -1; |
| int colnum = message->GetStartColumn(); |
| string.append(base::StringPrintf("%s:%i:%i %s\n", *filename, linenum, |
| colnum, *exception)); |
| v8::String::Utf8Value sourceline( |
| isolate, message->GetSourceLine(context).ToLocalChecked()); |
| string.append(base::StringPrintf("%s\n", *sourceline)); |
| } |
| return string; |
| } |
| |
| std::string ToStringOrNull(v8::Isolate* isolate, v8::Local<v8::Value> value) { |
| if (value.IsEmpty() || !value->IsString()) { |
| return ""; |
| } |
| return *v8::String::Utf8Value(isolate, value.As<v8::String>()); |
| } |
| |
| uint32_t CreateJavaScriptCacheKey(const std::string& javascript_engine_version, |
| uint32_t cached_data_version_tag, |
| const std::string& source, |
| const std::string& origin) { |
| uint32_t res = starboard::MurmurHash2_32(javascript_engine_version.c_str(), |
| javascript_engine_version.size(), |
| cached_data_version_tag); |
| res = starboard::MurmurHash2_32(source.c_str(), source.size(), res); |
| res = starboard::MurmurHash2_32(origin.c_str(), origin.size(), res); |
| return res; |
| } |
| |
| } // namespace |
| |
| V8cGlobalEnvironment::V8cGlobalEnvironment(v8::Isolate* isolate) |
| : isolate_(isolate), |
| destruction_helper_(isolate), |
| wrapper_factory_(new WrapperFactory(isolate)), |
| script_value_factory_(new V8cScriptValueFactory(isolate)) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| TRACE_EVENT0("cobalt::script", |
| "V8cGlobalEnvironment::V8cGlobalEnvironment()"); |
| wrapper_factory_.reset(new WrapperFactory(isolate)); |
| isolate_->SetData(kIsolateDataIndex, this); |
| DCHECK(isolate_->GetData(kIsolateDataIndex) == this); |
| |
| isolate_->SetAllowCodeGenerationFromStringsCallback( |
| AllowCodeGenerationFromStringsCallback); |
| |
| isolate_->SetAllowWasmCodeGenerationCallback( |
| [](v8::Local<v8::Context> context, v8::Local<v8::String> source) { |
| return false; |
| }); |
| |
| isolate_->AddMessageListenerWithErrorLevel( |
| MessageHandler, |
| v8::Isolate::kMessageError | v8::Isolate::kMessageWarning | |
| v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug | |
| v8::Isolate::kMessageLog); |
| } |
| |
| V8cGlobalEnvironment::~V8cGlobalEnvironment() { |
| TRACE_EVENT0("cobalt::script", |
| "V8cGlobalEnvironment::~V8cGlobalEnvironment()"); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| InvalidateWeakPtrs(); |
| } |
| |
| void V8cGlobalEnvironment::CreateGlobalObject() { |
| TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::CreateGlobalObject()"); |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| // Intentionally not an |EntryScope|, since the context doesn't exist yet. |
| v8::Isolate::Scope isolate_scope(isolate_); |
| v8::HandleScope handle_scope(isolate_); |
| v8::Local<v8::Context> context = v8::Context::New(isolate_); |
| context_.Reset(isolate_, context); |
| v8::Context::Scope context_scope(context); |
| |
| EvaluateAutomatics(); |
| } |
| |
| bool V8cGlobalEnvironment::EvaluateScript( |
| const scoped_refptr<SourceCode>& source_code, |
| std::string* out_result_utf8) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::EvaluateScript()"); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| EntryScope entry_scope(isolate_); |
| v8::Local<v8::Context> context = isolate_->GetCurrentContext(); |
| v8::TryCatch try_catch(isolate_); |
| |
| v8::Local<v8::Value> result; |
| if (!EvaluateScriptInternal(source_code).ToLocal(&result)) { |
| if (!try_catch.HasCaught()) { |
| LOG(WARNING) << "Script evaluation failed with no JavaScript exception."; |
| return false; |
| } |
| // The MessageHandler appears to never get called under a |v8::TryCatch| |
| // block, even if we re-throw it. We work around this by manually passing |
| // it to the MessageHandler. |
| MessageHandler(try_catch.Message(), try_catch.Exception()); |
| if (out_result_utf8) { |
| *out_result_utf8 = ExceptionToString(isolate_, try_catch); |
| } |
| return false; |
| } |
| |
| if (out_result_utf8) { |
| V8cExceptionState exception_state(isolate_); |
| FromJSValue(isolate_, result, kNoConversionFlags, &exception_state, |
| out_result_utf8); |
| } |
| |
| return true; |
| } |
| |
| bool V8cGlobalEnvironment::EvaluateScript( |
| const scoped_refptr<SourceCode>& source_code, |
| const scoped_refptr<Wrappable>& owning_object, |
| base::Optional<ValueHandleHolder::Reference>* out_value_handle) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::EvaluateScript()"); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| EntryScope entry_scope(isolate_); |
| v8::Local<v8::Context> context = isolate_->GetCurrentContext(); |
| v8::TryCatch try_catch(isolate_); |
| |
| v8::Local<v8::Value> result; |
| if (!EvaluateScriptInternal(source_code).ToLocal(&result)) { |
| if (!try_catch.HasCaught()) { |
| LOG(WARNING) << "Script evaluation failed with no JavaScript exception."; |
| } |
| // The MessageHandler appears to never get called under a |v8::TryCatch| |
| // block, even if we re-throw it. We work around this by manually passing |
| // it to the MessageHandler. |
| MessageHandler(try_catch.Message(), try_catch.Exception()); |
| return false; |
| } |
| |
| if (out_value_handle) { |
| V8cValueHandleHolder v8c_value_handle_holder(isolate_, result); |
| out_value_handle->emplace(owning_object.get(), v8c_value_handle_holder); |
| } |
| |
| return true; |
| } |
| |
| std::vector<StackFrame> V8cGlobalEnvironment::GetStackTrace(int max_frames) { |
| TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::GetStackTrace()"); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| // cobalt::script treats |max_frames| being set to 0 as "the entire stack", |
| // while V8 interprets the frame count being set to 0 as "give me 0 frames", |
| // so we have to translate between the two. |
| const int kV8CallMaxFrameAmount = 4096; |
| int v8_max_frames = (max_frames == 0) ? kV8CallMaxFrameAmount : max_frames; |
| |
| v8::HandleScope handle_scope(isolate_); |
| v8::Local<v8::StackTrace> stack_trace = |
| v8::StackTrace::CurrentStackTrace(isolate_, v8_max_frames); |
| |
| std::vector<StackFrame> result; |
| for (int i = 0; i < stack_trace->GetFrameCount(); i++) { |
| v8::Local<v8::StackFrame> stack_frame = stack_trace->GetFrame(isolate_, i); |
| v8::String::Utf8Value function_name(isolate_, |
| stack_frame->GetFunctionName()); |
| v8::String::Utf8Value script_name(isolate_, stack_frame->GetScriptName()); |
| std::string function_name_str, script_name_str; |
| if (*function_name) { |
| function_name_str = std::string(*function_name); |
| } |
| if (*script_name) { |
| script_name_str = std::string(*script_name); |
| } |
| result.emplace_back(stack_frame->GetLineNumber(), stack_frame->GetColumn(), |
| function_name_str, script_name_str); |
| } |
| |
| return result; |
| } |
| |
| void V8cGlobalEnvironment::AddRoot(Traceable* traceable) { |
| V8cEngine::GetFromIsolate(isolate_)->heap_tracer()->AddRoot(traceable); |
| } |
| |
| void V8cGlobalEnvironment::RemoveRoot(Traceable* traceable) { |
| V8cEngine::GetFromIsolate(isolate_)->heap_tracer()->RemoveRoot(traceable); |
| } |
| |
| void V8cGlobalEnvironment::PreventGarbageCollection( |
| const scoped_refptr<Wrappable>& wrappable) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| AddRoot(wrappable.get()); |
| } |
| |
| void V8cGlobalEnvironment::AllowGarbageCollection(Wrappable* wrappable) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| RemoveRoot(wrappable); |
| } |
| |
| void V8cGlobalEnvironment::DisableEval(const std::string& message) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| EntryScope entry_scope(isolate_); |
| v8::Local<v8::Context> context = isolate_->GetCurrentContext(); |
| context->AllowCodeGenerationFromStrings(false); |
| context->SetErrorMessageForCodeGenerationFromStrings( |
| v8::String::NewFromUtf8(isolate_, message.c_str(), |
| v8::NewStringType::kNormal) |
| .ToLocalChecked()); |
| } |
| |
| void V8cGlobalEnvironment::EnableEval() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| EntryScope entry_scope(isolate_); |
| v8::Local<v8::Context> context = isolate_->GetCurrentContext(); |
| context->AllowCodeGenerationFromStrings(true); |
| } |
| |
| void V8cGlobalEnvironment::DisableJit() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| LOG(INFO) << "V8 version " << V8_MAJOR_VERSION << '.' << V8_MINOR_VERSION |
| << "can only be run with JIT enabled, ignoring |DisableJit| call."; |
| } |
| |
| void V8cGlobalEnvironment::SetReportEvalCallback( |
| const base::Closure& report_eval) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| report_eval_ = report_eval; |
| } |
| |
| void V8cGlobalEnvironment::SetReportErrorCallback( |
| const ReportErrorCallback& report_error_callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| report_error_callback_ = report_error_callback; |
| } |
| |
| void V8cGlobalEnvironment::Bind(const std::string& identifier, |
| const scoped_refptr<Wrappable>& impl) { |
| TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::Bind()"); |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK(impl); |
| |
| EntryScope entry_scope(isolate_); |
| v8::Local<v8::Context> context = isolate_->GetCurrentContext(); |
| |
| v8::Local<v8::Object> wrapper = wrapper_factory_->GetWrapper(impl); |
| v8::Local<v8::Object> global_object = context->Global(); |
| |
| v8::Maybe<bool> set_result = global_object->Set( |
| context, |
| v8::String::NewFromUtf8(isolate_, identifier.c_str(), |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(), |
| wrapper); |
| DCHECK(set_result.FromJust()); |
| } |
| |
| void V8cGlobalEnvironment::BindTo(const std::string& identifier, |
| const scoped_refptr<Wrappable>& impl, |
| const std::string& local_object_name) { |
| TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::BindTo()"); |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK(impl); |
| |
| EntryScope entry_scope(isolate_); |
| v8::Local<v8::Context> context = isolate_->GetCurrentContext(); |
| |
| v8::Local<v8::Object> wrapper = wrapper_factory_->GetWrapper(impl); |
| v8::Local<v8::Object> global_object = context->Global(); |
| |
| v8::Local<v8::String> local_object_string( |
| v8::String::NewFromUtf8(isolate_, local_object_name.c_str(), |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked()); |
| v8::Local<v8::Object> local_object = v8::Local<v8::Object>::Cast( |
| global_object->Get(context, local_object_string).ToLocalChecked()); |
| |
| v8::Maybe<bool> set_result = local_object->Set( |
| context, |
| v8::String::NewFromUtf8(isolate_, identifier.c_str(), |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(), |
| wrapper); |
| DCHECK(set_result.FromJust()); |
| } |
| |
| ScriptValueFactory* V8cGlobalEnvironment::script_value_factory() { |
| DCHECK(script_value_factory_); |
| return script_value_factory_.get(); |
| } |
| |
| V8cGlobalEnvironment::DestructionHelper::~DestructionHelper() { |
| TRACE_EVENT0("cobalt::script", |
| "V8cGlobalEnvironment::DestructionHelper::~DestructionHelper()"); |
| |
| V8cEngine::GetFromIsolate(isolate_)->heap_tracer()->DisableForShutdown(); |
| isolate_->SetData(kIsolateDataIndex, nullptr); |
| isolate_->LowMemoryNotification(); |
| // Another GC to make sure global object is collected. |
| isolate_->LowMemoryNotification(); |
| isolate_->SetEmbedderHeapTracer(nullptr); |
| } |
| |
| // static |
| bool V8cGlobalEnvironment::AllowCodeGenerationFromStringsCallback( |
| v8::Local<v8::Context> context, v8::Local<v8::String> source) { |
| V8cGlobalEnvironment* global_environment = |
| V8cGlobalEnvironment::GetFromIsolate(context->GetIsolate()); |
| DCHECK(global_environment); |
| if (!global_environment->report_eval_.is_null()) { |
| global_environment->report_eval_.Run(); |
| } |
| // This callback should only be called while code generation from strings is |
| // not allowed from within V8, so this should always be false. Note that |
| // WebAssembly code generation will fall back to this callback if a |
| // WebAssembly callback has not been explicitly set, however we *have* set |
| // one. |
| DCHECK_EQ(context->IsCodeGenerationFromStringsAllowed(), false); |
| return context->IsCodeGenerationFromStringsAllowed(); |
| } |
| |
| // static |
| void V8cGlobalEnvironment::MessageHandler(v8::Local<v8::Message> message, |
| v8::Local<v8::Value> data) { |
| v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
| V8cGlobalEnvironment* global_environment = |
| V8cGlobalEnvironment::GetFromIsolate(isolate); |
| if (isolate->GetEnteredContext().IsEmpty()) { |
| return; |
| } |
| if (message->ErrorLevel() != v8::Isolate::kMessageError) { |
| return; |
| } |
| |
| v8::Local<v8::Context> context = isolate->GetEnteredContext(); |
| ErrorReport error_report; |
| error_report.message = *v8::String::Utf8Value(isolate, message->Get()); |
| error_report.filename = |
| ToStringOrNull(isolate, message->GetScriptResourceName()); |
| int line_number = 0; |
| int column_number = 0; |
| if (message->GetLineNumber(context).To(&line_number) && |
| message->GetStartColumn(context).To(&column_number)) { |
| column_number++; |
| } |
| error_report.line_number = line_number; |
| error_report.column_number = column_number; |
| error_report.is_muted = message->IsSharedCrossOrigin(); |
| error_report.error.reset(new V8cValueHandleHolder(isolate, data)); |
| if (!global_environment->report_error_callback_.is_null()) { |
| global_environment->report_error_callback_.Run(error_report); |
| } |
| } |
| |
| v8::MaybeLocal<v8::Value> V8cGlobalEnvironment::EvaluateScriptInternal( |
| const scoped_refptr<SourceCode>& source_code) { |
| TRACK_MEMORY_SCOPE("Javascript"); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| // Note that we expect an |EntryScope| and |v8::TryCatch| to have been set |
| // up by our caller. |
| V8cSourceCode* v8c_source_code = |
| base::polymorphic_downcast<V8cSourceCode*>(source_code.get()); |
| const base::SourceLocation& source_location = v8c_source_code->location(); |
| |
| v8::Local<v8::String> resource_name; |
| if (!v8::String::NewFromUtf8(isolate_, source_location.file_path.c_str(), |
| v8::NewStringType::kNormal) |
| .ToLocal(&resource_name)) { |
| // Technically possible, but whoa man should this never happen. |
| LOG(WARNING) << "Failed to convert source location file path \"" |
| << source_location.file_path << "\" to a V8 UTF-8 string."; |
| return {}; |
| } |
| |
| // Note that |v8::ScriptOrigin| offsets are 0-based, whereas |
| // |SourceLocation| line/column numbers are 1-based, so subtract 1 to |
| // translate between the two. |
| v8::ScriptOrigin script_origin( |
| /*resource_name=*/resource_name, |
| /*resource_line_offset=*/ |
| v8::Integer::New(isolate_, source_location.line_number - 1), |
| /*resource_column_offset=*/ |
| v8::Integer::New(isolate_, source_location.column_number - 1), |
| /*resource_is_shared_cross_origin=*/ |
| v8::Boolean::New(isolate_, v8c_source_code->is_muted())); |
| |
| v8::Local<v8::String> source; |
| if (!v8::String::NewFromUtf8(isolate_, v8c_source_code->source_utf8().c_str(), |
| v8::NewStringType::kNormal, |
| v8c_source_code->source_utf8().length()) |
| .ToLocal(&source)) { |
| LOG(WARNING) << "Failed to convert source code to V8 UTF-8 string."; |
| return {}; |
| } |
| |
| v8::Local<v8::Context> context = isolate_->GetCurrentContext(); |
| v8::Local<v8::Script> script; |
| |
| bool used_cache = false; |
| const CobaltExtensionJavaScriptCacheApi* javascript_cache_extension = |
| static_cast<const CobaltExtensionJavaScriptCacheApi*>( |
| SbSystemGetExtension(kCobaltExtensionJavaScriptCacheName)); |
| if (javascript_cache_extension && |
| strcmp(javascript_cache_extension->name, |
| kCobaltExtensionJavaScriptCacheName) == 0 && |
| javascript_cache_extension->version >= 1) { |
| TRACE_EVENT0("cobalt::script", |
| "V8cGlobalEnvironment::CompileWithCaching()"); |
| if (CompileWithCaching(javascript_cache_extension, context, v8c_source_code, |
| source, &script_origin) |
| .ToLocal(&script)) { |
| used_cache = true; |
| } else { |
| LOG(WARNING) << "Failed to compile script."; |
| return {}; |
| } |
| } |
| if (!used_cache) { |
| TRACE_EVENT0("cobalt::script", "v8::Script::Compile()"); |
| if (!v8::Script::Compile(context, source, &script_origin) |
| .ToLocal(&script)) { |
| LOG(WARNING) << "Failed to compile script."; |
| return {}; |
| } |
| } |
| |
| v8::Local<v8::Value> result; |
| { |
| TRACE_EVENT0("cobalt::script", "v8::Script::Run()"); |
| if (!script->Run(context).ToLocal(&result)) { |
| LOG(WARNING) << "Failed to run script."; |
| return {}; |
| } |
| } |
| |
| return result; |
| } |
| |
| v8::MaybeLocal<v8::Script> V8cGlobalEnvironment::CompileWithCaching( |
| const CobaltExtensionJavaScriptCacheApi* javascript_cache_extension, |
| v8::Local<v8::Context> context, V8cSourceCode* v8c_source_code, |
| v8::Local<v8::String> source, v8::ScriptOrigin* script_origin) { |
| const base::SourceLocation& source_location = v8c_source_code->location(); |
| |
| DLOG(INFO) << "CompileWithCaching: " << source_location.file_path.c_str() |
| << " " << v8c_source_code->location().file_path; |
| |
| bool successful_cache = false; |
| |
| std::string javascript_engine_version = |
| script::GetJavaScriptEngineNameAndVersion(); |
| uint32_t javascript_cache_key = CreateJavaScriptCacheKey( |
| javascript_engine_version, v8::ScriptCompiler::CachedDataVersionTag(), |
| v8c_source_code->source_utf8(), source_location.file_path); |
| const uint8_t* cache_data_buf = nullptr; |
| int cache_data_size = -1; |
| v8::Local<v8::Script> script; |
| if (javascript_cache_extension->GetCachedScript( |
| javascript_cache_key, v8c_source_code->source_utf8().size(), |
| &cache_data_buf, &cache_data_size)) { |
| DLOG(INFO) << "CompileWithCaching: Using cached resource"; |
| v8::ScriptCompiler::CachedData* cached_code = |
| new v8::ScriptCompiler::CachedData( |
| cache_data_buf, cache_data_size, |
| v8::ScriptCompiler::CachedData::BufferNotOwned); |
| // The script_source owns the cached_code object. |
| v8::ScriptCompiler::Source script_source(source, *script_origin, |
| cached_code); |
| |
| { |
| TRACE_EVENT0("cobalt::script", "v8::Script::Compile()"); |
| successful_cache = |
| v8::ScriptCompiler::Compile(context, &script_source, |
| v8::ScriptCompiler::kConsumeCodeCache) |
| .ToLocal(&script) && |
| !cached_code->rejected; |
| if (!successful_cache) { |
| LOG(WARNING) |
| << "CompileWithCaching: Failed to reuse the cached script rejected=" |
| << cached_code->rejected; |
| } |
| } |
| } |
| |
| if (cache_data_buf != nullptr) { |
| javascript_cache_extension->ReleaseCachedScriptData(cache_data_buf); |
| cache_data_buf = nullptr; |
| } |
| |
| if (!successful_cache) { |
| DLOG(INFO) << "CompileWithCaching: compile"; |
| { |
| TRACE_EVENT0("cobalt::script", "v8::Script::Compile()"); |
| if (!v8::Script::Compile(context, source, script_origin) |
| .ToLocal(&script)) { |
| LOG(WARNING) << "CompileWithCaching: Failed to compile script."; |
| return {}; |
| } |
| } |
| std::unique_ptr<v8::ScriptCompiler::CachedData> cached_data( |
| v8::ScriptCompiler::CreateCodeCache(script->GetUnboundScript())); |
| if (!javascript_cache_extension->StoreCachedScript( |
| javascript_cache_key, v8c_source_code->source_utf8().size(), |
| cached_data->data, cached_data->length)) { |
| LOG(WARNING) << "CompileWithCaching: Failed to store cached script"; |
| } |
| } |
| return script; |
| } |
| |
| void V8cGlobalEnvironment::EvaluateEmbeddedScript(const unsigned char* data, |
| size_t size, |
| const char* filename) { |
| TRACE_EVENT1("cobalt::script", "V8cGlobalEnvironment::EvaluateEmbeddedScript", |
| "filename", filename); |
| TRACK_MEMORY_SCOPE("Javascript"); |
| std::string source(reinterpret_cast<const char*>(data), size); |
| scoped_refptr<SourceCode> source_code = |
| new V8cSourceCode(source, base::SourceLocation(filename, 1, 1)); |
| std::string result; |
| bool success = EvaluateScript(source_code, &result); |
| if (!success) { |
| DLOG(FATAL) << result; |
| } |
| } |
| |
| void V8cGlobalEnvironment::EvaluateAutomatics() { |
| TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::EvaluateAutomatics()"); |
| EvaluateEmbeddedScript( |
| V8cEmbeddedResources::byte_length_queuing_strategy_js, |
| sizeof(V8cEmbeddedResources::byte_length_queuing_strategy_js), |
| "byte_length_queuing_strategy.js"); |
| EvaluateEmbeddedScript( |
| V8cEmbeddedResources::count_queuing_strategy_js, |
| sizeof(V8cEmbeddedResources::count_queuing_strategy_js), |
| "count_queuing_strategy.js"); |
| EvaluateEmbeddedScript(V8cEmbeddedResources::readable_stream_js, |
| sizeof(V8cEmbeddedResources::readable_stream_js), |
| "readable_stream.js"); |
| EvaluateEmbeddedScript(V8cEmbeddedResources::fetch_js, |
| sizeof(V8cEmbeddedResources::fetch_js), "fetch.js"); |
| } |
| |
| bool V8cGlobalEnvironment::HasInterfaceData(int key) const { |
| DCHECK_GE(key, 0); |
| if (key >= cached_interface_data_.size()) { |
| return false; |
| } |
| return !cached_interface_data_[key].IsEmpty(); |
| } |
| |
| v8::Local<v8::FunctionTemplate> V8cGlobalEnvironment::GetInterfaceData( |
| int key) const { |
| DCHECK(HasInterfaceData(key)); |
| return cached_interface_data_[key].Get(isolate_); |
| } |
| |
| void V8cGlobalEnvironment::AddInterfaceData( |
| int key, v8::Local<v8::FunctionTemplate> function_template) { |
| DCHECK(!HasInterfaceData(key)); |
| if (key >= cached_interface_data_.size()) { |
| cached_interface_data_.resize(key + 1); |
| } |
| DCHECK(!HasInterfaceData(key)); |
| cached_interface_data_[key].Set(isolate_, function_template); |
| } |
| |
| } // namespace v8c |
| } // namespace script |
| } // namespace cobalt |