зеркало из https://github.com/electron/electron.git
Merge pull request #13379 from electron/native-mate-in-electron
Merge native_mate into electron/electron
This commit is contained in:
Коммит
a96fc9170c
|
@ -7,9 +7,6 @@
|
|||
[submodule "vendor/breakpad"]
|
||||
path = vendor/breakpad
|
||||
url = https://github.com/electron/chromium-breakpad.git
|
||||
[submodule "vendor/native_mate"]
|
||||
path = vendor/native_mate
|
||||
url = https://github.com/electron/native-mate.git
|
||||
[submodule "vendor/crashpad"]
|
||||
path = vendor/crashpad
|
||||
url = https://github.com/electron/crashpad.git
|
||||
|
|
|
@ -129,7 +129,7 @@ These individual tutorials expand on topics discussed in the guide above.
|
|||
* [Menu](api/menu.md)
|
||||
* [MenuItem](api/menu-item.md)
|
||||
* [net](api/net.md)
|
||||
* [netLog](api/netLog.md)
|
||||
* [netLog](api/net-log.md)
|
||||
* [powerMonitor](api/power-monitor.md)
|
||||
* [powerSaveBlocker](api/power-save-blocker.md)
|
||||
* [protocol](api/protocol.md)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# API Contract
|
||||
|
||||
Breaking changes will be documented here, and deprecation warnings added to JS code where possible, at least [one major version](electron-versioning.md#semver) before the change is made.
|
||||
Breaking changes will be documented here, and deprecation warnings added to JS code where possible, at least [one major version](../tutorial/electron-versioning.md#semver) before the change is made.
|
||||
|
||||
# `FIXME` comments
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
'includes': [
|
||||
'features.gypi',
|
||||
'filenames.gypi',
|
||||
'vendor/native_mate/native_mate_files.gypi',
|
||||
'native_mate/native_mate_files.gypi',
|
||||
],
|
||||
'target_defaults': {
|
||||
'defines': [
|
||||
|
@ -300,7 +300,7 @@
|
|||
'include_dirs': [
|
||||
'.',
|
||||
'chromium_src',
|
||||
'vendor/native_mate',
|
||||
'native_mate',
|
||||
# Include atom_natives.h.
|
||||
'<(SHARED_INTERMEDIATE_DIR)',
|
||||
# Include directories for uv and node.
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,56 @@
|
|||
> A fork of Chromium's [gin library][chromium-gin-lib] that makes it easier to
|
||||
> marshal types between C++ and JavaScript.
|
||||
|
||||
# Overview
|
||||
|
||||
`native-mate` was forked from `gin` so that it could be used in
|
||||
[Electron][electron] without conflicting with Node's Environment. It has also
|
||||
been extended to allow Electron to create classes in JavaScript.
|
||||
|
||||
With the help of Chromium's `base` library, `native-mate` makes writing JS
|
||||
bindings very easy, and most of the intricate details of converting V8 types
|
||||
to C++ types and back are taken care of auto-magically. In most cases there's
|
||||
no need to use the raw V8 API to implement an API binding.
|
||||
|
||||
For example, here's an API binding that doesn't use `native-mate`:
|
||||
|
||||
```c++
|
||||
// static
|
||||
void Shell::OpenItem(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
base::FilePath file_path;
|
||||
if (!FromV8Arguments(args, &file_path))
|
||||
return node::ThrowTypeError("Bad argument");
|
||||
|
||||
platform_util::OpenItem(file_path);
|
||||
}
|
||||
|
||||
// static
|
||||
void Shell::Initialize(v8::Handle<v8::Object> target) {
|
||||
NODE_SET_METHOD(target, "openItem", OpenItem);
|
||||
}
|
||||
```
|
||||
|
||||
And here's the same API binding using `native-mate`:
|
||||
|
||||
```c++
|
||||
void Initialize(v8::Handle<v8::Object> exports) {
|
||||
mate::Dictionary dict(v8::Isolate::GetCurrent(), exports);
|
||||
dict.SetMethod("openItem", &platform_util::OpenItem);
|
||||
}
|
||||
```
|
||||
|
||||
# Code Structure
|
||||
|
||||
* `converter.h` - Templatized JS<->C++ conversion routines for many common C++
|
||||
types. You can define your own by specializing `Converter`.
|
||||
* `function_template.h` - Create JavaScript functions that dispatch to any C++
|
||||
function, member function pointer, or `base::Callback`.
|
||||
* `object_template_builder.h` - A handy utility for creation of `v8::ObjectTemplate`.
|
||||
* `wrappable.h` - Base class for C++ classes that want to be owned by the V8 GC.
|
||||
Wrappable objects are automatically deleted when GC discovers that nothing in
|
||||
the V8 heap refers to them. This is also an easy way to expose C++ objects to
|
||||
JavaScript.
|
||||
|
||||
|
||||
[chromium-gin-lib]: https://code.google.com/p/chromium/codesearch#chromium/src/gin/README.md&sq=package:chromium
|
||||
[electron]: http://electron.atom.io/
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#include "native_mate/arguments.h"
|
||||
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "native_mate/converter.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string V8TypeAsString(v8::Isolate* isolate, v8::Local<v8::Value> value) {
|
||||
if (value.IsEmpty())
|
||||
return "<empty handle>";
|
||||
v8::MaybeLocal<v8::String> details =
|
||||
value->ToDetailString(isolate->GetCurrentContext());
|
||||
std::string result;
|
||||
if (!details.IsEmpty())
|
||||
ConvertFromV8(isolate, details.ToLocalChecked(), &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Arguments::Arguments()
|
||||
: isolate_(NULL),
|
||||
info_(NULL),
|
||||
next_(0),
|
||||
insufficient_arguments_(false) {
|
||||
}
|
||||
|
||||
Arguments::Arguments(const v8::FunctionCallbackInfo<v8::Value>& info)
|
||||
: isolate_(info.GetIsolate()),
|
||||
info_(&info),
|
||||
next_(0),
|
||||
insufficient_arguments_(false) {
|
||||
}
|
||||
|
||||
Arguments::~Arguments() {
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Arguments::PeekNext() const {
|
||||
if (next_ >= info_->Length())
|
||||
return v8::Local<v8::Value>();
|
||||
return (*info_)[next_];
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Arguments::ThrowError() const {
|
||||
if (insufficient_arguments_)
|
||||
return ThrowTypeError("Insufficient number of arguments.");
|
||||
|
||||
return ThrowTypeError(base::StringPrintf(
|
||||
"Error processing argument at index %d, conversion failure from %s",
|
||||
next_, V8TypeAsString(isolate_, (*info_)[next_]).c_str()));
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Arguments::ThrowError(const std::string& message) const {
|
||||
isolate_->ThrowException(v8::Exception::Error(
|
||||
StringToV8(isolate_, message)));
|
||||
return v8::Undefined(isolate_);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Arguments::ThrowTypeError(
|
||||
const std::string& message) const {
|
||||
isolate_->ThrowException(v8::Exception::TypeError(
|
||||
StringToV8(isolate_, message)));
|
||||
return v8::Undefined(isolate_);
|
||||
}
|
||||
|
||||
} // namespace mate
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#ifndef NATIVE_MATE_ARGUMENTS_H_
|
||||
#define NATIVE_MATE_ARGUMENTS_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "native_mate/converter.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
// 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:
|
||||
Arguments();
|
||||
explicit Arguments(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
~Arguments();
|
||||
|
||||
v8::Local<v8::Object> GetHolder() const {
|
||||
return info_->Holder();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GetHolder(T* out) {
|
||||
return ConvertFromV8(isolate_, info_->Holder(), out);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GetData(T* out) {
|
||||
return ConvertFromV8(isolate_, info_->Data(), out);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GetNext(T* out) {
|
||||
if (next_ >= info_->Length()) {
|
||||
insufficient_arguments_ = true;
|
||||
return false;
|
||||
}
|
||||
v8::Local<v8::Value> val = (*info_)[next_];
|
||||
bool success = ConvertFromV8(isolate_, val, out);
|
||||
if (success)
|
||||
next_++;
|
||||
return success;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GetRemaining(std::vector<T>* out) {
|
||||
if (next_ >= info_->Length()) {
|
||||
insufficient_arguments_ = true;
|
||||
return false;
|
||||
}
|
||||
int remaining = info_->Length() - next_;
|
||||
out->resize(remaining);
|
||||
for (int i = 0; i < remaining; ++i) {
|
||||
v8::Local<v8::Value> val = (*info_)[next_++];
|
||||
if (!ConvertFromV8(isolate_, val, &out->at(i)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> GetThis() {
|
||||
return info_->This();
|
||||
}
|
||||
|
||||
bool IsConstructCall() const {
|
||||
return info_->IsConstructCall();
|
||||
}
|
||||
|
||||
int Length() const {
|
||||
return info_->Length();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Return(T val) {
|
||||
info_->GetReturnValue().Set(ConvertToV8(isolate_, val));
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> PeekNext() const;
|
||||
|
||||
v8::Local<v8::Value> ThrowError() const;
|
||||
v8::Local<v8::Value> ThrowError(const std::string& message) const;
|
||||
v8::Local<v8::Value> ThrowTypeError(const std::string& message) const;
|
||||
|
||||
v8::Isolate* isolate() const { return isolate_; }
|
||||
|
||||
private:
|
||||
v8::Isolate* isolate_;
|
||||
const v8::FunctionCallbackInfo<v8::Value>* info_;
|
||||
int next_;
|
||||
bool insufficient_arguments_;
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_ARGUMENTS_H_
|
|
@ -0,0 +1,148 @@
|
|||
// This file was GENERATED by command:
|
||||
// pump.py constructor.h.pump
|
||||
// DO NOT EDIT BY HAND!!!
|
||||
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#ifndef NATIVE_MATE_WRAPPABLE_CLASS_H_
|
||||
#define NATIVE_MATE_WRAPPABLE_CLASS_H_
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "native_mate/function_template.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace internal {
|
||||
|
||||
// This set of templates invokes a base::Callback by converting the Arguments
|
||||
// into native types. It relies on the function_template.h to provide helper
|
||||
// templates.
|
||||
inline WrappableBase* InvokeFactory(
|
||||
Arguments* args,
|
||||
const base::Callback<WrappableBase*()>& callback) {
|
||||
return callback.Run();
|
||||
};
|
||||
|
||||
template<typename P1>
|
||||
inline WrappableBase* InvokeFactory(
|
||||
Arguments* args,
|
||||
const base::Callback<WrappableBase*(P1)>& callback) {
|
||||
typename CallbackParamTraits<P1>::LocalType a1;
|
||||
if (!GetNextArgument(args, 0, true, &a1))
|
||||
return nullptr;
|
||||
return callback.Run(a1);
|
||||
};
|
||||
|
||||
template<typename P1, typename P2>
|
||||
inline WrappableBase* InvokeFactory(
|
||||
Arguments* args,
|
||||
const base::Callback<WrappableBase*(P1, P2)>& callback) {
|
||||
typename CallbackParamTraits<P1>::LocalType a1;
|
||||
typename CallbackParamTraits<P2>::LocalType a2;
|
||||
if (!GetNextArgument(args, 0, true, &a1) ||
|
||||
!GetNextArgument(args, 0, false, &a2))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2);
|
||||
};
|
||||
|
||||
template<typename P1, typename P2, typename P3>
|
||||
inline WrappableBase* InvokeFactory(
|
||||
Arguments* args,
|
||||
const base::Callback<WrappableBase*(P1, P2, P3)>& callback) {
|
||||
typename CallbackParamTraits<P1>::LocalType a1;
|
||||
typename CallbackParamTraits<P2>::LocalType a2;
|
||||
typename CallbackParamTraits<P3>::LocalType a3;
|
||||
if (!GetNextArgument(args, 0, true, &a1) ||
|
||||
!GetNextArgument(args, 0, false, &a2) ||
|
||||
!GetNextArgument(args, 0, false, &a3))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2, a3);
|
||||
};
|
||||
|
||||
template<typename P1, typename P2, typename P3, typename P4>
|
||||
inline WrappableBase* InvokeFactory(
|
||||
Arguments* args,
|
||||
const base::Callback<WrappableBase*(P1, P2, P3, P4)>& callback) {
|
||||
typename CallbackParamTraits<P1>::LocalType a1;
|
||||
typename CallbackParamTraits<P2>::LocalType a2;
|
||||
typename CallbackParamTraits<P3>::LocalType a3;
|
||||
typename CallbackParamTraits<P4>::LocalType a4;
|
||||
if (!GetNextArgument(args, 0, true, &a1) ||
|
||||
!GetNextArgument(args, 0, false, &a2) ||
|
||||
!GetNextArgument(args, 0, false, &a3) ||
|
||||
!GetNextArgument(args, 0, false, &a4))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2, a3, a4);
|
||||
};
|
||||
|
||||
template<typename P1, typename P2, typename P3, typename P4, typename P5>
|
||||
inline WrappableBase* InvokeFactory(
|
||||
Arguments* args,
|
||||
const base::Callback<WrappableBase*(P1, P2, P3, P4, P5)>& callback) {
|
||||
typename CallbackParamTraits<P1>::LocalType a1;
|
||||
typename CallbackParamTraits<P2>::LocalType a2;
|
||||
typename CallbackParamTraits<P3>::LocalType a3;
|
||||
typename CallbackParamTraits<P4>::LocalType a4;
|
||||
typename CallbackParamTraits<P5>::LocalType a5;
|
||||
if (!GetNextArgument(args, 0, true, &a1) ||
|
||||
!GetNextArgument(args, 0, false, &a2) ||
|
||||
!GetNextArgument(args, 0, false, &a3) ||
|
||||
!GetNextArgument(args, 0, false, &a4) ||
|
||||
!GetNextArgument(args, 0, false, &a5))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2, a3, a4, a5);
|
||||
};
|
||||
|
||||
template<typename P1, typename P2, typename P3, typename P4, typename P5,
|
||||
typename P6>
|
||||
inline WrappableBase* InvokeFactory(
|
||||
Arguments* args,
|
||||
const base::Callback<WrappableBase*(P1, P2, P3, P4, P5, P6)>& callback) {
|
||||
typename CallbackParamTraits<P1>::LocalType a1;
|
||||
typename CallbackParamTraits<P2>::LocalType a2;
|
||||
typename CallbackParamTraits<P3>::LocalType a3;
|
||||
typename CallbackParamTraits<P4>::LocalType a4;
|
||||
typename CallbackParamTraits<P5>::LocalType a5;
|
||||
typename CallbackParamTraits<P6>::LocalType a6;
|
||||
if (!GetNextArgument(args, 0, true, &a1) ||
|
||||
!GetNextArgument(args, 0, false, &a2) ||
|
||||
!GetNextArgument(args, 0, false, &a3) ||
|
||||
!GetNextArgument(args, 0, false, &a4) ||
|
||||
!GetNextArgument(args, 0, false, &a5) ||
|
||||
!GetNextArgument(args, 0, false, &a6))
|
||||
return nullptr;
|
||||
return callback.Run(a1, a2, a3, a4, a5, a6);
|
||||
};
|
||||
|
||||
template<typename Sig>
|
||||
void InvokeNew(const base::Callback<Sig>& factory,
|
||||
v8::Isolate* isolate, Arguments* args) {
|
||||
if (!args->IsConstructCall()) {
|
||||
args->ThrowError("Requires constructor call");
|
||||
return;
|
||||
}
|
||||
|
||||
WrappableBase* object;
|
||||
{
|
||||
// Don't continue if the constructor throws an exception.
|
||||
v8::TryCatch try_catch(isolate);
|
||||
object = internal::InvokeFactory(args, factory);
|
||||
if (try_catch.HasCaught()) {
|
||||
try_catch.ReThrow();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!object)
|
||||
args->ThrowError();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_WRAPPABLE_CLASS_H_
|
|
@ -0,0 +1,247 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#include "native_mate/converter.h"
|
||||
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
using v8::Array;
|
||||
using v8::Boolean;
|
||||
using v8::External;
|
||||
using v8::Function;
|
||||
using v8::Integer;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::Number;
|
||||
using v8::Object;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
namespace mate {
|
||||
|
||||
Local<Value> Converter<bool>::ToV8(Isolate* isolate, bool val) {
|
||||
return v8::Boolean::New(isolate, val);
|
||||
}
|
||||
|
||||
bool Converter<bool>::FromV8(Isolate* isolate, Local<Value> val, bool* out) {
|
||||
if (!val->IsBoolean())
|
||||
return false;
|
||||
*out = val->BooleanValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(OS_LINUX) && !defined(OS_FREEBSD)
|
||||
Local<Value> Converter<unsigned long>::ToV8(Isolate* isolate,
|
||||
unsigned long val) {
|
||||
return v8::Integer::New(isolate, val);
|
||||
}
|
||||
|
||||
bool Converter<unsigned long>::FromV8(Isolate* isolate, Local<Value> val,
|
||||
unsigned long* out) {
|
||||
if (!val->IsNumber())
|
||||
return false;
|
||||
*out = val->IntegerValue();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
Local<Value> Converter<int32_t>::ToV8(Isolate* isolate, int32_t val) {
|
||||
return v8::Integer::New(isolate, val);
|
||||
}
|
||||
|
||||
bool Converter<int32_t>::FromV8(Isolate* isolate, Local<Value> val,
|
||||
int32_t* out) {
|
||||
if (!val->IsInt32())
|
||||
return false;
|
||||
*out = val->Int32Value();
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<uint32_t>::ToV8(Isolate* isolate, uint32_t val) {
|
||||
return v8::Integer::NewFromUnsigned(isolate, val);
|
||||
}
|
||||
|
||||
bool Converter<uint32_t>::FromV8(Isolate* isolate, Local<Value> val,
|
||||
uint32_t* out) {
|
||||
if (!val->IsUint32())
|
||||
return false;
|
||||
*out = val->Uint32Value();
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<int64_t>::ToV8(Isolate* isolate, int64_t val) {
|
||||
return v8::Number::New(isolate, static_cast<double>(val));
|
||||
}
|
||||
|
||||
bool Converter<int64_t>::FromV8(Isolate* isolate, Local<Value> val,
|
||||
int64_t* out) {
|
||||
if (!val->IsNumber())
|
||||
return false;
|
||||
// Even though IntegerValue returns int64_t, JavaScript cannot represent
|
||||
// the full precision of int64_t, which means some rounding might occur.
|
||||
*out = val->IntegerValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<uint64_t>::ToV8(Isolate* isolate, uint64_t val) {
|
||||
return v8::Number::New(isolate, static_cast<double>(val));
|
||||
}
|
||||
|
||||
bool Converter<uint64_t>::FromV8(Isolate* isolate, Local<Value> val,
|
||||
uint64_t* out) {
|
||||
if (!val->IsNumber())
|
||||
return false;
|
||||
*out = static_cast<uint64_t>(val->IntegerValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<float>::ToV8(Isolate* isolate, float val) {
|
||||
return v8::Number::New(isolate, val);
|
||||
}
|
||||
|
||||
bool Converter<float>::FromV8(Isolate* isolate, Local<Value> val,
|
||||
float* out) {
|
||||
if (!val->IsNumber())
|
||||
return false;
|
||||
*out = static_cast<float>(val->NumberValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<double>::ToV8(Isolate* isolate, double val) {
|
||||
return v8::Number::New(isolate, val);
|
||||
}
|
||||
|
||||
bool Converter<double>::FromV8(Isolate* isolate, Local<Value> val,
|
||||
double* out) {
|
||||
if (!val->IsNumber())
|
||||
return false;
|
||||
*out = val->NumberValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<const char*>::ToV8(
|
||||
Isolate* isolate, const char* val) {
|
||||
return v8::String::NewFromUtf8(isolate, val);
|
||||
}
|
||||
|
||||
Local<Value> Converter<base::StringPiece>::ToV8(
|
||||
Isolate* isolate, const base::StringPiece& val) {
|
||||
return v8::String::NewFromUtf8(isolate,
|
||||
val.data(),
|
||||
v8::String::kNormalString,
|
||||
static_cast<uint32_t>(val.length()));
|
||||
}
|
||||
|
||||
Local<Value> Converter<std::string>::ToV8(Isolate* isolate,
|
||||
const std::string& val) {
|
||||
return Converter<base::StringPiece>::ToV8(isolate, val);
|
||||
}
|
||||
|
||||
bool Converter<std::string>::FromV8(Isolate* isolate, Local<Value> val,
|
||||
std::string* out) {
|
||||
if (!val->IsString())
|
||||
return false;
|
||||
Local<String> str = Local<String>::Cast(val);
|
||||
int length = str->Utf8Length();
|
||||
out->resize(length);
|
||||
str->WriteUtf8(&(*out)[0], length, NULL, String::NO_NULL_TERMINATION);
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<Local<Function>>::ToV8(Isolate* isolate,
|
||||
Local<Function> val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
bool Converter<Local<Function> >::FromV8(Isolate* isolate, Local<Value> val,
|
||||
Local<Function>* out) {
|
||||
if (!val->IsFunction())
|
||||
return false;
|
||||
*out = Local<Function>::Cast(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<Local<Object> >::ToV8(Isolate* isolate,
|
||||
Local<Object> val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
bool Converter<Local<Object> >::FromV8(Isolate* isolate, Local<Value> val,
|
||||
Local<Object>* out) {
|
||||
if (!val->IsObject())
|
||||
return false;
|
||||
*out = Local<Object>::Cast(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<Local<String> >::ToV8(Isolate* isolate,
|
||||
Local<String> val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
bool Converter<Local<String> >::FromV8(Isolate* isolate, Local<Value> val,
|
||||
Local<String>* out) {
|
||||
if (!val->IsString())
|
||||
return false;
|
||||
*out = Local<String>::Cast(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<Local<External> >::ToV8(Isolate* isolate,
|
||||
Local<External> val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
bool Converter<Local<External> >::FromV8(Isolate* isolate,
|
||||
v8::Local<Value> val,
|
||||
Local<External>* out) {
|
||||
if (!val->IsExternal())
|
||||
return false;
|
||||
*out = Local<External>::Cast(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<Local<Array> >::ToV8(Isolate* isolate,
|
||||
Local<Array> val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
bool Converter<Local<Array> >::FromV8(Isolate* isolate,
|
||||
v8::Local<Value> val,
|
||||
Local<Array>* out) {
|
||||
if (!val->IsArray())
|
||||
return false;
|
||||
*out = Local<Array>::Cast(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
Local<Value> Converter<Local<Value> >::ToV8(Isolate* isolate,
|
||||
Local<Value> val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
bool Converter<Local<Value> >::FromV8(Isolate* isolate, Local<Value> val,
|
||||
Local<Value>* out) {
|
||||
*out = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
|
||||
const base::StringPiece& val) {
|
||||
return v8::String::NewFromUtf8(isolate,
|
||||
val.data(),
|
||||
v8::String::kInternalizedString,
|
||||
static_cast<uint32_t>(val.length()));
|
||||
}
|
||||
|
||||
std::string V8ToString(v8::Local<v8::Value> value) {
|
||||
if (value.IsEmpty())
|
||||
return std::string();
|
||||
std::string result;
|
||||
if (!ConvertFromV8(NULL, value, &result))
|
||||
return std::string();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace mate
|
|
@ -0,0 +1,364 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#ifndef NATIVE_MATE_CONVERTER_H_
|
||||
#define NATIVE_MATE_CONVERTER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<typename KeyType>
|
||||
bool SetProperty(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> object,
|
||||
KeyType key,
|
||||
v8::Local<v8::Value> value) {
|
||||
auto maybe = object->Set(isolate->GetCurrentContext(), key, value);
|
||||
return !maybe.IsNothing() && maybe.FromJust();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct ToV8ReturnsMaybe {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename T, typename Enable = void>
|
||||
struct Converter {};
|
||||
|
||||
template<>
|
||||
struct Converter<void*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, void* val) {
|
||||
return v8::Undefined(isolate);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<std::nullptr_t> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, std::nullptr_t val) {
|
||||
return v8::Null(isolate);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<bool> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
bool val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
bool* out);
|
||||
};
|
||||
|
||||
#if !defined(OS_LINUX) && !defined(OS_FREEBSD)
|
||||
template<>
|
||||
struct Converter<unsigned long> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
unsigned long val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
unsigned long* out);
|
||||
};
|
||||
#endif
|
||||
|
||||
template<>
|
||||
struct Converter<int32_t> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
int32_t val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
int32_t* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<uint32_t> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
uint32_t val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
uint32_t* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<int64_t> {
|
||||
// Warning: JavaScript cannot represent 64 integers precisely.
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
int64_t val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
int64_t* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<uint64_t> {
|
||||
// Warning: JavaScript cannot represent 64 integers precisely.
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
uint64_t val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
uint64_t* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<float> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
float val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
float* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<double> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
double val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
double* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<const char*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const char* val);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<base::StringPiece> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const base::StringPiece& val);
|
||||
// No conversion out is possible because StringPiece does not contain storage.
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<std::string> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const std::string& val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
std::string* out);
|
||||
};
|
||||
|
||||
v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
|
||||
const base::StringPiece& input);
|
||||
|
||||
std::string V8ToString(v8::Local<v8::Value> value);
|
||||
|
||||
template<>
|
||||
struct Converter<v8::Local<v8::Function> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Function> val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
v8::Local<v8::Function>* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<v8::Local<v8::Object> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
v8::Local<v8::Object>* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<v8::Local<v8::String> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::String> val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
v8::Local<v8::String>* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<v8::Local<v8::External> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::External> val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
v8::Local<v8::External>* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<v8::Local<v8::Array> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Array> val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
v8::Local<v8::Array>* out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<v8::Local<v8::Value> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
v8::Local<v8::Value>* out);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Converter<std::vector<T> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const std::vector<T>& val) {
|
||||
v8::Local<v8::Array> result(
|
||||
v8::Array::New(isolate, static_cast<int>(val.size())));
|
||||
for (size_t i = 0; i < val.size(); ++i) {
|
||||
result->Set(static_cast<int>(i), Converter<T>::ToV8(isolate, val[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
std::vector<T>* out) {
|
||||
if (!val->IsArray())
|
||||
return false;
|
||||
|
||||
std::vector<T> result;
|
||||
v8::Local<v8::Array> array(v8::Local<v8::Array>::Cast(val));
|
||||
uint32_t length = array->Length();
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
T item;
|
||||
if (!Converter<T>::FromV8(isolate, array->Get(i), &item))
|
||||
return false;
|
||||
result.push_back(item);
|
||||
}
|
||||
|
||||
out->swap(result);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Converter<std::set<T> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const std::set<T>& val) {
|
||||
v8::Local<v8::Array> result(
|
||||
v8::Array::New(isolate, static_cast<int>(val.size())));
|
||||
typename std::set<T>::const_iterator it;
|
||||
int i;
|
||||
for (i = 0, it = val.begin(); it != val.end(); ++it, ++i)
|
||||
result->Set(i, Converter<T>::ToV8(isolate, *it));
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
std::set<T>* out) {
|
||||
if (!val->IsArray())
|
||||
return false;
|
||||
|
||||
std::set<T> result;
|
||||
v8::Local<v8::Array> array(v8::Local<v8::Array>::Cast(val));
|
||||
uint32_t length = array->Length();
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
T item;
|
||||
if (!Converter<T>::FromV8(isolate, array->Get(i), &item))
|
||||
return false;
|
||||
result.insert(item);
|
||||
}
|
||||
|
||||
out->swap(result);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Converter<std::map<std::string, T> > {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
std::map<std::string, T> * out) {
|
||||
if (!val->IsObject())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::Object> dict = val->ToObject();
|
||||
v8::Local<v8::Array> keys = dict->GetOwnPropertyNames();
|
||||
for (uint32_t i = 0; i < keys->Length(); ++i) {
|
||||
v8::Local<v8::Value> key = keys->Get(i);
|
||||
T value;
|
||||
if (Converter<T>::FromV8(isolate, dict->Get(key), &value))
|
||||
(*out)[V8ToString(key)] = std::move(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const std::map<std::string, T>& val) {
|
||||
v8::Local<v8::Object> result = v8::Object::New(isolate);
|
||||
for (auto i = val.begin(); i != val.end(); i++) {
|
||||
result->Set(Converter<T>::ToV8(isolate, i->first),
|
||||
Converter<T>::ToV8(isolate, i->second));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Convenience functions that deduce T.
|
||||
template<typename T>
|
||||
v8::Local<v8::Value> ConvertToV8(v8::Isolate* isolate, const T& input) {
|
||||
return Converter<T>::ToV8(isolate, input);
|
||||
}
|
||||
|
||||
inline v8::Local<v8::Value> ConvertToV8(v8::Isolate* isolate,
|
||||
const char* input) {
|
||||
return Converter<const char*>::ToV8(isolate, input);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
v8::MaybeLocal<v8::Value> ConvertToV8(v8::Local<v8::Context> context,
|
||||
const T& input) {
|
||||
return Converter<T>::ToV8(context, input);
|
||||
}
|
||||
|
||||
template<typename T, bool = ToV8ReturnsMaybe<T>::value> struct ToV8Traits;
|
||||
|
||||
template <typename T>
|
||||
struct ToV8Traits<T, true> {
|
||||
static bool TryConvertToV8(v8::Isolate* isolate,
|
||||
const T& input,
|
||||
v8::Local<v8::Value>* output) {
|
||||
auto maybe = ConvertToV8(isolate->GetCurrentContext(), input);
|
||||
if (maybe.IsEmpty())
|
||||
return false;
|
||||
*output = maybe.ToLocalChecked();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ToV8Traits<T, false> {
|
||||
static bool TryConvertToV8(v8::Isolate* isolate,
|
||||
const T& input,
|
||||
v8::Local<v8::Value>* output) {
|
||||
*output = ConvertToV8(isolate, input);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool TryConvertToV8(v8::Isolate* isolate,
|
||||
const T& input,
|
||||
v8::Local<v8::Value>* output) {
|
||||
return ToV8Traits<T>::TryConvertToV8(isolate, input, output);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ConvertFromV8(v8::Isolate* isolate, v8::Local<v8::Value> input,
|
||||
T* result) {
|
||||
return Converter<T>::FromV8(isolate, input, result);
|
||||
}
|
||||
|
||||
inline v8::Local<v8::String> StringToV8(
|
||||
v8::Isolate* isolate,
|
||||
const base::StringPiece& input) {
|
||||
return ConvertToV8(isolate, input).As<v8::String>();
|
||||
}
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_CONVERTER_H_
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#include "native_mate/dictionary.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
Dictionary::Dictionary()
|
||||
: isolate_(NULL) {
|
||||
}
|
||||
|
||||
Dictionary::Dictionary(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> object)
|
||||
: isolate_(isolate),
|
||||
object_(object) {
|
||||
}
|
||||
|
||||
Dictionary::~Dictionary() {
|
||||
}
|
||||
|
||||
Dictionary Dictionary::CreateEmpty(v8::Isolate* isolate) {
|
||||
return Dictionary(isolate, v8::Object::New(isolate));
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> Dictionary::GetHandle() const {
|
||||
return object_;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Converter<Dictionary>::ToV8(v8::Isolate* isolate,
|
||||
Dictionary val) {
|
||||
return val.GetHandle();
|
||||
}
|
||||
|
||||
bool Converter<Dictionary>::FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
Dictionary* out) {
|
||||
if (!val->IsObject() || val->IsFunction())
|
||||
return false;
|
||||
*out = Dictionary(isolate, v8::Local<v8::Object>::Cast(val));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mate
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#ifndef NATIVE_MATE_DICTIONARY_H_
|
||||
#define NATIVE_MATE_DICTIONARY_H_
|
||||
|
||||
#include "native_mate/converter.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Returns true if |maybe| is both a value, and that value is true.
|
||||
inline bool IsTrue(v8::Maybe<bool> maybe) {
|
||||
return maybe.IsJust() && maybe.FromJust();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// 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:
|
||||
Dictionary();
|
||||
Dictionary(v8::Isolate* isolate, v8::Local<v8::Object> object);
|
||||
virtual ~Dictionary();
|
||||
|
||||
static Dictionary CreateEmpty(v8::Isolate* isolate);
|
||||
|
||||
template<typename T>
|
||||
bool Get(const base::StringPiece& key, T* out) const {
|
||||
// Check for existence before getting, otherwise this method will always
|
||||
// returns true when T == v8::Local<v8::Value>.
|
||||
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
|
||||
v8::Local<v8::String> v8_key = StringToV8(isolate_, key);
|
||||
if (!internal::IsTrue(GetHandle()->Has(context, v8_key)))
|
||||
return false;
|
||||
|
||||
v8::Local<v8::Value> val;
|
||||
if (!GetHandle()->Get(context, v8_key).ToLocal(&val))
|
||||
return false;
|
||||
return ConvertFromV8(isolate_, val, out);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GetHidden(const base::StringPiece& key, T* out) const {
|
||||
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
|
||||
v8::Local<v8::Private> privateKey =
|
||||
v8::Private::ForApi(isolate_, StringToV8(isolate_, key));
|
||||
v8::Local<v8::Value> value;
|
||||
v8::Maybe<bool> result =
|
||||
GetHandle()->HasPrivate(context, privateKey);
|
||||
if (internal::IsTrue(result) &&
|
||||
GetHandle()->GetPrivate(context, privateKey).ToLocal(&value))
|
||||
return ConvertFromV8(isolate_, value, out);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Set(const base::StringPiece& key, const T& val) {
|
||||
v8::Local<v8::Value> v8_value;
|
||||
if (!TryConvertToV8(isolate_, val, &v8_value))
|
||||
return false;
|
||||
v8::Maybe<bool> result =
|
||||
GetHandle()->Set(isolate_->GetCurrentContext(),
|
||||
StringToV8(isolate_, key),
|
||||
v8_value);
|
||||
return !result.IsNothing() && result.FromJust();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool SetHidden(const base::StringPiece& key, T val) {
|
||||
v8::Local<v8::Value> v8_value;
|
||||
if (!TryConvertToV8(isolate_, val, &v8_value))
|
||||
return false;
|
||||
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
|
||||
v8::Local<v8::Private> privateKey =
|
||||
v8::Private::ForApi(isolate_, StringToV8(isolate_, key));
|
||||
v8::Maybe<bool> result =
|
||||
GetHandle()->SetPrivate(context, privateKey, v8_value);
|
||||
return !result.IsNothing() && result.FromJust();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool SetReadOnly(const base::StringPiece& key, T val) {
|
||||
v8::Local<v8::Value> v8_value;
|
||||
if (!TryConvertToV8(isolate_, val, &v8_value))
|
||||
return false;
|
||||
v8::Maybe<bool> result =
|
||||
GetHandle()->DefineOwnProperty(isolate_->GetCurrentContext(),
|
||||
StringToV8(isolate_, key),
|
||||
v8_value,
|
||||
v8::ReadOnly);
|
||||
return !result.IsNothing() && result.FromJust();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool SetMethod(const base::StringPiece& key, const T& callback) {
|
||||
return GetHandle()->Set(
|
||||
StringToV8(isolate_, key),
|
||||
CallbackTraits<T>::CreateTemplate(isolate_, callback)->GetFunction());
|
||||
}
|
||||
|
||||
bool Delete(const base::StringPiece& key) {
|
||||
v8::Maybe<bool> result = GetHandle()->Delete(isolate_->GetCurrentContext(),
|
||||
StringToV8(isolate_, key));
|
||||
return !result.IsNothing() && result.FromJust();
|
||||
}
|
||||
|
||||
bool IsEmpty() const { return isolate() == NULL; }
|
||||
|
||||
virtual v8::Local<v8::Object> GetHandle() const;
|
||||
|
||||
v8::Isolate* isolate() const { return isolate_; }
|
||||
|
||||
protected:
|
||||
v8::Isolate* isolate_;
|
||||
|
||||
private:
|
||||
v8::Local<v8::Object> object_;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<Dictionary> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
Dictionary val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
Dictionary* out);
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_DICTIONARY_H_
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#include "native_mate/function_template.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace internal {
|
||||
|
||||
CallbackHolderBase::CallbackHolderBase(v8::Isolate* isolate)
|
||||
: v8_ref_(isolate, v8::External::New(isolate, this)) {
|
||||
v8_ref_.SetWeak(this, &CallbackHolderBase::FirstWeakCallback,
|
||||
v8::WeakCallbackType::kParameter);
|
||||
}
|
||||
|
||||
CallbackHolderBase::~CallbackHolderBase() {
|
||||
DCHECK(v8_ref_.IsEmpty());
|
||||
}
|
||||
|
||||
v8::Local<v8::External> CallbackHolderBase::GetHandle(v8::Isolate* isolate) {
|
||||
return v8::Local<v8::External>::New(isolate, v8_ref_);
|
||||
}
|
||||
|
||||
// static
|
||||
void CallbackHolderBase::FirstWeakCallback(
|
||||
const v8::WeakCallbackInfo<CallbackHolderBase>& data) {
|
||||
data.GetParameter()->v8_ref_.Reset();
|
||||
data.SetSecondPassCallback(SecondWeakCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
void CallbackHolderBase::SecondWeakCallback(
|
||||
const v8::WeakCallbackInfo<CallbackHolderBase>& data) {
|
||||
delete data.GetParameter();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace mate
|
|
@ -0,0 +1,285 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#ifndef NATIVE_MATE_FUNCTION_TEMPLATE_H_
|
||||
#define NATIVE_MATE_FUNCTION_TEMPLATE_H_
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/logging.h"
|
||||
#include "native_mate/arguments.h"
|
||||
#include "native_mate/wrappable_base.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
enum CreateFunctionTemplateFlags {
|
||||
HolderIsFirstArgument = 1 << 0,
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct Destroyable {
|
||||
static void Destroy(Arguments* args) {
|
||||
if (IsDestroyed(args))
|
||||
return;
|
||||
|
||||
v8::Local<v8::Object> holder = args->GetHolder();
|
||||
delete static_cast<WrappableBase*>(
|
||||
holder->GetAlignedPointerFromInternalField(0));
|
||||
holder->SetAlignedPointerInInternalField(0, nullptr);
|
||||
}
|
||||
static bool IsDestroyed(Arguments* args) {
|
||||
v8::Local<v8::Object> holder = args->GetHolder();
|
||||
return holder->InternalFieldCount() == 0 ||
|
||||
holder->GetAlignedPointerFromInternalField(0) == nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct CallbackParamTraits {
|
||||
typedef T LocalType;
|
||||
};
|
||||
template<typename T>
|
||||
struct CallbackParamTraits<const T&> {
|
||||
typedef T LocalType;
|
||||
};
|
||||
template<typename T>
|
||||
struct CallbackParamTraits<const T*> {
|
||||
typedef T* LocalType;
|
||||
};
|
||||
|
||||
|
||||
// CallbackHolder and CallbackHolderBase are used to pass a base::Callback from
|
||||
// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to
|
||||
// DispatchToCallback, where it is invoked.
|
||||
|
||||
// This simple base class is used so that we can share a single object template
|
||||
// among every CallbackHolder instance.
|
||||
class CallbackHolderBase {
|
||||
public:
|
||||
v8::Local<v8::External> GetHandle(v8::Isolate* isolate);
|
||||
|
||||
protected:
|
||||
explicit CallbackHolderBase(v8::Isolate* isolate);
|
||||
virtual ~CallbackHolderBase();
|
||||
|
||||
private:
|
||||
static void FirstWeakCallback(
|
||||
const v8::WeakCallbackInfo<CallbackHolderBase>& data);
|
||||
static void SecondWeakCallback(
|
||||
const v8::WeakCallbackInfo<CallbackHolderBase>& data);
|
||||
|
||||
v8::Global<v8::External> v8_ref_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CallbackHolderBase);
|
||||
};
|
||||
|
||||
template<typename Sig>
|
||||
class CallbackHolder : public CallbackHolderBase {
|
||||
public:
|
||||
CallbackHolder(v8::Isolate* isolate,
|
||||
const base::Callback<Sig>& callback,
|
||||
int flags)
|
||||
: CallbackHolderBase(isolate), callback(callback), flags(flags) {}
|
||||
base::Callback<Sig> callback;
|
||||
int flags;
|
||||
private:
|
||||
virtual ~CallbackHolder() {}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CallbackHolder);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
|
||||
T* result) {
|
||||
if (is_first && (create_flags & HolderIsFirstArgument) != 0) {
|
||||
return args->GetHolder(result);
|
||||
} else {
|
||||
return args->GetNext(result);
|
||||
}
|
||||
}
|
||||
|
||||
// For advanced use cases, we allow callers to request the unparsed Arguments
|
||||
// object and poke around in it directly.
|
||||
inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
|
||||
Arguments* result) {
|
||||
*result = *args;
|
||||
return true;
|
||||
}
|
||||
inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first,
|
||||
Arguments** result) {
|
||||
*result = args;
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's common for clients to just need the isolate, so we make that easy.
|
||||
inline bool GetNextArgument(Arguments* args, int create_flags,
|
||||
bool is_first, v8::Isolate** result) {
|
||||
*result = args->isolate();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Classes for generating and storing an argument pack of integer indices
|
||||
// (based on well-known "indices trick", see: http://goo.gl/bKKojn):
|
||||
template <size_t... indices>
|
||||
struct IndicesHolder {};
|
||||
|
||||
template <size_t requested_index, size_t... indices>
|
||||
struct IndicesGenerator {
|
||||
using type = typename IndicesGenerator<requested_index - 1,
|
||||
requested_index - 1,
|
||||
indices...>::type;
|
||||
};
|
||||
template <size_t... indices>
|
||||
struct IndicesGenerator<0, indices...> {
|
||||
using type = IndicesHolder<indices...>;
|
||||
};
|
||||
|
||||
// Class template for extracting and storing single argument for callback
|
||||
// at position |index|.
|
||||
template <size_t index, typename ArgType>
|
||||
struct ArgumentHolder {
|
||||
using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
|
||||
|
||||
ArgLocalType value;
|
||||
bool ok;
|
||||
|
||||
ArgumentHolder(Arguments* args, int create_flags)
|
||||
: ok(false) {
|
||||
if (index == 0 &&
|
||||
(create_flags & HolderIsFirstArgument) &&
|
||||
Destroyable::IsDestroyed(args)) {
|
||||
args->ThrowError("Object has been destroyed");
|
||||
return;
|
||||
}
|
||||
ok = GetNextArgument(args, create_flags, index == 0, &value);
|
||||
if (!ok) {
|
||||
// Ideally we would include the expected c++ type in the error
|
||||
// message which we can access via typeid(ArgType).name()
|
||||
// however we compile with no-rtti, which disables typeid.
|
||||
args->ThrowError();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Class template for converting arguments from JavaScript to C++ and running
|
||||
// the callback with them.
|
||||
template <typename IndicesType, typename... ArgTypes>
|
||||
class Invoker {};
|
||||
|
||||
template <size_t... indices, typename... ArgTypes>
|
||||
class Invoker<IndicesHolder<indices...>, ArgTypes...>
|
||||
: public ArgumentHolder<indices, ArgTypes>... {
|
||||
public:
|
||||
// Invoker<> inherits from ArgumentHolder<> for each argument.
|
||||
// C++ has always been strict about the class initialization order,
|
||||
// so it is guaranteed ArgumentHolders will be initialized (and thus, will
|
||||
// extract arguments from Arguments) in the right order.
|
||||
Invoker(Arguments* args, int create_flags)
|
||||
: ArgumentHolder<indices, ArgTypes>(args, create_flags)..., args_(args) {
|
||||
// GCC thinks that create_flags is going unused, even though the
|
||||
// expansion above clearly makes use of it. Per jyasskin@, casting
|
||||
// to void is the commonly accepted way to convince the compiler
|
||||
// that you're actually using a parameter/varible.
|
||||
(void)create_flags;
|
||||
}
|
||||
|
||||
bool IsOK() {
|
||||
return And(ArgumentHolder<indices, ArgTypes>::ok...);
|
||||
}
|
||||
|
||||
template <typename ReturnType>
|
||||
void DispatchToCallback(base::Callback<ReturnType(ArgTypes...)> callback) {
|
||||
v8::MicrotasksScope script_scope(
|
||||
args_->isolate(), v8::MicrotasksScope::kRunMicrotasks);
|
||||
args_->Return(callback.Run(ArgumentHolder<indices, ArgTypes>::value...));
|
||||
}
|
||||
|
||||
// In C++, you can declare the function foo(void), but you can't pass a void
|
||||
// expression to foo. As a result, we must specialize the case of Callbacks
|
||||
// that have the void return type.
|
||||
void DispatchToCallback(base::Callback<void(ArgTypes...)> callback) {
|
||||
v8::MicrotasksScope script_scope(
|
||||
args_->isolate(), v8::MicrotasksScope::kRunMicrotasks);
|
||||
callback.Run(ArgumentHolder<indices, ArgTypes>::value...);
|
||||
}
|
||||
|
||||
private:
|
||||
static bool And() { return true; }
|
||||
template <typename... T>
|
||||
static bool And(bool arg1, T... args) {
|
||||
return arg1 && And(args...);
|
||||
}
|
||||
|
||||
Arguments* args_;
|
||||
};
|
||||
|
||||
// DispatchToCallback converts all the JavaScript arguments to C++ types and
|
||||
// invokes the base::Callback.
|
||||
template <typename Sig>
|
||||
struct Dispatcher {};
|
||||
|
||||
template <typename ReturnType, typename... ArgTypes>
|
||||
struct Dispatcher<ReturnType(ArgTypes...)> {
|
||||
static void DispatchToCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
Arguments args(info);
|
||||
v8::Local<v8::External> v8_holder;
|
||||
args.GetData(&v8_holder);
|
||||
CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>(
|
||||
v8_holder->Value());
|
||||
|
||||
typedef CallbackHolder<ReturnType(ArgTypes...)> HolderT;
|
||||
HolderT* holder = static_cast<HolderT*>(holder_base);
|
||||
|
||||
using Indices = typename IndicesGenerator<sizeof...(ArgTypes)>::type;
|
||||
Invoker<Indices, ArgTypes...> invoker(&args, holder->flags);
|
||||
if (invoker.IsOK())
|
||||
invoker.DispatchToCallback(holder->callback);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
|
||||
// CreateFunctionTemplate creates a v8::FunctionTemplate that will create
|
||||
// JavaScript functions that execute a provided C++ function or base::Callback.
|
||||
// JavaScript arguments are automatically converted via gin::Converter, as is
|
||||
// the return value of the C++ function, if any.
|
||||
//
|
||||
// NOTE: V8 caches FunctionTemplates for a lifetime of a web page for its own
|
||||
// internal reasons, thus it is generally a good idea to cache the template
|
||||
// returned by this function. Otherwise, repeated method invocations from JS
|
||||
// will create substantial memory leaks. See http://crbug.com/463487.
|
||||
template<typename Sig>
|
||||
v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
|
||||
v8::Isolate* isolate, const base::Callback<Sig> callback,
|
||||
int callback_flags = 0) {
|
||||
typedef internal::CallbackHolder<Sig> HolderT;
|
||||
HolderT* holder = new HolderT(isolate, callback, callback_flags);
|
||||
|
||||
return v8::FunctionTemplate::New(
|
||||
isolate,
|
||||
&internal::Dispatcher<Sig>::DispatchToCallback,
|
||||
ConvertToV8<v8::Local<v8::External> >(isolate,
|
||||
holder->GetHandle(isolate)));
|
||||
}
|
||||
|
||||
// CreateFunctionHandler installs a CallAsFunction handler on the given
|
||||
// object template that forwards to a provided C++ function or base::Callback.
|
||||
template<typename Sig>
|
||||
void CreateFunctionHandler(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> tmpl,
|
||||
const base::Callback<Sig> callback,
|
||||
int callback_flags = 0) {
|
||||
typedef internal::CallbackHolder<Sig> HolderT;
|
||||
HolderT* holder = new HolderT(isolate, callback, callback_flags);
|
||||
tmpl->SetCallAsFunctionHandler(&internal::Dispatcher<Sig>::DispatchToCallback,
|
||||
ConvertToV8<v8::Local<v8::External> >(
|
||||
isolate, holder->GetHandle(isolate)));
|
||||
}
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_FUNCTION_TEMPLATE_H_
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#ifndef NATIVE_MATE_HANDLE_H_
|
||||
#define NATIVE_MATE_HANDLE_H_
|
||||
|
||||
#include "native_mate/converter.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
// You can use mate::Handle on the stack to retain a mate::Wrappable object.
|
||||
// Currently we don't have a mechanism for retaining a mate::Wrappable object
|
||||
// in the C++ heap because strong references from C++ to V8 can cause memory
|
||||
// leaks.
|
||||
template<typename T>
|
||||
class Handle {
|
||||
public:
|
||||
Handle() : object_(NULL) {}
|
||||
|
||||
Handle(v8::Local<v8::Object> wrapper, T* object)
|
||||
: wrapper_(wrapper),
|
||||
object_(object) {
|
||||
}
|
||||
|
||||
bool IsEmpty() const { return !object_; }
|
||||
|
||||
void Clear() {
|
||||
wrapper_.Clear();
|
||||
object_ = NULL;
|
||||
}
|
||||
|
||||
T* operator->() const { return object_; }
|
||||
v8::Local<v8::Object> ToV8() const { return wrapper_; }
|
||||
T* get() const { return object_; }
|
||||
|
||||
private:
|
||||
v8::Local<v8::Object> wrapper_;
|
||||
T* object_;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Converter<mate::Handle<T> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const mate::Handle<T>& val) {
|
||||
return val.ToV8();
|
||||
}
|
||||
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
|
||||
mate::Handle<T>* out) {
|
||||
T* object = NULL;
|
||||
if (val->IsNull() || val->IsUndefined()) {
|
||||
*out = mate::Handle<T>();
|
||||
return true;
|
||||
}
|
||||
if (!Converter<T*>::FromV8(isolate, val, &object)) {
|
||||
return false;
|
||||
}
|
||||
*out = mate::Handle<T>(val->ToObject(), object);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// This function is a convenient way to create a handle from a raw pointer
|
||||
// without having to write out the type of the object explicitly.
|
||||
template<typename T>
|
||||
mate::Handle<T> CreateHandle(v8::Isolate* isolate, T* object) {
|
||||
return mate::Handle<T>(object->GetWrapper(), object);
|
||||
}
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_HANDLE_H_
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#include "native_mate/object_template_builder.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
ObjectTemplateBuilder::ObjectTemplateBuilder(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> templ)
|
||||
: isolate_(isolate), template_(templ) {
|
||||
}
|
||||
|
||||
ObjectTemplateBuilder::~ObjectTemplateBuilder() {
|
||||
}
|
||||
|
||||
ObjectTemplateBuilder& ObjectTemplateBuilder::SetImpl(
|
||||
const base::StringPiece& name, v8::Local<v8::Data> val) {
|
||||
template_->Set(StringToSymbol(isolate_, name), val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ObjectTemplateBuilder& ObjectTemplateBuilder::SetPropertyImpl(
|
||||
const base::StringPiece& name, v8::Local<v8::FunctionTemplate> getter,
|
||||
v8::Local<v8::FunctionTemplate> setter) {
|
||||
template_->SetAccessorProperty(StringToSymbol(isolate_, name), getter,
|
||||
setter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ObjectTemplateBuilder& ObjectTemplateBuilder::MakeDestroyable() {
|
||||
SetMethod("destroy", base::Bind(internal::Destroyable::Destroy));
|
||||
SetMethod("isDestroyed", base::Bind(internal::Destroyable::IsDestroyed));
|
||||
return *this;
|
||||
}
|
||||
|
||||
v8::Local<v8::ObjectTemplate> ObjectTemplateBuilder::Build() {
|
||||
v8::Local<v8::ObjectTemplate> result = template_;
|
||||
template_.Clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace mate
|
|
@ -0,0 +1,131 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#ifndef NATIVE_MATE_OBJECT_TEMPLATE_BUILDER_H_
|
||||
#define NATIVE_MATE_OBJECT_TEMPLATE_BUILDER_H_
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "native_mate/converter.h"
|
||||
#include "native_mate/function_template.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace {
|
||||
|
||||
// Base template - used only for non-member function pointers. Other types
|
||||
// either go to one of the below specializations, or go here and fail to compile
|
||||
// because of base::Bind().
|
||||
template<typename T, typename Enable = void>
|
||||
struct CallbackTraits {
|
||||
static v8::Local<v8::FunctionTemplate> CreateTemplate(
|
||||
v8::Isolate* isolate, T callback) {
|
||||
return CreateFunctionTemplate(isolate, base::Bind(callback));
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for base::Callback.
|
||||
template<typename T>
|
||||
struct CallbackTraits<base::Callback<T> > {
|
||||
static v8::Local<v8::FunctionTemplate> CreateTemplate(
|
||||
v8::Isolate* isolate, const base::Callback<T>& callback) {
|
||||
return CreateFunctionTemplate(isolate, callback);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for member function pointers. We need to handle this case
|
||||
// specially because the first parameter for callbacks to MFP should typically
|
||||
// come from the the JavaScript "this" object the function was called on, not
|
||||
// from the first normal parameter.
|
||||
template<typename T>
|
||||
struct CallbackTraits<T, typename std::enable_if<
|
||||
std::is_member_function_pointer<T>::value>::type> {
|
||||
static v8::Local<v8::FunctionTemplate> CreateTemplate(
|
||||
v8::Isolate* isolate, T callback) {
|
||||
int flags = HolderIsFirstArgument;
|
||||
return CreateFunctionTemplate(isolate, base::Bind(callback), flags);
|
||||
}
|
||||
};
|
||||
|
||||
// This specialization allows people to construct function templates directly if
|
||||
// they need to do fancier stuff.
|
||||
template<>
|
||||
struct CallbackTraits<v8::Local<v8::FunctionTemplate> > {
|
||||
static v8::Local<v8::FunctionTemplate> CreateTemplate(
|
||||
v8::Local<v8::FunctionTemplate> templ) {
|
||||
return templ;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// ObjectTemplateBuilder provides a handy interface to creating
|
||||
// v8::ObjectTemplate instances with various sorts of properties.
|
||||
class ObjectTemplateBuilder {
|
||||
public:
|
||||
explicit ObjectTemplateBuilder(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> templ);
|
||||
~ObjectTemplateBuilder();
|
||||
|
||||
// It's against Google C++ style to return a non-const ref, but we take some
|
||||
// poetic license here in order that all calls to Set() can be via the '.'
|
||||
// operator and line up nicely.
|
||||
template<typename T>
|
||||
ObjectTemplateBuilder& SetValue(const base::StringPiece& name, T val) {
|
||||
return SetImpl(name, ConvertToV8(isolate_, val));
|
||||
}
|
||||
|
||||
// In the following methods, T and U can be function pointer, member function
|
||||
// pointer, base::Callback, or v8::FunctionTemplate. Most clients will want to
|
||||
// use one of the first two options. Also see mate::CreateFunctionTemplate()
|
||||
// for creating raw function templates.
|
||||
template<typename T>
|
||||
ObjectTemplateBuilder& SetMethod(const base::StringPiece& name,
|
||||
T callback) {
|
||||
return SetImpl(name,
|
||||
CallbackTraits<T>::CreateTemplate(isolate_, callback));
|
||||
}
|
||||
template<typename T>
|
||||
ObjectTemplateBuilder& SetProperty(const base::StringPiece& name,
|
||||
T getter) {
|
||||
return SetPropertyImpl(
|
||||
name,
|
||||
CallbackTraits<T>::CreateTemplate(isolate_, getter),
|
||||
v8::Local<v8::FunctionTemplate>());
|
||||
}
|
||||
template<typename T, typename U>
|
||||
ObjectTemplateBuilder& SetProperty(const base::StringPiece& name,
|
||||
T getter,
|
||||
U setter) {
|
||||
return SetPropertyImpl(
|
||||
name,
|
||||
CallbackTraits<T>::CreateTemplate(isolate_, getter),
|
||||
CallbackTraits<U>::CreateTemplate(isolate_, setter));
|
||||
}
|
||||
|
||||
// Add "destroy" and "isDestroyed" methods.
|
||||
ObjectTemplateBuilder& MakeDestroyable();
|
||||
|
||||
v8::Local<v8::ObjectTemplate> Build();
|
||||
|
||||
private:
|
||||
ObjectTemplateBuilder& SetImpl(const base::StringPiece& name,
|
||||
v8::Local<v8::Data> val);
|
||||
ObjectTemplateBuilder& SetPropertyImpl(
|
||||
const base::StringPiece& name, v8::Local<v8::FunctionTemplate> getter,
|
||||
v8::Local<v8::FunctionTemplate> setter);
|
||||
|
||||
v8::Isolate* isolate_;
|
||||
|
||||
// ObjectTemplateBuilder should only be used on the stack.
|
||||
v8::Local<v8::ObjectTemplate> template_;
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_OBJECT_TEMPLATE_BUILDER_H_
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2014 Cheng Zhao. All rights reserved.
|
||||
// Use of this source code is governed by MIT license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
#include "native_mate/persistent_dictionary.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
PersistentDictionary::PersistentDictionary() {
|
||||
}
|
||||
|
||||
PersistentDictionary::PersistentDictionary(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> object)
|
||||
: handle_(new RefCountedPersistent<v8::Object>(isolate, object)) {
|
||||
isolate_ = isolate;
|
||||
}
|
||||
|
||||
PersistentDictionary::~PersistentDictionary() {
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> PersistentDictionary::GetHandle() const {
|
||||
return handle_->NewHandle();
|
||||
}
|
||||
|
||||
bool Converter<PersistentDictionary>::FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
PersistentDictionary* out) {
|
||||
if (!val->IsObject())
|
||||
return false;
|
||||
*out = PersistentDictionary(isolate, v8::Local<v8::Object>::Cast(val));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mate
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2014 Cheng Zhao. All rights reserved.
|
||||
// Use of this source code is governed by MIT license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
#ifndef NATIVE_MATE_PERSISTENT_DICTIONARY_H_
|
||||
#define NATIVE_MATE_PERSISTENT_DICTIONARY_H_
|
||||
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/scoped_persistent.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
// Like Dictionary, but stores object in persistent handle so you can keep it
|
||||
// safely on heap.
|
||||
class PersistentDictionary : public Dictionary {
|
||||
public:
|
||||
PersistentDictionary();
|
||||
PersistentDictionary(v8::Isolate* isolate, v8::Local<v8::Object> object);
|
||||
virtual ~PersistentDictionary();
|
||||
|
||||
v8::Local<v8::Object> GetHandle() const override;
|
||||
|
||||
private:
|
||||
scoped_refptr<RefCountedPersistent<v8::Object> > handle_;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<PersistentDictionary> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
PersistentDictionary* out);
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_PERSISTENT_DICTIONARY_H_
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "native_mate/promise.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
Promise::Promise()
|
||||
: isolate_(NULL) {
|
||||
}
|
||||
|
||||
Promise::Promise(v8::Isolate* isolate)
|
||||
: isolate_(isolate) {
|
||||
resolver_ = v8::Promise::Resolver::New(isolate);
|
||||
}
|
||||
|
||||
Promise::~Promise() {
|
||||
}
|
||||
|
||||
Promise Promise::Create(v8::Isolate* isolate) {
|
||||
return Promise(isolate);
|
||||
}
|
||||
|
||||
Promise Promise::Create() {
|
||||
return Promise::Create(v8::Isolate::GetCurrent());
|
||||
}
|
||||
|
||||
void Promise::RejectWithErrorMessage(const std::string& string) {
|
||||
v8::Local<v8::String> error_message =
|
||||
v8::String::NewFromUtf8(isolate(), string.c_str());
|
||||
v8::Local<v8::Value> error = v8::Exception::Error(error_message);
|
||||
resolver_->Reject(mate::ConvertToV8(isolate(), error));
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> Promise::GetHandle() const {
|
||||
return resolver_->GetPromise();
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Converter<Promise>::ToV8(v8::Isolate* isolate,
|
||||
Promise val) {
|
||||
return val.GetHandle();
|
||||
}
|
||||
|
||||
} // namespace mate
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef NATIVE_MATE_PROMISE_H_
|
||||
#define NATIVE_MATE_PROMISE_H_
|
||||
|
||||
#include "native_mate/converter.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
class Promise {
|
||||
public:
|
||||
Promise();
|
||||
Promise(v8::Isolate* isolate);
|
||||
virtual ~Promise();
|
||||
|
||||
static Promise Create(v8::Isolate* isolate);
|
||||
static Promise Create();
|
||||
|
||||
v8::Isolate* isolate() const { return isolate_; }
|
||||
|
||||
virtual v8::Local<v8::Object> GetHandle() const;
|
||||
|
||||
template<typename T>
|
||||
void Resolve(T* value) {
|
||||
resolver_->Resolve(mate::ConvertToV8(isolate(), value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Reject(T* value) {
|
||||
resolver_->Reject(mate::ConvertToV8(isolate(), value));
|
||||
}
|
||||
|
||||
void RejectWithErrorMessage(const std::string& error);
|
||||
|
||||
protected:
|
||||
v8::Isolate* isolate_;
|
||||
|
||||
private:
|
||||
v8::Local<v8::Promise::Resolver> resolver_;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<Promise> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
Promise val);
|
||||
// TODO(MarshallOfSound): Implement FromV8 to allow promise chaining
|
||||
// in native land
|
||||
// static bool FromV8(v8::Isolate* isolate,
|
||||
// v8::Local<v8::Value> val,
|
||||
// Promise* out);
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_PROMISE_H_
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2014 Cheng Zhao. All rights reserved.
|
||||
// Use of this source code is governed by MIT license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
#ifndef NATIVE_MATE_SCOPED_PERSISTENT_H_
|
||||
#define NATIVE_MATE_SCOPED_PERSISTENT_H_
|
||||
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "native_mate/converter.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
// A v8::Persistent handle to a V8 value which destroys and clears the
|
||||
// underlying handle on destruction.
|
||||
template <typename T>
|
||||
class ScopedPersistent {
|
||||
public:
|
||||
ScopedPersistent() : isolate_(v8::Isolate::GetCurrent()) {}
|
||||
|
||||
ScopedPersistent(v8::Isolate* isolate, v8::Local<v8::Value> handle)
|
||||
: isolate_(isolate) {
|
||||
reset(isolate, v8::Local<T>::Cast(handle));
|
||||
}
|
||||
|
||||
~ScopedPersistent() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset(v8::Isolate* isolate, v8::Local<T> handle) {
|
||||
if (!handle.IsEmpty()) {
|
||||
isolate_ = isolate;
|
||||
handle_.Reset(isolate, handle);
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
handle_.Reset();
|
||||
}
|
||||
|
||||
bool IsEmpty() const {
|
||||
return handle_.IsEmpty();
|
||||
}
|
||||
|
||||
v8::Local<T> NewHandle() const {
|
||||
return NewHandle(isolate_);
|
||||
}
|
||||
|
||||
v8::Local<T> NewHandle(v8::Isolate* isolate) const {
|
||||
if (handle_.IsEmpty())
|
||||
return v8::Local<T>();
|
||||
return v8::Local<T>::New(isolate, handle_);
|
||||
}
|
||||
|
||||
template<typename P, typename C>
|
||||
void SetWeak(P* parameter, C callback) {
|
||||
handle_.SetWeak(parameter, callback);
|
||||
}
|
||||
|
||||
v8::Isolate* isolate() const { return isolate_; }
|
||||
|
||||
private:
|
||||
v8::Isolate* isolate_;
|
||||
v8::Persistent<T> handle_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedPersistent);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class RefCountedPersistent : public ScopedPersistent<T>,
|
||||
public base::RefCounted<RefCountedPersistent<T>> {
|
||||
public:
|
||||
RefCountedPersistent() {}
|
||||
|
||||
RefCountedPersistent(v8::Isolate* isolate, v8::Local<v8::Value> handle)
|
||||
: ScopedPersistent<T>(isolate, handle) {
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class base::RefCounted<RefCountedPersistent<T>>;
|
||||
|
||||
~RefCountedPersistent() {}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(RefCountedPersistent);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Converter<ScopedPersistent<T> > {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const ScopedPersistent<T>& val) {
|
||||
return val.NewHandle(isolate);
|
||||
}
|
||||
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
ScopedPersistent<T>* out) {
|
||||
v8::Local<T> converted;
|
||||
if (!Converter<v8::Local<T> >::FromV8(isolate, val, &converted))
|
||||
return false;
|
||||
|
||||
out->reset(isolate, converted);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_SCOPED_PERSISTENT_H_
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#include "native_mate/wrappable.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
WrappableBase::WrappableBase() : isolate_(nullptr) {}
|
||||
|
||||
WrappableBase::~WrappableBase() {
|
||||
if (wrapper_.IsEmpty())
|
||||
return;
|
||||
|
||||
GetWrapper()->SetAlignedPointerInInternalField(0, nullptr);
|
||||
wrapper_.ClearWeak();
|
||||
wrapper_.Reset();
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> WrappableBase::GetWrapper() const {
|
||||
if (!wrapper_.IsEmpty())
|
||||
return v8::Local<v8::Object>::New(isolate_, wrapper_);
|
||||
else
|
||||
return v8::Local<v8::Object>();
|
||||
}
|
||||
|
||||
void WrappableBase::InitWith(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> wrapper) {
|
||||
CHECK(wrapper_.IsEmpty());
|
||||
isolate_ = isolate;
|
||||
wrapper->SetAlignedPointerInInternalField(0, this);
|
||||
wrapper_.Reset(isolate, wrapper);
|
||||
wrapper_.SetWeak(this, FirstWeakCallback, v8::WeakCallbackType::kParameter);
|
||||
|
||||
// Call object._init if we have one.
|
||||
v8::Local<v8::Function> init;
|
||||
if (Dictionary(isolate, wrapper).Get("_init", &init))
|
||||
init->Call(wrapper, 0, nullptr);
|
||||
|
||||
AfterInit(isolate);
|
||||
}
|
||||
|
||||
// static
|
||||
void WrappableBase::FirstWeakCallback(
|
||||
const v8::WeakCallbackInfo<WrappableBase>& data) {
|
||||
WrappableBase* wrappable = data.GetParameter();
|
||||
wrappable->wrapper_.Reset();
|
||||
data.SetSecondPassCallback(SecondWeakCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
void WrappableBase::SecondWeakCallback(
|
||||
const v8::WeakCallbackInfo<WrappableBase>& data) {
|
||||
WrappableBase* wrappable = data.GetParameter();
|
||||
delete wrappable;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
void* FromV8Impl(v8::Isolate* isolate, v8::Local<v8::Value> val) {
|
||||
if (!val->IsObject())
|
||||
return nullptr;
|
||||
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(val);
|
||||
if (obj->InternalFieldCount() != 1)
|
||||
return nullptr;
|
||||
return obj->GetAlignedPointerFromInternalField(0);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace mate
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE.chromium file.
|
||||
|
||||
#ifndef NATIVE_MATE_WRAPPABLE_H_
|
||||
#define NATIVE_MATE_WRAPPABLE_H_
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "native_mate/converter.h"
|
||||
#include "native_mate/constructor.h"
|
||||
#include "gin/per_isolate_data.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace internal {
|
||||
|
||||
void* FromV8Impl(v8::Isolate* isolate, v8::Local<v8::Value> val);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template<typename T>
|
||||
class Wrappable : public WrappableBase {
|
||||
public:
|
||||
Wrappable() {}
|
||||
|
||||
template<typename Sig>
|
||||
static void SetConstructor(v8::Isolate* isolate,
|
||||
const base::Callback<Sig>& constructor) {
|
||||
v8::Local<v8::FunctionTemplate> templ = CreateFunctionTemplate(
|
||||
isolate, base::Bind(&internal::InvokeNew<Sig>, constructor));
|
||||
templ->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
T::BuildPrototype(isolate, templ);
|
||||
gin::PerIsolateData::From(isolate)->SetFunctionTemplate(
|
||||
&kWrapperInfo, templ);
|
||||
}
|
||||
|
||||
static v8::Local<v8::FunctionTemplate> GetConstructor(v8::Isolate* isolate) {
|
||||
// Fill the object template.
|
||||
auto data = gin::PerIsolateData::From(isolate);
|
||||
auto templ = data->GetFunctionTemplate(&kWrapperInfo);
|
||||
if (templ.IsEmpty()) {
|
||||
templ = v8::FunctionTemplate::New(isolate);
|
||||
templ->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
T::BuildPrototype(isolate, templ);
|
||||
data->SetFunctionTemplate(&kWrapperInfo, templ);
|
||||
}
|
||||
return templ;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Init the class with T::BuildPrototype.
|
||||
void Init(v8::Isolate* isolate) {
|
||||
v8::Local<v8::FunctionTemplate> templ = GetConstructor(isolate);
|
||||
|
||||
// |wrapper| may be empty in some extreme cases, e.g., when
|
||||
// Object.prototype.constructor is overwritten.
|
||||
v8::Local<v8::Object> wrapper;
|
||||
if (!templ->InstanceTemplate()->NewInstance(
|
||||
isolate->GetCurrentContext()).ToLocal(&wrapper)) {
|
||||
// The current wrappable object will be no longer managed by V8. Delete
|
||||
// this now.
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
InitWith(isolate, wrapper);
|
||||
}
|
||||
|
||||
private:
|
||||
static gin::WrapperInfo kWrapperInfo;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Wrappable);
|
||||
};
|
||||
|
||||
// static
|
||||
template<typename T>
|
||||
gin::WrapperInfo Wrappable<T>::kWrapperInfo = { gin::kEmbedderNativeGin };
|
||||
|
||||
// This converter handles any subclass of Wrappable.
|
||||
template <typename T>
|
||||
struct Converter<T*,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<T*, WrappableBase*>::value>::type> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, T* val) {
|
||||
if (val)
|
||||
return val->GetWrapper();
|
||||
else
|
||||
return v8::Null(isolate);
|
||||
}
|
||||
|
||||
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val, T** out) {
|
||||
*out = static_cast<T*>(static_cast<WrappableBase*>(
|
||||
internal::FromV8Impl(isolate, val)));
|
||||
return *out != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_WRAPPABLE_H_
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef NATIVE_MATE_WRAPPABLE_BASE_H_
|
||||
#define NATIVE_MATE_WRAPPABLE_BASE_H_
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace internal {
|
||||
struct Destroyable;
|
||||
}
|
||||
|
||||
// Wrappable is a base class for C++ objects that have corresponding v8 wrapper
|
||||
// objects. To retain a Wrappable object on the stack, use a gin::Handle.
|
||||
//
|
||||
// USAGE:
|
||||
// // my_class.h
|
||||
// class MyClass : Wrappable<MyClass> {
|
||||
// public:
|
||||
// ...
|
||||
// };
|
||||
//
|
||||
// Subclasses should also typically have private constructors and expose a
|
||||
// static Create function that returns a mate::Handle. Forcing creators through
|
||||
// this static Create function will enforce that clients actually create a
|
||||
// wrapper for the object. If clients fail to create a wrapper for a wrappable
|
||||
// object, the object will leak because we use the weak callback from the
|
||||
// wrapper as the signal to delete the wrapped object.
|
||||
class WrappableBase {
|
||||
public:
|
||||
WrappableBase();
|
||||
virtual ~WrappableBase();
|
||||
|
||||
// Retrieve the v8 wrapper object cooresponding to this object.
|
||||
v8::Local<v8::Object> GetWrapper() const;
|
||||
|
||||
// Returns the Isolate this object is created in.
|
||||
v8::Isolate* isolate() const { return isolate_; }
|
||||
|
||||
protected:
|
||||
// Called after the "_init" method gets called in JavaScript.
|
||||
virtual void AfterInit(v8::Isolate* isolate) {}
|
||||
|
||||
// Bind the C++ class to the JS wrapper.
|
||||
// This method should only be called by classes using Constructor.
|
||||
virtual void InitWith(v8::Isolate* isolate, v8::Local<v8::Object> wrapper);
|
||||
|
||||
private:
|
||||
friend struct internal::Destroyable;
|
||||
|
||||
static void FirstWeakCallback(
|
||||
const v8::WeakCallbackInfo<WrappableBase>& data);
|
||||
static void SecondWeakCallback(
|
||||
const v8::WeakCallbackInfo<WrappableBase>& data);
|
||||
|
||||
v8::Isolate* isolate_;
|
||||
v8::Global<v8::Object> wrapper_; // Weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WrappableBase);
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // NATIVE_MATE_WRAPPABLE_BASE_H_
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
'variables': {
|
||||
'native_mate_files': [
|
||||
'native_mate/arguments.cc',
|
||||
'native_mate/arguments.h',
|
||||
'native_mate/constructor.h',
|
||||
'native_mate/converter.cc',
|
||||
'native_mate/converter.h',
|
||||
'native_mate/dictionary.cc',
|
||||
'native_mate/dictionary.h',
|
||||
'native_mate/function_template.cc',
|
||||
'native_mate/function_template.h',
|
||||
'native_mate/handle.h',
|
||||
'native_mate/object_template_builder.cc',
|
||||
'native_mate/object_template_builder.h',
|
||||
'native_mate/persistent_dictionary.cc',
|
||||
'native_mate/persistent_dictionary.h',
|
||||
'native_mate/scoped_persistent.h',
|
||||
'native_mate/wrappable.cc',
|
||||
'native_mate/wrappable.h',
|
||||
'native_mate/wrappable_base.h',
|
||||
'native_mate/promise.h',
|
||||
'native_mate/promise.cc',
|
||||
],
|
||||
},
|
||||
}
|
|
@ -0,0 +1,855 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2008, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""pump v0.2.0 - Pretty Useful for Meta Programming.
|
||||
|
||||
A tool for preprocessor meta programming. Useful for generating
|
||||
repetitive boilerplate code. Especially useful for writing C++
|
||||
classes, functions, macros, and templates that need to work with
|
||||
various number of arguments.
|
||||
|
||||
USAGE:
|
||||
pump.py SOURCE_FILE
|
||||
|
||||
EXAMPLES:
|
||||
pump.py foo.cc.pump
|
||||
Converts foo.cc.pump to foo.cc.
|
||||
|
||||
GRAMMAR:
|
||||
CODE ::= ATOMIC_CODE*
|
||||
ATOMIC_CODE ::= $var ID = EXPRESSION
|
||||
| $var ID = [[ CODE ]]
|
||||
| $range ID EXPRESSION..EXPRESSION
|
||||
| $for ID SEPARATOR [[ CODE ]]
|
||||
| $($)
|
||||
| $ID
|
||||
| $(EXPRESSION)
|
||||
| $if EXPRESSION [[ CODE ]] ELSE_BRANCH
|
||||
| [[ CODE ]]
|
||||
| RAW_CODE
|
||||
SEPARATOR ::= RAW_CODE | EMPTY
|
||||
ELSE_BRANCH ::= $else [[ CODE ]]
|
||||
| $elif EXPRESSION [[ CODE ]] ELSE_BRANCH
|
||||
| EMPTY
|
||||
EXPRESSION has Python syntax.
|
||||
"""
|
||||
|
||||
__author__ = 'wan@google.com (Zhanyong Wan)'
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
TOKEN_TABLE = [
|
||||
(re.compile(r'\$var\s+'), '$var'),
|
||||
(re.compile(r'\$elif\s+'), '$elif'),
|
||||
(re.compile(r'\$else\s+'), '$else'),
|
||||
(re.compile(r'\$for\s+'), '$for'),
|
||||
(re.compile(r'\$if\s+'), '$if'),
|
||||
(re.compile(r'\$range\s+'), '$range'),
|
||||
(re.compile(r'\$[_A-Za-z]\w*'), '$id'),
|
||||
(re.compile(r'\$\(\$\)'), '$($)'),
|
||||
(re.compile(r'\$'), '$'),
|
||||
(re.compile(r'\[\[\n?'), '[['),
|
||||
(re.compile(r'\]\]\n?'), ']]'),
|
||||
]
|
||||
|
||||
|
||||
class Cursor:
|
||||
"""Represents a position (line and column) in a text file."""
|
||||
|
||||
def __init__(self, line=-1, column=-1):
|
||||
self.line = line
|
||||
self.column = column
|
||||
|
||||
def __eq__(self, rhs):
|
||||
return self.line == rhs.line and self.column == rhs.column
|
||||
|
||||
def __ne__(self, rhs):
|
||||
return not self == rhs
|
||||
|
||||
def __lt__(self, rhs):
|
||||
return self.line < rhs.line or (
|
||||
self.line == rhs.line and self.column < rhs.column)
|
||||
|
||||
def __le__(self, rhs):
|
||||
return self < rhs or self == rhs
|
||||
|
||||
def __gt__(self, rhs):
|
||||
return rhs < self
|
||||
|
||||
def __ge__(self, rhs):
|
||||
return rhs <= self
|
||||
|
||||
def __str__(self):
|
||||
if self == Eof():
|
||||
return 'EOF'
|
||||
else:
|
||||
return '%s(%s)' % (self.line + 1, self.column)
|
||||
|
||||
def __add__(self, offset):
|
||||
return Cursor(self.line, self.column + offset)
|
||||
|
||||
def __sub__(self, offset):
|
||||
return Cursor(self.line, self.column - offset)
|
||||
|
||||
def Clone(self):
|
||||
"""Returns a copy of self."""
|
||||
|
||||
return Cursor(self.line, self.column)
|
||||
|
||||
|
||||
# Special cursor to indicate the end-of-file.
|
||||
def Eof():
|
||||
"""Returns the special cursor to denote the end-of-file."""
|
||||
return Cursor(-1, -1)
|
||||
|
||||
|
||||
class Token:
|
||||
"""Represents a token in a Pump source file."""
|
||||
|
||||
def __init__(self, start=None, end=None, value=None, token_type=None):
|
||||
if start is None:
|
||||
self.start = Eof()
|
||||
else:
|
||||
self.start = start
|
||||
if end is None:
|
||||
self.end = Eof()
|
||||
else:
|
||||
self.end = end
|
||||
self.value = value
|
||||
self.token_type = token_type
|
||||
|
||||
def __str__(self):
|
||||
return 'Token @%s: \'%s\' type=%s' % (
|
||||
self.start, self.value, self.token_type)
|
||||
|
||||
def Clone(self):
|
||||
"""Returns a copy of self."""
|
||||
|
||||
return Token(self.start.Clone(), self.end.Clone(), self.value,
|
||||
self.token_type)
|
||||
|
||||
|
||||
def StartsWith(lines, pos, string):
|
||||
"""Returns True iff the given position in lines starts with 'string'."""
|
||||
|
||||
return lines[pos.line][pos.column:].startswith(string)
|
||||
|
||||
|
||||
def FindFirstInLine(line, token_table):
|
||||
best_match_start = -1
|
||||
for (regex, token_type) in token_table:
|
||||
m = regex.search(line)
|
||||
if m:
|
||||
# We found regex in lines
|
||||
if best_match_start < 0 or m.start() < best_match_start:
|
||||
best_match_start = m.start()
|
||||
best_match_length = m.end() - m.start()
|
||||
best_match_token_type = token_type
|
||||
|
||||
if best_match_start < 0:
|
||||
return None
|
||||
|
||||
return (best_match_start, best_match_length, best_match_token_type)
|
||||
|
||||
|
||||
def FindFirst(lines, token_table, cursor):
|
||||
"""Finds the first occurrence of any string in strings in lines."""
|
||||
|
||||
start = cursor.Clone()
|
||||
cur_line_number = cursor.line
|
||||
for line in lines[start.line:]:
|
||||
if cur_line_number == start.line:
|
||||
line = line[start.column:]
|
||||
m = FindFirstInLine(line, token_table)
|
||||
if m:
|
||||
# We found a regex in line.
|
||||
(start_column, length, token_type) = m
|
||||
if cur_line_number == start.line:
|
||||
start_column += start.column
|
||||
found_start = Cursor(cur_line_number, start_column)
|
||||
found_end = found_start + length
|
||||
return MakeToken(lines, found_start, found_end, token_type)
|
||||
cur_line_number += 1
|
||||
# We failed to find str in lines
|
||||
return None
|
||||
|
||||
|
||||
def SubString(lines, start, end):
|
||||
"""Returns a substring in lines."""
|
||||
|
||||
if end == Eof():
|
||||
end = Cursor(len(lines) - 1, len(lines[-1]))
|
||||
|
||||
if start >= end:
|
||||
return ''
|
||||
|
||||
if start.line == end.line:
|
||||
return lines[start.line][start.column:end.column]
|
||||
|
||||
result_lines = ([lines[start.line][start.column:]] +
|
||||
lines[start.line + 1:end.line] +
|
||||
[lines[end.line][:end.column]])
|
||||
return ''.join(result_lines)
|
||||
|
||||
|
||||
def StripMetaComments(_str):
|
||||
"""Strip meta comments from each line in the given string."""
|
||||
|
||||
# First, completely remove lines containing nothing but a meta
|
||||
# comment, including the trailing \n.
|
||||
_str = re.sub(r'^\s*\$\$.*\n', '', _str)
|
||||
|
||||
# Then, remove meta comments from contentful lines.
|
||||
return re.sub(r'\s*\$\$.*', '', _str)
|
||||
|
||||
|
||||
def MakeToken(lines, start, end, token_type):
|
||||
"""Creates a new instance of Token."""
|
||||
|
||||
return Token(start, end, SubString(lines, start, end), token_type)
|
||||
|
||||
|
||||
def ParseToken(lines, pos, regex, token_type):
|
||||
line = lines[pos.line][pos.column:]
|
||||
m = regex.search(line)
|
||||
if m and not m.start():
|
||||
return MakeToken(lines, pos, pos + m.end(), token_type)
|
||||
else:
|
||||
print 'ERROR: %s expected at %s.' % (token_type, pos)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
ID_REGEX = re.compile(r'[_A-Za-z]\w*')
|
||||
EQ_REGEX = re.compile(r'=')
|
||||
REST_OF_LINE_REGEX = re.compile(r'.*?(?=$|\$\$)')
|
||||
OPTIONAL_WHITE_SPACES_REGEX = re.compile(r'\s*')
|
||||
WHITE_SPACE_REGEX = re.compile(r'\s')
|
||||
DOT_DOT_REGEX = re.compile(r'\.\.')
|
||||
|
||||
|
||||
def Skip(lines, pos, regex):
|
||||
line = lines[pos.line][pos.column:]
|
||||
m = re.search(regex, line)
|
||||
if m and not m.start():
|
||||
return pos + m.end()
|
||||
else:
|
||||
return pos
|
||||
|
||||
|
||||
def SkipUntil(lines, pos, regex, token_type):
|
||||
line = lines[pos.line][pos.column:]
|
||||
m = re.search(regex, line)
|
||||
if m:
|
||||
return pos + m.start()
|
||||
else:
|
||||
print ('ERROR: %s expected on line %s after column %s.' %
|
||||
(token_type, pos.line + 1, pos.column))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def ParseExpTokenInParens(lines, pos):
|
||||
def ParseInParens(pos):
|
||||
pos = Skip(lines, pos, OPTIONAL_WHITE_SPACES_REGEX)
|
||||
pos = Skip(lines, pos, r'\(')
|
||||
pos = Parse(pos)
|
||||
pos = Skip(lines, pos, r'\)')
|
||||
return pos
|
||||
|
||||
def Parse(pos):
|
||||
pos = SkipUntil(lines, pos, r'\(|\)', ')')
|
||||
if SubString(lines, pos, pos + 1) == '(':
|
||||
pos = Parse(pos + 1)
|
||||
pos = Skip(lines, pos, r'\)')
|
||||
return Parse(pos)
|
||||
else:
|
||||
return pos
|
||||
|
||||
start = pos.Clone()
|
||||
pos = ParseInParens(pos)
|
||||
return MakeToken(lines, start, pos, 'exp')
|
||||
|
||||
|
||||
def RStripNewLineFromToken(token):
|
||||
if token.value.endswith('\n'):
|
||||
return Token(token.start, token.end, token.value[:-1], token.token_type)
|
||||
else:
|
||||
return token
|
||||
|
||||
|
||||
def TokenizeLines(lines, pos):
|
||||
while True:
|
||||
found = FindFirst(lines, TOKEN_TABLE, pos)
|
||||
if not found:
|
||||
yield MakeToken(lines, pos, Eof(), 'code')
|
||||
return
|
||||
|
||||
if found.start == pos:
|
||||
prev_token = None
|
||||
prev_token_rstripped = None
|
||||
else:
|
||||
prev_token = MakeToken(lines, pos, found.start, 'code')
|
||||
prev_token_rstripped = RStripNewLineFromToken(prev_token)
|
||||
|
||||
if found.token_type == '$var':
|
||||
if prev_token_rstripped:
|
||||
yield prev_token_rstripped
|
||||
yield found
|
||||
id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
|
||||
yield id_token
|
||||
pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX)
|
||||
|
||||
eq_token = ParseToken(lines, pos, EQ_REGEX, '=')
|
||||
yield eq_token
|
||||
pos = Skip(lines, eq_token.end, r'\s*')
|
||||
|
||||
if SubString(lines, pos, pos + 2) != '[[':
|
||||
exp_token = ParseToken(lines, pos, REST_OF_LINE_REGEX, 'exp')
|
||||
yield exp_token
|
||||
pos = Cursor(exp_token.end.line + 1, 0)
|
||||
elif found.token_type == '$for':
|
||||
if prev_token_rstripped:
|
||||
yield prev_token_rstripped
|
||||
yield found
|
||||
id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
|
||||
yield id_token
|
||||
pos = Skip(lines, id_token.end, WHITE_SPACE_REGEX)
|
||||
elif found.token_type == '$range':
|
||||
if prev_token_rstripped:
|
||||
yield prev_token_rstripped
|
||||
yield found
|
||||
id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
|
||||
yield id_token
|
||||
pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX)
|
||||
|
||||
dots_pos = SkipUntil(lines, pos, DOT_DOT_REGEX, '..')
|
||||
yield MakeToken(lines, pos, dots_pos, 'exp')
|
||||
yield MakeToken(lines, dots_pos, dots_pos + 2, '..')
|
||||
pos = dots_pos + 2
|
||||
new_pos = Cursor(pos.line + 1, 0)
|
||||
yield MakeToken(lines, pos, new_pos, 'exp')
|
||||
pos = new_pos
|
||||
elif found.token_type == '$':
|
||||
if prev_token:
|
||||
yield prev_token
|
||||
yield found
|
||||
exp_token = ParseExpTokenInParens(lines, found.end)
|
||||
yield exp_token
|
||||
pos = exp_token.end
|
||||
elif (found.token_type == ']]' or found.token_type == '$if' or
|
||||
found.token_type == '$elif' or found.token_type == '$else'):
|
||||
if prev_token_rstripped:
|
||||
yield prev_token_rstripped
|
||||
yield found
|
||||
pos = found.end
|
||||
else:
|
||||
if prev_token:
|
||||
yield prev_token
|
||||
yield found
|
||||
pos = found.end
|
||||
|
||||
|
||||
def Tokenize(s):
|
||||
"""A generator that yields the tokens in the given string."""
|
||||
if s != '':
|
||||
lines = s.splitlines(True)
|
||||
for token in TokenizeLines(lines, Cursor(0, 0)):
|
||||
yield token
|
||||
|
||||
|
||||
class CodeNode:
|
||||
def __init__(self, atomic_code_list=None):
|
||||
self.atomic_code = atomic_code_list
|
||||
|
||||
|
||||
class VarNode:
|
||||
def __init__(self, identifier=None, atomic_code=None):
|
||||
self.identifier = identifier
|
||||
self.atomic_code = atomic_code
|
||||
|
||||
|
||||
class RangeNode:
|
||||
def __init__(self, identifier=None, exp1=None, exp2=None):
|
||||
self.identifier = identifier
|
||||
self.exp1 = exp1
|
||||
self.exp2 = exp2
|
||||
|
||||
|
||||
class ForNode:
|
||||
def __init__(self, identifier=None, sep=None, code=None):
|
||||
self.identifier = identifier
|
||||
self.sep = sep
|
||||
self.code = code
|
||||
|
||||
|
||||
class ElseNode:
|
||||
def __init__(self, else_branch=None):
|
||||
self.else_branch = else_branch
|
||||
|
||||
|
||||
class IfNode:
|
||||
def __init__(self, exp=None, then_branch=None, else_branch=None):
|
||||
self.exp = exp
|
||||
self.then_branch = then_branch
|
||||
self.else_branch = else_branch
|
||||
|
||||
|
||||
class RawCodeNode:
|
||||
def __init__(self, token=None):
|
||||
self.raw_code = token
|
||||
|
||||
|
||||
class LiteralDollarNode:
|
||||
def __init__(self, token):
|
||||
self.token = token
|
||||
|
||||
|
||||
class ExpNode:
|
||||
def __init__(self, token, python_exp):
|
||||
self.token = token
|
||||
self.python_exp = python_exp
|
||||
|
||||
|
||||
def PopFront(a_list):
|
||||
head = a_list[0]
|
||||
a_list[:1] = []
|
||||
return head
|
||||
|
||||
|
||||
def PushFront(a_list, elem):
|
||||
a_list[:0] = [elem]
|
||||
|
||||
|
||||
def PopToken(a_list, token_type=None):
|
||||
token = PopFront(a_list)
|
||||
if token_type is not None and token.token_type != token_type:
|
||||
print 'ERROR: %s expected at %s' % (token_type, token.start)
|
||||
print 'ERROR: %s found instead' % (token,)
|
||||
sys.exit(1)
|
||||
|
||||
return token
|
||||
|
||||
|
||||
def PeekToken(a_list):
|
||||
if not a_list:
|
||||
return None
|
||||
|
||||
return a_list[0]
|
||||
|
||||
|
||||
def ParseExpNode(token):
|
||||
python_exp = re.sub(r'([_A-Za-z]\w*)', r'self.GetValue("\1")', token.value)
|
||||
return ExpNode(token, python_exp)
|
||||
|
||||
|
||||
def ParseElseNode(tokens):
|
||||
def Pop(token_type=None):
|
||||
return PopToken(tokens, token_type)
|
||||
|
||||
_next = PeekToken(tokens)
|
||||
if not next:
|
||||
return None
|
||||
if _next.token_type == '$else':
|
||||
Pop('$else')
|
||||
Pop('[[')
|
||||
code_node = ParseCodeNode(tokens)
|
||||
Pop(']]')
|
||||
return code_node
|
||||
elif _next.token_type == '$elif':
|
||||
Pop('$elif')
|
||||
exp = Pop('code')
|
||||
Pop('[[')
|
||||
code_node = ParseCodeNode(tokens)
|
||||
Pop(']]')
|
||||
inner_else_node = ParseElseNode(tokens)
|
||||
return CodeNode([IfNode(ParseExpNode(exp), code_node, inner_else_node)])
|
||||
elif not _next.value.strip():
|
||||
Pop('code')
|
||||
return ParseElseNode(tokens)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def ParseAtomicCodeNode(tokens):
|
||||
def Pop(token_type=None):
|
||||
return PopToken(tokens, token_type)
|
||||
|
||||
head = PopFront(tokens)
|
||||
t = head.token_type
|
||||
if t == 'code':
|
||||
return RawCodeNode(head)
|
||||
elif t == '$var':
|
||||
id_token = Pop('id')
|
||||
Pop('=')
|
||||
_next = PeekToken(tokens)
|
||||
if _next.token_type == 'exp':
|
||||
exp_token = Pop()
|
||||
return VarNode(id_token, ParseExpNode(exp_token))
|
||||
Pop('[[')
|
||||
code_node = ParseCodeNode(tokens)
|
||||
Pop(']]')
|
||||
return VarNode(id_token, code_node)
|
||||
elif t == '$for':
|
||||
id_token = Pop('id')
|
||||
next_token = PeekToken(tokens)
|
||||
if next_token.token_type == 'code':
|
||||
sep_token = next_token
|
||||
Pop('code')
|
||||
else:
|
||||
sep_token = None
|
||||
Pop('[[')
|
||||
code_node = ParseCodeNode(tokens)
|
||||
Pop(']]')
|
||||
return ForNode(id_token, sep_token, code_node)
|
||||
elif t == '$if':
|
||||
exp_token = Pop('code')
|
||||
Pop('[[')
|
||||
code_node = ParseCodeNode(tokens)
|
||||
Pop(']]')
|
||||
else_node = ParseElseNode(tokens)
|
||||
return IfNode(ParseExpNode(exp_token), code_node, else_node)
|
||||
elif t == '$range':
|
||||
id_token = Pop('id')
|
||||
exp1_token = Pop('exp')
|
||||
Pop('..')
|
||||
exp2_token = Pop('exp')
|
||||
return RangeNode(id_token, ParseExpNode(exp1_token),
|
||||
ParseExpNode(exp2_token))
|
||||
elif t == '$id':
|
||||
return ParseExpNode(Token(head.start + 1, head.end, head.value[1:], 'id'))
|
||||
elif t == '$($)':
|
||||
return LiteralDollarNode(head)
|
||||
elif t == '$':
|
||||
exp_token = Pop('exp')
|
||||
return ParseExpNode(exp_token)
|
||||
elif t == '[[':
|
||||
code_node = ParseCodeNode(tokens)
|
||||
Pop(']]')
|
||||
return code_node
|
||||
else:
|
||||
PushFront(tokens, head)
|
||||
return None
|
||||
|
||||
|
||||
def ParseCodeNode(tokens):
|
||||
atomic_code_list = []
|
||||
while True:
|
||||
if not tokens:
|
||||
break
|
||||
atomic_code_node = ParseAtomicCodeNode(tokens)
|
||||
if atomic_code_node:
|
||||
atomic_code_list.append(atomic_code_node)
|
||||
else:
|
||||
break
|
||||
return CodeNode(atomic_code_list)
|
||||
|
||||
|
||||
def ParseToAST(pump_src_text):
|
||||
"""Convert the given Pump source text into an AST."""
|
||||
tokens = list(Tokenize(pump_src_text))
|
||||
code_node = ParseCodeNode(tokens)
|
||||
return code_node
|
||||
|
||||
|
||||
class Env:
|
||||
def __init__(self):
|
||||
self.variables = []
|
||||
self.ranges = []
|
||||
|
||||
def Clone(self):
|
||||
clone = Env()
|
||||
clone.variables = self.variables[:]
|
||||
clone.ranges = self.ranges[:]
|
||||
return clone
|
||||
|
||||
def PushVariable(self, var, value):
|
||||
# If value looks like an int, store it as an int.
|
||||
try:
|
||||
int_value = int(value)
|
||||
if ('%s' % int_value) == value:
|
||||
value = int_value
|
||||
except Exception:
|
||||
pass
|
||||
self.variables[:0] = [(var, value)]
|
||||
|
||||
def PopVariable(self):
|
||||
self.variables[:1] = []
|
||||
|
||||
def PushRange(self, var, lower, upper):
|
||||
self.ranges[:0] = [(var, lower, upper)]
|
||||
|
||||
def PopRange(self):
|
||||
self.ranges[:1] = []
|
||||
|
||||
def GetValue(self, identifier):
|
||||
for (var, value) in self.variables:
|
||||
if identifier == var:
|
||||
return value
|
||||
|
||||
print 'ERROR: meta variable %s is undefined.' % (identifier,)
|
||||
sys.exit(1)
|
||||
|
||||
def EvalExp(self, exp):
|
||||
try:
|
||||
result = eval(exp.python_exp)
|
||||
except Exception, e:
|
||||
print 'ERROR: caught exception %s: %s' % (e.__class__.__name__, e)
|
||||
print ('ERROR: failed to evaluate meta expression %s at %s' %
|
||||
(exp.python_exp, exp.token.start))
|
||||
sys.exit(1)
|
||||
return result
|
||||
|
||||
def GetRange(self, identifier):
|
||||
for (var, lower, upper) in self.ranges:
|
||||
if identifier == var:
|
||||
return (lower, upper)
|
||||
|
||||
print 'ERROR: range %s is undefined.' % (identifier,)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Output:
|
||||
def __init__(self):
|
||||
self.string = ''
|
||||
|
||||
def GetLastLine(self):
|
||||
index = self.string.rfind('\n')
|
||||
if index < 0:
|
||||
return ''
|
||||
|
||||
return self.string[index + 1:]
|
||||
|
||||
def Append(self, s):
|
||||
self.string += s
|
||||
|
||||
|
||||
def RunAtomicCode(env, node, output):
|
||||
if isinstance(node, VarNode):
|
||||
identifier = node.identifier.value.strip()
|
||||
result = Output()
|
||||
RunAtomicCode(env.Clone(), node.atomic_code, result)
|
||||
value = result.string
|
||||
env.PushVariable(identifier, value)
|
||||
elif isinstance(node, RangeNode):
|
||||
identifier = node.identifier.value.strip()
|
||||
lower = int(env.EvalExp(node.exp1))
|
||||
upper = int(env.EvalExp(node.exp2))
|
||||
env.PushRange(identifier, lower, upper)
|
||||
elif isinstance(node, ForNode):
|
||||
identifier = node.identifier.value.strip()
|
||||
if node.sep is None:
|
||||
sep = ''
|
||||
else:
|
||||
sep = node.sep.value
|
||||
(lower, upper) = env.GetRange(identifier)
|
||||
for i in range(lower, upper + 1):
|
||||
new_env = env.Clone()
|
||||
new_env.PushVariable(identifier, i)
|
||||
RunCode(new_env, node.code, output)
|
||||
if i != upper:
|
||||
output.Append(sep)
|
||||
elif isinstance(node, RawCodeNode):
|
||||
output.Append(node.raw_code.value)
|
||||
elif isinstance(node, IfNode):
|
||||
cond = env.EvalExp(node.exp)
|
||||
if cond:
|
||||
RunCode(env.Clone(), node.then_branch, output)
|
||||
elif node.else_branch is not None:
|
||||
RunCode(env.Clone(), node.else_branch, output)
|
||||
elif isinstance(node, ExpNode):
|
||||
value = env.EvalExp(node)
|
||||
output.Append('%s' % (value,))
|
||||
elif isinstance(node, LiteralDollarNode):
|
||||
output.Append('$')
|
||||
elif isinstance(node, CodeNode):
|
||||
RunCode(env.Clone(), node, output)
|
||||
else:
|
||||
print 'BAD'
|
||||
print node
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def RunCode(env, code_node, output):
|
||||
for atomic_code in code_node.atomic_code:
|
||||
RunAtomicCode(env, atomic_code, output)
|
||||
|
||||
|
||||
def IsSingleLineComment(cur_line):
|
||||
return '//' in cur_line
|
||||
|
||||
|
||||
def IsInPreprocessorDirective(prev_lines, cur_line):
|
||||
if cur_line.lstrip().startswith('#'):
|
||||
return True
|
||||
return prev_lines and prev_lines[-1].endswith('\\')
|
||||
|
||||
|
||||
def WrapComment(line, output):
|
||||
loc = line.find('//')
|
||||
before_comment = line[:loc].rstrip()
|
||||
if before_comment == '':
|
||||
indent = loc
|
||||
else:
|
||||
output.append(before_comment)
|
||||
indent = len(before_comment) - len(before_comment.lstrip())
|
||||
prefix = indent*' ' + '// '
|
||||
max_len = 80 - len(prefix)
|
||||
comment = line[loc + 2:].strip()
|
||||
segs = [seg for seg in re.split(r'(\w+\W*)', comment) if seg != '']
|
||||
cur_line = ''
|
||||
for seg in segs:
|
||||
if len((cur_line + seg).rstrip()) < max_len:
|
||||
cur_line += seg
|
||||
else:
|
||||
if cur_line.strip() != '':
|
||||
output.append(prefix + cur_line.rstrip())
|
||||
cur_line = seg.lstrip()
|
||||
if cur_line.strip() != '':
|
||||
output.append(prefix + cur_line.strip())
|
||||
|
||||
|
||||
def WrapCode(line, line_concat, output):
|
||||
indent = len(line) - len(line.lstrip())
|
||||
prefix = indent*' ' # Prefix of the current line
|
||||
max_len = 80 - indent - len(line_concat) # Maximum length of the current line
|
||||
new_prefix = prefix + 4*' ' # Prefix of a continuation line
|
||||
new_max_len = max_len - 4 # Maximum length of a continuation line
|
||||
# Prefers to wrap a line after a ',' or ';'.
|
||||
segs = [seg for seg in re.split(r'([^,;]+[,;]?)', line.strip()) if seg != '']
|
||||
cur_line = '' # The current line without leading spaces.
|
||||
for seg in segs:
|
||||
# If the line is still too long, wrap at a space.
|
||||
while cur_line == '' and len(seg.strip()) > max_len:
|
||||
seg = seg.lstrip()
|
||||
split_at = seg.rfind(' ', 0, max_len)
|
||||
output.append(prefix + seg[:split_at].strip() + line_concat)
|
||||
seg = seg[split_at + 1:]
|
||||
prefix = new_prefix
|
||||
max_len = new_max_len
|
||||
|
||||
if len((cur_line + seg).rstrip()) < max_len:
|
||||
cur_line = (cur_line + seg).lstrip()
|
||||
else:
|
||||
output.append(prefix + cur_line.rstrip() + line_concat)
|
||||
prefix = new_prefix
|
||||
max_len = new_max_len
|
||||
cur_line = seg.lstrip()
|
||||
if cur_line.strip() != '':
|
||||
output.append(prefix + cur_line.strip())
|
||||
|
||||
|
||||
def WrapPreprocessorDirective(line, output):
|
||||
WrapCode(line, ' \\', output)
|
||||
|
||||
|
||||
def WrapPlainCode(line, output):
|
||||
WrapCode(line, '', output)
|
||||
|
||||
|
||||
def IsMultiLineIWYUPragma(line):
|
||||
return re.search(r'/\* IWYU pragma: ', line)
|
||||
|
||||
|
||||
def IsHeaderGuardIncludeOrOneLineIWYUPragma(line):
|
||||
return (re.match(r'^#(ifndef|define|endif\s*//)\s*[\w_]+\s*$', line) or
|
||||
re.match(r'^#include\s', line) or
|
||||
# Don't break IWYU pragmas, either; that causes iwyu.py problems.
|
||||
re.search(r'// IWYU pragma: ', line))
|
||||
|
||||
|
||||
def WrapLongLine(line, output):
|
||||
line = line.rstrip()
|
||||
if len(line) <= 80:
|
||||
output.append(line)
|
||||
elif IsSingleLineComment(line):
|
||||
if IsHeaderGuardIncludeOrOneLineIWYUPragma(line):
|
||||
# The style guide made an exception to allow long header guard lines,
|
||||
# includes and IWYU pragmas.
|
||||
output.append(line)
|
||||
else:
|
||||
WrapComment(line, output)
|
||||
elif IsInPreprocessorDirective(output, line):
|
||||
if IsHeaderGuardIncludeOrOneLineIWYUPragma(line):
|
||||
# The style guide made an exception to allow long header guard lines,
|
||||
# includes and IWYU pragmas.
|
||||
output.append(line)
|
||||
else:
|
||||
WrapPreprocessorDirective(line, output)
|
||||
elif IsMultiLineIWYUPragma(line):
|
||||
output.append(line)
|
||||
else:
|
||||
WrapPlainCode(line, output)
|
||||
|
||||
|
||||
def BeautifyCode(string):
|
||||
lines = string.splitlines()
|
||||
output = []
|
||||
for line in lines:
|
||||
WrapLongLine(line, output)
|
||||
output2 = [line.rstrip() for line in output]
|
||||
return '\n'.join(output2) + '\n'
|
||||
|
||||
|
||||
def ConvertFromPumpSource(src_text):
|
||||
"""Return the text generated from the given Pump source text."""
|
||||
ast = ParseToAST(StripMetaComments(src_text))
|
||||
output = Output()
|
||||
RunCode(Env(), ast, output)
|
||||
return BeautifyCode(output.string)
|
||||
|
||||
|
||||
def main(argv):
|
||||
if len(argv) == 1:
|
||||
print __doc__
|
||||
sys.exit(1)
|
||||
|
||||
file_path = argv[-1]
|
||||
output_str = ConvertFromPumpSource(file(file_path, 'r').read())
|
||||
if file_path.endswith('.pump'):
|
||||
output_file_path = file_path[:-5]
|
||||
else:
|
||||
output_file_path = '-'
|
||||
if output_file_path == '-':
|
||||
print output_str,
|
||||
else:
|
||||
output_file = file(output_file_path, 'w')
|
||||
output_file.write('// This file was GENERATED by command:\n')
|
||||
output_file.write('// %s %s\n' %
|
||||
(os.path.basename(__file__), os.path.basename(file_path)))
|
||||
output_file.write('// DO NOT EDIT BY HAND!!!\n\n')
|
||||
output_file.write(output_str)
|
||||
output_file.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 875706f66008e03a0c7a699de16d7e2bde0efb90
|
Загрузка…
Ссылка в новой задаче