зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1376799 - Optimize Object.prototype.toString. r=evilpie
--HG-- extra : rebase_source : 01f1dfcdf9bb991c8c40cf8dc9b521501456350f
This commit is contained in:
Родитель
8ebd97f178
Коммит
f660d24cb3
|
@ -16,6 +16,7 @@
|
|||
#include "jit/InlinableNatives.h"
|
||||
#include "js/UniquePtr.h"
|
||||
#include "vm/AsyncFunction.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
@ -432,6 +433,61 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
|
|||
}
|
||||
#endif /* JS_HAS_TOSOURCE */
|
||||
|
||||
static bool
|
||||
GetBuiltinTagSlow(JSContext* cx, HandleObject obj, MutableHandleString builtinTag)
|
||||
{
|
||||
// Step 4.
|
||||
bool isArray;
|
||||
if (!IsArray(cx, obj, &isArray))
|
||||
return false;
|
||||
|
||||
// Step 5.
|
||||
if (isArray) {
|
||||
builtinTag.set(cx->names().objectArray);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Steps 6-13.
|
||||
ESClass cls;
|
||||
if (!GetBuiltinClass(cx, obj, &cls))
|
||||
return false;
|
||||
|
||||
switch (cls) {
|
||||
case ESClass::String:
|
||||
builtinTag.set(cx->names().objectString);
|
||||
return true;
|
||||
case ESClass::Arguments:
|
||||
builtinTag.set(cx->names().objectArguments);
|
||||
return true;
|
||||
case ESClass::Error:
|
||||
builtinTag.set(cx->names().objectError);
|
||||
return true;
|
||||
case ESClass::Boolean:
|
||||
builtinTag.set(cx->names().objectBoolean);
|
||||
return true;
|
||||
case ESClass::Number:
|
||||
builtinTag.set(cx->names().objectNumber);
|
||||
return true;
|
||||
case ESClass::Date:
|
||||
builtinTag.set(cx->names().objectDate);
|
||||
return true;
|
||||
case ESClass::RegExp:
|
||||
builtinTag.set(cx->names().objectRegExp);
|
||||
return true;
|
||||
default:
|
||||
if (obj->isCallable()) {
|
||||
// Non-standard: Prevent <object> from showing up as Function.
|
||||
RootedObject unwrapped(cx, CheckedUnwrap(obj));
|
||||
if (!unwrapped || !unwrapped->getClass()->isDOMClass()) {
|
||||
builtinTag.set(cx->names().objectFunction);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
builtinTag.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 19.1.3.6
|
||||
bool
|
||||
js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
@ -455,53 +511,63 @@ js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!obj)
|
||||
return false;
|
||||
|
||||
// Step 4.
|
||||
bool isArray;
|
||||
if (!IsArray(cx, obj, &isArray))
|
||||
return false;
|
||||
|
||||
// Step 5.
|
||||
RootedString builtinTag(cx);
|
||||
if (isArray) {
|
||||
builtinTag = cx->names().objectArray;
|
||||
} else {
|
||||
// Steps 6-13.
|
||||
ESClass cls;
|
||||
if (!GetBuiltinClass(cx, obj, &cls))
|
||||
const Class* clasp = obj->getClass();
|
||||
if (MOZ_UNLIKELY(clasp->isProxy())) {
|
||||
if (!GetBuiltinTagSlow(cx, obj, &builtinTag))
|
||||
return false;
|
||||
} else {
|
||||
// Optimize the non-proxy case to bypass GetBuiltinClass.
|
||||
if (clasp == &PlainObject::class_ || clasp == &UnboxedPlainObject::class_) {
|
||||
// This is not handled by GetBuiltinTagSlow, but this case is by far
|
||||
// the most common so we optimize it here.
|
||||
builtinTag = cx->names().objectObject;
|
||||
|
||||
switch (cls) {
|
||||
case ESClass::String:
|
||||
} else if (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_) {
|
||||
builtinTag = cx->names().objectArray;
|
||||
|
||||
} else if (clasp == &JSFunction::class_) {
|
||||
builtinTag = cx->names().objectFunction;
|
||||
|
||||
} else if (clasp == &StringObject::class_) {
|
||||
builtinTag = cx->names().objectString;
|
||||
break;
|
||||
case ESClass::Arguments:
|
||||
builtinTag = cx->names().objectArguments;
|
||||
break;
|
||||
case ESClass::Error:
|
||||
builtinTag = cx->names().objectError;
|
||||
break;
|
||||
case ESClass::Boolean:
|
||||
builtinTag = cx->names().objectBoolean;
|
||||
break;
|
||||
case ESClass::Number:
|
||||
|
||||
} else if (clasp == &NumberObject::class_) {
|
||||
builtinTag = cx->names().objectNumber;
|
||||
break;
|
||||
case ESClass::Date:
|
||||
|
||||
} else if (clasp == &BooleanObject::class_) {
|
||||
builtinTag = cx->names().objectBoolean;
|
||||
|
||||
} else if (clasp == &DateObject::class_) {
|
||||
builtinTag = cx->names().objectDate;
|
||||
break;
|
||||
case ESClass::RegExp:
|
||||
|
||||
} else if (clasp == &RegExpObject::class_) {
|
||||
builtinTag = cx->names().objectRegExp;
|
||||
break;
|
||||
default:
|
||||
if (obj->isCallable()) {
|
||||
// Non-standard: Prevent <object> from showing up as Function.
|
||||
RootedObject unwrapped(cx, CheckedUnwrap(obj));
|
||||
if (!unwrapped || !unwrapped->getClass()->isDOMClass())
|
||||
builtinTag = cx->names().objectFunction;
|
||||
}
|
||||
break;
|
||||
|
||||
} else if (obj->is<ArgumentsObject>()) {
|
||||
builtinTag = cx->names().objectArguments;
|
||||
|
||||
} else if (obj->is<ErrorObject>()) {
|
||||
builtinTag = cx->names().objectError;
|
||||
|
||||
} else if (obj->isCallable() && !obj->getClass()->isDOMClass()) {
|
||||
// Non-standard: Prevent <object> from showing up as Function.
|
||||
builtinTag = cx->names().objectFunction;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
// Assert this fast path is correct and matches BuiltinTagSlow. The
|
||||
// only exception is the PlainObject case: we special-case it here
|
||||
// because it's so common, but BuiltinTagSlow doesn't handle this.
|
||||
RootedString builtinTagSlow(cx);
|
||||
if (!GetBuiltinTagSlow(cx, obj, &builtinTagSlow))
|
||||
return false;
|
||||
if (clasp == &PlainObject::class_ || clasp == &UnboxedPlainObject::class_)
|
||||
MOZ_ASSERT(!builtinTagSlow);
|
||||
else
|
||||
MOZ_ASSERT(builtinTagSlow == builtinTag);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Step 14.
|
||||
// Currently omitted for non-standard fallback.
|
||||
|
||||
|
@ -515,22 +581,16 @@ js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
|
|||
// Non-standard (bug 1277801): Use ClassName as a fallback in the interim
|
||||
if (!builtinTag) {
|
||||
const char* className = GetObjectClassName(cx, obj);
|
||||
// "[object Object]" is by far the most common case at this point,
|
||||
// so we optimize it here.
|
||||
if (strcmp(className, "Object") == 0) {
|
||||
builtinTag = cx->names().objectObject;
|
||||
} else {
|
||||
StringBuffer sb(cx);
|
||||
if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
|
||||
!sb.append("]"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
builtinTag = sb.finishAtom();
|
||||
if (!builtinTag)
|
||||
return false;
|
||||
StringBuffer sb(cx);
|
||||
if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
|
||||
!sb.append("]"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
builtinTag = sb.finishAtom();
|
||||
if (!builtinTag)
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setString(builtinTag);
|
||||
|
|
|
@ -2201,56 +2201,6 @@ JS::IdentifyStandardConstructor(JSObject* obj)
|
|||
return JSProto_Null;
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::isCallable() const
|
||||
{
|
||||
if (is<JSFunction>())
|
||||
return true;
|
||||
return callHook() != nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::isConstructor() const
|
||||
{
|
||||
if (is<JSFunction>()) {
|
||||
const JSFunction& fun = as<JSFunction>();
|
||||
return fun.isConstructor();
|
||||
}
|
||||
return constructHook() != nullptr;
|
||||
}
|
||||
|
||||
JSNative
|
||||
JSObject::callHook() const
|
||||
{
|
||||
const js::Class* clasp = getClass();
|
||||
|
||||
if (JSNative call = clasp->getCall())
|
||||
return call;
|
||||
|
||||
if (is<js::ProxyObject>()) {
|
||||
const js::ProxyObject& p = as<js::ProxyObject>();
|
||||
if (p.handler()->isCallable(const_cast<JSObject*>(this)))
|
||||
return js::proxy_Call;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSNative
|
||||
JSObject::constructHook() const
|
||||
{
|
||||
const js::Class* clasp = getClass();
|
||||
|
||||
if (JSNative construct = clasp->getConstruct())
|
||||
return construct;
|
||||
|
||||
if (is<js::ProxyObject>()) {
|
||||
const js::ProxyObject& p = as<js::ProxyObject>();
|
||||
if (p.handler()->isConstructor(const_cast<JSObject*>(this)))
|
||||
return js::proxy_Construct;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
js::LookupProperty(JSContext* cx, HandleObject obj, js::HandleId id,
|
||||
MutableHandleObject objp, MutableHandle<PropertyResult> propp)
|
||||
|
@ -3025,18 +2975,6 @@ js::UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id)
|
|||
return UnwatchGuts(cx, obj, id);
|
||||
}
|
||||
|
||||
const char*
|
||||
js::GetObjectClassName(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
assertSameCompartment(cx, obj);
|
||||
|
||||
if (obj->is<ProxyObject>())
|
||||
return Proxy::className(cx, obj);
|
||||
|
||||
return obj->getClass()->name;
|
||||
}
|
||||
|
||||
|
||||
/* * */
|
||||
|
||||
extern bool
|
||||
|
|
|
@ -516,10 +516,10 @@ class JSObject : public js::gc::Cell
|
|||
/*
|
||||
* Back to generic stuff.
|
||||
*/
|
||||
bool isCallable() const;
|
||||
bool isConstructor() const;
|
||||
JSNative callHook() const;
|
||||
JSNative constructHook() const;
|
||||
MOZ_ALWAYS_INLINE bool isCallable() const;
|
||||
MOZ_ALWAYS_INLINE bool isConstructor() const;
|
||||
MOZ_ALWAYS_INLINE JSNative callHook() const;
|
||||
MOZ_ALWAYS_INLINE JSNative constructHook() const;
|
||||
|
||||
MOZ_ALWAYS_INLINE void finalize(js::FreeOp* fop);
|
||||
|
||||
|
@ -683,23 +683,6 @@ JSObject::writeBarrierPost(void* cellp, JSObject* prev, JSObject* next)
|
|||
buffer->unputCell(static_cast<js::gc::Cell**>(cellp));
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
inline bool
|
||||
IsCallable(const Value& v)
|
||||
{
|
||||
return v.isObject() && v.toObject().isCallable();
|
||||
}
|
||||
|
||||
// ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
|
||||
inline bool
|
||||
IsConstructor(const Value& v)
|
||||
{
|
||||
return v.isObject() && v.toObject().isConstructor();
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
class JSValueArray {
|
||||
public:
|
||||
const js::Value* array;
|
||||
|
@ -1087,7 +1070,7 @@ ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp)
|
|||
* toString support. (This isn't called GetClassName because there's a macro in
|
||||
* <windows.h> with that name.)
|
||||
*/
|
||||
extern const char*
|
||||
MOZ_ALWAYS_INLINE const char*
|
||||
GetObjectClassName(JSContext* cx, HandleObject obj);
|
||||
|
||||
/*
|
||||
|
|
|
@ -823,6 +823,80 @@ InitClass(JSContext* cx, HandleObject obj, HandleObject parent_proto,
|
|||
NativeObject** ctorp = nullptr,
|
||||
gc::AllocKind ctorKind = gc::AllocKind::FUNCTION);
|
||||
|
||||
MOZ_ALWAYS_INLINE const char*
|
||||
GetObjectClassName(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
assertSameCompartment(cx, obj);
|
||||
|
||||
if (obj->is<ProxyObject>())
|
||||
return Proxy::className(cx, obj);
|
||||
|
||||
return obj->getClass()->name;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsCallable(const Value& v)
|
||||
{
|
||||
return v.isObject() && v.toObject().isCallable();
|
||||
}
|
||||
|
||||
// ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
|
||||
inline bool
|
||||
IsConstructor(const Value& v)
|
||||
{
|
||||
return v.isObject() && v.toObject().isConstructor();
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
JSObject::isCallable() const
|
||||
{
|
||||
if (is<JSFunction>())
|
||||
return true;
|
||||
return callHook() != nullptr;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
JSObject::isConstructor() const
|
||||
{
|
||||
if (is<JSFunction>()) {
|
||||
const JSFunction& fun = as<JSFunction>();
|
||||
return fun.isConstructor();
|
||||
}
|
||||
return constructHook() != nullptr;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE JSNative
|
||||
JSObject::callHook() const
|
||||
{
|
||||
const js::Class* clasp = getClass();
|
||||
|
||||
if (JSNative call = clasp->getCall())
|
||||
return call;
|
||||
|
||||
if (is<js::ProxyObject>()) {
|
||||
const js::ProxyObject& p = as<js::ProxyObject>();
|
||||
if (p.handler()->isCallable(const_cast<JSObject*>(this)))
|
||||
return js::proxy_Call;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE JSNative
|
||||
JSObject::constructHook() const
|
||||
{
|
||||
const js::Class* clasp = getClass();
|
||||
|
||||
if (JSNative construct = clasp->getConstruct())
|
||||
return construct;
|
||||
|
||||
if (is<js::ProxyObject>()) {
|
||||
const js::ProxyObject& p = as<js::ProxyObject>();
|
||||
if (p.handler()->isConstructor(const_cast<JSObject*>(this)))
|
||||
return js::proxy_Construct;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif /* jsobjinlines_h */
|
||||
|
|
Загрузка…
Ссылка в новой задаче