提交 2a5e18ff 编写于 作者: R Rafael Weinstein

Merge the tonic layer from skydart branch back to master

The tonic layer is analogous to "gin" for v8. It provides wrappers and helpers around common dart types and implements the basic infrastructure for allowing dart wrappers around sky DOM objects to have proper GC behavior.

R=abarth@chromium.org
TBR=eseidel
BUG=

Review URL: https://codereview.chromium.org/924593002
上级 1ad365c3
# Copyright 2015 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 file.
source_set("tonic") {
sources = [
"dart_api_scope.h",
"dart_builtin.cc",
"dart_builtin.h",
"dart_class_library.cc",
"dart_class_library.h",
"dart_class_provider.cc",
"dart_class_provider.h",
"dart_converter.cc",
"dart_converter.h",
"dart_error.cc",
"dart_error.h",
"dart_gc_context.cc",
"dart_gc_context.h",
"dart_gc_controller.cc",
"dart_gc_controller.h",
"dart_gc_visitor.cc",
"dart_gc_visitor.h",
"dart_isolate_scope.cc",
"dart_isolate_scope.h",
"dart_persistent_value.cc",
"dart_persistent_value.h",
"dart_state.cc",
"dart_state.h",
"dart_string.cc",
"dart_string.h",
"dart_string_cache.cc",
"dart_string_cache.h",
"dart_value.cc",
"dart_value.h",
"dart_wrappable.cc",
"dart_wrappable.h",
"dart_wrapper_info.h",
]
deps = [
"//base",
"//sky/engine/wtf",
]
public_deps = [
"//dart/runtime/bin:libdart_withcore",
]
}
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_API_SCOPE_H_
#define SKY_ENGINE_TONIC_DART_API_SCOPE_H_
#include "base/macros.h"
#include "dart/runtime/include/dart_api.h"
namespace blink {
class DartApiScope {
public:
DartApiScope() { Dart_EnterScope(); }
~DartApiScope() { Dart_ExitScope(); }
private:
DISALLOW_COPY_AND_ASSIGN(DartApiScope);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_API_SCOPE_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_builtin.h"
#include <stdlib.h>
#include <string.h>
#include "base/logging.h"
namespace blink {
DartBuiltin::DartBuiltin(const Natives* natives, size_t count)
: natives_(natives), count_(count) {
}
DartBuiltin::~DartBuiltin() {
}
Dart_NativeFunction DartBuiltin::Resolver(Dart_Handle name,
int argument_count,
bool* auto_setup_scope) const {
const char* function_name = nullptr;
Dart_Handle result = Dart_StringToCString(name, &function_name);
DART_CHECK_VALID(result);
DCHECK(function_name != nullptr);
DCHECK(auto_setup_scope != nullptr);
*auto_setup_scope = true;
for (size_t i = 0; i < count_; ++i) {
const Natives& entry = natives_[i];
if (!strcmp(function_name, entry.name) &&
(entry.argument_count == argument_count)) {
return entry.function;
}
}
return nullptr;
}
const uint8_t* DartBuiltin::Symbolizer(Dart_NativeFunction native_function) const {
for (size_t i = 0; i < count_; ++i) {
const Natives& entry = natives_[i];
if (entry.function == native_function)
return reinterpret_cast<const uint8_t*>(entry.name);
}
return nullptr;
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_BUILTIN_H_
#define SKY_ENGINE_TONIC_DART_BUILTIN_H_
#include "base/macros.h"
#include "dart/runtime/include/dart_api.h"
namespace blink {
class DartBuiltin {
public:
struct Natives {
const char* name;
Dart_NativeFunction function;
int argument_count;
};
DartBuiltin(const Natives* natives, size_t count);
~DartBuiltin();
Dart_NativeFunction Resolver(Dart_Handle name,
int argument_count,
bool* auto_setup_scope) const;
const uint8_t* Symbolizer(Dart_NativeFunction native_function) const;
private:
const Natives* natives_;
size_t count_;
DISALLOW_COPY_AND_ASSIGN(DartBuiltin);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_BUILTIN_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_class_library.h"
#include "base/logging.h"
#include "sky/engine/tonic/dart_wrapper_info.h"
namespace blink {
DartClassLibrary::DartClassLibrary() : provider_(nullptr) {
}
DartClassLibrary::~DartClassLibrary() {
// Note that we don't need to delete these persistent handles because this
// object lives as long as the isolate. The handles will get deleted when the
// isolate dies.
}
Dart_PersistentHandle DartClassLibrary::GetClass(const DartWrapperInfo& info) {
DCHECK(provider_);
const auto& result = cache_.add(&info, nullptr);
if (!result.isNewEntry)
return result.storedValue->value;
Dart_Handle class_handle = provider_->GetClassByName(info.interface_name);
result.storedValue->value = Dart_NewPersistentHandle(class_handle);
return result.storedValue->value;
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_CLASS_LIBRARY_H_
#define SKY_ENGINE_TONIC_DART_CLASS_LIBRARY_H_
#include "base/macros.h"
#include "dart/runtime/include/dart_api.h"
#include "sky/engine/tonic/dart_class_provider.h"
#include "sky/engine/wtf/HashMap.h"
namespace blink {
struct DartWrapperInfo;
class DartClassLibrary {
public:
explicit DartClassLibrary();
~DartClassLibrary();
void set_provider(DartClassProvider* provider) { provider_ = provider; }
Dart_PersistentHandle GetClass(const DartWrapperInfo& info);
private:
DartClassProvider* provider_;
HashMap<const DartWrapperInfo*, Dart_PersistentHandle> cache_;
DISALLOW_COPY_AND_ASSIGN(DartClassLibrary);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_CLASS_LIBRARY_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_class_provider.h"
namespace blink {
DartClassProvider::~DartClassProvider() {
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_CLASS_PROVIDER_H_
#define SKY_ENGINE_TONIC_DART_CLASS_PROVIDER_H_
#include "dart/runtime/include/dart_api.h"
namespace blink {
class DartClassProvider {
public:
virtual Dart_Handle GetClassByName(const char* class_name) = 0;
protected:
virtual ~DartClassProvider();
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_CLASS_PROVIDER_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_converter.h"
// This file exists to ensure dart_converter.h doesn't miss any dependencies.
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_CONVERTER_H_
#define SKY_ENGINE_TONIC_DART_CONVERTER_H_
#include <string>
#include "sky/engine/tonic/dart_state.h"
#include "sky/engine/tonic/dart_string.h"
#include "sky/engine/tonic/dart_string_cache.h"
#include "sky/engine/tonic/dart_value.h"
#include "sky/engine/wtf/text/StringUTF8Adaptor.h"
#include "sky/engine/wtf/text/WTFString.h"
namespace blink {
// DartConvert converts types back and forth from Sky to Dart. The template
// parameter |T| determines what kind of type conversion to perform.
template <typename T, typename Enable = void>
struct DartConverter {};
////////////////////////////////////////////////////////////////////////////////
// Boolean
template <>
struct DartConverter<bool> {
static Dart_Handle ToDart(bool val) { return Dart_NewBoolean(val); }
static void SetReturnValue(Dart_NativeArguments args, bool val) {
Dart_SetBooleanReturnValue(args, val);
}
static bool FromDart(Dart_Handle handle) {
bool result = 0;
Dart_BooleanValue(handle, &result);
return result;
}
static bool FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception) {
bool result = false;
Dart_GetNativeBooleanArgument(args, index, &result);
return result;
}
};
////////////////////////////////////////////////////////////////////////////////
// Numbers
template <typename T>
struct DartConverterInteger {
static Dart_Handle ToDart(T val) { return Dart_NewInteger(val); }
static void SetReturnValue(Dart_NativeArguments args, T val) {
Dart_SetIntegerReturnValue(args, val);
}
static T FromDart(Dart_Handle handle) {
int64_t result = 0;
Dart_IntegerToInt64(handle, &result);
return result;
}
static T FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception) {
int64_t result = 0;
Dart_GetNativeIntegerArgument(args, index, &result);
return result;
}
};
template <>
struct DartConverter<int> : public DartConverterInteger<int> {};
template <>
struct DartConverter<unsigned> : public DartConverterInteger<unsigned> {};
template <>
struct DartConverter<long long> : public DartConverterInteger<long long> {};
template <>
struct DartConverter<unsigned long long> {
static Dart_Handle ToDart(unsigned long long val) {
// FIXME: WebIDL unsigned long long is guaranteed to fit into 64-bit
// unsigned,
// so we need a dart API for constructing an integer from uint64_t.
DCHECK(val <= 0x7fffffffffffffffLL);
return Dart_NewInteger(static_cast<int64_t>(val));
}
static void SetReturnValue(Dart_NativeArguments args,
unsigned long long val) {
DCHECK(val <= 0x7fffffffffffffffLL);
Dart_SetIntegerReturnValue(args, val);
}
static unsigned long long FromDart(Dart_Handle handle) {
int64_t result = 0;
Dart_IntegerToInt64(handle, &result);
return result;
}
static unsigned long long FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception) {
int64_t result = 0;
Dart_GetNativeIntegerArgument(args, index, &result);
return result;
}
};
template <typename T>
struct DartConverterFloatingPoint {
static Dart_Handle ToDart(T val) { return Dart_NewDouble(val); }
static void SetReturnValue(Dart_NativeArguments args, T val) {
Dart_SetDoubleReturnValue(args, val);
}
static T FromDart(Dart_Handle handle) {
double result = 0;
Dart_DoubleValue(handle, &result);
return result;
}
static T FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception) {
double result = 0;
Dart_GetNativeDoubleArgument(args, index, &result);
return result;
}
};
template <>
struct DartConverter<float> : public DartConverterFloatingPoint<float> {};
template <>
struct DartConverter<double> : public DartConverterFloatingPoint<double> {};
////////////////////////////////////////////////////////////////////////////////
// Strings
template <>
struct DartConverter<String> {
static Dart_Handle ToDart(DartState* state, const String& val) {
if (val.isEmpty())
return Dart_EmptyString();
return Dart_HandleFromWeakPersistent(state->string_cache().Get(val.impl()));
}
static void SetReturnValue(Dart_NativeArguments args,
const String& val,
bool auto_scope = true) {
// TODO(abarth): What should we do with auto_scope?
if (val.isEmpty()) {
Dart_SetReturnValue(args, Dart_EmptyString());
return;
}
DartState* state = DartState::Current();
Dart_SetWeakHandleReturnValue(args, state->string_cache().Get(val.impl()));
}
static void SetReturnValueWithNullCheck(Dart_NativeArguments args,
const String& val,
bool auto_scope = true) {
if (val.isNull())
Dart_SetReturnValue(args, Dart_Null());
else
SetReturnValue(args, val, auto_scope);
}
static String FromDart(Dart_Handle handle) {
intptr_t char_size = 0;
intptr_t length = 0;
void* peer = nullptr;
Dart_Handle result =
Dart_StringGetProperties(handle, &char_size, &length, &peer);
if (peer)
return String(static_cast<StringImpl*>(peer));
if (Dart_IsError(result))
return String();
return ExternalizeDartString(handle);
}
static String FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception,
bool auto_scope = true) {
// TODO(abarth): What should we do with auto_scope?
void* peer = nullptr;
Dart_Handle handle = Dart_GetNativeStringArgument(args, index, &peer);
if (peer)
return reinterpret_cast<StringImpl*>(peer);
if (Dart_IsError(handle))
return String();
return ExternalizeDartString(handle);
}
static String FromArgumentsWithNullCheck(Dart_NativeArguments args,
int index,
Dart_Handle& exception,
bool auto_scope = true) {
// TODO(abarth): What should we do with auto_scope?
void* peer = nullptr;
Dart_Handle handle = Dart_GetNativeStringArgument(args, index, &peer);
if (peer)
return reinterpret_cast<StringImpl*>(peer);
if (Dart_IsError(handle) || Dart_IsNull(handle))
return String();
return ExternalizeDartString(handle);
}
};
template <>
struct DartConverter<AtomicString> {
static Dart_Handle ToDart(DartState* state, const AtomicString& val) {
return DartConverter<String>::ToDart(state, val.string());
}
};
////////////////////////////////////////////////////////////////////////////////
// Collections
template <typename T>
struct DartConverter<Vector<T>> {
static Dart_Handle ToDart(const Vector<T>& val) {
Dart_Handle list = Dart_NewList(val.size());
if (Dart_IsError(list))
return list;
for (size_t i = 0; i < val.size(); i++) {
Dart_Handle result =
Dart_ListSetAt(list, i, DartConverter<T>::ToDart(val[i]));
if (Dart_IsError(result))
return result;
}
return list;
}
static Vector<T> FromDart(Dart_Handle handle) {
Vector<T> result;
if (!Dart_IsList(handle))
return result;
intptr_t length = 0;
Dart_ListLength(handle, &length);
result.reserveCapacity(length);
for (intptr_t i = 0; i < length; ++i) {
Dart_Handle element = Dart_ListGetAt(handle, i);
DCHECK(!Dart_IsError(element));
result.append(DartConverter<T>::FromDart(element));
}
return result;
}
static Vector<T> FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception,
bool auto_scope = true) {
// TODO(abarth): What should we do with auto_scope?
return FromDart(Dart_GetNativeArgument(args, index));
}
};
////////////////////////////////////////////////////////////////////////////////
// DartValue
template <>
struct DartConverter<DartValue> {
static Dart_Handle ToDart(DartState* state, DartValue* val) {
return val->dart_value();
}
static void SetReturnValue(Dart_NativeArguments args, DartValue* val) {
Dart_SetReturnValue(args, val->dart_value());
}
};
////////////////////////////////////////////////////////////////////////////////
// Convience wrappers for commonly used conversions
inline Dart_Handle StringToDart(DartState* state, const String& val) {
return DartConverter<String>::ToDart(state, val);
}
inline Dart_Handle StringToDart(DartState* state, const AtomicString& val) {
return DartConverter<AtomicString>::ToDart(state, val);
}
inline String StringFromDart(Dart_Handle handle) {
return DartConverter<String>::FromDart(handle);
}
////////////////////////////////////////////////////////////////////////////////
// Convience wrappers using type inference for ease of code generation
template <typename T>
inline Dart_Handle VectorToDart(const Vector<T>& val) {
return DartConverter<Vector<T>>::ToDart(val);
}
template<typename T>
Dart_Handle ToDart(const T& object) {
return DartConverter<T>::ToDart(object);
}
////////////////////////////////////////////////////////////////////////////////
// std::string support (slower, but more convienent for some clients)
inline Dart_Handle StdStringToDart(const std::string& val) {
return Dart_NewStringFromUTF8(reinterpret_cast<const uint8_t*>(val.data()),
val.length());
}
inline std::string StdStringFromDart(Dart_Handle handle) {
String string = StringFromDart(handle);
StringUTF8Adaptor utf8(string);
return std::string(utf8.data(), utf8.length());
}
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_CONVERTER_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_error.h"
#include "base/logging.h"
namespace blink {
namespace DartError {
const char kInvalidArgument[] = "Invalid argument.";
} // namespace DartError
bool LogIfError(Dart_Handle handle) {
if (Dart_IsError(handle)) {
LOG(ERROR) << Dart_GetError(handle);
return true;
}
return false;
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_ERROR_H_
#define SKY_ENGINE_TONIC_DART_ERROR_H_
#include "dart/runtime/include/dart_api.h"
namespace blink {
namespace DartError {
extern const char kInvalidArgument[];
} // namespace DartError
bool LogIfError(Dart_Handle handle);
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_ERROR_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_gc_context.h"
namespace blink {
DartGCContext::DartGCContext() : builder_(Dart_NewWeakReferenceSetBuilder()) {
}
DartGCContext::~DartGCContext() {
}
Dart_WeakReferenceSet DartGCContext::AddToSetForRoot(
const void* root,
Dart_WeakPersistentHandle handle) {
const auto& it = references_.add(root, nullptr);
if (!it.isNewEntry) {
Dart_AppendToWeakReferenceSet(it.storedValue->value, handle, handle);
return it.storedValue->value;
}
it.storedValue->value = Dart_NewWeakReferenceSet(builder_, handle, handle);
return it.storedValue->value;
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_GC_CONTEXT_H_
#define SKY_ENGINE_TONIC_DART_GC_CONTEXT_H_
#include "base/macros.h"
#include "dart/runtime/include/dart_api.h"
#include "sky/engine/wtf/HashMap.h"
namespace blink {
class DartGCContext {
public:
DartGCContext();
~DartGCContext();
Dart_WeakReferenceSet AddToSetForRoot(const void* root,
Dart_WeakPersistentHandle handle);
private:
Dart_WeakReferenceSetBuilder builder_;
HashMap<const void*, Dart_WeakReferenceSet> references_;
DISALLOW_COPY_AND_ASSIGN(DartGCContext);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_GC_CONTEXT_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_gc_controller.h"
#include "dart/runtime/include/dart_api.h"
#include "sky/engine/tonic/dart_gc_context.h"
#include "sky/engine/tonic/dart_gc_visitor.h"
#include "sky/engine/tonic/dart_wrappable.h"
namespace blink {
namespace {
DartGCContext* g_gc_context = nullptr;
DartWrappable* GetWrappable(intptr_t* fields) {
return reinterpret_cast<DartWrappable*>(fields[DartWrappable::kPeerIndex]);
}
void Visit(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
intptr_t native_field_count,
intptr_t* native_fields) {
if (!native_field_count)
return;
DCHECK(native_field_count == DartWrappable::kNumberOfNativeFields);
DartGCVisitor visitor(g_gc_context);
GetWrappable(native_fields)->AcceptDartGCVisitor(visitor);
}
} // namespace
void DartGCPrologue() {
Dart_EnterScope();
DCHECK(!g_gc_context);
g_gc_context = new DartGCContext();
Dart_VisitPrologueWeakHandles(Visit);
}
void DartGCEpilogue() {
delete g_gc_context;
g_gc_context = nullptr;
Dart_ExitScope();
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_GC_CONTROLLER_H_
#define SKY_ENGINE_TONIC_DART_GC_CONTROLLER_H_
namespace blink {
void DartGCPrologue();
void DartGCEpilogue();
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_GC_CONTROLLER_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_gc_visitor.h"
#include "sky/engine/tonic/dart_gc_context.h"
namespace blink {
DartGCVisitor::DartGCVisitor(DartGCContext* context)
: context_(context), current_set_(nullptr) {
}
DartGCVisitor::~DartGCVisitor() {
}
void DartGCVisitor::AddToSetForRoot(const void* root,
Dart_WeakPersistentHandle handle) {
Dart_WeakReferenceSet set = context_->AddToSetForRoot(root, handle);
DCHECK(!current_set_ || current_set_ == set);
current_set_ = set;
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_GC_VISITOR_H_
#define SKY_ENGINE_TONIC_DART_GC_VISITOR_H_
#include "base/logging.h"
#include "dart/runtime/include/dart_api.h"
namespace blink {
class DartGCContext;
class DartGCVisitor {
public:
explicit DartGCVisitor(DartGCContext* context);
~DartGCVisitor();
bool have_found_set() const {
return !!current_set_;
}
Dart_WeakReferenceSet current_set() const {
DCHECK(have_found_set());
return current_set_;
}
void AddToSetForRoot(const void* root, Dart_WeakPersistentHandle handle);
private:
DartGCContext* context_;
Dart_WeakReferenceSet current_set_;
DISALLOW_COPY_AND_ASSIGN(DartGCVisitor);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_GC_VISITOR_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_isolate_scope.h"
namespace blink {
DartIsolateScope::DartIsolateScope(Dart_Isolate isolate) {
isolate_ = isolate;
previous_ = Dart_CurrentIsolate();
if (previous_ == isolate_)
return;
if (previous_)
Dart_ExitIsolate();
Dart_EnterIsolate(isolate_);
}
DartIsolateScope::~DartIsolateScope() {
DCHECK(Dart_CurrentIsolate() == isolate_);
if (previous_ == isolate_)
return;
Dart_ExitIsolate();
if (previous_)
Dart_EnterIsolate(previous_);
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_ISOLATE_SCOPE_H_
#define SKY_ENGINE_TONIC_DART_ISOLATE_SCOPE_H_
#include "base/logging.h"
#include "dart/runtime/include/dart_api.h"
namespace blink {
// DartIsolateScope is a helper class for entering and exiting a given isolate.
class DartIsolateScope {
public:
explicit DartIsolateScope(Dart_Isolate isolate);
~DartIsolateScope();
private:
Dart_Isolate isolate_;
Dart_Isolate previous_;
DISALLOW_COPY_AND_ASSIGN(DartIsolateScope);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_ISOLATE_SCOPE_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_persistent_value.h"
#include "sky/engine/tonic/dart_isolate_scope.h"
#include "sky/engine/tonic/dart_state.h"
namespace blink {
DartPersistentValue::DartPersistentValue() : value_(nullptr) {
}
DartPersistentValue::DartPersistentValue(DartState* dart_state,
Dart_Handle value)
: value_(nullptr) {
Set(dart_state, value);
}
DartPersistentValue::~DartPersistentValue() {
Clear();
}
void DartPersistentValue::Set(DartState* dart_state, Dart_Handle value) {
DCHECK(is_empty());
dart_state_ = dart_state->GetWeakPtr();
value_ = Dart_NewPersistentHandle(value);
}
void DartPersistentValue::Clear() {
if (!value_ || !dart_state_.get())
return;
DartIsolateScope scope(dart_state_->isolate());
Dart_DeletePersistentHandle(value_);
dart_state_ = base::WeakPtr<DartState>();
value_ = nullptr;
}
Dart_Handle DartPersistentValue::Release() {
if (!value_)
return nullptr;
Dart_Handle local = Dart_HandleFromPersistent(value_);
Clear();
return local;
}
}
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_PERSISTENT_VALUE_H_
#define SKY_ENGINE_TONIC_DART_PERSISTENT_VALUE_H_
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "dart/runtime/include/dart_api.h"
namespace blink {
class DartState;
// DartPersistentValue is a bookkeeping class to help pair calls to
// Dart_NewPersistentHandle with Dart_DeletePersistentHandle. Consider using
// this class instead of holding a Dart_PersistentHandle directly so that you
// don't leak the Dart_PersistentHandle.
class DartPersistentValue {
public:
DartPersistentValue();
DartPersistentValue(DartState* dart_state, Dart_Handle value);
~DartPersistentValue();
Dart_PersistentHandle value() const { return value_; }
bool is_empty() const { return !value_; }
void Set(DartState* dart_state, Dart_Handle value);
void Clear();
Dart_Handle Release();
const base::WeakPtr<DartState>& dart_state() const { return dart_state_; }
private:
base::WeakPtr<DartState> dart_state_;
Dart_PersistentHandle value_;
DISALLOW_COPY_AND_ASSIGN(DartPersistentValue);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_PERSISTENT_VALUE_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_state.h"
#include "sky/engine/tonic/dart_class_library.h"
#include "sky/engine/tonic/dart_string_cache.h"
#include "sky/engine/wtf/PassOwnPtr.h"
namespace blink {
DartState::Scope::Scope(DartState* dart_state) {
}
DartState::Scope::~Scope() {
}
DartState::DartState()
: isolate_(NULL),
class_library_(adoptPtr(new DartClassLibrary)),
string_cache_(adoptPtr(new DartStringCache)),
weak_factory_(this) {
}
DartState::~DartState() {
}
DartState* DartState::From(Dart_Isolate isolate) {
return static_cast<DartState*>(Dart_IsolateData(isolate));
}
DartState* DartState::Current() {
return static_cast<DartState*>(Dart_CurrentIsolateData());
}
base::WeakPtr<DartState> DartState::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_STATE_H_
#define SKY_ENGINE_TONIC_DART_STATE_H_
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/supports_user_data.h"
#include "dart/runtime/include/dart_api.h"
#include "sky/engine/wtf/OwnPtr.h"
#include "sky/engine/wtf/PassRefPtr.h"
#include "sky/engine/wtf/RefCounted.h"
namespace blink {
class DartStringCache;
class DartClassLibrary;
// DartState represents the state associated with a given Dart isolate. The
// lifetime of this object is controlled by the DartVM. If you want to hold a
// reference to a DartState instance, please hold a base::WeakPtr<DartState>.
//
// DartState is analogous to gin::PerIsolateData and JSC::ExecState.
class DartState : public base::SupportsUserData {
public:
class Scope {
public:
Scope(DartState* dart_state);
~Scope();
};
DartState();
virtual ~DartState();
static DartState* From(Dart_Isolate isolate);
static DartState* Current();
base::WeakPtr<DartState> GetWeakPtr();
Dart_Isolate isolate() { return isolate_; }
void set_isolate(Dart_Isolate isolate) {
CHECK(!isolate_);
isolate_ = isolate;
}
DartClassLibrary& class_library() { return *class_library_; }
DartStringCache& string_cache() { return *string_cache_; }
private:
Dart_Isolate isolate_;
OwnPtr<DartClassLibrary> class_library_;
OwnPtr<DartStringCache> string_cache_;
base::WeakPtrFactory<DartState> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(DartState);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_STATE_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_string.h"
#include "base/logging.h"
namespace blink {
namespace {
void FinalizeString(void* string_impl) {
DCHECK(string_impl);
reinterpret_cast<StringImpl*>(string_impl)->deref();
}
template <typename CharType>
String Externalize(Dart_Handle handle, intptr_t length) {
if (!length)
return StringImpl::empty();
CharType* buffer = nullptr;
RefPtr<StringImpl> string_impl =
StringImpl::createUninitialized(length, buffer);
string_impl->ref(); // Balanced in FinalizeString.
Dart_Handle result =
Dart_MakeExternalString(handle, buffer, length * sizeof(CharType),
string_impl.get(), FinalizeString);
DCHECK(!Dart_IsError(result));
return String(string_impl.release());
}
} // namespace
Dart_Handle CreateDartString(StringImpl* string_impl) {
if (!string_impl)
return Dart_EmptyString();
string_impl->ref(); // Balanced in FinalizeString.
if (string_impl->is8Bit()) {
return Dart_NewExternalLatin1String(string_impl->characters8(),
string_impl->length(), string_impl,
FinalizeString);
} else {
return Dart_NewExternalUTF16String(string_impl->characters16(),
string_impl->length(), string_impl,
FinalizeString);
}
}
String ExternalizeDartString(Dart_Handle handle) {
DCHECK(Dart_IsString(handle));
DCHECK(!Dart_IsExternalString(handle));
bool is_latin1 = Dart_IsStringLatin1(handle);
intptr_t length;
Dart_StringLength(handle, &length);
if (is_latin1)
return Externalize<LChar>(handle, length);
return Externalize<UChar>(handle, length);
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_STRING_H_
#define SKY_ENGINE_TONIC_DART_STRING_H_
#include "base/logging.h"
#include "dart/runtime/include/dart_api.h"
#include "sky/engine/wtf/text/WTFString.h"
namespace blink {
Dart_Handle CreateDartString(StringImpl* string_impl);
String ExternalizeDartString(Dart_Handle handle);
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_STRING_H_
// 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_string_cache.h"
#include "sky/engine/tonic/dart_state.h"
#include "sky/engine/tonic/dart_string.h"
namespace blink {
DartStringCache::DartStringCache() : last_dart_string_(nullptr) {
}
DartStringCache::~DartStringCache() {
}
Dart_WeakPersistentHandle DartStringCache::GetSlow(StringImpl* string_impl,
bool auto_scope) {
if (Dart_WeakPersistentHandle string = cache_.get(string_impl)) {
last_dart_string_ = string;
last_string_impl_ = string_impl;
return string;
}
if (!auto_scope)
Dart_EnterScope();
Dart_Handle string = CreateDartString(string_impl);
DCHECK(!Dart_IsError(string));
intptr_t size_in_bytes = string_impl->sizeInBytes();
Dart_WeakPersistentHandle wrapper = Dart_NewWeakPersistentHandle(
string, string_impl, size_in_bytes, FinalizeCacheEntry);
string_impl->ref(); // Balanced in FinalizeCacheEntry.
cache_.set(string_impl, wrapper);
last_dart_string_ = wrapper;
last_string_impl_ = string_impl;
if (!auto_scope)
Dart_ExitScope();
return wrapper;
}
void DartStringCache::FinalizeCacheEntry(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
DartState* state = reinterpret_cast<DartState*>(isolate_callback_data);
StringImpl* string_impl = reinterpret_cast<StringImpl*>(peer);
DartStringCache& cache = state->string_cache();
Dart_WeakPersistentHandle cached_handle = cache.cache_.take(string_impl);
ASSERT_UNUSED(cached_handle, handle == cached_handle);
if (cache.last_dart_string_ == handle) {
cache.last_dart_string_ = nullptr;
cache.last_string_impl_ = nullptr;
}
string_impl->deref();
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_STRING_CACHE_H_
#define SKY_ENGINE_TONIC_DART_STRING_CACHE_H_
#include "base/logging.h"
#include "dart/runtime/include/dart_api.h"
#include "sky/engine/wtf/HashMap.h"
#include "sky/engine/wtf/RefPtr.h"
#include "sky/engine/wtf/text/StringHash.h"
#include "sky/engine/wtf/text/StringImpl.h"
namespace blink {
// DartStringCache maintains a mapping between WTF Strings and Dart strings.
// When you create a Dart string from a WTF String, the underlying character
// data is shared between the two systems.
class DartStringCache {
public:
DartStringCache();
~DartStringCache();
Dart_WeakPersistentHandle Get(StringImpl* string_impl,
bool auto_scope = true) {
DCHECK(string_impl);
if (last_string_impl_.get() == string_impl)
return last_dart_string_;
return GetSlow(string_impl, auto_scope);
}
private:
Dart_WeakPersistentHandle GetSlow(StringImpl* string_impl, bool auto_scope);
static void FinalizeCacheEntry(void*, Dart_WeakPersistentHandle, void* peer);
typedef HashMap<StringImpl*, Dart_WeakPersistentHandle> StringCache;
StringCache cache_;
Dart_WeakPersistentHandle last_dart_string_;
RefPtr<StringImpl> last_string_impl_;
DISALLOW_COPY_AND_ASSIGN(DartStringCache);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_STRING_CACHE_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_value.h"
namespace blink {
DartValue::DartValue() {
}
DartValue::DartValue(DartState* dart_state, Dart_Handle value)
: dart_value_(dart_state, value) {
}
DartValue::~DartValue() {
}
bool DartValue::Equals(DartValue* other) const {
DCHECK(other);
if (is_empty())
return other->is_empty();
if (other->is_empty())
return false;
return Dart_IdentityEquals(dart_value(), other->dart_value());
}
void DartValue::Clear() {
dart_value_.Clear();
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_VALUE_H_
#define SKY_ENGINE_TONIC_DART_VALUE_H_
#include "base/logging.h"
#include "dart/runtime/include/dart_api.h"
#include "sky/engine/tonic/dart_persistent_value.h"
#include "sky/engine/tonic/dart_state.h"
#include "sky/engine/wtf/RefPtr.h"
namespace blink {
// DartValue is a convience wrapper around DartPersistentValue that lets clients
// use RefPtr to keep track of the number of references to the underlying Dart
// object. Be careful when retaining RefPtr<DartValue> in the heap because the
// VM's garbage collector cannot break cycles that involve the C++ heap, which
// can lead to memory leaks.
class DartValue : public RefCounted<DartValue> {
public:
static PassRefPtr<DartValue> Create(DartState* dart_state,
Dart_Handle value) {
return adoptRef(new DartValue(dart_state, value));
}
static PassRefPtr<DartValue> Create() { return adoptRef(new DartValue()); }
~DartValue();
Dart_Handle dart_value() const { return dart_value_.value(); }
bool is_empty() const { return !dart_value(); }
bool is_null() const {
DCHECK(!is_empty());
return Dart_IsNull(dart_value());
}
bool is_function() const {
DCHECK(!is_empty());
return Dart_IsClosure(dart_value());
}
bool Equals(DartValue* other) const;
void Clear();
private:
DartValue();
DartValue(DartState* dart_state, Dart_Handle value);
DartPersistentValue dart_value_;
DISALLOW_COPY_AND_ASSIGN(DartValue);
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_VALUE_H_
// Copyright 2015 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 file.
#include "sky/engine/config.h"
#include "sky/engine/tonic/dart_wrappable.h"
#include "sky/engine/tonic/dart_class_library.h"
#include "sky/engine/tonic/dart_error.h"
#include "sky/engine/tonic/dart_state.h"
#include "sky/engine/tonic/dart_wrapper_info.h"
namespace blink {
DartWrappable::~DartWrappable() {
CHECK(!dart_wrapper_);
}
void DartWrappable::AcceptDartGCVisitor(DartGCVisitor& visitor) const {
}
Dart_Handle DartWrappable::CreateDartWrapper(DartState* dart_state) {
DCHECK(!dart_wrapper_);
const DartWrapperInfo& info = GetDartWrapperInfo();
Dart_PersistentHandle type = dart_state->class_library().GetClass(info);
DCHECK(!LogIfError(type));
intptr_t native_fields[kNumberOfNativeFields];
native_fields[kPeerIndex] = reinterpret_cast<intptr_t>(this);
native_fields[kWrapperInfoIndex] = reinterpret_cast<intptr_t>(&info);
Dart_Handle wrapper =
Dart_AllocateWithNativeFields(type, kNumberOfNativeFields, native_fields);
DCHECK(!LogIfError(wrapper));
info.ref_object(this); // Balanced in FinalizeDartWrapper.
dart_wrapper_ = Dart_NewPrologueWeakPersistentHandle(
wrapper, this, info.size_in_bytes, &FinalizeDartWrapper);
return wrapper;
}
void DartWrappable::FinalizeDartWrapper(void* isolate_callback_data,
Dart_WeakPersistentHandle wrapper,
void* peer) {
DartWrappable* wrappable = reinterpret_cast<DartWrappable*>(peer);
wrappable->dart_wrapper_ = nullptr;
const DartWrapperInfo& info = wrappable->GetDartWrapperInfo();
info.deref_object(wrappable); // Balanced in CreateDartWrapper.
}
DartWrappable* DartConverterWrappable::FromDart(Dart_Handle handle) {
intptr_t* peer = 0;
Dart_Handle result =
Dart_GetNativeInstanceField(handle, DartWrappable::kPeerIndex, peer);
if (Dart_IsError(result))
return nullptr;
return reinterpret_cast<DartWrappable*>(peer);
}
DartWrappable* DartConverterWrappable::FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception) {
intptr_t native_fields[DartWrappable::kNumberOfNativeFields];
Dart_Handle result = Dart_GetNativeFieldsOfArgument(
args, index, DartWrappable::kNumberOfNativeFields, native_fields);
if (Dart_IsError(result)) {
exception = Dart_NewStringFromCString(DartError::kInvalidArgument);
return nullptr;
}
return reinterpret_cast<DartWrappable*>(
native_fields[DartWrappable::kPeerIndex]);
}
DartWrappable* DartConverterWrappable::FromArgumentsWithNullCheck(
Dart_NativeArguments args, int index, Dart_Handle& exception) {
Dart_Handle handle = Dart_GetNativeArgument(args, index);
if (Dart_IsNull(handle))
return nullptr;
intptr_t native_fields[DartWrappable::kNumberOfNativeFields];
Dart_Handle result = Dart_GetNativeFieldsOfArgument(
args, index, DartWrappable::kNumberOfNativeFields, native_fields);
if (Dart_IsError(result)) {
exception = Dart_NewStringFromCString(DartError::kInvalidArgument);
return nullptr;
}
return reinterpret_cast<DartWrappable*>(
native_fields[DartWrappable::kPeerIndex]);
}
} // namespace blink
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_WRAPPABLE_H_
#define SKY_ENGINE_TONIC_DART_WRAPPABLE_H_
#include "base/logging.h"
#include "base/template_util.h"
#include "dart/runtime/include/dart_api.h"
#include "sky/engine/tonic/dart_converter.h"
#include "sky/engine/tonic/dart_error.h"
#include "sky/engine/tonic/dart_state.h"
#include "sky/engine/tonic/dart_wrapper_info.h"
namespace blink {
class DartGCVisitor;
struct DartWrapperInfo;
// DartWrappable is a base class that you can inherit from in order to be
// exposed to Dart code as an interface.
class DartWrappable {
public:
enum DartNativeFields {
kPeerIndex, // Must be first to work with Dart_GetNativeReceiver.
kWrapperInfoIndex,
kNumberOfNativeFields,
};
DartWrappable() : dart_wrapper_(nullptr) {}
// Subclasses that wish to expose a new interface must override this function
// and provide information about their wrapper. There is no need to call your
// base class's implementation of this function.
virtual const DartWrapperInfo& GetDartWrapperInfo() const = 0;
// Subclasses that wish to integrate with the Dart garbage collector should
// override this function. Please call your base class's AcceptDartGCVisitor
// at the end of your override.
virtual void AcceptDartGCVisitor(DartGCVisitor& visitor) const;
Dart_Handle CreateDartWrapper(DartState* dart_state);
Dart_WeakPersistentHandle dart_wrapper() const { return dart_wrapper_; }
protected:
virtual ~DartWrappable();
private:
static void FinalizeDartWrapper(void* isolate_callback_data,
Dart_WeakPersistentHandle wrapper,
void* peer);
Dart_WeakPersistentHandle dart_wrapper_;
DISALLOW_COPY_AND_ASSIGN(DartWrappable);
};
#define DEFINE_WRAPPERTYPEINFO() \
public: \
const DartWrapperInfo& GetDartWrapperInfo() const override { \
return dart_wrapper_info_; \
} \
private: \
static const DartWrapperInfo& dart_wrapper_info_
struct DartConverterWrappable {
static DartWrappable* FromDart(Dart_Handle handle);
static DartWrappable* FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception);
static DartWrappable* FromArgumentsWithNullCheck(Dart_NativeArguments args,
int index,
Dart_Handle& exception);
};
template<typename T>
struct DartConverter<
T*,
typename base::enable_if<
base::is_convertible<T*, DartWrappable*>::value>::type> {
static Dart_Handle ToDart(DartWrappable* val) {
if (!val)
return Dart_Null();
if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper())
return Dart_HandleFromWeakPersistent(wrapper);
return val->CreateDartWrapper(DartState::Current());
}
static void SetReturnValue(Dart_NativeArguments args,
DartWrappable* val,
bool auto_scope = true) {
if (!val)
Dart_SetReturnValue(args, Dart_Null());
else if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper())
Dart_SetWeakHandleReturnValue(args, wrapper);
else
Dart_SetReturnValue(args, val->CreateDartWrapper(DartState::Current()));
}
static T* FromDart(Dart_Handle handle) {
// TODO(abarth): We're missing a type check.
return static_cast<T*>(DartConverterWrappable::FromDart(handle));
}
static T* FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception,
bool auto_scope = true) {
// TODO(abarth): We're missing a type check.
return static_cast<T*>(DartConverterWrappable::FromArguments(
args, index, exception));
}
static T* FromArgumentsWithNullCheck(Dart_NativeArguments args,
int index,
Dart_Handle& exception,
bool auto_scope = true) {
// TODO(abarth): We're missing a type check.
return static_cast<T*>(DartConverterWrappable::FromArgumentsWithNullCheck(
args, index, exception));
}
};
template<typename T>
struct DartConverter<RefPtr<T>> {
static Dart_Handle ToDart(RefPtr<T> val) {
return DartConverter<T*>::ToDart(val.get());
}
};
template<typename T>
static T* GetReceiver(Dart_NativeArguments args) {
intptr_t receiver;
Dart_Handle result = Dart_GetNativeReceiver(args, &receiver);
DCHECK(!Dart_IsError(result));
return static_cast<T*>(reinterpret_cast<DartWrappable*>(receiver));
}
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_WRAPPABLE_H_
// Copyright 2015 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 file.
#ifndef SKY_ENGINE_TONIC_DART_WRAPPER_INFO_H_
#define SKY_ENGINE_TONIC_DART_WRAPPER_INFO_H_
#include "base/macros.h"
namespace blink {
class DartWrappable;
typedef void (*DartWrappableAccepter)(DartWrappable*);
struct DartWrapperInfo {
const char* interface_name;
const size_t size_in_bytes;
const DartWrappableAccepter ref_object;
const DartWrappableAccepter deref_object;
private:
DartWrapperInfo(const DartWrapperInfo&) = delete;
DartWrapperInfo& operator=(const DartWrapperInfo&) = delete;
};
} // namespace blink
#endif // SKY_ENGINE_TONIC_DART_WRAPPER_INFO_H_
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册