From 3881ad1c4b49db71a24b6ed2e1208ce6e954e1c0 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 28 Oct 2015 17:36:01 +0800 Subject: [PATCH] Don't leak FunctionTemplate when converting C++ callback --- atom/common/native_mate_converters/callback.cc | 62 ++++++++++++++++++++++++++ atom/common/native_mate_converters/callback.h | 33 +++++++++++++- filenames.gypi | 1 + 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 atom/common/native_mate_converters/callback.cc diff --git a/atom/common/native_mate_converters/callback.cc b/atom/common/native_mate_converters/callback.cc new file mode 100644 index 0000000..0be90f6 --- /dev/null +++ b/atom/common/native_mate_converters/callback.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2015 GitHub, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/common/native_mate_converters/callback.h" + +namespace mate { + +namespace internal { + +namespace { + +struct TranslaterHolder { + Translater translater; +}; + +// Cached JavaScript version of |CallTranslater|. +v8::Persistent g_call_translater; + +void CallTranslater(v8::Local external, mate::Arguments* args) { + TranslaterHolder* holder = static_cast(external->Value()); + holder->translater.Run(args); +} + +// func.bind(func, arg1). +// NB(zcbenz): Using C++11 version crashes VS. +v8::Local BindFunctionWith(v8::Isolate* isolate, + v8::Local context, + v8::Local func, + v8::Local arg1) { + v8::MaybeLocal bind = func->Get(mate::StringToV8(isolate, "bind")); + CHECK(!bind.IsEmpty()); + v8::Local bind_func = + v8::Local::Cast(bind.ToLocalChecked()); + v8::Local converted[] = { func, arg1 }; + return bind_func->Call( + context, func, arraysize(converted), converted).ToLocalChecked(); +} + +} // namespace + +v8::Local CreateFunctionFromTranslater( + v8::Isolate* isolate, const Translater& translater) { + // The FunctionTemplate is cached. + if (g_call_translater.IsEmpty()) + g_call_translater.Reset( + isolate, + mate::CreateFunctionTemplate(isolate, base::Bind(&CallTranslater))); + + v8::Local call_translater = + v8::Local::New(isolate, g_call_translater); + TranslaterHolder* holder = new TranslaterHolder; + holder->translater = translater; + return BindFunctionWith(isolate, + isolate->GetCurrentContext(), + call_translater->GetFunction(), + v8::External::New(isolate, holder)); +} + +} // namespace internal + +} // namespace mate diff --git a/atom/common/native_mate_converters/callback.h b/atom/common/native_mate_converters/callback.h index 68ea911..4349b99 100644 --- a/atom/common/native_mate_converters/callback.h +++ b/atom/common/native_mate_converters/callback.h @@ -20,6 +20,7 @@ namespace internal { typedef scoped_refptr > SafeV8Function; +// Helper to invoke a V8 function with C++ parameters. template struct V8FunctionInvoker {}; @@ -81,13 +82,41 @@ struct V8FunctionInvoker { } }; +// Helper to pass a C++ funtion to JavaScript. +using Translater = base::Callback; +v8::Local CreateFunctionFromTranslater( + v8::Isolate* isolate, const Translater& translater); + +// Calls callback with Arguments. +template +struct NativeFunctionInvoker {}; + +template +struct NativeFunctionInvoker { + static void Go(base::Callback val, Arguments* args) { + using Indices = typename IndicesGenerator::type; + Invoker invoker(args, 0); + if (invoker.IsOK()) + invoker.DispatchToCallback(val); + } +}; + +// Create a static function that accepts generic callback. +template +Translater ConvertToTranslater(const base::Callback& val) { + return base::Bind(&NativeFunctionInvoker::Go, val); +} + } // namespace internal template struct Converter > { static v8::Local ToV8(v8::Isolate* isolate, - const base::Callback& val) { - return CreateFunctionTemplate(isolate, val)->GetFunction(); + const base::Callback& val) { + // We don't use CreateFunctionTemplate here because it creates a new + // FunctionTemplate everytime, which is cached by V8 and causes leaks. + internal::Translater translater = internal::ConvertToTranslater(val); + return internal::CreateFunctionFromTranslater(isolate, translater); } static bool FromV8(v8::Isolate* isolate, v8::Local val, diff --git a/filenames.gypi b/filenames.gypi index a294a1e..65bf6b7 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -304,6 +304,7 @@ 'atom/common/native_mate_converters/accelerator_converter.h', 'atom/common/native_mate_converters/blink_converter.cc', 'atom/common/native_mate_converters/blink_converter.h', + 'atom/common/native_mate_converters/callback.cc', 'atom/common/native_mate_converters/callback.h', 'atom/common/native_mate_converters/file_path_converter.h', 'atom/common/native_mate_converters/gfx_converter.cc', -- 2.7.4