Commit 80195fc5 authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[Compile] Refactor CompilerDispatcher for inner function compilation jobs

Refactors the CompilerDispatcher to be able to enqueue eager inner functions
for off-thread compilation during top-level compilation of a script.

Unoptimized compile jobs are simplified to only have two phases - compile
and finalization. Only finalization requires heap access (and therefore
needs to be run on the main thread). The change also introduces a requirement
to register a SFI with a given compile job after that job is posted, this
is due to the fact that an SFI won't necessarily exist at the point the job
is posted, but is created later when top-level compile is being finalized.
Logic in the compile dispatcher is update to deal with the fact that a job
may not be able to progress if it doesn't yet have an associated SFI
registered with it.

BUG=v8:8041

Change-Id: I66cccd626136738304a7cab0e501fc65cf342514
Reviewed-on: https://chromium-review.googlesource.com/1215782
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56088}
parent a50baa24
......@@ -242,6 +242,17 @@ const AstRawString* AstValueFactory::GetString(Handle<String> literal) {
return result;
}
const AstRawString* AstValueFactory::CloneFromOtherFactory(
const AstRawString* raw_string) {
const AstRawString* result = GetString(
raw_string->hash_field(), raw_string->is_one_byte(),
Vector<const byte>(raw_string->raw_data(), raw_string->byte_length()));
// Check we weren't trying to clone a string that was already in this
// ast-value-factory.
DCHECK_NE(result, raw_string);
return result;
}
AstConsString* AstValueFactory::NewConsString() {
AstConsString* new_string = new (zone_) AstConsString;
DCHECK_NOT_NULL(new_string);
......
......@@ -297,6 +297,11 @@ class AstValueFactory {
return GetTwoByteStringInternal(literal);
}
const AstRawString* GetString(Handle<String> literal);
// Clones an AstRawString from another ast value factory, adding it to this
// factory and returning the clone.
const AstRawString* CloneFromOtherFactory(const AstRawString* raw_string);
V8_EXPORT_PRIVATE AstConsString* NewConsString();
V8_EXPORT_PRIVATE AstConsString* NewConsString(const AstRawString* str);
V8_EXPORT_PRIVATE AstConsString* NewConsString(const AstRawString* str1,
......
......@@ -2261,7 +2261,7 @@ class FunctionLiteral final : public Expression {
void mark_as_iife() { bit_field_ = IIFEBit::update(bit_field_, true); }
bool is_iife() const { return IIFEBit::decode(bit_field_); }
bool is_top_level() const {
bool is_toplevel() const {
return function_literal_id() == FunctionLiteral::kIdTypeTopLevel;
}
bool is_wrapped() const { return function_type() == kWrapped; }
......
......@@ -21,9 +21,7 @@ class V8_EXPORT_PRIVATE CompilerDispatcherJob {
enum class Status {
kInitial,
kPrepared,
kCompiled,
kHasErrorsToReport,
kReadyToFinalize,
kDone,
kFailed,
};
......@@ -48,26 +46,19 @@ class V8_EXPORT_PRIVATE CompilerDispatcherJob {
// Return true if the next step can be run on any thread.
bool NextStepCanRunOnAnyThread() const {
return status() == Status::kPrepared;
return status() == Status::kInitial;
}
// Casts to implementations.
const UnoptimizedCompileJob* AsUnoptimizedCompileJob() const;
// Transition from kInitial to kPrepared. Must only be invoked on the
// main thread.
virtual void PrepareOnMainThread(Isolate* isolate) = 0;
// Transition from kPrepared to kCompiled (or kReportErrors).
// Transition from kInitial to kReadyToFinalize.
virtual void Compile(bool on_background_thread) = 0;
// Transition from kCompiled to kDone (or kFailed). Must only be invoked on
// the main thread.
virtual void FinalizeOnMainThread(Isolate* isolate) = 0;
// Transition from kReportErrors to kFailed. Must only be invoked on the main
// thread.
virtual void ReportErrorsOnMainThread(Isolate* isolate) = 0;
// Transition from kReadyToFinalize to kDone (or kFailed). Must only be
// invoked on the main thread.
virtual void FinalizeOnMainThread(Isolate* isolate,
Handle<SharedFunctionInfo> shared) = 0;
// Free all resources. Must only be invoked on the main thread.
virtual void ResetOnMainThread(Isolate* isolate) = 0;
......@@ -75,9 +66,6 @@ class V8_EXPORT_PRIVATE CompilerDispatcherJob {
// Estimate how long the next step will take using the tracer.
virtual double EstimateRuntimeOfNextStepInMs() const = 0;
// Print short description of job. Must only be invoked on the main thread.
virtual void ShortPrintOnMainThread() = 0;
protected:
void set_status(Status status) { status_ = status; }
......
......@@ -13,6 +13,7 @@
#include "src/base/atomic-utils.h"
#include "src/base/macros.h"
#include "src/base/optional.h"
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/semaphore.h"
......@@ -27,6 +28,7 @@ enum class MemoryPressureLevel;
namespace internal {
class AstRawString;
class AstValueFactory;
class CancelableTaskManager;
class CompilerDispatcherJob;
......@@ -37,6 +39,7 @@ class FunctionLiteral;
class Isolate;
class ParseInfo;
class SharedFunctionInfo;
class WorkerThreadRuntimeCallStats;
class Zone;
template <typename T>
......@@ -79,24 +82,23 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
// Returns true if the compiler dispatcher is enabled.
bool IsEnabled() const;
// Enqueue a job for parse and compile. Returns true if a job was enqueued.
bool Enqueue(Handle<SharedFunctionInfo> function);
base::Optional<JobId> Enqueue(const ParseInfo* outer_parse_info,
const AstRawString* function_name,
const FunctionLiteral* function_literal);
// Like Enqueue, but also advances the job so that it can potentially
// continue running on a background thread (if at all possible). Returns
// true if the job was enqueued.
bool EnqueueAndStep(Handle<SharedFunctionInfo> function);
// Registers the given |function| with the compilation job |job_id|.
void RegisterSharedFunctionInfo(JobId job_id, SharedFunctionInfo* function);
// Returns true if there is a pending job for the given function.
// Returns true if there is a pending job with the given id.
bool IsEnqueued(JobId job_id) const;
// Returns true if there is a pending job registered for the given function.
bool IsEnqueued(Handle<SharedFunctionInfo> function) const;
// Blocks until the given function is compiled (and does so as fast as
// possible). Returns true if the compile job was successful.
bool FinishNow(Handle<SharedFunctionInfo> function);
// Blocks until all jobs are finished.
void FinishAllNow();
// Aborts a given job. Blocks if requested.
void Abort(Handle<SharedFunctionInfo> function, BlockingBehavior blocking);
......@@ -124,15 +126,15 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
FRIEND_TEST(CompilerDispatcherTest, CompileMultipleOnBackgroundThread);
typedef std::map<JobId, std::unique_ptr<CompilerDispatcherJob>> JobMap;
typedef std::map<JobId, Handle<SharedFunctionInfo>> JobIdToSharedMap;
typedef IdentityMap<JobId, FreeStoreAllocationPolicy> SharedToJobIdMap;
class AbortTask;
class WorkerTask;
class IdleTask;
bool CanEnqueue();
void WaitForJobIfRunningOnBackground(CompilerDispatcherJob* job);
void AbortInactiveJobs();
bool CanEnqueue();
bool CanEnqueue(Handle<SharedFunctionInfo> function);
JobMap::const_iterator GetJobFor(Handle<SharedFunctionInfo> shared) const;
void ConsiderJobForBackgroundProcessing(CompilerDispatcherJob* job);
void ScheduleMoreWorkerTasksIfNeeded();
......@@ -141,17 +143,16 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
void ScheduleAbortTask();
void DoBackgroundWork();
void DoIdleWork(double deadline_in_seconds);
JobId Enqueue(std::unique_ptr<CompilerDispatcherJob> job);
JobId EnqueueAndStep(std::unique_ptr<CompilerDispatcherJob> job);
// Returns job if not removed otherwise iterator following the removed job.
JobMap::const_iterator RemoveIfFinished(JobMap::const_iterator job);
// Returns iterator to the inserted job.
JobMap::const_iterator InsertJob(std::unique_ptr<CompilerDispatcherJob> job);
// Returns iterator following the removed job.
JobMap::const_iterator RemoveJob(JobMap::const_iterator job);
bool FinishNow(CompilerDispatcherJob* job);
Isolate* isolate_;
AccountingAllocator* allocator_;
WorkerThreadRuntimeCallStats* worker_thread_runtime_call_stats_;
Platform* platform_;
size_t max_stack_size_;
......@@ -168,6 +169,9 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
// Mapping from job_id to job.
JobMap jobs_;
// Mapping from job_id to SharedFunctionInfo.
JobIdToSharedMap job_id_to_shared_;
// Mapping from SharedFunctionInfo to the corresponding unoptimized
// compilation's JobId;
SharedToJobIdMap shared_to_unoptimized_job_id_;
......
......@@ -15,6 +15,8 @@
namespace v8 {
namespace internal {
class AccountingAllocator;
class AstRawString;
class AstValueFactory;
class AstStringConstants;
class CompilerDispatcherTracer;
......@@ -27,31 +29,25 @@ class SharedFunctionInfo;
class String;
class UnicodeCache;
class UnoptimizedCompilationJob;
class Utf16CharacterStream;
class WorkerThreadRuntimeCallStats;
class V8_EXPORT_PRIVATE UnoptimizedCompileJob : public CompilerDispatcherJob {
public:
// Creates a UnoptimizedCompileJob in the initial state.
UnoptimizedCompileJob(Isolate* isolate, CompilerDispatcherTracer* tracer,
Handle<SharedFunctionInfo> shared,
size_t max_stack_size);
UnoptimizedCompileJob(
CompilerDispatcherTracer* tracer, AccountingAllocator* allocator,
const ParseInfo* outer_parse_info, const AstRawString* function_name,
const FunctionLiteral* function_literal,
WorkerThreadRuntimeCallStats* worker_thread_runtime_stats,
size_t max_stack_size);
~UnoptimizedCompileJob() override;
Handle<SharedFunctionInfo> shared() const { return shared_; }
// Returns true if this UnoptimizedCompileJob was created for the given
// function.
bool IsAssociatedWith(Handle<SharedFunctionInfo> shared) const;
// CompilerDispatcherJob implementation.
void PrepareOnMainThread(Isolate* isolate) override;
void Compile(bool on_background_thread) override;
void FinalizeOnMainThread(Isolate* isolate) override;
void ReportErrorsOnMainThread(Isolate* isolate) override;
void FinalizeOnMainThread(Isolate* isolate,
Handle<SharedFunctionInfo> shared) override;
void ResetOnMainThread(Isolate* isolate) override;
double EstimateRuntimeOfNextStepInMs() const override;
void ShortPrintOnMainThread() override;
private:
friend class CompilerDispatcherTest;
......@@ -59,15 +55,8 @@ class V8_EXPORT_PRIVATE UnoptimizedCompileJob : public CompilerDispatcherJob {
void ResetDataOnMainThread(Isolate* isolate);
Context* context() { return *context_; }
int main_thread_id_;
CompilerDispatcherTracer* tracer_;
AccountingAllocator* allocator_;
Handle<Context> context_; // Global handle.
Handle<SharedFunctionInfo> shared_; // Global handle.
Handle<String> source_; // Global handle.
Handle<String> wrapper_; // Global handle.
WorkerThreadRuntimeCallStats* worker_thread_runtime_stats_;
size_t max_stack_size_;
......
......@@ -872,9 +872,13 @@ class RuntimeCallTimer final {
V(CompileBackgroundScript) \
V(CompileBackgroundRewriteReturnResult) \
V(CompileBackgroundScopeAnalysis) \
V(CompileBackgroundUnoptimizedCompileJob) \
V(CompileDeserialize) \
V(CompileEval) \
V(CompileAnalyse) \
V(CompileEnqueueOnDispatcher) \
V(CompileFinalizeUnoptimizedCompileJob) \
V(CompileFinishNowOnDispatcher) \
V(CompileFunction) \
V(CompileGetFromOptimizedCodeMap) \
V(CompileIgnition) \
......@@ -883,6 +887,7 @@ class RuntimeCallTimer final {
V(CompileScopeAnalysis) \
V(CompileScript) \
V(CompileSerialize) \
V(CompileUnoptimizedCompileJob) \
V(CompileWaitForDispatcher) \
V(DeoptimizeCode) \
V(FunctionCallback) \
......
......@@ -1820,11 +1820,11 @@ bool BytecodeGenerator::ShouldOptimizeAsOneShot() const {
if (loop_depth_ > 0) return false;
// non-top-level iife is likely to be executed multiple times and so
// shouldn`t be optimized as one-shot
bool is_top_level_iife = info()->literal()->is_iife() &&
current_scope()->outer_scope()->is_script_scope();
return info()->literal()->is_top_level() || is_top_level_iife;
// A non-top-level iife is likely to be executed multiple times and so
// shouldn`t be optimized as one-shot.
bool is_toplevel_iife = info()->literal()->is_iife() &&
current_scope()->outer_scope()->is_script_scope();
return info()->literal()->is_toplevel() || is_toplevel_iife;
}
void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
......
......@@ -16,7 +16,7 @@
namespace v8 {
namespace internal {
ParseInfo::ParseInfo(Isolate* isolate, AccountingAllocator* zone_allocator)
ParseInfo::ParseInfo(AccountingAllocator* zone_allocator)
: zone_(base::make_unique<Zone>(zone_allocator, ZONE_NAME)),
flags_(0),
extension_(nullptr),
......@@ -37,7 +37,10 @@ ParseInfo::ParseInfo(Isolate* isolate, AccountingAllocator* zone_allocator)
function_name_(nullptr),
runtime_call_stats_(nullptr),
source_range_map_(nullptr),
literal_(nullptr) {
literal_(nullptr) {}
ParseInfo::ParseInfo(Isolate* isolate, AccountingAllocator* zone_allocator)
: ParseInfo(zone_allocator) {
set_hash_seed(isolate->heap()->HashSeed());
set_stack_limit(isolate->stack_guard()->real_climit());
set_unicode_cache(isolate->unicode_cache());
......@@ -54,6 +57,18 @@ ParseInfo::ParseInfo(Isolate* isolate)
LOG(isolate, ScriptEvent(Logger::ScriptEventType::kReserveId, script_id_));
}
template <typename T>
void ParseInfo::SetFunctionInfo(T function) {
set_is_named_expression(function->is_named_expression());
set_language_mode(function->language_mode());
set_function_kind(function->kind());
set_declaration(function->is_declaration());
set_requires_instance_fields_initializer(
function->requires_instance_fields_initializer());
set_toplevel(function->is_toplevel());
set_wrapped_as_function(function->is_wrapped());
}
ParseInfo::ParseInfo(Isolate* isolate, Handle<SharedFunctionInfo> shared)
: ParseInfo(isolate, isolate->allocator()) {
// Do not support re-parsing top-level function of a wrapped script.
......@@ -61,19 +76,13 @@ ParseInfo::ParseInfo(Isolate* isolate, Handle<SharedFunctionInfo> shared)
// wrapped script at all.
DCHECK_IMPLIES(is_toplevel(), !Script::cast(shared->script())->is_wrapped());
set_toplevel(shared->is_toplevel());
set_wrapped_as_function(shared->is_wrapped());
set_allow_lazy_parsing(FLAG_lazy_inner_functions);
set_is_named_expression(shared->is_named_expression());
set_asm_wasm_broken(shared->is_asm_wasm_broken());
set_start_position(shared->StartPosition());
set_end_position(shared->EndPosition());
function_literal_id_ = shared->FunctionLiteralId(isolate);
set_language_mode(shared->language_mode());
set_function_kind(shared->kind());
set_declaration(shared->is_declaration());
set_requires_instance_fields_initializer(
shared->requires_instance_fields_initializer());
set_asm_wasm_broken(shared->is_asm_wasm_broken());
SetFunctionInfo(shared);
Handle<Script> script(Script::cast(shared->script()), isolate);
set_script(script);
......@@ -99,6 +108,41 @@ ParseInfo::ParseInfo(Isolate* isolate, Handle<Script> script)
script->IsUserJavaScript());
}
// static
std::unique_ptr<ParseInfo> ParseInfo::FromParent(
const ParseInfo* outer_parse_info, AccountingAllocator* zone_allocator,
const FunctionLiteral* literal, const AstRawString* function_name) {
std::unique_ptr<ParseInfo> result =
base::make_unique<ParseInfo>(zone_allocator);
// Replicate shared state of the outer_parse_info.
result->flags_ = outer_parse_info->flags_;
result->script_id_ = outer_parse_info->script_id_;
result->set_logger(outer_parse_info->logger());
result->set_ast_string_constants(outer_parse_info->ast_string_constants());
result->set_hash_seed(outer_parse_info->hash_seed());
DCHECK_EQ(outer_parse_info->parameters_end_pos(), kNoSourcePosition);
DCHECK_NULL(outer_parse_info->extension());
DCHECK(outer_parse_info->maybe_outer_scope_info().is_null());
// Clone the function_name AstRawString into the ParseInfo's own
// AstValueFactory.
const AstRawString* cloned_function_name =
result->GetOrCreateAstValueFactory()->CloneFromOtherFactory(
function_name);
// Setup function specific details.
DCHECK(!literal->is_toplevel());
result->set_function_name(cloned_function_name);
result->set_start_position(literal->start_position());
result->set_end_position(literal->end_position());
result->set_function_literal_id(literal->function_literal_id());
result->SetFunctionInfo(literal);
return result;
}
ParseInfo::~ParseInfo() = default;
DeclarationScope* ParseInfo::scope() const { return literal()->scope(); }
......
......@@ -38,11 +38,18 @@ class Zone;
// A container for the inputs, configuration options, and outputs of parsing.
class V8_EXPORT_PRIVATE ParseInfo {
public:
ParseInfo(Isolate*);
explicit ParseInfo(AccountingAllocator* zone_allocator);
explicit ParseInfo(Isolate*);
ParseInfo(Isolate*, AccountingAllocator* zone_allocator);
ParseInfo(Isolate* isolate, Handle<Script> script);
ParseInfo(Isolate* isolate, Handle<SharedFunctionInfo> shared);
// Creates a new parse info based on parent top-level |outer_parse_info| for
// function |literal|.
static std::unique_ptr<ParseInfo> FromParent(
const ParseInfo* outer_parse_info, AccountingAllocator* zone_allocator,
const FunctionLiteral* literal, const AstRawString* function_name);
~ParseInfo();
Handle<Script> CreateScript(Isolate* isolate, Handle<String> source,
......@@ -203,7 +210,7 @@ class V8_EXPORT_PRIVATE ParseInfo {
//--------------------------------------------------------------------------
Handle<Script> script() const { return script_; }
void set_script(Handle<Script> script);
void ClearScriptHandle() { script_ = Handle<Script>(); }
MaybeHandle<ScopeInfo> maybe_outer_scope_info() const {
return maybe_outer_scope_info_;
}
......@@ -225,6 +232,11 @@ class V8_EXPORT_PRIVATE ParseInfo {
private:
void SetScriptForToplevelCompile(Isolate* isolate, Handle<Script> script);
// Set function info flags based on those in either FunctionLiteral or
// SharedFunctionInfo |function|
template <typename T>
void SetFunctionInfo(T function);
// Various configuration flags for parsing.
enum Flag {
// ---------- Input flags ---------------------------
......
......@@ -723,11 +723,13 @@ ProducedPreParsedScopeData* ZoneConsumedPreParsedScopeData::GetChildData(
std::unique_ptr<ConsumedPreParsedScopeData> ConsumedPreParsedScopeData::For(
Isolate* isolate, Handle<PreParsedScopeData> data) {
DCHECK(!data.is_null());
return base::make_unique<OnHeapConsumedPreParsedScopeData>(isolate, data);
}
std::unique_ptr<ConsumedPreParsedScopeData> ConsumedPreParsedScopeData::For(
Zone* zone, ZonePreParsedScopeData* data) {
if (data == nullptr) return {};
return base::make_unique<ZoneConsumedPreParsedScopeData>(zone, data);
}
......
......@@ -6,10 +6,13 @@
#include "include/v8.h"
#include "src/api.h"
#include "src/base/template-utils.h"
#include "src/handles.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/objects.h"
#include "src/parsing/scanner-character-streams.h"
#include "src/parsing/scanner.h"
namespace v8 {
namespace internal {
......@@ -17,13 +20,13 @@ namespace test {
Handle<String> CreateSource(Isolate* isolate,
ExternalOneByteString::Resource* maybe_resource) {
static const char test_script[] = "(x) { x*x; }";
if (maybe_resource) {
return isolate->factory()
->NewExternalStringFromOneByte(maybe_resource)
.ToHandleChecked();
if (!maybe_resource) {
static const char test_script[] = "(x) { x*x; }";
maybe_resource = new test::ScriptResource(test_script, strlen(test_script));
}
return isolate->factory()->NewStringFromAsciiChecked(test_script);
return isolate->factory()
->NewExternalStringFromOneByte(maybe_resource)
.ToHandleChecked();
}
Handle<SharedFunctionInfo> CreateSharedFunctionInfo(
......@@ -51,6 +54,23 @@ Handle<SharedFunctionInfo> CreateSharedFunctionInfo(
return scope.CloseAndEscape(shared);
}
std::unique_ptr<ParseInfo> OuterParseInfoForShared(
Isolate* isolate, Handle<SharedFunctionInfo> shared) {
Handle<Script> script =
Handle<Script>::cast(handle(shared->script(), isolate));
std::unique_ptr<ParseInfo> result =
base::make_unique<ParseInfo>(isolate, script);
// Create a character stream to simulate the parser having done so for the
// to-level ParseProgram.
Handle<String> source(String::cast(script->source()), isolate);
std::unique_ptr<Utf16CharacterStream> stream(
ScannerStream::For(isolate, source));
result->set_character_stream(std::move(stream));
return result;
}
} // namespace test
} // namespace internal
} // namespace v8
......@@ -46,6 +46,8 @@ Handle<String> CreateSource(
Handle<SharedFunctionInfo> CreateSharedFunctionInfo(
Isolate* isolate,
v8::String::ExternalOneByteStringResource* maybe_resource);
std::unique_ptr<ParseInfo> OuterParseInfoForShared(
Isolate* isolate, Handle<SharedFunctionInfo> shared);
} // namespace test
} // namespace internal
......
......@@ -62,6 +62,16 @@ Local<Value> TestWithIsolate::RunJS(const char* source) {
return script->Run(isolate()->GetCurrentContext()).ToLocalChecked();
}
Local<Value> TestWithIsolate::RunJS(
String::ExternalOneByteStringResource* source) {
Local<Script> script =
v8::Script::Compile(
isolate()->GetCurrentContext(),
v8::String::NewExternalOneByte(isolate(), source).ToLocalChecked())
.ToLocalChecked();
return script->Run(isolate()->GetCurrentContext()).ToLocalChecked();
}
TestWithContext::TestWithContext()
: context_(Context::New(isolate())), context_scope_(context_) {}
......@@ -93,6 +103,11 @@ Handle<Object> TestWithIsolate::RunJSInternal(const char* source) {
return Utils::OpenHandle(*::v8::TestWithIsolate::RunJS(source));
}
Handle<Object> TestWithIsolate::RunJSInternal(
::v8::String::ExternalOneByteStringResource* source) {
return Utils::OpenHandle(*::v8::TestWithIsolate::RunJS(source));
}
base::RandomNumberGenerator* TestWithIsolate::random_number_generator() const {
return isolate()->random_number_generator();
}
......
......@@ -37,6 +37,7 @@ class TestWithIsolate : public virtual ::testing::Test {
}
Local<Value> RunJS(const char* source);
Local<Value> RunJS(String::ExternalOneByteStringResource* source);
static void SetUpTestCase();
static void TearDownTestCase();
......@@ -88,6 +89,13 @@ class TestWithIsolate : public virtual ::v8::TestWithIsolate {
return Handle<T>::cast(RunJSInternal(source));
}
Handle<Object> RunJSInternal(const char* source);
template <typename T = Object>
Handle<T> RunJS(::v8::String::ExternalOneByteStringResource* source) {
return Handle<T>::cast(RunJSInternal(source));
}
Handle<Object> RunJSInternal(
::v8::String::ExternalOneByteStringResource* source);
base::RandomNumberGenerator* random_number_generator() const;
private:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment