Fast native constructors, bug 579471.

This commit is contained in:
Brian Hackett 2010-08-03 12:20:48 -07:00
parent 9aab972de1
commit b02ce0b886
14 changed files with 138 additions and 73 deletions

View File

@ -1008,7 +1008,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 */
@ -1043,7 +1044,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 */
@ -2959,31 +2962,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 = NewObject(cx, &js_ArrayClass, NULL, NULL);
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;
}
@ -3033,7 +3034,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;

View File

@ -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

View File

@ -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;

View File

@ -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___ */

View File

@ -2460,7 +2460,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);
@ -2604,7 +2605,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),

View File

@ -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));

View File

@ -294,7 +294,12 @@ namespace js {
bool
ComputeThisFromArgv(JSContext *cx, Value *argv)
{
JS_ASSERT(!argv[-1].isMagic()); // check for SynthesizeFrame poisoning
/*
* 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);
@ -1195,6 +1200,23 @@ InvokeConstructor(JSContext *cx, const InvokeArgsGuard &args)
JSObject *obj2 = &vp->toObject();
/*
* 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 = (JSFunction *) obj2->getPrivate();
if (fun->isFastConstructor()) {
vp[1].setMagic(JS_FAST_CONSTRUCTOR);
FastNative fn = (FastNative)fun->u.n.native;
if (!fn(cx, args.getArgc(), vp))
return JS_FALSE;
JS_ASSERT(!vp->isPrimitive());
return JS_TRUE;
}
}
/*
* Get the constructor prototype object for this function.
* Use the nominal 'this' parameter slot, vp[1], as a local

View File

@ -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;
}
@ -3254,7 +3255,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;
@ -3403,7 +3404,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;

View File

@ -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 {

View File

@ -805,7 +805,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,
@ -3123,23 +3124,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;
}
@ -3229,7 +3235,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)

View File

@ -1159,7 +1159,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 {

View File

@ -11572,22 +11572,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;
}
return callNative(argc, mode);

View File

@ -260,6 +260,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 */

View File

@ -418,6 +418,17 @@ stubs::SlowNew(VMFrame &f, uint32 argc)
return ret;
}
if (fun->isFastConstructor()) {
vp[1].setMagic(JS_FAST_CONSTRUCTOR);
FastNative fn = (FastNative)fun->u.n.native;
if (!fn(cx, argc, vp))
THROWV(NULL);
JS_ASSERT(!vp->isPrimitive());
return NULL;
}
}
if (!InvokeConstructor(cx, InvokeArgsGuard(vp, argc)))