0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-12-23 15:50:11 -05:00

add Object + ObjectTemplate internal field support (#477)

The rusty_v8 API deviates slightly from the V8 C++ API because the
latter is definitely unsound when you pass in out-of-range indexes.
This commit is contained in:
Ben Noordhuis 2020-09-30 01:04:59 +02:00 committed by GitHub
parent 9bedd96d24
commit 6b90cbe499
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 137 additions and 0 deletions

View file

@ -733,6 +733,15 @@ const v8::Object* v8__ObjectTemplate__NewInstance(
ptr_to_local(&self)->NewInstance(ptr_to_local(&context)));
}
int v8__ObjectTemplate__InternalFieldCount(const v8::ObjectTemplate& self) {
return ptr_to_local(&self)->InternalFieldCount();
}
void v8__ObjectTemplate__SetInternalFieldCount(
const v8::ObjectTemplate& self, int value) {
ptr_to_local(&self)->SetInternalFieldCount(value);
}
const v8::Object* v8__Object__New(v8::Isolate* isolate) {
return local_to_ptr(v8::Object::New(isolate));
}
@ -866,6 +875,20 @@ MaybeBool v8__Object__DeleteIndex(const v8::Object& self,
ptr_to_local(&self)->Delete(ptr_to_local(&context), index));
}
int v8__Object__InternalFieldCount(const v8::Object& self) {
return ptr_to_local(&self)->InternalFieldCount();
}
const v8::Value* v8__Object__GetInternalField(const v8::Object& self,
int index) {
return local_to_ptr(ptr_to_local(&self)->GetInternalField(index));
}
void v8__Object__SetInternalField(const v8::Object& self, int index,
const v8::Value& value) {
ptr_to_local(&self)->SetInternalField(index, ptr_to_local(&value));
}
const v8::Array* v8__Array__New(v8::Isolate* isolate, int length) {
return local_to_ptr(v8::Array::New(isolate, length));
}

View file

@ -13,6 +13,7 @@ use crate::Name;
use crate::Object;
use crate::PropertyAttribute;
use crate::Value;
use std::convert::TryFrom;
extern "C" {
fn v8__Object__New(isolate: *mut Isolate) -> *const Object;
@ -107,6 +108,16 @@ extern "C" {
context: *const Context,
index: u32,
) -> MaybeBool;
fn v8__Object__InternalFieldCount(this: *const Object) -> int;
fn v8__Object__GetInternalField(
this: *const Object,
index: int,
) -> *const Value;
fn v8__Object__SetInternalField(
this: *const Object,
index: int,
value: *const Value,
);
fn v8__Array__New(isolate: *mut Isolate, length: int) -> *const Array;
fn v8__Array__New_with_elements(
@ -404,6 +415,48 @@ impl Object {
}
.into()
}
/// Gets the number of internal fields for this Object.
pub fn internal_field_count(&self) -> usize {
let count = unsafe { v8__Object__InternalFieldCount(self) };
usize::try_from(count).expect("bad internal field count") // Can't happen.
}
/// Gets the value from an internal field.
pub fn get_internal_field<'s>(
&self,
scope: &mut HandleScope<'s>,
index: usize,
) -> Option<Local<'s, Value>> {
// Trying to access out-of-bounds internal fields makes V8 abort
// in debug mode and access out-of-bounds memory in release mode.
// The C++ API takes an i32 but doesn't check for indexes < 0, which
// results in an out-of-bounds access in both debug and release mode.
if index < self.internal_field_count() {
if let Ok(index) = int::try_from(index) {
return unsafe {
scope.cast_local(|_| v8__Object__GetInternalField(self, index))
};
}
}
None
}
/// Sets the value in an internal field. Returns false when the index
/// is out of bounds, true otherwise.
pub fn set_internal_field(&self, index: usize, value: Local<Value>) -> bool {
// Trying to access out-of-bounds internal fields makes V8 abort
// in debug mode and access out-of-bounds memory in release mode.
// The C++ API takes an i32 but doesn't check for indexes < 0, which
// results in an out-of-bounds access in both debug and release mode.
if index < self.internal_field_count() {
if let Ok(index) = int::try_from(index) {
unsafe { v8__Object__SetInternalField(self, index, &*value) };
return true;
}
}
false
}
}
impl Array {

View file

@ -4,6 +4,7 @@ use crate::data::Name;
use crate::data::ObjectTemplate;
use crate::data::Template;
use crate::isolate::Isolate;
use crate::support::int;
use crate::support::MapFnTo;
use crate::Context;
use crate::Function;
@ -14,6 +15,7 @@ use crate::Object;
use crate::PropertyAttribute;
use crate::String;
use crate::NONE;
use std::convert::TryFrom;
extern "C" {
fn v8__Template__Set(
@ -44,6 +46,12 @@ extern "C" {
this: *const ObjectTemplate,
context: *const Context,
) -> *const Object;
fn v8__ObjectTemplate__InternalFieldCount(this: *const ObjectTemplate)
-> int;
fn v8__ObjectTemplate__SetInternalFieldCount(
this: *const ObjectTemplate,
value: int,
);
}
impl Template {
@ -132,4 +140,25 @@ impl ObjectTemplate {
})
}
}
/// Gets the number of internal fields for objects generated from
/// this template.
pub fn internal_field_count(&self) -> usize {
let count = unsafe { v8__ObjectTemplate__InternalFieldCount(self) };
usize::try_from(count).expect("bad internal field count") // Can't happen.
}
/// Sets the number of internal fields for objects generated from
/// this template.
pub fn set_internal_field_count(&self, value: usize) -> bool {
// The C++ API takes an i32 but trying to set a value < 0
// results in unpredictable behavior, hence we disallow it.
match int::try_from(value) {
Err(_) => false,
Ok(value) => {
unsafe { v8__ObjectTemplate__SetInternalFieldCount(self, value) };
true
}
}
}
}

View file

@ -1132,6 +1132,25 @@ fn json() {
}
}
#[test]
fn no_internal_field() {
let _setup_guard = setup();
let isolate = &mut v8::Isolate::new(Default::default());
{
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
let object = v8::Object::new(scope);
let value = v8::Integer::new(scope, 42).into();
assert_eq!(0, object.internal_field_count());
for index in &[0, 1, 1337] {
assert!(object.get_internal_field(scope, *index).is_none());
assert_eq!(false, object.set_internal_field(*index, value));
assert!(object.get_internal_field(scope, *index).is_none());
}
}
}
#[test]
fn object_template() {
let _setup_guard = setup();
@ -1142,11 +1161,23 @@ fn object_template() {
let function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback);
let name = v8::String::new(scope, "f").unwrap();
let attr = v8::READ_ONLY + v8::DONT_ENUM + v8::DONT_DELETE;
object_templ.set_internal_field_count(1);
object_templ.set_with_attr(name.into(), function_templ.into(), attr);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
let object = object_templ.new_instance(scope).unwrap();
assert!(!object.is_null_or_undefined());
assert_eq!(1, object.internal_field_count());
let value = object.get_internal_field(scope, 0).unwrap();
assert!(value.is_undefined());
let fortytwo = v8::Integer::new(scope, 42).into();
assert_eq!(true, object.set_internal_field(0, fortytwo));
let value = object.get_internal_field(scope, 0).unwrap();
assert!(value.same_value(fortytwo));
let name = v8::String::new(scope, "g").unwrap();
context.global(scope).define_own_property(
scope,
@ -1189,6 +1220,7 @@ fn object_template_from_function_template() {
function_templ.set_class_name(expected_class_name);
let object_templ =
v8::ObjectTemplate::new_from_template(scope, function_templ);
assert_eq!(0, object_templ.internal_field_count());
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
let object = object_templ.new_instance(scope).unwrap();