From 615add84639fe78656fb2741b75f4a77a32ad321 Mon Sep 17 00:00:00 2001 From: "vegorov@chromium.org" Date: Thu, 14 Jul 2011 11:00:04 +0000 Subject: [PATCH] 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 --- include/v8.h | 104 ++++++++++++++++++++++++++++++++ src/api.cc | 156 ++++++++++++++++++++++++++++++++++++++++++++++++ test/cctest/test-api.cc | 84 ++++++++++++++++++++++++++ 3 files changed, 344 insertions(+) diff --git a/include/v8.h b/include/v8.h index 0872411..343be1b 100644 --- a/include/v8.h +++ b/include/v8.h @@ -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; @@ -929,6 +932,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. */ V8EXPORT bool IsRegExp() const; @@ -1745,6 +1768,63 @@ class Date : public Object { /** + * A Number object (ECMA-262, 4.3.21). + */ +class NumberObject : public Object { + public: + V8EXPORT static Local 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 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 New(Handle value); + + /** + * Returns the String held by the object. + */ + V8EXPORT Local 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). */ class RegExp : public Object { @@ -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(value); +} + + +NumberObject* NumberObject::Cast(v8::Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); +} + + +BooleanObject* BooleanObject::Cast(v8::Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); +} + + RegExp* RegExp::Cast(v8::Value* value) { #ifdef V8_ENABLE_CHECKS CheckCast(value); diff --git a/src/api.cc b/src/api.cc index dc1f90c..11d1922 100644 --- a/src/api.cc +++ b/src/api.cc @@ -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 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 obj = Utils::OpenHandle(this); + return obj->HasSpecificClassOf(isolate->heap()->Number_symbol()); +} + + +static i::Object* LookupBuiltin(i::Isolate* isolate, + const char* builtin_name) { + i::Handle symbol = + isolate->factory()->LookupAsciiSymbol(builtin_name); + i::Handle builtins = isolate->js_builtins_object(); + return builtins->GetPropertyNoExceptionThrown(*symbol); +} + + +static bool CheckConstructor(i::Isolate* isolate, + i::Handle 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 obj = Utils::OpenHandle(this); + if (obj->IsJSObject()) { + i::Handle 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 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 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 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 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 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 obj = Utils::OpenHandle(that); @@ -4427,6 +4516,73 @@ Local v8::Object::New() { } +Local 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 number = isolate->factory()->NewNumber(value); + i::Handle 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 obj = Utils::OpenHandle(this); + i::Handle jsvalue = i::Handle::cast(obj); + return jsvalue->value()->Number(); +} + + +Local 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 boolean(value ? isolate->heap()->true_value() + : isolate->heap()->false_value()); + i::Handle 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 obj = Utils::OpenHandle(this); + i::Handle jsvalue = i::Handle::cast(obj); + return jsvalue->value()->IsTrue(); +} + + +Local v8::StringObject::New(Handle value) { + i::Isolate* isolate = i::Isolate::Current(); + EnsureInitializedForIsolate(isolate, "v8::StringObject::New()"); + LOG_API(isolate, "StringObject::New"); + ENTER_V8(isolate); + i::Handle obj = + isolate->factory()->ToObject(Utils::OpenHandle(*value)); + return Utils::ToLocal(obj); +} + + +Local v8::StringObject::StringValue() const { + i::Isolate* isolate = i::Isolate::Current(); + if (IsDeadCheck(isolate, "v8::StringObject::StringValue()")) { + return Local(); + } + LOG_API(isolate, "StringObject::StringValue"); + i::Handle obj = Utils::OpenHandle(this); + i::Handle jsvalue = i::Handle::cast(obj); + return Utils::ToLocal( + i::Handle(i::String::cast(jsvalue->value()))); +} + + Local v8::Date::New(double time) { i::Isolate* isolate = i::Isolate::Current(); EnsureInitializedForIsolate(isolate, "v8::Date::New()"); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 8d8770f..9769fb7 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -1026,6 +1026,90 @@ THREADED_TEST(OutOfSignedRangeUnsignedInteger) { } +THREADED_TEST(IsNativeError) { + v8::HandleScope scope; + LocalContext env; + v8::Handle syntax_error = CompileRun( + "var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; "); + CHECK(syntax_error->IsNativeError()); + v8::Handle not_error = CompileRun("{a:42}"); + CHECK(!not_error->IsNativeError()); + v8::Handle not_object = CompileRun("42"); + CHECK(!not_object->IsNativeError()); +} + + +THREADED_TEST(StringObject) { + v8::HandleScope scope; + LocalContext env; + v8::Handle boxed_string = CompileRun("new String(\"test\")"); + CHECK(boxed_string->IsStringObject()); + v8::Handle unboxed_string = CompileRun("\"test\""); + CHECK(!unboxed_string->IsStringObject()); + v8::Handle boxed_not_string = CompileRun("new Number(42)"); + CHECK(!boxed_not_string->IsStringObject()); + v8::Handle not_object = CompileRun("0"); + CHECK(!not_object->IsStringObject()); + v8::Handle as_boxed = boxed_string.As(); + CHECK(!as_boxed.IsEmpty()); + Local the_string = as_boxed->StringValue(); + CHECK(!the_string.IsEmpty()); + ExpectObject("\"test\"", the_string); + v8::Handle new_boxed_string = v8::StringObject::New(the_string); + CHECK(new_boxed_string->IsStringObject()); + as_boxed = new_boxed_string.As(); + the_string = as_boxed->StringValue(); + CHECK(!the_string.IsEmpty()); + ExpectObject("\"test\"", the_string); +} + + +THREADED_TEST(NumberObject) { + v8::HandleScope scope; + LocalContext env; + v8::Handle boxed_number = CompileRun("new Number(42)"); + CHECK(boxed_number->IsNumberObject()); + v8::Handle unboxed_number = CompileRun("42"); + CHECK(!unboxed_number->IsNumberObject()); + v8::Handle boxed_not_number = CompileRun("new Boolean(false)"); + CHECK(!boxed_not_number->IsNumberObject()); + v8::Handle as_boxed = boxed_number.As(); + CHECK(!as_boxed.IsEmpty()); + double the_number = as_boxed->NumberValue(); + CHECK_EQ(42.0, the_number); + v8::Handle new_boxed_number = v8::NumberObject::New(43); + CHECK(new_boxed_number->IsNumberObject()); + as_boxed = new_boxed_number.As(); + the_number = as_boxed->NumberValue(); + CHECK_EQ(43.0, the_number); +} + + +THREADED_TEST(BooleanObject) { + v8::HandleScope scope; + LocalContext env; + v8::Handle boxed_boolean = CompileRun("new Boolean(true)"); + CHECK(boxed_boolean->IsBooleanObject()); + v8::Handle unboxed_boolean = CompileRun("true"); + CHECK(!unboxed_boolean->IsBooleanObject()); + v8::Handle boxed_not_boolean = CompileRun("new Number(42)"); + CHECK(!boxed_not_boolean->IsBooleanObject()); + v8::Handle as_boxed = + boxed_boolean.As(); + CHECK(!as_boxed.IsEmpty()); + bool the_boolean = as_boxed->BooleanValue(); + CHECK_EQ(true, the_boolean); + v8::Handle boxed_true = v8::BooleanObject::New(true); + v8::Handle boxed_false = v8::BooleanObject::New(false); + CHECK(boxed_true->IsBooleanObject()); + CHECK(boxed_false->IsBooleanObject()); + as_boxed = boxed_true.As(); + CHECK_EQ(true, as_boxed->BooleanValue()); + as_boxed = boxed_false.As(); + CHECK_EQ(false, as_boxed->BooleanValue()); +} + + THREADED_TEST(Number) { v8::HandleScope scope; LocalContext env; -- 2.7.4