Commit 615add84 authored by vegorov@chromium.org's avatar vegorov@chromium.org

Expose APIs for detecting boxed primitives, native errors and Math.

While implementing structured clone I found that I need support
for detecting and creating objects using the builtin Number, String
and Boolean constructors; this CL adds this support. I also need
to be able to detect entities of "native object type (e.g., Error)",
hence the new IsNativeError() calls.

(ref: http://www.whatwg.org/specs/web-apps/current-work/multipage/urls.html#safe-passing-of-structured-data)

Patch by Luke Zarko.

Review URL: http://codereview.chromium.org/7344013

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8653 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 3cbd1a1d
......@@ -80,9 +80,11 @@ namespace v8 {
class Context;
class String;
class StringObject;
class Value;
class Utils;
class Number;
class NumberObject;
class Object;
class Array;
class Int32;
......@@ -90,6 +92,7 @@ class Uint32;
class External;
class Primitive;
class Boolean;
class BooleanObject;
class Integer;
class Function;
class Date;
......@@ -928,6 +931,26 @@ class Value : public Data {
*/
V8EXPORT bool IsDate() const;
/**
* Returns true if this value is a Boolean object.
*/
V8EXPORT bool IsBooleanObject() const;
/**
* Returns true if this value is a Number object.
*/
V8EXPORT bool IsNumberObject() const;
/**
* Returns true if this value is a String object.
*/
V8EXPORT bool IsStringObject() const;
/**
* Returns true if this value is a NativeError.
*/
V8EXPORT bool IsNativeError() const;
/**
* Returns true if this value is a RegExp.
*/
......@@ -1744,6 +1767,63 @@ class Date : public Object {
};
/**
* A Number object (ECMA-262, 4.3.21).
*/
class NumberObject : public Object {
public:
V8EXPORT static Local<Value> New(double value);
/**
* Returns the Number held by the object.
*/
V8EXPORT double NumberValue() const;
static inline NumberObject* Cast(v8::Value* obj);
private:
V8EXPORT static void CheckCast(v8::Value* obj);
};
/**
* A Boolean object (ECMA-262, 4.3.15).
*/
class BooleanObject : public Object {
public:
V8EXPORT static Local<Value> New(bool value);
/**
* Returns the Boolean held by the object.
*/
V8EXPORT bool BooleanValue() const;
static inline BooleanObject* Cast(v8::Value* obj);
private:
V8EXPORT static void CheckCast(v8::Value* obj);
};
/**
* A String object (ECMA-262, 4.3.18).
*/
class StringObject : public Object {
public:
V8EXPORT static Local<Value> New(Handle<String> value);
/**
* Returns the String held by the object.
*/
V8EXPORT Local<String> StringValue() const;
static inline StringObject* Cast(v8::Value* obj);
private:
V8EXPORT static void CheckCast(v8::Value* obj);
};
/**
* An instance of the built-in RegExp constructor (ECMA-262, 15.10).
*/
......@@ -4010,6 +4090,30 @@ Date* Date::Cast(v8::Value* value) {
}
StringObject* StringObject::Cast(v8::Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);
#endif
return static_cast<StringObject*>(value);
}
NumberObject* NumberObject::Cast(v8::Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);
#endif
return static_cast<NumberObject*>(value);
}
BooleanObject* BooleanObject::Cast(v8::Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);
#endif
return static_cast<BooleanObject*>(value);
}
RegExp* RegExp::Cast(v8::Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);
......
......@@ -2167,6 +2167,65 @@ bool Value::IsDate() const {
}
bool Value::IsStringObject() const {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::Value::IsStringObject()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->HasSpecificClassOf(isolate->heap()->String_symbol());
}
bool Value::IsNumberObject() const {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::Value::IsNumberObject()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->HasSpecificClassOf(isolate->heap()->Number_symbol());
}
static i::Object* LookupBuiltin(i::Isolate* isolate,
const char* builtin_name) {
i::Handle<i::String> symbol =
isolate->factory()->LookupAsciiSymbol(builtin_name);
i::Handle<i::JSBuiltinsObject> builtins = isolate->js_builtins_object();
return builtins->GetPropertyNoExceptionThrown(*symbol);
}
static bool CheckConstructor(i::Isolate* isolate,
i::Handle<i::JSObject> obj,
const char* class_name) {
return obj->map()->constructor() == LookupBuiltin(isolate, class_name);
}
bool Value::IsNativeError() const {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::Value::IsNativeError()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
if (obj->IsJSObject()) {
i::Handle<i::JSObject> js_obj(i::JSObject::cast(*obj));
return CheckConstructor(isolate, js_obj, "$Error") ||
CheckConstructor(isolate, js_obj, "$EvalError") ||
CheckConstructor(isolate, js_obj, "$RangeError") ||
CheckConstructor(isolate, js_obj, "$ReferenceError") ||
CheckConstructor(isolate, js_obj, "$SyntaxError") ||
CheckConstructor(isolate, js_obj, "$TypeError") ||
CheckConstructor(isolate, js_obj, "$URIError");
} else {
return false;
}
}
bool Value::IsBooleanObject() const {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::Value::IsBooleanObject()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->HasSpecificClassOf(isolate->heap()->Boolean_symbol());
}
bool Value::IsRegExp() const {
if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsRegExp()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
......@@ -2362,6 +2421,36 @@ void v8::Date::CheckCast(v8::Value* that) {
}
void v8::StringObject::CheckCast(v8::Value* that) {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::StringObject::Cast()")) return;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->HasSpecificClassOf(isolate->heap()->String_symbol()),
"v8::StringObject::Cast()",
"Could not convert to StringObject");
}
void v8::NumberObject::CheckCast(v8::Value* that) {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::NumberObject::Cast()")) return;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->HasSpecificClassOf(isolate->heap()->Number_symbol()),
"v8::NumberObject::Cast()",
"Could not convert to NumberObject");
}
void v8::BooleanObject::CheckCast(v8::Value* that) {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::BooleanObject::Cast()")) return;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->HasSpecificClassOf(isolate->heap()->Boolean_symbol()),
"v8::BooleanObject::Cast()",
"Could not convert to BooleanObject");
}
void v8::RegExp::CheckCast(v8::Value* that) {
if (IsDeadCheck(i::Isolate::Current(), "v8::RegExp::Cast()")) return;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
......@@ -4427,6 +4516,73 @@ Local<v8::Object> v8::Object::New() {
}
Local<v8::Value> v8::NumberObject::New(double value) {
i::Isolate* isolate = i::Isolate::Current();
EnsureInitializedForIsolate(isolate, "v8::NumberObject::New()");
LOG_API(isolate, "NumberObject::New");
ENTER_V8(isolate);
i::Handle<i::Object> number = isolate->factory()->NewNumber(value);
i::Handle<i::Object> obj = isolate->factory()->ToObject(number);
return Utils::ToLocal(obj);
}
double v8::NumberObject::NumberValue() const {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::NumberObject::NumberValue()")) return 0;
LOG_API(isolate, "NumberObject::NumberValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
return jsvalue->value()->Number();
}
Local<v8::Value> v8::BooleanObject::New(bool value) {
i::Isolate* isolate = i::Isolate::Current();
EnsureInitializedForIsolate(isolate, "v8::BooleanObject::New()");
LOG_API(isolate, "BooleanObject::New");
ENTER_V8(isolate);
i::Handle<i::Object> boolean(value ? isolate->heap()->true_value()
: isolate->heap()->false_value());
i::Handle<i::Object> obj = isolate->factory()->ToObject(boolean);
return Utils::ToLocal(obj);
}
bool v8::BooleanObject::BooleanValue() const {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::BooleanObject::BooleanValue()")) return 0;
LOG_API(isolate, "BooleanObject::BooleanValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
return jsvalue->value()->IsTrue();
}
Local<v8::Value> v8::StringObject::New(Handle<String> value) {
i::Isolate* isolate = i::Isolate::Current();
EnsureInitializedForIsolate(isolate, "v8::StringObject::New()");
LOG_API(isolate, "StringObject::New");
ENTER_V8(isolate);
i::Handle<i::Object> obj =
isolate->factory()->ToObject(Utils::OpenHandle(*value));
return Utils::ToLocal(obj);
}
Local<v8::String> v8::StringObject::StringValue() const {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::StringObject::StringValue()")) {
return Local<v8::String>();
}
LOG_API(isolate, "StringObject::StringValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
return Utils::ToLocal(
i::Handle<i::String>(i::String::cast(jsvalue->value())));
}
Local<v8::Value> v8::Date::New(double time) {
i::Isolate* isolate = i::Isolate::Current();
EnsureInitializedForIsolate(isolate, "v8::Date::New()");
......
......@@ -1026,6 +1026,90 @@ THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
}
THREADED_TEST(IsNativeError) {
v8::HandleScope scope;
LocalContext env;
v8::Handle<Value> syntax_error = CompileRun(
"var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; ");
CHECK(syntax_error->IsNativeError());
v8::Handle<Value> not_error = CompileRun("{a:42}");
CHECK(!not_error->IsNativeError());
v8::Handle<Value> not_object = CompileRun("42");
CHECK(!not_object->IsNativeError());
}
THREADED_TEST(StringObject) {
v8::HandleScope scope;
LocalContext env;
v8::Handle<Value> boxed_string = CompileRun("new String(\"test\")");
CHECK(boxed_string->IsStringObject());
v8::Handle<Value> unboxed_string = CompileRun("\"test\"");
CHECK(!unboxed_string->IsStringObject());
v8::Handle<Value> boxed_not_string = CompileRun("new Number(42)");
CHECK(!boxed_not_string->IsStringObject());
v8::Handle<Value> not_object = CompileRun("0");
CHECK(!not_object->IsStringObject());
v8::Handle<v8::StringObject> as_boxed = boxed_string.As<v8::StringObject>();
CHECK(!as_boxed.IsEmpty());
Local<v8::String> the_string = as_boxed->StringValue();
CHECK(!the_string.IsEmpty());
ExpectObject("\"test\"", the_string);
v8::Handle<v8::Value> new_boxed_string = v8::StringObject::New(the_string);
CHECK(new_boxed_string->IsStringObject());
as_boxed = new_boxed_string.As<v8::StringObject>();
the_string = as_boxed->StringValue();
CHECK(!the_string.IsEmpty());
ExpectObject("\"test\"", the_string);
}
THREADED_TEST(NumberObject) {
v8::HandleScope scope;
LocalContext env;
v8::Handle<Value> boxed_number = CompileRun("new Number(42)");
CHECK(boxed_number->IsNumberObject());
v8::Handle<Value> unboxed_number = CompileRun("42");
CHECK(!unboxed_number->IsNumberObject());
v8::Handle<Value> boxed_not_number = CompileRun("new Boolean(false)");
CHECK(!boxed_not_number->IsNumberObject());
v8::Handle<v8::NumberObject> as_boxed = boxed_number.As<v8::NumberObject>();
CHECK(!as_boxed.IsEmpty());
double the_number = as_boxed->NumberValue();
CHECK_EQ(42.0, the_number);
v8::Handle<v8::Value> new_boxed_number = v8::NumberObject::New(43);
CHECK(new_boxed_number->IsNumberObject());
as_boxed = new_boxed_number.As<v8::NumberObject>();
the_number = as_boxed->NumberValue();
CHECK_EQ(43.0, the_number);
}
THREADED_TEST(BooleanObject) {
v8::HandleScope scope;
LocalContext env;
v8::Handle<Value> boxed_boolean = CompileRun("new Boolean(true)");
CHECK(boxed_boolean->IsBooleanObject());
v8::Handle<Value> unboxed_boolean = CompileRun("true");
CHECK(!unboxed_boolean->IsBooleanObject());
v8::Handle<Value> boxed_not_boolean = CompileRun("new Number(42)");
CHECK(!boxed_not_boolean->IsBooleanObject());
v8::Handle<v8::BooleanObject> as_boxed =
boxed_boolean.As<v8::BooleanObject>();
CHECK(!as_boxed.IsEmpty());
bool the_boolean = as_boxed->BooleanValue();
CHECK_EQ(true, the_boolean);
v8::Handle<v8::Value> boxed_true = v8::BooleanObject::New(true);
v8::Handle<v8::Value> boxed_false = v8::BooleanObject::New(false);
CHECK(boxed_true->IsBooleanObject());
CHECK(boxed_false->IsBooleanObject());
as_boxed = boxed_true.As<v8::BooleanObject>();
CHECK_EQ(true, as_boxed->BooleanValue());
as_boxed = boxed_false.As<v8::BooleanObject>();
CHECK_EQ(false, as_boxed->BooleanValue());
}
THREADED_TEST(Number) {
v8::HandleScope scope;
LocalContext env;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment