Skip to content

Commit

Permalink
[Gin] Add documentation to explain how Gin works
Browse files Browse the repository at this point in the history
This code is likely to evolve over time, but we've reached a stage where the
basic structure is mostly in place. This CL documents this structure so that
future developers will understand what we have in mind now.

R=aa@chromium.org
BUG=none

Review URL: https://codereview.chromium.org/88913006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237485 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
abarth@chromium.org committed Nov 27, 2013
1 parent 3af5a0a commit 60531d5
Show file tree
Hide file tree
Showing 15 changed files with 127 additions and 11 deletions.
3 changes: 3 additions & 0 deletions gin/arguments.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

namespace gin {

// Arguments is a wrapper around v8::FunctionCallbackInfo that integrates
// with Converter to make it easier to marshall arguments and return values
// between V8 and C++.
class Arguments {
public:
explicit Arguments(const v8::FunctionCallbackInfo<v8::Value>& info);
Expand Down
3 changes: 3 additions & 0 deletions gin/context_holder.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace gin {

class PerContextData;

// ContextHolder is a generic class for holding a v8::Context. Rather than
// using ContextHolder directly, most code should use a subclass of
// ContextHolder, such as Runner.
class ContextHolder {
public:
explicit ContextHolder(v8::Isolate* isolate);
Expand Down
12 changes: 12 additions & 0 deletions gin/dictionary.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@

namespace gin {

// Dictionary is useful when writing bindings for a function that either
// receives an arbitrary JavaScript object as an argument or returns an
// arbitrary JavaScript object as a result. For example, Dictionary is useful
// when you might use the |dictionary| type in WebIDL:
//
// http://heycam.github.io/webidl/#idl-dictionaries
//
// WARNING: You cannot retain a Dictionary object in the heap. The underlying
// storage for Dictionary is tied to the closest enclosing
// v8::HandleScope. Generally speaking, you should store a Dictionary
// on the stack.
//
class Dictionary {
public:
explicit Dictionary(v8::Isolate* isolate);
Expand Down
2 changes: 2 additions & 0 deletions gin/modules/console.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

namespace gin {

// The Console module provides a basic API for printing to stdout. Over time,
// we'd like to evolve the API to match window.console in browsers.
class Console {
public:
static const char kModuleName[];
Expand Down
9 changes: 9 additions & 0 deletions gin/modules/file_module_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,25 @@

namespace gin {

// FileModuleProvider knows how to load AMD modules off disk. It searches for
// modules in the directories indiciated by |search_paths|. Although we still
// read from the file system on the main thread, we'll eventually want to move
// the reads to a background thread.
class FileModuleProvider {
public:
explicit FileModuleProvider(
const std::vector<base::FilePath>& search_paths);
~FileModuleProvider();

// Searches for modules with |ids| in the file system. If found, the modules
// will be executed asynchronously by |runner|.
void AttempToLoadModules(Runner* runner, const std::set<std::string>& ids);

private:
std::vector<base::FilePath> search_paths_;

// We'll only search for a given module once. We remember the set of modules
// we've already looked for in |attempted_ids_|.
std::set<std::string> attempted_ids_;

DISALLOW_COPY_AND_ASSIGN(FileModuleProvider);
Expand Down
8 changes: 8 additions & 0 deletions gin/modules/module_runner_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ namespace gin {
typedef v8::Local<v8::ObjectTemplate> (*ModuleTemplateGetter)(
v8::Isolate* isolate);

// Emebedders that use AMD modules will probably want to use a RunnerDelegate
// that inherits from ModuleRunnerDelegate. ModuleRunnerDelegate lets embedders
// register built-in modules and routes module requests to FileModuleProvider.
class ModuleRunnerDelegate : public RunnerDelegate {
public:
explicit ModuleRunnerDelegate(
const std::vector<base::FilePath>& search_paths);
virtual ~ModuleRunnerDelegate();

// Lets you register a built-in module. Built-in modules are instantiated by
// creating a new instance of a v8::ObjectTemplate rather than by executing
// code. This function takes a ModuleTemplateGetter rather than a
// v8::ObjectTemplate directly so that embedders can create object templates
// lazily.
void AddBuiltinModule(const std::string& id, ModuleTemplateGetter templ);

private:
Expand Down
10 changes: 9 additions & 1 deletion gin/per_context_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,23 @@ namespace gin {

class Runner;

// Embedders can store additional per-context data by subclassing
// ContextSupplement.
class ContextSupplement {
public:
ContextSupplement();
virtual ~ContextSupplement();

// Detach will be called before ContextHolder disposes the v8::Context.
// Embedders should not interact with |context| after Detach has been called.
virtual void Detach(v8::Handle<v8::Context> context) = 0;

private:
DISALLOW_COPY_AND_ASSIGN(ContextSupplement);
};

// There is one instance of PerContextData per v8::Context managed by Gin. This
// class stores all the Gin-related data that varies per context.
class PerContextData {
public:
explicit PerContextData(v8::Handle<v8::Context> context);
Expand All @@ -34,8 +40,10 @@ class PerContextData {
static PerContextData* From(v8::Handle<v8::Context>);
void Detach(v8::Handle<v8::Context> context);

void set_runner(Runner* runner) { runner_ = runner; }
// The Runner associated with this context. To execute script in this context,
// please use the appropriate API on Runner.
Runner* runner() const { return runner_; }
void set_runner(Runner* runner) { runner_ = runner; }

void AddSupplement(scoped_ptr<ContextSupplement> supplement);

Expand Down
11 changes: 11 additions & 0 deletions gin/per_isolate_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,27 @@

namespace gin {

// There is one instance of PerIsolateData per v8::Isolate managed by Gin. This
// class stores all the Gin-related data that varies per isolate.
class PerIsolateData {
public:
explicit PerIsolateData(v8::Isolate* isolate);
~PerIsolateData();

static PerIsolateData* From(v8::Isolate* isolate);

// Each isolate is associated with a collection of v8::ObjectTemplates and
// v8::FunctionTemplates. Typically these template objects are created
// lazily.
void SetObjectTemplate(WrapperInfo* info,
v8::Local<v8::ObjectTemplate> object_template);
void SetFunctionTemplate(WrapperInfo* info,
v8::Local<v8::FunctionTemplate> function_template);

// These are low-level functions for retrieving object or function templates
// stored in this object. Because these templates are often created lazily,
// most clients should call higher-level functions that know how to populate
// these templates if they haven't already been created.
v8::Local<v8::ObjectTemplate> GetObjectTemplate(WrapperInfo* info);
v8::Local<v8::FunctionTemplate> GetFunctionTemplate(WrapperInfo* info);

Expand All @@ -34,6 +43,8 @@ class PerIsolateData {
typedef std::map<
WrapperInfo*, v8::Eternal<v8::FunctionTemplate> > FunctionTemplateMap;

// PerIsolateData doesn't actually own |isolate_|. Instead, the isolate is
// owned by the IsolateHolder, which also owns the PerIsolateData.
v8::Isolate* isolate_;
ObjectTemplateMap object_templates_;
FunctionTemplateMap function_templates_;
Expand Down
16 changes: 10 additions & 6 deletions gin/public/isolate_holder.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ namespace gin {

class PerIsolateData;

// To embed Gin, first create an instance of IsolateHolder to hold the
// v8::Isolate in which you will execute JavaScript. You might wish to subclass
// IsolateHolder if you want to tie more state to the lifetime of the
//
// You can use gin in two modes: either gin manages V8, or the gin-embedder
// manages gin. If gin manages V8, use the IsolateHolder constructor without
// parameters, otherwise, the gin-embedder needs to create v8::Isolates and
// pass them to IsolateHolder.
//
// It is not possible to mix the two.
class IsolateHolder {
public:
// You can use gin in two modes: either gin manages V8, or the gin-embedder
// manages gin. If gin manages V8, use the IsolateHolder constructor without
// parameters, otherwise, the gin-embedder needs to create v8::Isolates and
// pass them to IsolateHolder.
//
// It is not possible to mix the two.
IsolateHolder();
explicit IsolateHolder(v8::Isolate* isolate);

Expand Down
9 changes: 9 additions & 0 deletions gin/runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace gin {
class Runner;
class TryCatch;

// Subclass RunnerDelegate to customize the behavior of |Runner|. Typical
// embedders will want to subclass one of the specialized RunnerDelegates,
// such as ModuleRunnerDelegate.
class RunnerDelegate {
public:
RunnerDelegate();
Expand All @@ -28,11 +31,15 @@ class RunnerDelegate {
virtual void UnhandledException(Runner* runner, TryCatch& try_catch);
};

// Runner lets you run code in a v8::Context. Upon construction, Runner will
// create a v8::Context. Upon destruction, Runner will dispose the context.
class Runner : public ContextHolder {
public:
Runner(RunnerDelegate* delegate, v8::Isolate* isolate);
~Runner();

// Before running script in this context, you'll need to enter the runner's
// context by creating an instance of Runner::Scope on the stack.
void Run(const std::string& source, const std::string& resource_name);
void Run(v8::Handle<v8::Script> script);

Expand All @@ -45,6 +52,8 @@ class Runner : public ContextHolder {
return context()->Global();
}

// Useful for running script in this context asynchronously. Rather than
// holding a raw pointer to the runner, consider holding a WeakPtr.
base::WeakPtr<Runner> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
Expand Down
5 changes: 5 additions & 0 deletions gin/test/file_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

namespace gin {

// FileRunnerDelegate is a simple RunnerDelegate that's useful for running
// tests. The FileRunnerDelegate provides built-in modules for "console" and
// "gtest" that are useful when writing unit tests.
//
// TODO(abarth): Rename FileRunnerDelegate to TestRunnerDelegate.
class FileRunnerDelegate : public ModuleRunnerDelegate {
public:
FileRunnerDelegate();
Expand Down
3 changes: 3 additions & 0 deletions gin/test/gtest.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

namespace gin {

// This module provides bindings to gtest. Most tests should use an idiomatic
// JavaScript testing API, but this module is available for tests that need a
// low-level integration with gtest.
class GTest {
public:
static const char kModuleName[];
Expand Down
3 changes: 2 additions & 1 deletion gin/test/v8_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ namespace gin {

class IsolateHolder;

// A base class for tests that use v8.
// V8Test is a simple harness for testing interactions with V8. V8Test doesn't
// have any dependencies on Gin's module system.
class V8Test : public testing::Test {
public:
V8Test();
Expand Down
7 changes: 4 additions & 3 deletions gin/try_catch.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GIN_EXCEPTION_H_
#define GIN_EXCEPTION_H_
#ifndef GIN_TRY_CATCH_H_
#define GIN_TRY_CATCH_H_

#include <string>

Expand All @@ -12,6 +12,7 @@

namespace gin {

// TryCatch is a convenient wrapper around v8::TryCatch.
class TryCatch {
public:
TryCatch();
Expand All @@ -28,4 +29,4 @@ class TryCatch {

} // namespace gin

#endif // GIN_EXCEPTION_H_
#endif // GIN_TRY_CATCH_H_
37 changes: 37 additions & 0 deletions gin/wrappable.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,44 @@

namespace gin {

// Wrappable is an abstract base class for C++ objects that have cooresponding
// v8 wrapper objects. Wrappable are RefCounted, which means they can be
// retained either by V8's garbage collector or by a scoped_refptr.
//
// WARNING: If you retain a Wrappable object with a scoped_refptr, it's possible
// that the v8 wrapper can "fall off" if the wrapper object is not
// referenced elsewhere in the V8 heap. Although Wrappable opens a
// handle to the wrapper object, we make that handle as weak, which
// means V8 is free to reclaim the wrapper. (We can't make the handle
// strong without risking introducing a memory leak if an object that
// holds a scoped_refptr is reachable from the wrapper.)
//
class Wrappable : public base::RefCounted<Wrappable> {
public:
// Subclasses must return the WrapperInfo object associated with the
// v8::ObjectTemplate for their subclass. When creating a v8 wrapper for
// this object, we'll look up the appropriate v8::ObjectTemplate in the
// PerIsolateData using this WrapperInfo pointer.
virtual WrapperInfo* GetWrapperInfo() = 0;

// Subclasses much also contain a static member variable named |kWrapperInfo|
// of type WrapperInfo:
//
// static WrapperInfo kWrapperInfo;
//
// If |obj| is a concrete instance of the subclass, then obj->GetWrapperInfo()
// must return &kWrapperInfo.
//
// We use both the dynamic |GetWrapperInfo| function and the static
// |kWrapperInfo| member variable during wrapping and the unwrapping. During
// wrapping, we use GetWrapperInfo() to make sure we use the correct
// v8::ObjectTemplate for the object regardless of the declared C++ type.
// During unwrapping, we use the static member variable to prevent type errors
// during the downcast from Wrappable to the subclass.

// Retrieve (or create) the v8 wrapper object cooresponding to this object.
// To customize the wrapper created for a subclass, override GetWrapperInfo()
// instead of overriding this function.
v8::Handle<v8::Object> GetWrapper(v8::Isolate* isolate);

protected:
Expand Down Expand Up @@ -52,6 +86,9 @@ struct WrappableConverter {
if (!Converter<Wrappable*>::FromV8(val, &wrappable)
|| wrappable->GetWrapperInfo() != &T::kWrapperInfo)
return false;
// Currently we require that you unwrap to the exact runtime type of the
// wrapped object.
// TODO(abarth): Support unwrapping to a base class.
*out = static_cast<T*>(wrappable);
return true;
}
Expand Down

0 comments on commit 60531d5

Please sign in to comment.