Bug 579471 - fast constructors (r=dmandelin,lw)

This commit is contained in:
Brian Hackett 2010-08-10 16:38:08 -07:00
Родитель 04dc9788a5
Коммит 3a7d7158e0
13 изменённых файлов: 150 добавлений и 88 удалений

Просмотреть файл

@ -1007,7 +1007,8 @@ Class js_ArrayClass = {
"Array",
Class::NON_NATIVE |
JSCLASS_HAS_RESERVED_SLOTS(JSObject::DENSE_ARRAY_FIXED_RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
JSCLASS_HAS_CACHED_PROTO(JSProto_Array) |
JSCLASS_FAST_CONSTRUCTOR,
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -1042,7 +1043,9 @@ Class js_ArrayClass = {
Class js_SlowArrayClass = {
"Array",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
JSCLASS_HAS_PRIVATE |
JSCLASS_HAS_CACHED_PROTO(JSProto_Array) |
JSCLASS_FAST_CONSTRUCTOR,
slowarray_addProperty,
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -2953,31 +2956,29 @@ NewDenseArrayObject(JSContext *cx)
}
JSBool
js_Array(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
js_Array(JSContext *cx, uintN argc, Value *vp)
{
jsuint length;
const Value *vector;
/* If called without new, replace obj with a new Array object. */
if (!JS_IsConstructing(cx)) {
obj = NewDenseArrayObject(cx);
if (!obj)
return JS_FALSE;
rval->setObject(*obj);
}
/* Whether called with 'new' or not, use a new Array object. */
JSObject *obj = NewDenseArrayObject(cx);
if (!obj)
return JS_FALSE;
vp->setObject(*obj);
if (argc == 0) {
length = 0;
vector = NULL;
} else if (argc > 1) {
length = (jsuint) argc;
vector = argv;
} else if (!argv[0].isNumber()) {
vector = vp + 2;
} else if (!vp[2].isNumber()) {
length = 1;
vector = argv;
vector = vp + 2;
} else {
length = ValueIsLength(cx, &argv[0]);
if (argv[0].isNull())
length = ValueIsLength(cx, vp + 2);
if (vp[2].isNull())
return JS_FALSE;
vector = NULL;
}
@ -3027,7 +3028,7 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewPreallocatedArray, CONTEXT, OBJECT, I
JSObject *
js_InitArrayClass(JSContext *cx, JSObject *obj)
{
JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, (Native) js_Array, 1,
NULL, array_methods, NULL, array_static_methods);
if (!proto)
return NULL;

Просмотреть файл

@ -239,7 +239,7 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id,
/* Array constructor native. Exposed only so the JIT can know its address. */
JSBool
js_Array(JSContext* cx, JSObject* obj, uintN argc, js::Value* argv, js::Value* rval);
js_Array(JSContext *cx, uintN argc, js::Value *vp);
/*
* Friend api function that allows direct creation of an array object with a

Просмотреть файл

@ -484,7 +484,8 @@ msFromTime(jsdouble t)
Class js_DateClass = {
js_Date_str,
JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_FIXED_RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
JSCLASS_HAS_CACHED_PROTO(JSProto_Date) |
JSCLASS_FAST_CONSTRUCTOR,
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -2302,28 +2303,28 @@ static JSFunctionSpec date_methods[] = {
};
JSBool
js_Date(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
js_Date(JSContext *cx, uintN argc, Value *vp)
{
/* Date called as function. */
if (!JS_IsConstructing(cx))
return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, rval);
if (!vp[1].isMagic(JS_FAST_CONSTRUCTOR))
return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, vp);
/* Date called as constructor. */
jsdouble d;
if (argc == 0) {
d = NowAsMillis();
} else if (argc == 1) {
if (!argv[0].isString()) {
if (!vp[2].isString()) {
/* the argument is a millisecond number */
if (!ValueToNumber(cx, argv[0], &d))
if (!ValueToNumber(cx, vp[2], &d))
return JS_FALSE;
d = TIMECLIP(d);
} else {
/* the argument is a string; parse it. */
JSString *str = js_ValueToString(cx, argv[0]);
JSString *str = js_ValueToString(cx, vp[2]);
if (!str)
return JS_FALSE;
argv[0].setString(str);
vp[2].setString(str);
if (!date_parseString(str, &d, cx))
d = js_NaN;
@ -2332,7 +2333,7 @@ js_Date(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
}
} else {
jsdouble msec_time;
if (!date_msecFromArgs(cx, argc, argv, &msec_time))
if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
return JS_FALSE;
if (JSDOUBLE_IS_FINITE(msec_time)) {
@ -2341,7 +2342,13 @@ js_Date(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
}
d = msec_time;
}
return SetUTCTime(cx, obj, d);
JSObject *obj = js_NewDateObjectMsec(cx, d);
if (!obj)
return JS_FALSE;
vp->setObject(*obj);
return JS_TRUE;
}
JSObject *
@ -2349,7 +2356,7 @@ js_InitDateClass(JSContext *cx, JSObject *obj)
{
/* set static LocalTZA */
LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
JSObject *proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS,
JSObject *proto = js_InitClass(cx, obj, NULL, &js_DateClass, (Native) js_Date, MAXARGS,
NULL, date_methods, NULL, date_static_methods);
if (!proto)
return NULL;

Просмотреть файл

@ -132,6 +132,6 @@ js_IntervalNow();
/* Date constructor native. Exposed only so the JIT can know its address. */
JSBool
js_Date(JSContext *cx, JSObject *obj, uintN argc, js::Value *argv, js::Value *rval);
js_Date(JSContext *cx, uintN argc, js::Value *vp);
#endif /* jsdate_h___ */

Просмотреть файл

@ -2435,7 +2435,8 @@ js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
/* Initialize all function members. */
fun->nargs = uint16(nargs);
fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRCINFO);
fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK |
JSFUN_TRCINFO | JSFUN_FAST_NATIVE_CTOR);
if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
JS_ASSERT(!native);
JS_ASSERT(nargs == 0);
@ -2579,7 +2580,8 @@ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, Native native,
} else {
gsop = NULL;
}
fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
fun = js_NewFunction(cx, NULL, native, nargs,
attrs & (JSFUN_FLAGS_MASK | JSFUN_TRCINFO), obj, atom);
if (!fun)
return NULL;
if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), ObjectValue(*fun),

Просмотреть файл

@ -101,6 +101,17 @@ typedef union JSLocalNames {
appear to call itself via its own name
or arguments.callee */
#define JSFUN_FAST_NATIVE_CTOR 0x0002 /* JSFastNative directly invokable
* during construction. */
/*
* Extra JSCLASS flag indicating the native passed to JS_InitClass is
* a fast native constructor. This is internal for now as the 'this' value passed
* to such a constructor is a magic value, and there is no way to query this
* in the API. See bug 581263.
*/
#define JSCLASS_FAST_CONSTRUCTOR (1<<4)
#define JSFUN_EXPR_CLOSURE 0x1000 /* expression closure: function(x) x*x */
#define JSFUN_TRCINFO 0x2000 /* when set, u.n.trcinfo is non-null,
JSFunctionSpec::call points to a
@ -165,12 +176,13 @@ struct JSFunction : public JSObject
} u;
JSAtom *atom; /* name for diagnostics and decompiling */
bool optimizedClosure() const { return FUN_KIND(this) > JSFUN_INTERPRETED; }
bool needsWrapper() const { return FUN_NULL_CLOSURE(this) && u.i.skipmin != 0; }
bool isInterpreted() const { return FUN_INTERPRETED(this); }
bool isFastNative() const { return !!(flags & JSFUN_FAST_NATIVE); }
bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); }
unsigned minArgs() const { return FUN_MINARGS(this); }
bool optimizedClosure() const { return FUN_KIND(this) > JSFUN_INTERPRETED; }
bool needsWrapper() const { return FUN_NULL_CLOSURE(this) && u.i.skipmin != 0; }
bool isInterpreted() const { return FUN_INTERPRETED(this); }
bool isFastNative() const { return !!(flags & JSFUN_FAST_NATIVE); }
bool isFastConstructor() const { return !!(flags & JSFUN_FAST_NATIVE_CTOR); }
bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); }
unsigned minArgs() const { return FUN_MINARGS(this); }
uintN countVars() const {
JS_ASSERT(FUN_INTERPRETED(this));

Просмотреть файл

@ -286,7 +286,12 @@ namespace js {
JSObject *
ComputeThisFromArgv(JSContext *cx, Value *argv)
{
JS_ASSERT(!argv[-1].isMagic(JS_THIS_POISON));
/*
* Check for SynthesizeFrame poisoning and fast constructors which
* didn't check their vp properly.
*/
JS_ASSERT(!argv[-1].isMagic());
if (argv[-1].isNull())
return ComputeGlobalThis(cx, argv);
@ -1126,19 +1131,35 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef)
return false;
}
Class *clasp = &js_ObjectClass;
/*
* Call fast constructors without making the object first.
* The native will be able to make the right new object faster.
*/
if (obj2->isFunction()) {
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj2);
if (fun->isFastConstructor()) {
args.thisv().setMagic(JS_FAST_CONSTRUCTOR);
FastNative fn = (FastNative)fun->u.n.native;
if (!fn(cx, args.argc(), args.base()))
return JS_FALSE;
JS_ASSERT(!args.rval().isPrimitive());
return JS_TRUE;
}
/* Get the class, for natives that aren't fast constructors. */
if (!fun->isInterpreted() && fun->u.n.clasp)
clasp = fun->u.n.clasp;
}
Value protov;
if (!obj2->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov))
return false;
JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
JSObject *parent = obj2->getParent();
Class *clasp = &js_ObjectClass;
if (obj2->getClass() == &js_FunctionClass) {
JSFunction *f = GET_FUNCTION_PRIVATE(cx, obj2);
if (!f->isInterpreted() && f->u.n.clasp)
clasp = f->u.n.clasp;
}
JSObject* obj = NewObject<WithProto::Class>(cx, clasp, proto, parent);
if (!obj)

Просмотреть файл

@ -106,7 +106,8 @@ JS_FRIEND_DATA(const JSObjectMap) JSObjectMap::sharedNonNative(JSObjectMap::SHAP
Class js_ObjectClass = {
js_Object_str,
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_FAST_CONSTRUCTOR,
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -2554,25 +2555,25 @@ static JSFunctionSpec object_static_methods[] = {
};
JSBool
js_Object(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
js_Object(JSContext *cx, uintN argc, Value *vp)
{
JSObject *obj;
if (argc == 0) {
/* Trigger logic below to construct a blank object. */
obj = NULL;
} else {
/* If argv[0] is null or undefined, obj comes back null. */
if (!js_ValueToObjectOrNull(cx, argv[0], &obj))
if (!js_ValueToObjectOrNull(cx, vp[2], &obj))
return JS_FALSE;
}
if (!obj) {
JS_ASSERT(!argc || argv[0].isNull() || argv[0].isUndefined());
if (JS_IsConstructing(cx))
return JS_TRUE;
/* Make an object whether this was called with 'new' or not. */
JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined());
obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
if (!obj)
return JS_FALSE;
}
rval->setObject(*obj);
vp->setObject(*obj);
return JS_TRUE;
}
@ -3253,7 +3254,7 @@ Class js_BlockClass = {
JSObject *
js_InitObjectClass(JSContext *cx, JSObject *obj)
{
JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1,
JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, (Native) js_Object, 1,
object_props, object_methods, NULL, object_static_methods);
if (!proto)
return NULL;
@ -3402,7 +3403,11 @@ js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
ctor = proto;
} else {
fun = js_NewFunction(cx, NULL, constructor, nargs, 0, obj, atom);
uint16 flags = 0;
if (clasp->flags & JSCLASS_FAST_CONSTRUCTOR)
flags |= JSFUN_FAST_NATIVE | JSFUN_FAST_NATIVE_CTOR;
fun = js_NewFunction(cx, NULL, constructor, nargs, flags, obj, atom);
if (!fun)
goto bad;

Просмотреть файл

@ -1332,7 +1332,7 @@ js_InferFlags(JSContext *cx, uintN defaultFlags);
/* Object constructor native. Exposed only so the JIT can know its address. */
JSBool
js_Object(JSContext *cx, JSObject *obj, uintN argc, js::Value *argv, js::Value *rval);
js_Object(JSContext *cx, uintN argc, js::Value *vp);
namespace js {

Просмотреть файл

@ -817,7 +817,8 @@ str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
Class js_StringClass = {
js_String_str,
JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_CACHED_PROTO(JSProto_String),
JSCLASS_HAS_CACHED_PROTO(JSProto_String) |
JSCLASS_FAST_CONSTRUCTOR,
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
str_getProperty,
@ -3057,23 +3058,28 @@ const char JSString::deflatedUnitStringTable[] = {
#undef U8
JSBool
js_String(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
js_String(JSContext *cx, uintN argc, Value *vp)
{
JSString *str;
if (argc > 0) {
str = js_ValueToString(cx, argv[0]);
str = js_ValueToString(cx, vp[2]);
if (!str)
return JS_FALSE;
argv[0].setString(str);
vp[2].setString(str);
} else {
str = cx->runtime->emptyString;
}
if (!JS_IsConstructing(cx)) {
rval->setString(str);
return JS_TRUE;
if (vp[1].isMagic(JS_FAST_CONSTRUCTOR)) {
JSObject *obj = NewBuiltinClassInstance(cx, &js_StringClass);
if (!obj)
return JS_FALSE;
obj->setPrimitiveThis(StringValue(str));
vp->setObject(*obj);
} else {
vp->setString(str);
}
obj->setPrimitiveThis(StringValue(str));
return JS_TRUE;
}
@ -3163,7 +3169,7 @@ js_InitStringClass(JSContext *cx, JSObject *obj)
if (!JS_DefineFunctions(cx, obj, string_functions))
return NULL;
proto = js_InitClass(cx, obj, NULL, &js_StringClass, js_String, 1,
proto = js_InitClass(cx, obj, NULL, &js_StringClass, (Native) js_String, 1,
NULL, string_methods,
NULL, string_static_methods);
if (!proto)

Просмотреть файл

@ -1151,7 +1151,7 @@ js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp,
JSString *str, uint32 quote);
extern JSBool
js_String(JSContext *cx, JSObject *obj, uintN argc, js::Value *argv, js::Value *rval);
js_String(JSContext *cx, uintN argc, js::Value *vp);
namespace js {

Просмотреть файл

@ -11458,11 +11458,19 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
if (!clasp->isNative())
RETURN_STOP("new with non-native ops");
args[0] = INS_CONSTOBJ(funobj);
args[1] = INS_CONSTPTR(clasp);
args[2] = cx_ins;
newobj_ins = lir->insCall(&js_NewInstance_ci, args);
guard(false, lir->insEqP_0(newobj_ins), OOM_EXIT);
if (fun->isFastConstructor()) {
vp[1].setMagic(JS_FAST_CONSTRUCTOR);
newobj_ins = INS_CONST(JS_FAST_CONSTRUCTOR);
/* Treat this as a regular call, the constructor will behave correctly. */
mode = JSOP_CALL;
} else {
args[0] = INS_CONSTOBJ(funobj);
args[1] = INS_CONSTPTR(clasp);
args[2] = cx_ins;
newobj_ins = lir->insCall(&js_NewInstance_ci, args);
guard(false, lir->insEqP_0(newobj_ins), OOM_EXIT);
}
this_ins = newobj_ins;
} else if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
this_ins = INS_CONSTOBJ(funobj->getParent());
@ -11624,22 +11632,20 @@ TraceRecorder::functionCall(uintN argc, JSOp mode)
return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW);
}
if (FUN_SLOW_NATIVE(fun)) {
Native native = fun->u.n.native;
Value* argv = &tval + 1;
if (native == js_Array)
return newArray(&fval.toObject(), argc, argv, &fval);
if (native == js_String && argc == 1) {
if (mode == JSOP_NEW)
return newString(&fval.toObject(), 1, argv, &fval);
if (!argv[0].isPrimitive()) {
CHECK_STATUS(guardNativeConversion(argv[0]));
return callImacro(call_imacros.String);
}
set(&fval, stringify(argv[0]));
pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
return RECORD_CONTINUE;
FastNative native = FUN_FAST_NATIVE(fun);
Value* argv = &tval + 1;
if (native == js_Array)
return newArray(&fval.toObject(), argc, argv, &fval);
if (native == js_String && argc == 1) {
if (mode == JSOP_NEW)
return newString(&fval.toObject(), 1, argv, &fval);
if (!argv[0].isPrimitive()) {
CHECK_STATUS(guardNativeConversion(argv[0]));
return callImacro(call_imacros.String);
}
set(&fval, stringify(argv[0]));
pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
return RECORD_CONTINUE;
}
RecordingStatus rs = callNative(argc, mode);
@ -13513,10 +13519,11 @@ TraceRecorder::record_NativeCallComplete()
if (pendingSpecializedNative == IGNORE_NATIVE_CALL_COMPLETE_CALLBACK)
return ARECORD_CONTINUE;
jsbytecode* pc = cx->regs->pc;
#ifdef DEBUG
JS_ASSERT(pendingSpecializedNative);
jsbytecode* pc = cx->regs->pc;
JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY || *pc == JSOP_NEW || *pc == JSOP_SETPROP);
#endif
Value& v = stackval(-1);
LIns* v_ins = get(&v);
@ -13550,7 +13557,7 @@ TraceRecorder::record_NativeCallComplete()
* indicating the error status.
*/
if (*pc == JSOP_NEW) {
if (pendingSpecializedNative->flags & JSTN_CONSTRUCTOR) {
LIns *cond_ins;
LIns *x;

Просмотреть файл

@ -258,6 +258,7 @@ typedef enum JSWhyMagic
* enumerated like a native object. */
JS_NO_ITER_VALUE, /* there is not a pending iterator value */
JS_GENERATOR_CLOSING, /* exception value thrown when closing a generator */
JS_FAST_CONSTRUCTOR, /* 'this' value for fast natives invoked with 'new' */
JS_NO_CONSTANT, /* compiler sentinel value */
JS_THIS_POISON, /* used in debug builds to catch tracing errors */
JS_GENERIC_MAGIC /* for local use */