1
0
Fork 0
mirror of https://github.com/rubyjs/therubyracer synced 2023-03-27 23:21:42 -04:00

Implement Object.SetAccessor, Has and Delete

This commit is contained in:
Georgy Angelov 2015-07-16 17:37:48 +00:00
parent 905037ea50
commit 07352d62c1
9 changed files with 387 additions and 55 deletions

27
ext/v8/enums.h Normal file
View file

@ -0,0 +1,27 @@
#include "rr.h"
namespace rr {
class Enums {
public:
static void Init() {
ClassBuilder("AccessControl").
defineConst("DEFAULT", INT2FIX(v8::DEFAULT)).
defineConst("ALL_CAN_READ", INT2FIX(v8::ALL_CAN_READ)).
defineConst("ALL_CAN_WRITE", INT2FIX(v8::ALL_CAN_WRITE)).
defineConst("PROHIBITS_OVERWRITING", INT2FIX(v8::PROHIBITS_OVERWRITING));
ClassBuilder("PropertyAttribute").
defineConst("None", INT2FIX(v8::None)).
defineConst("ReadOnly", INT2FIX(v8::ReadOnly)).
defineConst("DontEnum", INT2FIX(v8::DontEnum)).
defineConst("DontDelete", INT2FIX(v8::DontDelete));
}
template <class T>
static T fromRubyValue(VALUE rb_int) {
return (T)NUM2INT(rb_int);
}
};
};

View file

@ -9,6 +9,7 @@ using namespace rr;
extern "C" {
void Init_init() {
V8::Init();
Enums::Init();
Isolate::Init();
Handles::Init();
Context::Init();
@ -26,13 +27,14 @@ extern "C" {
Symbol::Init();
Function::Init();
FunctionCallbackInfo::Init();
PropertyCallbackInfo::Init();
PropertyCallbackInfoVoid::Init();
ReturnValue::Init();
Script::Init();
ScriptOrigin::Init();
Array::Init();
External::Init();
// Accessor::Init();
// Invocation::Init();
// Signature::Init();
// Date::Init();

View file

@ -1,7 +1,6 @@
#include "rr.h"
namespace rr {
void Object::Init() {
ClassBuilder("Object", Value::Class).
defineSingletonMethod("New", &New).
@ -9,6 +8,9 @@ namespace rr {
defineMethod("Set", &Set).
defineMethod("Get", &Get).
defineMethod("GetIdentityHash", &GetIdentityHash).
defineMethod("Has", &Has).
defineMethod("Delete", &Delete).
defineMethod("SetAccessor", &SetAccessor).
store(&Class);
}
@ -49,11 +51,53 @@ namespace rr {
VALUE Object::GetIdentityHash(VALUE self) {
Object object(self);
Locker lock(object.getIsolate());
Locker lock(object);
return INT2FIX(object->GetIdentityHash());
}
VALUE Object::Has(VALUE self, VALUE r_context, VALUE key) {
Object object(self);
Locker lock(object);
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
return Bool::Maybe(object->Has(Context(r_context), Uint32_t(key)));
} else {
return Bool::Maybe(object->Has(Context(r_context), *Value(key)));
}
}
VALUE Object::Delete(VALUE self, VALUE r_context, VALUE key) {
Object object(self);
Locker lock(object);
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
return Bool::Maybe(object->Delete(Context(r_context), Uint32_t(key)));
} else {
return Bool::Maybe(object->Delete(Context(r_context), *Value(key)));
}
}
VALUE Object::SetAccessor(int argc, VALUE* argv, VALUE self) {
VALUE r_context, name, getter, setter, data, settings, attribute;
rb_scan_args(argc, argv, "52", &r_context, &name, &getter, &setter, &data, &settings, &attribute);
Object object(self);
Context context(r_context);
Isolate isolate(object.getIsolate());
Locker lock(isolate);
return Bool::Maybe(object->SetAccessor(
context,
Name(name),
&PropertyCallbackInfo::invokeGetter,
RTEST(setter) ? &PropertyCallbackInfoVoid::invokeSetter : 0,
v8::MaybeLocal<v8::Value>(PropertyCallbackInfo::wrapData(isolate, getter, setter, data)),
RTEST(settings) ? Enums::fromRubyValue<v8::AccessControl>(settings) : v8::DEFAULT,
RTEST(attribute) ? Enums::fromRubyValue<v8::PropertyAttribute>(attribute) : v8::None
));
}
Object::operator VALUE() {
Isolate isolate(getIsolate());
Locker lock(isolate);
@ -89,4 +133,5 @@ namespace rr {
return Ref<v8::Object>::operator VALUE();
}
}

View file

@ -6,53 +6,14 @@ namespace rr {
class Object : public Ref<v8::Object> {
public:
static void Init();
static VALUE New(VALUE self, VALUE isolate);
static VALUE Set(VALUE self, VALUE context, VALUE key, VALUE value);
// static VALUE ForceSet(VALUE self, VALUE key, VALUE value);
static VALUE Get(VALUE self, VALUE context, VALUE key);
// static VALUE GetPropertyAttributes(VALUE self, VALUE key);
// static VALUE Has(VALUE self, VALUE key);
// static VALUE Delete(VALUE self, VALUE key);
// static VALUE ForceDelete(VALUE self, VALUE key);
// static VALUE SetAccessor(int argc, VALUE* argv, VALUE self);
// static VALUE GetPropertyNames(VALUE self);
// static VALUE GetOwnPropertyNames(VALUE self);
// static VALUE GetPrototype(VALUE self);
// static VALUE SetPrototype(VALUE self, VALUE prototype);
// static VALUE FindInstanceInPrototypeChain(VALUE self, VALUE impl);
// static VALUE ObjectProtoToString(VALUE self);
// static VALUE GetConstructorName(VALUE self);
// static VALUE InternalFieldCount(VALUE self);
// static VALUE GetInternalField(VALUE self, VALUE idx);
// static VALUE SetInternalField(VALUE self, VALUE idx, VALUE value);
// static VALUE HasOwnProperty(VALUE self, VALUE key);
// static VALUE HasRealNamedProperty(VALUE self, VALUE key);
// static VALUE HasRealIndexedProperty(VALUE self, VALUE idx);
// static VALUE HasRealNamedCallbackProperty(VALUE self, VALUE key);
// static VALUE GetRealNamedPropertyInPrototypeChain(VALUE self, VALUE key);
// static VALUE GetRealNamedProperty(VALUE self, VALUE key);
// static VALUE HasNamedLookupInterceptor(VALUE self);
// static VALUE HasIndexedLookupInterceptor(VALUE self);
// static VALUE TurnOnAccessCheck(VALUE self);
static VALUE New(VALUE self, VALUE rb_isolate);
static VALUE Set(VALUE self, VALUE r_context, VALUE key, VALUE value);
static VALUE Get(VALUE self, VALUE r_context, VALUE key);
static VALUE GetIdentityHash(VALUE self);
// static VALUE SetHiddenValue(VALUE self, VALUE key, VALUE value);
// static VALUE GetHiddenValue(VALUE self, VALUE key);
// static VALUE DeleteHiddenValue(VALUE self, VALUE key);
// static VALUE IsDirty(VALUE self);
// static VALUE Clone(VALUE self);
// static VALUE CreationContext(VALUE self);
// static VALUE SetIndexedPropertiesToPixelData(VALUE self, VALUE data, VALUE length);
// static VALUE GetIndexedPropertiesPixelData(VALUE self);
// static VALUE HasIndexedPropertiesInPixelData(VALUE self);
// static VALUE GetIndexedPropertiesPixelDataLength(VALUE self);
// static VALUE SetIndexedPropertiesToExternalArrayData(VALUE self);
// static VALUE HasIndexedPropertiesInExternalArrayData(VALUE self);
// static VALUE GetIndexedPropertiesExternalArrayData(VALUE self);
// static VALUE GetIndexedPropertiesExternalArrayDataType(VALUE self);
// static VALUE GetIndexedPropertiesExternalArrayDataLength(VALUE self);
// static VALUE IsCallable(VALUE self);
// static VALUE CallAsFunction(VALUE self, VALUE recv, VALUE argv);
// static VALUE CallAsConstructor(VALUE self, VALUE argv);
static VALUE Has(VALUE self, VALUE r_context, VALUE key);
static VALUE Delete(VALUE self, VALUE r_context, VALUE key);
static VALUE SetAccessor(int argc, VALUE* argv, VALUE self);
inline Object(VALUE value) : Ref<v8::Object>(value) {}
inline Object(v8::Isolate* isolate, v8::Handle<v8::Object> object) : Ref<v8::Object>(isolate, object) {}

View file

@ -0,0 +1,84 @@
// -*- mode: c++ -*-
#ifndef RR_PROPERTY_CALLBACK_VOID_H
#define RR_PROPERTY_CALLBACK_VOID_H
namespace rr {
typedef Wrapper<v8::PropertyCallbackInfo<void>> PropertyCallbackInfoVoidWrapper;
class PropertyCallbackInfoVoid : public PropertyCallbackInfoVoidWrapper {
public:
inline PropertyCallbackInfoVoid(v8::PropertyCallbackInfo<void> info) :
PropertyCallbackInfoVoidWrapper(info) {}
inline PropertyCallbackInfoVoid(VALUE self) : PropertyCallbackInfoVoidWrapper(self) {}
/**
* Call the Ruby code associated with this callback.
*
* Unpack the Ruby code, and the callback data from the C++
* callback data, and then invoke that code.
*
* Note: This function implements the `v8::AccessorNameSetterCallback` API.
*/
static void invokeSetter(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast<v8::Value>(info.Data());
v8::Local<v8::String> callback_key = v8::String::NewFromUtf8(isolate, "rr::setter");
VALUE code(External::unwrap(holder->GetHiddenValue(callback_key)));
VALUE rb_property;
if (property->IsSymbol()) {
rb_property = Symbol(isolate, v8::Local<v8::Symbol>::Cast(property));
} else {
rb_property = String(isolate, property->ToString());
}
Unlocker unlock(info.GetIsolate());
rb_funcall(
code, rb_intern("call"), 3,
rb_property,
(VALUE)Value::handleToRubyObject(isolate, value),
(VALUE)PropertyCallbackInfoVoid(info)
);
}
static VALUE This(VALUE self) {
PropertyCallbackInfoVoid info(self);
Locker lock(info->GetIsolate());
return Object(info->GetIsolate(), info->This());
}
static VALUE Data(VALUE self) {
PropertyCallbackInfoVoid info(self);
Isolate isolate(info->GetIsolate());
Locker lock(isolate);
v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast<v8::Value>(info->Data());
v8::Local<v8::String> data_key = v8::String::NewFromUtf8(isolate, "rr::data");
v8::Local<v8::Value> data(holder->GetHiddenValue(data_key));
return Value::handleToRubyObject(info->GetIsolate(), data);
}
static VALUE GetIsolate(VALUE self) {
PropertyCallbackInfoVoid info(self);
return Isolate(info->GetIsolate());
}
static inline void Init() {
ClassBuilder("PropertyCallbackVoidInfo").
defineMethod("This", &This).
defineMethod("Data", &Data).
defineMethod("GetIsolate", &GetIsolate).
store(&Class);
}
v8::Local<v8::Value> data;
};
}
#endif /* RR_PROPERTY_CALLBACK_VOID_H */

107
ext/v8/property-callback.h Normal file
View file

@ -0,0 +1,107 @@
// -*- mode: c++ -*-
#ifndef RR_PROPERTY_CALLBACK_H
#define RR_PROPERTY_CALLBACK_H
namespace rr {
typedef Wrapper<v8::PropertyCallbackInfo<v8::Value>> PropertyCallbackInfoWrapper;
class PropertyCallbackInfo : public PropertyCallbackInfoWrapper {
public:
inline PropertyCallbackInfo(v8::PropertyCallbackInfo<v8::Value> info) :
PropertyCallbackInfoWrapper(info) {}
inline PropertyCallbackInfo(VALUE self) : PropertyCallbackInfoWrapper(self) {}
/**
* Package up the callback data for this object so that it can
* invoke Ruby callables.
*
* Each accessor can have one `v8::Value` associated with it
* that is passed to the `SetAccessor` method. To support this
* same API from ruby, we take the passed data constructor *and*
* the callbacks and store them *both* in a single `v8::Object`
* which we use for the C++ level callback data.
*/
static v8::Local<v8::Value> wrapData(v8::Isolate* isolate, VALUE r_getter, VALUE r_setter, VALUE r_data) {
v8::Local<v8::Object> holder = v8::Object::New(isolate);
v8::Local<v8::String> getter_key = v8::String::NewFromUtf8(isolate, "rr::getter");
v8::Local<v8::String> setter_key = v8::String::NewFromUtf8(isolate, "rr::setter");
v8::Local<v8::String> data_key = v8::String::NewFromUtf8(isolate, "rr::data");
holder->SetHiddenValue(getter_key, External::wrap(isolate, r_getter));
holder->SetHiddenValue(setter_key, External::wrap(isolate, r_setter));
holder->SetHiddenValue(data_key, Value(r_data));
return holder;
}
/**
* Call the Ruby code associated with this callback.
*
* Unpack the Ruby code, and the callback data from the C++
* callback data, and then invoke that code.
*
* Note: This function implements the `v8::AccessorNameGetterCallback` API.
*/
static void invokeGetter(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast<v8::Value>(info.Data());
v8::Local<v8::String> callback_key = v8::String::NewFromUtf8(isolate, "rr::getter");
VALUE code(External::unwrap(holder->GetHiddenValue(callback_key)));
VALUE rb_property;
if (property->IsSymbol()) {
rb_property = Symbol(isolate, v8::Local<v8::Symbol>::Cast(property));
} else {
rb_property = String(isolate, property->ToString());
}
Unlocker unlock(info.GetIsolate());
rb_funcall(code, rb_intern("call"), 2, rb_property, (VALUE)PropertyCallbackInfo(info));
}
static VALUE This(VALUE self) {
PropertyCallbackInfo info(self);
Locker lock(info->GetIsolate());
return Object(info->GetIsolate(), info->This());
}
static VALUE Data(VALUE self) {
PropertyCallbackInfo info(self);
Isolate isolate(info->GetIsolate());
Locker lock(isolate);
v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast<v8::Value>(info->Data());
v8::Local<v8::String> data_key = v8::String::NewFromUtf8(isolate, "rr::data");
v8::Local<v8::Value> data(holder->GetHiddenValue(data_key));
return Value::handleToRubyObject(info->GetIsolate(), data);
}
static VALUE GetIsolate(VALUE self) {
PropertyCallbackInfo info(self);
return Isolate(info->GetIsolate());
}
static VALUE GetReturnValue(VALUE self) {
PropertyCallbackInfo info(self);
Locker lock(info->GetIsolate());
return ReturnValue(info->GetReturnValue());
}
static inline void Init() {
ClassBuilder("PropertyCallbackInfo").
defineMethod("This", &This).
defineMethod("Data", &Data).
defineMethod("GetIsolate", &GetIsolate).
defineMethod("GetReturnValue", &GetReturnValue).
store(&Class);
}
};
}
#endif /* RR_PROPERTY_CALLBACK_H */

View file

@ -17,6 +17,8 @@ inline VALUE not_implemented(const char* message) {
}
#include "class_builder.h"
#include "enums.h"
#include "maybe.h"
#include "equiv.h"
#include "bool.h"
@ -34,25 +36,29 @@ inline VALUE not_implemented(const char* message) {
#include "value.h"
#include "boolean.h"
#include "object.h"
#include "array.h"
#include "primitive.h"
#include "undefined.h"
#include "null.h"
#include "number.h"
#include "integer.h"
#include "external.h"
// This one is named v8_string to avoid name collisions with C's string.h
#include "name.h"
// This one is named v8_string to avoid name collisions with C's string.h
#include "rr_string.h"
#include "symbol.h"
#include "function.h"
#include "object.h"
#include "return-value.h"
#include "property-callback-void.h"
#include "property-callback.h"
#include "array.h"
#include "script.h"
#include "script-origin.h"
#include "function.h"
#include "return-value.h"
#include "function-callback.h"
#endif

21
spec/c/enum_spec.rb Normal file
View file

@ -0,0 +1,21 @@
require 'c_spec_helper'
describe V8::C do
describe 'AccessControl' do
it 'has all enum values' do
expect(V8::C::AccessControl.const_defined? 'DEFAULT').to be true
expect(V8::C::AccessControl.const_defined? 'ALL_CAN_READ').to be true
expect(V8::C::AccessControl.const_defined? 'ALL_CAN_WRITE').to be true
expect(V8::C::AccessControl.const_defined? 'PROHIBITS_OVERWRITING').to be true
end
end
describe 'PropertyAttribute' do
it 'has all enum values' do
expect(V8::C::PropertyAttribute.const_defined? 'None').to be true
expect(V8::C::PropertyAttribute.const_defined? 'ReadOnly').to be true
expect(V8::C::PropertyAttribute.const_defined? 'DontEnum').to be true
expect(V8::C::PropertyAttribute.const_defined? 'DontDelete').to be true
end
end
end

View file

@ -21,6 +21,85 @@ describe V8::C::Object do
expect(maybe.FromJust().Utf8Value).to eq 'bar'
end
it 'can determine if a key has been set' do
o = V8::C::Object.New(@isolate)
key = V8::C::String.NewFromUtf8(@isolate, 'foo')
o.Set(@ctx, key, key)
maybe = o.Has(@ctx, key)
expect(maybe.IsJust()).to be true
expect(maybe.FromJust()).to eq true
end
it 'can delete keys' do
o = V8::C::Object.New(@isolate)
key = V8::C::String.NewFromUtf8(@isolate, 'foo')
o.Set(@ctx, key, key)
maybe = o.Delete(@ctx, key)
expect(maybe.IsJust()).to be true
expect(maybe.FromJust()).to eq true
maybe = o.Has(@ctx, key)
expect(maybe.IsJust()).to be true
expect(maybe.FromJust()).to eq false
end
describe '#SetAccessor' do
it 'can set getters' do
o = V8::C::Object.New(@isolate)
key = V8::C::String.NewFromUtf8(@isolate, 'foo')
data = V8::C::String.NewFromUtf8(@isolate, 'data')
get_value = V8::C::String.NewFromUtf8(@isolate, 'bar')
get_name = nil
get_data = nil
getter = proc do |name, info|
get_name = name
get_data = info.Data
info.GetReturnValue.Set(get_value)
end
o.SetAccessor(@ctx, key, getter, nil, data)
maybe = o.Get(@ctx, key)
expect(maybe.IsJust).to be true
expect(maybe.FromJust.StrictEquals(get_value)).to be true
expect(get_name.Equals(key)).to be true
expect(get_data.StrictEquals(data)).to be true
end
it 'can set setters' do
o = V8::C::Object.New(@isolate)
key = V8::C::String.NewFromUtf8(@isolate, 'foo')
data = V8::C::String.NewFromUtf8(@isolate, 'data')
set_value = nil
set_data = nil
setter = proc do |name, value, info|
set_data = info.Data
set_value = value
end
o.SetAccessor(@ctx, key, proc { }, setter, data)
maybe = o.Set(@ctx, key, data)
expect(maybe.IsJust).to be true
expect(maybe.FromJust).to be true
expect(set_data.StrictEquals(data)).to be true
expect(set_value.StrictEquals(data)).to be true
end
end
# TODO: Enable this when the methods are implemented in the extension
# it 'can retrieve all property names' do
# o = V8::C::Object.New