From 68e21b09144ce5ee92b4148eab8224d7d1721d4e Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Mon, 14 Feb 2011 14:04:07 -0600 Subject: [PATCH] Bug 630865 - Resolve interpreted function prototypes more eagerly. r=dvander. --- js/src/jit-test/tests/basic/bug630865-1.js | 6 ++ js/src/jit-test/tests/basic/bug630865-2.js | 6 ++ js/src/jit-test/tests/basic/bug630865-3.js | 12 +++ js/src/jit-test/tests/basic/bug630865-4.js | 10 ++ js/src/jit-test/tests/basic/bug630865-5.js | 13 +++ js/src/jit-test/tests/basic/bug630865-6.js | 11 +++ .../tests/basic/testConstructorArgs-1.js | 11 +++ .../tests/basic/testConstructorArgs-2.js | 12 +++ .../tests/basic/testConstructorArgs-3.js | 11 +++ js/src/jsbuiltins.h | 7 +- js/src/jsfun.cpp | 92 ++++++++++++------ js/src/jsfun.h | 15 +++ js/src/jsobj.cpp | 62 ++++-------- js/src/jstracer.cpp | 97 +++++++++++-------- js/src/jstracer.h | 6 +- 15 files changed, 255 insertions(+), 116 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug630865-1.js create mode 100644 js/src/jit-test/tests/basic/bug630865-2.js create mode 100644 js/src/jit-test/tests/basic/bug630865-3.js create mode 100644 js/src/jit-test/tests/basic/bug630865-4.js create mode 100644 js/src/jit-test/tests/basic/bug630865-5.js create mode 100644 js/src/jit-test/tests/basic/bug630865-6.js create mode 100644 js/src/jit-test/tests/basic/testConstructorArgs-1.js create mode 100644 js/src/jit-test/tests/basic/testConstructorArgs-2.js create mode 100644 js/src/jit-test/tests/basic/testConstructorArgs-3.js diff --git a/js/src/jit-test/tests/basic/bug630865-1.js b/js/src/jit-test/tests/basic/bug630865-1.js new file mode 100644 index 000000000000..5203d1383d78 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug630865-1.js @@ -0,0 +1,6 @@ +Object.defineProperty(Function.prototype, "prototype", {set:function(){}}); +var x; +for (var i = 0; i < HOTLOOP + 2; i++) + x = new Function.prototype; +assertEq(toString.call(x), "[object Object]"); +assertEq(Object.getPrototypeOf(x), Object.prototype); diff --git a/js/src/jit-test/tests/basic/bug630865-2.js b/js/src/jit-test/tests/basic/bug630865-2.js new file mode 100644 index 000000000000..4616891b3b31 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug630865-2.js @@ -0,0 +1,6 @@ +Function.prototype.prototype = function () {}; +var x; +for (var i = 0; i < HOTLOOP + 2; i++) + x = new Function.prototype; +assertEq(toString.call(x), "[object Object]"); +assertEq(Object.getPrototypeOf(x), Function.prototype.prototype); diff --git a/js/src/jit-test/tests/basic/bug630865-3.js b/js/src/jit-test/tests/basic/bug630865-3.js new file mode 100644 index 000000000000..5933b2cb3123 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug630865-3.js @@ -0,0 +1,12 @@ +var a = []; +function next() { + var x = {}; + a.push(x); + return x; +} +Object.defineProperty(Function.prototype, 'prototype', {get: next}); +var b = []; +for (var i = 0; i < HOTLOOP + 2; i++) + b[i] = new Function.prototype; +for (var i = 0; i < HOTLOOP + 2; i++) + assertEq(Object.getPrototypeOf(b[i]), a[i]); diff --git a/js/src/jit-test/tests/basic/bug630865-4.js b/js/src/jit-test/tests/basic/bug630865-4.js new file mode 100644 index 000000000000..fd9f8b5e2bb7 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug630865-4.js @@ -0,0 +1,10 @@ +Object.defineProperty(Function.prototype, 'prototype', + {get: function () { if (i == HOTLOOP + 1) throw "X"; }}); +var x; +try { + for (var i = 0; i < HOTLOOP + 2; i++) + x = new Function.prototype; +} catch (exc) { + assertEq(i, HOTLOOP + 1); + assertEq(exc, "X"); +} diff --git a/js/src/jit-test/tests/basic/bug630865-5.js b/js/src/jit-test/tests/basic/bug630865-5.js new file mode 100644 index 000000000000..7057ab29fe1f --- /dev/null +++ b/js/src/jit-test/tests/basic/bug630865-5.js @@ -0,0 +1,13 @@ +function C(a, b) { + this.a = a; + this.b = b; +} +var f = C.bind(null, 2); +Object.defineProperty(f, "prototype", {get: function () { throw "FAIL"; }}); +var x; +for (var i = 0; i < HOTLOOP + 2; i++) + x = new f(i); +assertEq(toString.call(x), "[object Object]"); +assertEq(Object.getPrototypeOf(x), C.prototype); +assertEq(x.a, 2); +assertEq(x.b, HOTLOOP + 1); diff --git a/js/src/jit-test/tests/basic/bug630865-6.js b/js/src/jit-test/tests/basic/bug630865-6.js new file mode 100644 index 000000000000..2080726ddcfc --- /dev/null +++ b/js/src/jit-test/tests/basic/bug630865-6.js @@ -0,0 +1,11 @@ +var a = []; +var x, i; +for (i = 0; i < HOTLOOP + 10; i++) { + a[i] = function (b) { this.b = b; }; + if (i != HOTLOOP + 9) + x = a[i].prototype; +} +for (i = 0; i < HOTLOOP + 10; i++) + x = new a[i]; +assertEq(toString.call(x), "[object Object]"); +assertEq(Object.getPrototypeOf(x), a[HOTLOOP + 9].prototype); diff --git a/js/src/jit-test/tests/basic/testConstructorArgs-1.js b/js/src/jit-test/tests/basic/testConstructorArgs-1.js new file mode 100644 index 000000000000..81630f064de6 --- /dev/null +++ b/js/src/jit-test/tests/basic/testConstructorArgs-1.js @@ -0,0 +1,11 @@ +function f(a, b) { + this.a = a; + assertEq(b, 'x'); +} + +for (var x = 0; x < RUNLOOP; ++x) { + f.prototype = {}; + var obj = new f(x, 'x'); + assertEq(obj.a, x); + assertEq(Object.getPrototypeOf(obj), f.prototype); +} diff --git a/js/src/jit-test/tests/basic/testConstructorArgs-2.js b/js/src/jit-test/tests/basic/testConstructorArgs-2.js new file mode 100644 index 000000000000..5a85470776bd --- /dev/null +++ b/js/src/jit-test/tests/basic/testConstructorArgs-2.js @@ -0,0 +1,12 @@ +function f(a, b, c) { + this.a = a; + assertEq(b, 'x'); + assertEq(c, void 0); +} + +for (var x = 0; x < RUNLOOP; ++x) { + f.prototype = {}; + var obj = new f(x, 'x'); // fewer than f.length arguments + assertEq(obj.a, x); + assertEq(Object.getPrototypeOf(obj), f.prototype); +} diff --git a/js/src/jit-test/tests/basic/testConstructorArgs-3.js b/js/src/jit-test/tests/basic/testConstructorArgs-3.js new file mode 100644 index 000000000000..4b1eb55c0862 --- /dev/null +++ b/js/src/jit-test/tests/basic/testConstructorArgs-3.js @@ -0,0 +1,11 @@ +function f(a) { + this.a = a; + assertEq(arguments[1], 'x'); +} + +for (var x = 0; x < RUNLOOP; ++x) { + f.prototype = {}; + var obj = new f(x, 'x'); // more than f.length arguments + assertEq(obj.a, x); + assertEq(Object.getPrototypeOf(obj), f.prototype); +} diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index e923fa622536..538181464d0e 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -188,8 +188,8 @@ struct ClosureVarInfo; #define _JS_CTYPE_THIS _JS_CTYPE(JSObject *, _JS_PTR,"T", "", INFALLIBLE) #define _JS_CTYPE_THIS_DOUBLE _JS_CTYPE(jsdouble, _JS_F64,"D", "", INFALLIBLE) #define _JS_CTYPE_THIS_STRING _JS_CTYPE(JSString *, _JS_PTR,"S", "", INFALLIBLE) -#define _JS_CTYPE_CALLEE _JS_CTYPE(JSObject *, _JS_PTR,"f","", INFALLIBLE) -#define _JS_CTYPE_CALLEE_PROTOTYPE _JS_CTYPE(JSObject *, _JS_PTR,"p","", INFALLIBLE) +#define _JS_CTYPE_CALLEE _JS_CTYPE(JSObject *, _JS_PTR,"f", "", INFALLIBLE) +#define _JS_CTYPE_CALLEE_PROTOTYPE _JS_CTYPE(JSObject *, _JS_PTR,"p", "", INFALLIBLE) #define _JS_CTYPE_FUNCTION _JS_CTYPE(JSFunction *, _JS_PTR, --, --, INFALLIBLE) #define _JS_CTYPE_PC _JS_CTYPE(jsbytecode *, _JS_PTR,"P", "", INFALLIBLE) #define _JS_CTYPE_VALUEPTR _JS_CTYPE(js::Value *, _JS_PTR, --, --, INFALLIBLE) @@ -233,7 +233,8 @@ struct ClosureVarInfo; #define _JS_CTYPE_CVIPTR _JS_CTYPE(const ClosureVarInfo *, _JS_PTR, --, --, INFALLIBLE) #define _JS_CTYPE_FRAMEINFO _JS_CTYPE(FrameInfo *, _JS_PTR, --, --, INFALLIBLE) #define _JS_CTYPE_PICTABLE _JS_CTYPE(PICTable *, _JS_PTR, --, --, INFALLIBLE) - +#define _JS_CTYPE_UINTN _JS_CTYPE(uintN, _JS_PTR, --, --, INFALLIBLE) + /* * The "VALUE" type is used to indicate that a native takes a js::Value * parameter by value. Unfortunately, for technical reasons, we can't simply diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index c136f4811222..fcae690dade6 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1704,6 +1704,46 @@ fun_enumerate(JSContext *cx, JSObject *obj) return true; } +static JSObject * +ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj) +{ + JSFunction *fun = obj->getFunctionPrivate(); + JS_ASSERT(fun->isInterpreted()); + JS_ASSERT(!fun->isFunctionPrototype()); + + /* + * Assert that fun is not a compiler-created function object, which + * must never leak to script or embedding code and then be mutated. + * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above. + */ + JS_ASSERT(!IsInternalFunctionObject(obj)); + JS_ASSERT(!obj->isBoundFunction()); + + /* + * Make the prototype object an instance of Object with the same parent + * as the function object itself. + */ + JSObject *parent = obj->getParent(); + JSObject *proto; + if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) + return NULL; + proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent); + if (!proto) + return NULL; + + /* + * ECMA (15.3.5.2) says that a user-defined function's .prototype property + * is non-configurable, non-enumerable, and (initially) writable. Hence + * JSPROP_PERMANENT below. By contrast, the built-in constructors, such as + * Object (15.2.3.1) and Function (15.3.3.1), have non-writable + * .prototype properties. Those are eagerly defined, with attributes + * JSPROP_PERMANENT | JSPROP_READONLY, in js_InitClass. + */ + if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT)) + return NULL; + return proto; +} + static JSBool fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) @@ -1730,36 +1770,8 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (fun->isNative() || fun->isFunctionPrototype()) return true; - /* - * Assert that fun is not a compiler-created function object, which - * must never leak to script or embedding code and then be mutated. - * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above. - */ - JS_ASSERT(!IsInternalFunctionObject(obj)); - JS_ASSERT(!obj->isBoundFunction()); - - /* - * Make the prototype object an instance of Object with the same parent - * as the function object itself. - */ - JSObject *parent = obj->getParent(); - JSObject *proto; - if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) + if (!ResolveInterpretedFunctionPrototype(cx, obj)) return false; - proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent); - if (!proto) - return false; - - /* - * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for - * user-defined functions, but DontEnum | ReadOnly | DontDelete for - * native "system" constructors such as Object or Function. So lazily - * set the former here in fun_resolve, but eagerly define the latter - * in js_InitClass, with the right attributes. - */ - if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT)) - return false; - *objp = obj; return true; } @@ -2630,6 +2642,28 @@ IsBuiltinFunctionConstructor(JSFunction *fun) return fun->maybeNative() == Function; } +const Shape * +LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj) +{ + JSFunction *fun = funobj->getFunctionPrivate(); + JS_ASSERT(fun->isInterpreted()); + JS_ASSERT(!fun->isFunctionPrototype()); + JS_ASSERT(!funobj->isBoundFunction()); + + jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); + const Shape *shape = funobj->nativeLookup(id); + if (!shape) { + if (!ResolveInterpretedFunctionPrototype(cx, funobj)) + return false; + shape = funobj->nativeLookup(id); + } + JS_ASSERT(!shape->configurable()); + JS_ASSERT(shape->isDataDescriptor()); + JS_ASSERT(shape->hasSlot()); + JS_ASSERT(!shape->isMethod()); + return shape; +} + } static JSBool diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 2c647ba97ddc..a52092c9ed19 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -432,6 +432,21 @@ GetFunctionNameBytes(JSContext *cx, JSFunction *fun, JSAutoByteString *bytes) extern JS_FRIEND_API(bool) IsBuiltinFunctionConstructor(JSFunction *fun); +/* + * Preconditions: funobj->isInterpreted() && !funobj->isFunctionPrototype() && + * !funobj->isBoundFunction(). This is sufficient to establish that funobj has + * a non-configurable non-method .prototype data property, thought it might not + * have been resolved yet, and its value could be anything. + * + * Return the shape of the .prototype property of funobj, resolving it if + * needed. On error, return NULL. + * + * This is not safe to call on trace because it defines properties, which can + * trigger lookups that could reenter. + */ +const Shape * +LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj); + } /* namespace js */ extern JSString * diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index b75a0439edcf..a6a65331707d 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2996,59 +2996,37 @@ js_String_tn(JSContext* cx, JSObject* proto, JSString* str) JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0, nanojit::ACCSET_STORE_ANY) -JSObject* FASTCALL -js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) +JSObject * FASTCALL +js_CreateThisFromTrace(JSContext *cx, JSObject *ctor, uintN protoSlot) { - JS_ASSERT(JS_ON_TRACE(cx)); +#ifdef DEBUG JS_ASSERT(ctor->isFunction()); - - if (!ctor->ensureClassReservedSlots(cx)) - return NULL; - - jsid classPrototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); - const Shape *shape = ctor->nativeLookup(classPrototypeId); - Value pval = shape ? ctor->getSlot(shape->slot) : MagicValue(JS_GENERIC_MAGIC); + JS_ASSERT(ctor->getFunctionPrivate()->isInterpreted()); + jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); + const Shape *shape = ctor->nativeLookup(id); + JS_ASSERT(shape->slot == protoSlot); + JS_ASSERT(!shape->configurable()); + JS_ASSERT(!shape->isMethod()); +#endif JSObject *parent = ctor->getParent(); JSObject *proto; - if (pval.isObject()) { - /* An object in ctor.prototype, let's use it as the new instance's proto. */ - proto = &pval.toObject(); + const Value &protov = ctor->getSlotRef(protoSlot); + if (protov.isObject()) { + proto = &protov.toObject(); } else { - /* A hole or a primitive: either way, we need to get Object.prototype. */ + /* + * GetInterpretedFunctionPrototype found that ctor.prototype is + * primitive. Use Object.prototype for proto, per ES5 13.2.2 step 7. + */ if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) return NULL; - - if (pval.isMagic(JS_GENERIC_MAGIC)) { - /* - * No ctor.prototype was set, so we inline-expand and optimize - * fun_resolve's prototype creation code. - */ - proto = NewNativeClassInstance(cx, clasp, proto, parent); - if (!proto) - return NULL; - JSFunction *fun = ctor->getFunctionPrivate(); - if (!fun->isNative() && !fun->isFunctionPrototype()) { - if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT)) - return NULL; - } - } else { - /* - * A primitive value in .prototype means to use Object.prototype - * for proto. See ES5 13.2.2 step 7. - */ - } } - /* - * FIXME: 561785 at least. Quasi-natives including XML objects prevent us - * from easily or unconditionally calling NewNativeClassInstance here. - */ - gc::FinalizeKind kind = NewObjectGCKind(cx, clasp); - return NewNonFunction(cx, clasp, proto, parent, kind); + gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + return NewNativeClassInstance(cx, &js_ObjectClass, proto, parent, kind); } - -JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0, +JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, OBJECT, UINTN, 0, nanojit::ACCSET_STORE_ANY) #else /* !JS_TRACER */ diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 1206fa2fdf6e..075a82df8ff7 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -10483,13 +10483,6 @@ TraceRecorder::record_EnterFrame() RETURN_STOP_A("recursion started inlining"); } - if (fp->isConstructing()) { - LIns* args[] = { callee_ins, w.nameImmpNonGC(&js_ObjectClass), cx_ins }; - LIns* tv_ins = w.call(&js_CreateThisFromTrace_ci, args); - guard(false, w.eqp0(tv_ins), OOM_EXIT); - set(&fp->thisValue(), tv_ins); - } - return ARECORD_CONTINUE; } @@ -11440,7 +11433,8 @@ TraceRecorder::callNative(uintN argc, JSOp mode) Value* vp = &stackval(0 - (2 + argc)); JSObject* funobj = &vp[0].toObject(); - JSFunction* fun = GET_FUNCTION_PRIVATE(cx, funobj); + JSFunction* fun = funobj->getFunctionPrivate(); + JS_ASSERT(fun->isNative()); Native native = fun->u.n.native; switch (argc) { @@ -11616,39 +11610,24 @@ TraceRecorder::callNative(uintN argc, JSOp mode) clasp = &js_ObjectClass; JS_ASSERT(((jsuword) clasp & 3) == 0); - // Abort on |new Function|. js_CreateThis would allocate a regular- - // sized JSObject, not a Function-sized one. (The Function ctor would - // deep-bail anyway but let's not go there.) + // Abort on |new Function|. (FIXME: This restriction might not + // unnecessary now that the constructor creates the new function object + // itself.) if (clasp == &js_FunctionClass) RETURN_STOP("new Function"); if (!clasp->isNative()) RETURN_STOP("new with non-native ops"); - if (fun->isConstructor()) { - vp[1].setMagicWithObjectOrNullPayload(NULL); - newobj_ins = w.immpMagicNull(); + // Don't trace |new Math.sin(0)|. + if (!fun->isConstructor()) + RETURN_STOP("new with non-constructor native function"); - /* Treat this as a regular call, the constructor will behave correctly. */ - mode = JSOP_CALL; - } else { - args[0] = w.immpObjGC(funobj); - args[1] = w.immpNonGC(clasp); - args[2] = cx_ins; - newobj_ins = w.call(&js_CreateThisFromTrace_ci, args); - guard(false, w.eqp0(newobj_ins), OOM_EXIT); + vp[1].setMagicWithObjectOrNullPayload(NULL); + newobj_ins = w.immpMagicNull(); - /* - * emitNativeCall may take a snapshot below. To avoid having a type - * mismatch (e.g., where get(&vp[1]) is an object and vp[1] is - * null), we make sure vp[1] is some object. The actual object - * doesn't matter; JSOP_NEW and InvokeConstructor both overwrite - * vp[1] without observing its value. - * - * N.B. tracing specializes for functions, so pick a non-function. - */ - vp[1].setObject(*globalObj); - } + /* Treat this as a regular call, the constructor will behave correctly. */ + mode = JSOP_CALL; this_ins = newobj_ins; } else { this_ins = get(&vp[1]); @@ -13741,6 +13720,42 @@ TraceRecorder::guardArguments(JSObject *obj, LIns* obj_ins, unsigned *depthp) return afp; } +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::createThis(JSObject& ctor, LIns* ctor_ins, LIns** thisobj_insp) +{ + JS_ASSERT(ctor.getFunctionPrivate()->isInterpreted()); + if (ctor.getFunctionPrivate()->isFunctionPrototype()) + RETURN_STOP("new Function.prototype"); + if (ctor.isBoundFunction()) + RETURN_STOP("new applied to bound function"); + + // Given the above conditions, ctor.prototype is a non-configurable data + // property with a slot. + jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); + const Shape *shape = LookupInterpretedFunctionPrototype(cx, &ctor); + if (!shape) + RETURN_ERROR("new f: error resolving f.prototype"); + + // At run time ctor might be a different instance of the same function. Its + // .prototype property might not be resolved yet. Guard on the function + // object's shape to make sure .prototype is there. + // + // However, if ctor_ins is constant, which is usual, we don't need to + // guard: .prototype is non-configurable, and an object's non-configurable + // data properties always stay in the same slot for the life of the object. + if (!ctor_ins->isImmP()) + guardShape(ctor_ins, &ctor, ctor.shape(), "ctor_shape", snapshot(MISMATCH_EXIT)); + + // Pass the slot of ctor.prototype to js_CreateThisFromTrace. We can only + // bake the slot into the trace, not the value, since .prototype is + // writable. + uintN protoSlot = shape->slot; + LIns* args[] = { w.nameImmw(protoSlot), ctor_ins, cx_ins }; + *thisobj_insp = w.call(&js_CreateThisFromTrace_ci, args); + guard(false, w.eqp0(*thisobj_insp), OOM_EXIT); + return RECORD_CONTINUE; +} + JS_REQUIRES_STACK RecordingStatus TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, bool constructing) { @@ -13752,14 +13767,10 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, */ if (fun->script()->isEmpty()) { LIns* rval_ins; - if (constructing) { - LIns* args[] = { get(&fval), w.nameImmpNonGC(&js_ObjectClass), cx_ins }; - LIns* tv_ins = w.call(&js_CreateThisFromTrace_ci, args); - guard(false, w.eqp0(tv_ins), OOM_EXIT); - rval_ins = tv_ins; - } else { + if (constructing) + CHECK_STATUS(createThis(fval.toObject(), get(&fval), &rval_ins)); + else rval_ins = w.immiUndefined(); - } stack(-2 - argc, rval_ins); return RECORD_CONTINUE; } @@ -13769,6 +13780,12 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, JSStackFrame* const fp = cx->fp(); + if (constructing) { + LIns* thisobj_ins; + CHECK_STATUS(createThis(fval.toObject(), get(&fval), &thisobj_ins)); + stack(-argc - 1, thisobj_ins); + } + // Generate a type map for the outgoing frame and stash it in the LIR unsigned stackSlots = NativeStackSlots(cx, 0 /* callDepth */); FrameInfo* fi = (FrameInfo*) diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 4cf1209feeb9..62f213c498a6 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1479,15 +1479,17 @@ class TraceRecorder VMSideExit* exit); JS_REQUIRES_STACK RecordingStatus guardPrototypeHasNoIndexedProperties(JSObject* obj, nanojit::LIns* obj_ins, - VMSideExit *exit); + VMSideExit* exit); JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v); JS_REQUIRES_STACK void clearReturningFrameFromNativeTracker(); JS_REQUIRES_STACK void putActivationObjects(); + JS_REQUIRES_STACK RecordingStatus createThis(JSObject& ctor, nanojit::LIns* ctor_ins, + nanojit::LIns** thisobj_insp); JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee); JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins, unsigned *depthp); JS_REQUIRES_STACK nanojit::LIns* guardArgsLengthNotAssigned(nanojit::LIns* argsobj_ins); - JS_REQUIRES_STACK void guardNotHole(nanojit::LIns *argsobj_ins, nanojit::LIns *ids_ins); + JS_REQUIRES_STACK void guardNotHole(nanojit::LIns* argsobj_ins, nanojit::LIns* ids_ins); JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSObject* ctor, nanojit::LIns*& proto_ins); JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSProtoKey key,