Skip to content

Commit

Permalink
mega-patch:
Browse files Browse the repository at this point in the history
- add error types (closes #9)
- type upcast/downcast methods (closes #5)
- variant type for pattern matching on JS values
- very carefully audited uses of Scopes in all APIs
- renamed Tagged to Any
- corrected the object type hierarchy: SomeObject is emulated "root" with Array and Function emulated "subtypes"
- all three object types implement the Object trait
- PropertyName abstraction allows overloaded get/set methods
- very carefully audited the length invariants for strings
  • Loading branch information
Dave Herman committed Dec 2, 2015
1 parent d1c8d63 commit 4aa23ed
Show file tree
Hide file tree
Showing 13 changed files with 574 additions and 236 deletions.
34 changes: 34 additions & 0 deletions crates/nanny-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ use std::os::raw::c_void;
use raw::{FunctionCallbackInfo, EscapableHandleScope, Isolate, Local};
use buf::Buf;

// analog C enum `tag_t` defined in nanny.h
#[repr(C)]
#[derive(PartialEq, Eq)]
pub enum Tag {
Null,
Undefined,
Boolean,
Integer,
Number,
String,
Object,
Array,
Function,
Other
}

extern "system" {
pub fn Nan_FunctionCallbackInfo_SetReturnValue(info: &FunctionCallbackInfo, value: Local);
pub fn Nan_FunctionCallbackInfo_GetIsolate(info: &FunctionCallbackInfo) -> &Isolate;
Expand All @@ -30,6 +46,9 @@ extern "system" {
pub fn Nan_NewArray(out: &mut Local, isolate: *mut Isolate, length: u32);
pub fn Node_ArraySet(array: &mut Local, index: u32, value: Local) -> bool;
pub fn Nan_Get_Index(out: &mut Local, object: &mut Local, index: u32) -> bool;
pub fn Nanny_Set_Index(out: &mut bool, object: &mut Local, index: u32, val: &mut Local) -> bool;
pub fn Nanny_Get_Bytes(out: &mut Local, object: &mut Local, key: *const u8, len: i32) -> bool;
pub fn Nanny_Set_Bytes(out: &mut bool, object: &mut Local, key: *const u8, len: i32, val: &mut Local) -> bool;
pub fn Nan_Get(out: &mut Local, object: &mut Local, key: &mut Local) -> bool;
pub fn Nan_Set(out: &mut bool, object: &mut Local, key: &mut Local, val: &Local) -> bool;
pub fn Node_ArrayLength(array: &Local) -> u32;
Expand All @@ -50,4 +69,19 @@ extern "system" {

pub fn Nanny_FunctionKernel(obj: &Local) -> *mut c_void;
pub fn Nanny_NewFunction(out: &mut Local, isolate: *mut c_void, callback: *mut c_void, kernel: *mut c_void) -> bool;
pub fn Nanny_TagOf(val: &Local) -> Tag;
pub fn Nanny_IsUndefined(val: &Local) -> bool;
pub fn Nanny_IsNull(val: &Local) -> bool;
pub fn Nanny_IsInteger(val: &Local) -> bool;
pub fn Nanny_IsNumber(val: &Local) -> bool;
pub fn Nanny_IsBoolean(val: &Local) -> bool;
pub fn Nanny_IsString(val: &Local) -> bool;
pub fn Nanny_IsObject(val: &Local) -> bool;
pub fn Nanny_IsArray(val: &Local) -> bool;
pub fn Nanny_IsFunction(val: &Local) -> bool;
pub fn Nanny_IsTypeError(val: &Local) -> bool;

pub fn Nanny_ThrowAny(val: &Local);
pub fn Nanny_NewTypeError(out: &mut Local, msg: *const u8) -> bool;
pub fn Nanny_ThrowTypeError(msg: *const u8);
}
106 changes: 106 additions & 0 deletions crates/nanny-sys/src/nanny.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,34 @@ extern "C" bool Nan_Get_Index(v8::Local<v8::Value> *out, v8::Local<v8::Object> *
return maybe.ToLocal(out);
}

extern "C" bool Nanny_Set_Index(bool *out, v8::Local<v8::Object> *object, uint32_t index, v8::Local<v8::Value> *val) {
Nan::Maybe<bool> maybe = Nan::Set(*object, index, *val);
return maybe.IsJust() && (*out = maybe.FromJust(), true);
}

extern "C" bool Nanny_Get_Bytes(v8::Local<v8::Value> *out, v8::Local<v8::Object> *obj, const uint8_t *data, int32_t len) {
Nan::HandleScope scope;
Nan::MaybeLocal<v8::String> maybe_key = v8::String::NewFromOneByte(v8::Isolate::GetCurrent(), data, v8::NewStringType::kNormal, len);
v8::Local<v8::String> key;
if (!maybe_key.ToLocal(&key)) {
return false;
}
Nan::MaybeLocal<v8::Value> maybe = Nan::Get(*obj, key);
return maybe.ToLocal(out);
}

extern "C" bool Nanny_Set_Bytes(bool *out, v8::Local<v8::Object> *obj, const uint8_t *data, int32_t len, v8::Local<v8::Value> *val) {
// FIXME: abstract the key construction logic to avoid duplication with ^^
Nan::HandleScope scope;
Nan::MaybeLocal<v8::String> maybe_key = v8::String::NewFromOneByte(v8::Isolate::GetCurrent(), data, v8::NewStringType::kNormal, len);
v8::Local<v8::String> key;
if (!maybe_key.ToLocal(&key)) {
return false;
}
Nan::Maybe<bool> maybe = Nan::Set(*obj, key, *val);
return maybe.IsJust() && (*out = maybe.FromJust(), true);
}

extern "C" bool Nan_Get(v8::Local<v8::Value> *out, v8::Local<v8::Object> *obj, v8::Local<v8::Value> *key) {
Nan::MaybeLocal<v8::Value> maybe = Nan::Get(*obj, *key);
return maybe.ToLocal(out);
Expand Down Expand Up @@ -190,3 +218,81 @@ extern "C" bool Nanny_NewFunction(v8::Local<v8::Function> *out, v8::Isolate *iso
extern "C" void *Nanny_FunctionKernel(v8::Local<v8::Object> *obj) {
return Nan::ObjectWrap::Unwrap<KernelWrapper>(*obj)->GetKernel();
}

extern "C" tag_t Nanny_TagOf(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return val->IsNull() ? tag_null
: val->IsUndefined() ? tag_undefined
: (val->IsTrue() || val->IsFalse()) ? tag_boolean
: (val->IsInt32() || val->IsUint32()) ? tag_integer // FIXME: this isn't right for large int64s
: val->IsNumber() ? tag_number
: val->IsString() ? tag_string
: val->IsArray() ? tag_array
: val->IsFunction() ? tag_function
: val->IsObject() ? tag_object
: tag_other;
}

extern "C" bool Nanny_IsUndefined(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return val->IsUndefined();
}

extern "C" bool Nanny_IsNull(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return val->IsNull();
}

extern "C" bool Nanny_IsInteger(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return val->IsInt32() || val->IsUint32();
}

extern "C" bool Nanny_IsNumber(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return val->IsNumber();
}

extern "C" bool Nanny_IsBoolean(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return val->IsBoolean();
}

extern "C" bool Nanny_IsString(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return val->IsString();
}

extern "C" bool Nanny_IsObject(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
// FIXME: is the null check superfluous?
return val->IsObject() && !val->IsNull();
}

extern "C" bool Nanny_IsArray(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return val->IsArray();
}

extern "C" bool Nanny_IsFunction(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return val->IsFunction();
}

extern "C" bool Nanny_IsTypeError(v8::Local<v8::Value> *p) {
v8::Local<v8::Value> val = *p;
return false; // FIXME: implement this
}

extern "C" void Nanny_ThrowAny(v8::Local<v8::Value> *val) {
Nan::ThrowError(*val);
}

extern "C" bool Nanny_NewTypeError(v8::Local<v8::Value> *out, const char *msg) {
*out = Nan::TypeError(msg);
return true;
}

extern "C" void Nanny_ThrowTypeError(const char *msg) {
Nan::ThrowTypeError(msg);
}
37 changes: 35 additions & 2 deletions crates/nanny-sys/src/nanny.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,24 @@
#include <v8.h>

typedef struct {
void* data;
size_t len;
void* data;
size_t len;
} buf_t;

// analog Rust enum `Tag` defined in lib.rs
typedef enum {
tag_null,
tag_undefined,
tag_boolean,
tag_integer,
tag_number,
tag_string,
tag_object,
tag_array,
tag_function,
tag_other
} tag_t;

extern "C" {

void Nan_FunctionCallbackInfo_SetReturnValue(Nan::FunctionCallbackInfo<v8::Value> *info, v8::Local<v8::Value> value);
Expand Down Expand Up @@ -35,6 +49,9 @@ extern "C" {
bool Node_ArraySet(v8::Local<v8::Array> *array, uint32_t index, v8::Local<v8::Value> value);
uint32_t Node_ArrayLength(v8::Local<v8::Array> *array);
bool Nan_Get_Index(v8::Local<v8::Value> *out, v8::Local<v8::Object> *object, uint32_t index);
bool Nanny_Set_Index(bool *out, v8::Local<v8::Object> *object, uint32_t index, v8::Local<v8::Value> *val);
bool Nanny_Get_Bytes(v8::Local<v8::Value> *out, v8::Local<v8::Object> *object, const uint8_t *key, int32_t len);
bool Nanny_Set_Bytes(bool *out, v8::Local<v8::Object> *object, const uint8_t *key, int32_t len, v8::Local<v8::Value> *val);
bool Nan_Get(v8::Local<v8::Value> *out, v8::Local<v8::Object> *object, v8::Local<v8::Value> *key);
bool Nan_Set(bool *out, v8::Local<v8::Object> *obj, v8::Local<v8::Value> *key, v8::Local<v8::Value> *val);

Expand All @@ -61,4 +78,20 @@ extern "C" {

bool Nanny_NewFunction(v8::Local<v8::Function> *out, v8::Isolate *isolate, Nan::FunctionCallback callback, void *kernel);
void *Nanny_FunctionKernel(v8::Local<v8::Object> *obj);

tag_t Nanny_TagOf(v8::Local<v8::Value> *val);
bool Nanny_IsUndefined(v8::Local<v8::Value> *val);
bool Nanny_IsNull(v8::Local<v8::Value> *val);
bool Nanny_IsBoolean(v8::Local<v8::Value> *val);
bool Nanny_IsInteger(v8::Local<v8::Value> *val);
bool Nanny_IsNumber(v8::Local<v8::Value> *val);
bool Nanny_IsString(v8::Local<v8::Value> *val);
bool Nanny_IsObject(v8::Local<v8::Value> *val);
bool Nanny_IsArray(v8::Local<v8::Value> *val);
bool Nanny_IsFunction(v8::Local<v8::Value> *val);
bool Nanny_IsTypeError(v8::Local<v8::Value> *val);

void Nanny_ThrowAny(v8::Local<v8::Value> *val);
bool Nanny_NewTypeError(v8::Local<v8::Value> *out, const char *msg);
void Nanny_ThrowTypeError(const char *msg);
}
1 change: 1 addition & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub use internal::error::{throw, TypeError};
51 changes: 16 additions & 35 deletions src/internal/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ use std::str;
use std::str::Utf8Error;

use vm::Throw;
use internal::value::{Value, Object, ObjectInternal, Tagged, TaggedInternal};
use internal::error::TypeError;
use internal::value::{SomeObject, Any, AnyInternal, Object, build};
use internal::mem::Handle;
use nanny_sys::raw;
use nanny_sys::{Nan_NewBuffer, Node_Buffer_Data, Node_Buffer_Value_HasInstance, Node_Buffer_Object_HasInstance};
use nanny_sys::{Nan_NewBuffer, Node_Buffer_Data, Node_Buffer_Value_HasInstance};
use scope::Scope;
use nanny_sys::buf::Buf;

Expand All @@ -28,8 +29,8 @@ impl IndexMut<usize> for Buffer {
}

impl Buffer {
pub fn new<'a, T: Scope<'a>>(_: &mut T, size: u32) -> Option<Handle<'a, Object>> {
Object::build_opt(|out| { unsafe { Nan_NewBuffer(out, size) } })
pub fn new<'a, T: Scope<'a>>(_: &mut T, size: u32) -> Result<Handle<'a, SomeObject>, Throw> {
build(|out| { unsafe { Nan_NewBuffer(out, size) } })
}

pub fn data(&self) -> Buf {
Expand All @@ -46,40 +47,12 @@ impl Buffer {

pub fn check_str(&self) -> Result<&str, Throw> {
self.as_str().map_err(|_| {
// FIXME: throw a type error
Throw
TypeError::throw::<()>("buffer contents are invalid UTF-8").err().unwrap()
})
}
}

impl Value {
pub fn as_buffer<'a, T: Scope<'a>>(&self, _: &mut T) -> Option<Handle<'a, Buffer>> {
if unsafe { Node_Buffer_Value_HasInstance(self.to_raw_ref()) } {
Some(self.cast(Buffer))
} else {
None
}
}
}

impl Object {
pub fn as_buffer<'a, T: Scope<'a>>(&self, _: &mut T) -> Option<Handle<'a, Buffer>> {
if unsafe { Node_Buffer_Object_HasInstance(self.to_raw_ref()) } {
Some(self.cast(Buffer))
} else {
None
}
}

pub fn check_buffer<'a, T: Scope<'a>>(&self, scope: &mut T) -> Result<Handle<'a, Buffer>, Throw> {
self.as_buffer(scope).ok_or_else(|| {
// FIXME: throw a type error
Throw
})
}
}

impl TaggedInternal for Buffer {
impl AnyInternal for Buffer {
fn to_raw_mut_ref(&mut self) -> &mut raw::Local {
let &mut Buffer(ref mut local) = self;
local
Expand All @@ -89,6 +62,14 @@ impl TaggedInternal for Buffer {
let &Buffer(ref local) = self;
local
}

fn from_raw(h: raw::Local) -> Self { Buffer(h) }

fn is_typeof<Other: Any>(other: Other) -> bool {
unsafe { Node_Buffer_Value_HasInstance(other.to_raw_ref()) }
}
}

impl Tagged for Buffer { }
impl Any for Buffer { }

impl Object for Buffer { }
63 changes: 63 additions & 0 deletions src/internal/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::mem;
use std::ffi::CString;

use nanny_sys::{Nanny_ThrowAny, Nanny_NewTypeError, Nanny_IsTypeError, Nanny_ThrowTypeError};
use nanny_sys::raw;

use internal::vm::{Throw, Result};
use internal::value::{SomeObject, Any, AnyInternal, Object, build};
use internal::mem::Handle;
use scope::Scope;

pub fn throw<'a, T: Any, U>(v: Handle<'a, T>) -> Result<U> {
unsafe {
Nanny_ThrowAny(v.to_raw_ref());
}
Err(Throw)
}

#[repr(C)]
#[derive(Clone, Copy)]
pub struct TypeError(raw::Local);

impl AnyInternal for TypeError {
fn to_raw_mut_ref(&mut self) -> &mut raw::Local {
let &mut TypeError(ref mut local) = self;
local
}

fn to_raw_ref(&self) -> &raw::Local {
let &TypeError(ref local) = self;
local
}

fn from_raw(h: raw::Local) -> Self { TypeError(h) }

fn is_typeof<Other: Any>(other: Other) -> bool {
unsafe { Nanny_IsTypeError(other.to_raw_ref()) }
}
}

impl Any for TypeError { }

impl Object for TypeError { }

fn message(msg: &str) -> CString {
CString::new(msg).ok().unwrap_or_else(|| { CString::new("").ok().unwrap() })
}

impl TypeError {
// FIXME: use an overload trait to allow either &str or value::String
pub fn new<'a, T: Scope<'a>>(_: &mut T, msg: &str) -> Result<Handle<'a, SomeObject>> {
let msg = &message(msg);
build(|out| { unsafe { Nanny_NewTypeError(out, mem::transmute(msg.as_ptr())) } })
}

pub fn throw<T>(msg: &str) -> Result<T> {
let msg = &message(msg);
unsafe {
Nanny_ThrowTypeError(mem::transmute(msg.as_ptr()));
}
Err(Throw)
}
}
Loading

0 comments on commit 4aa23ed

Please sign in to comment.