diff --git a/js/src/builtins.tbl b/js/src/builtins.tbl index b156dd28a25..788e22606c7 100644 --- a/js/src/builtins.tbl +++ b/js/src/builtins.tbl @@ -57,6 +57,10 @@ BUILTIN3(String_getelem, LO, LO, LO, LO, JSString*, JSContext*, JSString* BUILTIN2(String_fromCharCode, LO, LO, LO, JSString*, JSContext*, jsint, 1, 1) BUILTIN2(String_p_charCodeAt, LO, LO, LO, jsint, JSString*, jsint, 1, 1) BUILTIN3(String_p_concat_1int, LO, LO, LO, LO, JSString*, JSContest*, JSString*, jsint, 1, 1) +BUILTIN3(String_p_match, LO, LO, LO, LO, JSObject*, JSContext*, JSString*, JSObject*, 1, 1) +BUILTIN4(String_p_replace_fun, LO, LO, LO, LO, LO, JSString*, JSContext*, JSString*, JSObject*, JSObject*, 1, 1) +BUILTIN4(String_p_replace_str, LO, LO, LO, LO, LO, JSString*, JSContext*, JSString*, JSObject*, JSString*, 1, 1) +BUILTIN5(String_p_replace_str3, LO, LO, LO, LO, LO, LO, JSString*, JSContext*, JSString*, JSString*, JSString*, JSString*, 1, 1) BUILTIN1(Math_random, LO, F, jsdouble, JSRuntime*, 1, 1) BUILTIN2(EqualStrings, LO, LO, LO, bool, JSString*, JSString*, 1, 1) BUILTIN2(CompareStrings, LO, LO, LO, bool, JSString*, JSString*, 1, 1) @@ -69,6 +73,7 @@ BUILTIN2(CloseIterator, LO, LO, LO, bool, JSContext*, jsval, 1, BUILTIN2(CallTree, LO, LO, LO, nanojit::GuardRecord*, avmplus::InterpState*, nanojit::Fragment*, 0, 0) BUILTIN2(FastNewObject, LO, LO, LO, JSObject*, JSContext*, JSObject*, 1, 1) BUILTIN3(AddProperty, LO, LO, LO, LO, bool, JSContext*, JSObject*, JSScopeProperty*, 1, 1) +BUILTIN3(CallGetter, LO, LO, LO, LO, jsval, JSContext*, JSObject*, JSScopeProperty*, 1, 1) BUILTIN2(TypeOfObject, LO, LO, LO, JSString*, JSContext*, JSObject*, 1, 1) BUILTIN2(TypeOfBoolean, LO, LO, LO, JSString*, JSContext*, jsint, 1, 1) BUILTIN2(NumberToString, LO, F, LO, JSString*, JSContext*, jsdouble, 1, 1) diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index 998e86332f0..cf6de4ca268 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -266,6 +266,55 @@ js_String_p_concat_1int(JSContext* cx, JSString* str, jsint i) return js_ConcatStrings(cx, str, istr); } +JSObject* FASTCALL +js_String_p_match(JSContext* cx, JSString* str, JSObject* regexp) +{ + jsval vp[4] = { JSVAL_NULL, STRING_TO_JSVAL(str), OBJECT_TO_JSVAL(regexp) }; + if (!js_str_match(cx, 1, vp)) + return (JSObject*) JSVAL_TO_BOOLEAN(JSVAL_VOID); + JS_ASSERT(JSVAL_IS_NULL(vp[0]) || + (!JSVAL_IS_PRIMITIVE(vp[0]) && OBJ_IS_ARRAY(cx, JSVAL_TO_OBJECT(vp[0])))); + return JSVAL_TO_OBJECT(vp[0]); +} + +JSString* FASTCALL +js_String_p_replace_fun(JSContext* cx, JSString* str, JSObject* regexp, JSObject* lambda) +{ + jsval vp[4] = { + JSVAL_NULL, STRING_TO_JSVAL(str), OBJECT_TO_JSVAL(regexp), OBJECT_TO_JSVAL(lambda) + }; + if (!js_StringReplaceHelper(cx, 2, lambda, NULL, vp)) + return NULL; + JS_ASSERT(JSVAL_IS_STRING(vp[0])); + return JSVAL_TO_STRING(vp[0]); +} + +JSString* FASTCALL +js_String_p_replace_str(JSContext* cx, JSString* str, JSObject* regexp, JSString* repstr) +{ + jsval vp[4] = { + JSVAL_NULL, STRING_TO_JSVAL(str), OBJECT_TO_JSVAL(regexp), STRING_TO_JSVAL(repstr) + }; + if (!js_StringReplaceHelper(cx, 2, NULL, repstr, vp)) + return NULL; + JS_ASSERT(JSVAL_IS_STRING(vp[0])); + return JSVAL_TO_STRING(vp[0]); +} + +JSString* FASTCALL +js_String_p_replace_str3(JSContext* cx, JSString* str, JSString* patstr, JSString* repstr, + JSString* flagstr) +{ + jsval vp[5] = { + JSVAL_NULL, STRING_TO_JSVAL(str), STRING_TO_JSVAL(patstr), STRING_TO_JSVAL(repstr), + STRING_TO_JSVAL(flagstr) + }; + if (!js_StringReplaceHelper(cx, 3, NULL, repstr, vp)) + return NULL; + JS_ASSERT(JSVAL_IS_STRING(vp[0])); + return JSVAL_TO_STRING(vp[0]); +} + jsdouble FASTCALL js_StringToNumber(JSContext* cx, JSString* str) { @@ -444,6 +493,16 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) return false; } +jsval FASTCALL +js_CallGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) +{ + JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop)); + jsval v; + if (!SPROP_GET(cx, sprop, obj, obj, &v)) + return JSVAL_ERROR_COOKIE; + return v; +} + JSString* FASTCALL js_TypeOfObject(JSContext* cx, JSObject* obj) { @@ -504,8 +563,10 @@ js_BooleanToNumber(JSContext* cx, jsint unboxed) { (intptr_t)&js_##op, (at0 << 4) | (at1 << 2) | atr, cse, fold NAME(op) }, #define BUILTIN3(op, at0, at1, at2, atr, tr, t0, t1, t2, cse, fold) \ { (intptr_t)&js_##op, (at0 << 6) | (at1 << 4) | (at2 << 2) | atr, cse, fold NAME(op) }, -#define BUILTIN4(op, at0, at1, at2, at3, atr, tr, t0, t1, t2, t3, cse, fold) \ +#define BUILTIN4(op, at0, at1, at2, at3, atr, tr, t0, t1, t2, t3, cse, fold) \ { (intptr_t)&js_##op, (at0 << 8) | (at1 << 6) | (at2 << 4) | (at3 << 2) | atr, cse, fold NAME(op) }, +#define BUILTIN5(op, at0, at1, at2, at3, at4, atr, tr, t0, t1, t2, t3, t4, cse, fold) \ + { (intptr_t)&js_##op, (at0 << 10) | (at1 << 8) | (at2 << 6) | (at3 << 4) | (at4 << 2) | atr, cse, fold NAME(op) }, struct CallInfo builtins[] = { #include "builtins.tbl" diff --git a/js/src/jsfile.cpp b/js/src/jsfile.cpp index d03f7b724cd..c5f60ae5533 100644 --- a/js/src/jsfile.cpp +++ b/js/src/jsfile.cpp @@ -1905,7 +1905,7 @@ file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSFILE_CHECK_NATIVE("list"); if (argc==1) { - if (JSVAL_IS_REGEXP(cx, argv[0])) { + if (VALUE_IS_REGEXP(cx, argv[0])) { re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); }else if (VALUE_IS_FUNCTION(cx, argv[0])) { diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index 2e04fef4f32..8acadcc38b9 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -3592,15 +3592,6 @@ out: /************************************************************************/ -enum regexp_tinyid { - REGEXP_SOURCE = -1, - REGEXP_GLOBAL = -2, - REGEXP_IGNORE_CASE = -3, - REGEXP_LAST_INDEX = -4, - REGEXP_MULTILINE = -5, - REGEXP_STICKY = -6 -}; - #define REGEXP_PROP_ATTRS (JSPROP_PERMANENT | JSPROP_SHARED) #define RO_REGEXP_PROP_ATTRS (REGEXP_PROP_ATTRS | JSPROP_READONLY) diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index b92ac869dad..12d74a3bf0c 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -141,12 +141,21 @@ js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res); extern void js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res); -#define JSVAL_IS_REGEXP(cx, v) \ +#define VALUE_IS_REGEXP(cx, v) \ (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) && \ OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_RegExpClass) extern JSClass js_RegExpClass; +enum regexp_tinyid { + REGEXP_SOURCE = -1, + REGEXP_GLOBAL = -2, + REGEXP_IGNORE_CASE = -3, + REGEXP_LAST_INDEX = -4, + REGEXP_MULTILINE = -5, + REGEXP_STICKY = -6 +}; + extern JSObject * js_InitRegExpClass(JSContext *cx, JSObject *obj); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index ab39fe3373f..02f4b17c190 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1167,7 +1167,7 @@ match_or_replace(JSContext *cx, NORMALIZE_THIS(cx, vp, str); data->str = str; - if (argc != 0 && JSVAL_IS_REGEXP(cx, vp[2])) { + if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) { reobj = JSVAL_TO_OBJECT(vp[2]); re = (JSRegExp *) JS_GetPrivate(cx, reobj); } else { @@ -1230,8 +1230,8 @@ match_or_replace(JSContext *cx, test = JS_TRUE; } else { /* - * MODE_MATCH implies str_match is being called from a script or a - * scripted function. If the caller cares only about testing null + * MODE_MATCH implies js_str_match is being called from a script or + * a scripted function. If the caller cares only about testing null * vs. non-null return value, optimize away the array object that * would normally be returned in *vp. */ @@ -1306,8 +1306,8 @@ match_glob(JSContext *cx, jsint count, GlobData *data) return OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(count), &v); } -static JSBool -str_match(JSContext *cx, uintN argc, jsval *vp) +JSBool +js_str_match(JSContext *cx, uintN argc, jsval *vp) { JSTempValueRooter tvr; MatchData mdata; @@ -1425,7 +1425,7 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) * Save the regExpStatics from the current regexp, since they may be * clobbered by a RegExp usage in the lambda function. Note that all * members of JSRegExpStatics are JSSubStrings, so not GC roots, save - * input, which is rooted otherwise via vp[1] in str_replace. + * input, which is rooted otherwise via vp[1] in js_str_replace. */ JSRegExpStatics save = cx->regExpStatics; JSBool freeMoreParens = JS_FALSE; @@ -1605,15 +1605,11 @@ replace_glob(JSContext *cx, jsint count, GlobData *data) return JS_TRUE; } -static JSBool -str_replace(JSContext *cx, uintN argc, jsval *vp) +JSBool +js_str_replace(JSContext *cx, uintN argc, jsval *vp) { JSObject *lambda; - JSString *repstr, *str; - ReplaceData rdata; - JSBool ok; - jschar *chars; - size_t leftlen, rightlen, length; + JSString *repstr; if (argc >= 2 && JS_TypeOfValue(cx, vp[3]) == JSTYPE_FUNCTION) { lambda = JSVAL_TO_OBJECT(vp[3]); @@ -1625,6 +1621,19 @@ str_replace(JSContext *cx, uintN argc, jsval *vp) return JS_FALSE; } + return js_StringReplaceHelper(cx, argc, lambda, repstr, vp); +} + +JSBool +js_StringReplaceHelper(JSContext *cx, uintN argc, JSObject *lambda, + JSString *repstr, jsval *vp) +{ + ReplaceData rdata; + JSBool ok; + size_t leftlen, rightlen, length; + jschar *chars; + JSString *str; + /* * For ECMA Edition 3, the first argument is to be converted to a string * to match in a "flat" sense (without regular expression metachars having @@ -1838,7 +1847,7 @@ str_split(JSContext *cx, uintN argc, jsval *vp) v = STRING_TO_JSVAL(str); ok = OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(0), &v); } else { - if (JSVAL_IS_REGEXP(cx, vp[2])) { + if (VALUE_IS_REGEXP(cx, vp[2])) { re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(vp[2])); sep = &tmp; @@ -2251,9 +2260,9 @@ static JSFunctionSpec string_methods[] = { JS_FN("localeCompare", str_localeCompare, 1,GENERIC_PRIMITIVE), /* Perl-ish methods (search is actually Python-esque). */ - JS_FN("match", str_match, 1,GENERIC_PRIMITIVE), + JS_FN("match", js_str_match, 1,GENERIC_PRIMITIVE), JS_FN("search", str_search, 1,GENERIC_PRIMITIVE), - JS_FN("replace", str_replace, 2,GENERIC_PRIMITIVE), + JS_FN("replace", js_str_replace, 2,GENERIC_PRIMITIVE), JS_FN("split", str_split, 2,GENERIC_PRIMITIVE), #if JS_HAS_PERL_SUBSTR JS_FN("substr", str_substr, 2,GENERIC_PRIMITIVE), diff --git a/js/src/jsstr.h b/js/src/jsstr.h index d4273d09e9d..a346b2a5662 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -608,10 +608,21 @@ js_GetStringBytes(JSContext *cx, JSString *str); extern void js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str); -JSBool +/* Export a few natives and a helper to other files in SpiderMonkey. */ +extern JSBool js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); +extern JSBool +js_str_match(JSContext *cx, uintN argc, jsval *vp); + +extern JSBool +js_str_replace(JSContext *cx, uintN argc, jsval *vp); + +extern JSBool +js_StringReplaceHelper(JSContext *cx, uintN argc, JSObject *lambda, + JSString *repstr, jsval *vp); + /* * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at * least 6 bytes long. Return the number of UTF-8 bytes of data written. diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 404082c49dc..70802d4d9c6 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -60,6 +60,7 @@ #include "jsiter.h" #include "jsobj.h" #include "jsopcode.h" +#include "jsregexp.h" #include "jsscope.h" #include "jsscript.h" #include "jstracer.h" @@ -2369,21 +2370,19 @@ TraceRecorder::test_property_cache_direct_slot(JSObject* obj, LIns* obj_ins, uin return true; } - /* Handle only gets and sets on the directly addressed object. */ - if (obj2 != obj) - ABORT_TRACE("PC hit on prototype chain"); + /* Insist if setting on obj being the directly addressed object. */ + uint32 setflags = (js_CodeSpec[*cx->fp->regs->pc].format & (JOF_SET | JOF_INCDEC)); + if (setflags && obj2 != obj) + ABORT_TRACE("JOF_SET opcode hit prototype chain"); /* Don't trace getter or setter calls, our caller wants a direct slot. */ if (PCVAL_IS_SPROP(pcval)) { JSScopeProperty* sprop = PCVAL_TO_SPROP(pcval); - if (js_CodeSpec[*cx->fp->regs->pc].format & JOF_SET) { - if (!SPROP_HAS_STUB_SETTER(sprop)) - ABORT_TRACE("non-stub setter"); - } else { - if (!SPROP_HAS_STUB_GETTER(sprop)) - ABORT_TRACE("non-stub getter"); - } + if (setflags && !SPROP_HAS_STUB_SETTER(sprop)) + ABORT_TRACE("non-stub setter"); + if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) + ABORT_TRACE("non-stub getter"); if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj))) ABORT_TRACE("no valid slot"); slot = sprop->slot; @@ -2462,7 +2461,7 @@ TraceRecorder::box_jsval(jsval v, LIns*& v_ins) if (isNumber(v)) { LIns* args[] = { v_ins, cx_ins }; v_ins = lir->insCall(F_BoxDouble, args); - guard(false, lir->ins2(LIR_eq, v_ins, lir->insImmPtr((void*)JSVAL_ERROR_COOKIE)), + guard(false, lir->ins2(LIR_eq, v_ins, INS_CONSTPTR(JSVAL_ERROR_COOKIE)), OOM_EXIT); return true; } @@ -3197,8 +3196,8 @@ TraceRecorder::record_JSOP_GETELEM() LIns* args[] = { get(&r), get(&l), cx_ins }; LIns* v_ins = lir->insCall(F_Any_getelem, args); - guard(false, lir->ins2(LIR_eq, v_ins, lir->insImmPtr((void*)JSVAL_ERROR_COOKIE)), - MISMATCH_EXIT); + guard(false, lir->ins2(LIR_eq, v_ins, INS_CONSTPTR(JSVAL_ERROR_COOKIE)), + MISMATCH_EXIT); if (!unbox_jsval(v, v_ins)) ABORT_TRACE("JSOP_GETELEM"); set(&l, v_ins); @@ -3404,23 +3403,27 @@ TraceRecorder::record_JSOP_CALL() JSTNErrType errtype; JSClass *tclasp; } knownNatives[] = { - { js_math_sin, F_Math_sin, "", "d", INFALLIBLE, NULL }, - { js_math_cos, F_Math_cos, "", "d", INFALLIBLE, NULL }, - { js_math_pow, F_Math_pow, "", "dd", INFALLIBLE, NULL }, - { js_math_sqrt, F_Math_sqrt, "", "d", INFALLIBLE, NULL }, - { js_math_floor, F_Math_floor, "", "d", INFALLIBLE, NULL }, - { js_str_substring, F_String_p_substring, "TC", "ii", FAIL_NULL, NULL }, - { js_str_substring, F_String_p_substring_1, "TC", "i", FAIL_NULL, NULL }, - { js_str_fromCharCode, F_String_fromCharCode, "C", "i", FAIL_NULL, NULL }, - { js_str_charCodeAt, F_String_p_charCodeAt, "T", "i", FAIL_NEG, NULL }, - { js_str_charAt, F_String_getelem, "TC", "i", FAIL_NULL, NULL }, - { js_math_random, F_Math_random, "R", "", INFALLIBLE, NULL }, - { js_str_concat, F_String_p_concat_1int, "TC", "i", FAIL_NULL, NULL }, - { js_array_join, F_Array_p_join, "TC", "s", FAIL_NULL, NULL }, - { js_obj_hasOwnProperty, F_Object_p_hasOwnProperty, - "TC", "s", FAIL_VOID, NULL }, + { js_math_sin, F_Math_sin, "", "d", INFALLIBLE, NULL }, + { js_math_cos, F_Math_cos, "", "d", INFALLIBLE, NULL }, + { js_math_pow, F_Math_pow, "", "dd", INFALLIBLE, NULL }, + { js_math_sqrt, F_Math_sqrt, "", "d", INFALLIBLE, NULL }, + { js_math_floor, F_Math_floor, "", "d", INFALLIBLE, NULL }, + { js_str_substring, F_String_p_substring, "TC", "ii", FAIL_NULL, NULL }, + { js_str_substring, F_String_p_substring_1, "TC", "i", FAIL_NULL, NULL }, + { js_str_fromCharCode, F_String_fromCharCode, "C", "i", FAIL_NULL, NULL }, + { js_str_charCodeAt, F_String_p_charCodeAt, "T", "i", FAIL_NEG, NULL }, + { js_str_charAt, F_String_getelem, "TC", "i", FAIL_NULL, NULL }, + { js_str_match, F_String_p_match, "TC", "r", FAIL_VOID, NULL }, + { js_str_replace, F_String_p_replace_str3,"TC","sss", FAIL_NULL, NULL }, + { js_str_replace, F_String_p_replace_str, "TC", "sr", FAIL_NULL, NULL }, + { js_str_replace, F_String_p_replace_fun, "TC", "fr", FAIL_NULL, NULL }, + { js_math_random, F_Math_random, "R", "", INFALLIBLE, NULL }, + { js_str_concat, F_String_p_concat_1int, "TC", "i", FAIL_NULL, NULL }, + { js_array_join, F_Array_p_join, "TC", "s", FAIL_NULL, NULL }, + { js_obj_hasOwnProperty, F_Object_p_hasOwnProperty, + "TC", "s", FAIL_VOID, NULL }, { js_obj_propertyIsEnumerable, F_Object_p_propertyIsEnumerable, - "TC", "s", FAIL_NEG, NULL }, + "TC", "s", FAIL_NEG, NULL }, }; for (uintN i = 0; i < JS_ARRAY_LENGTH(knownNatives); i++) { @@ -3430,7 +3433,7 @@ TraceRecorder::record_JSOP_CALL() uintN knownargc = strlen(known->argtypes); if (argc != knownargc) - continue; // might have another specialization for this argc + continue; intN prefixc = strlen(known->prefix); LIns* args[5]; @@ -3439,14 +3442,14 @@ TraceRecorder::record_JSOP_CALL() jsval& thisval = stackval(0 - (argc + 1)); LIns* thisval_ins = get(&thisval); - if (known->tclasp) { - if (JSVAL_IS_PRIMITIVE(thisval)) - ABORT_TRACE("known native with class guard called on primitive this"); - if (!guardClass(JSVAL_TO_OBJECT(thisval), thisval_ins, known->tclasp)) - return false; + if (known->tclasp && + !JSVAL_IS_PRIMITIVE(thisval) && + !guardClass(JSVAL_TO_OBJECT(thisval), thisval_ins, known->tclasp)) { + continue; /* might have another specialization for |this| */ } #define HANDLE_PREFIX(i) \ + JS_BEGIN_MACRO \ argtype = known->prefix[i]; \ if (argtype == 'C') { \ *argp = cx_ins; \ @@ -3457,7 +3460,8 @@ TraceRecorder::record_JSOP_CALL() } else { \ JS_NOT_REACHED("unknown prefix arg type"); \ } \ - argp--; + argp--; \ + JS_END_MACRO switch (prefixc) { case 2: @@ -3475,23 +3479,32 @@ TraceRecorder::record_JSOP_CALL() #undef HANDLE_PREFIX #define HANDLE_ARG(i) \ + JS_BEGIN_MACRO \ + jsval& arg = stackval(-(i + 1)); \ argtype = known->argtypes[i]; \ if (argtype == 'd' || argtype == 'i') { \ - jsval& arg = stackval(-(i + 1)); \ if (!isNumber(arg)) \ - ABORT_TRACE("arg in position " #i " must be numeric"); \ + continue; /* might have another specialization for arg */ \ *argp = get(&arg); \ if (argtype == 'i') \ *argp = f2i(*argp); \ } else if (argtype == 's') { \ - jsval& arg = stackval(-(i + 1)); \ if (!JSVAL_IS_STRING(arg)) \ - ABORT_TRACE("arg in position " #i " must be a string"); \ + continue; /* might have another specialization for arg */ \ + *argp = get(&arg); \ + } else if (argtype == 'r') { \ + if (!VALUE_IS_REGEXP(cx, arg)) \ + continue; /* might have another specialization for arg */ \ + *argp = get(&arg); \ + } else if (argtype == 'f') { \ + if (!VALUE_IS_FUNCTION(cx, arg)) \ + continue; /* might have another specialization for arg */ \ *argp = get(&arg); \ } else { \ - JS_NOT_REACHED("unknown arg type"); \ + continue; /* might have another specialization for arg */ \ } \ - argp--; + argp--; \ + JS_END_MACRO switch (strlen(known->argtypes)) { case 4: @@ -3515,16 +3528,22 @@ TraceRecorder::record_JSOP_CALL() #undef HANDLE_ARG LIns* res_ins = lir->insCall(known->builtin, args); - if (known->errtype == FAIL_NULL) { + switch (known->errtype) { + case FAIL_NULL: guard(false, lir->ins_eq0(res_ins), OOM_EXIT); - } else if (known->errtype == FAIL_NEG) { + break; + case FAIL_NEG: + { res_ins = lir->ins1(LIR_i2f, res_ins); - jsdpun u; u.d = 0.0; guard(false, lir->ins2(LIR_flt, res_ins, lir->insImmq(u.u64)), OOM_EXIT); - } else if (known->errtype == FAIL_VOID) { + break; + } + case FAIL_VOID: guard(false, lir->ins2i(LIR_eq, res_ins, 2), OOM_EXIT); + break; + default:; } set(&fval, res_ins); return true; @@ -3543,7 +3562,8 @@ TraceRecorder::name(jsval*& vp) LIns* obj_ins = lir->insLoadi(lir->insLoadi(cx_ins, offsetof(JSContext, fp)), offsetof(JSStackFrame, scopeChain)); - /* Can't use getProp here, because we don't want unboxing. */ + + /* Can't use prop here, because we don't want unboxing from global slots. */ uint32 slot; if (!test_property_cache_direct_slot(obj, obj_ins, slot)) return false; @@ -3569,18 +3589,61 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins) ABORT_TRACE("prop op aliases global"); guard(false, lir->ins2(LIR_eq, obj_ins, lir->insImmPtr((void*)globalObj)), MISMATCH_EXIT); - if (!test_property_cache_direct_slot(obj, obj_ins, slot)) + /* + * Property cache ensures that we are dealing with an existing property, + * and guards the shape for us. + */ + JSObject* obj2; + jsuword pcval; + if (!test_property_cache(obj, obj_ins, obj2, pcval)) return false; /* Check for non-existent property reference, which results in undefined. */ - if (slot == SPROP_INVALID_SLOT) { - const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc]; - JS_ASSERT(cs.ndefs == 1); + const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc]; + if (PCVAL_IS_NULL(pcval)) { v_ins = lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_VOID)); + JS_ASSERT(cs.ndefs == 1); stack(-cs.nuses, v_ins); return true; } + /* Insist if setting on obj being the directly addressed object. */ + uint32 setflags = (cs.format & (JOF_SET | JOF_INCDEC)); + if (setflags && obj2 != obj) + ABORT_TRACE("JOF_SET opcode hit prototype chain"); + + /* Don't trace getter or setter calls, our caller wants a direct slot. */ + if (PCVAL_IS_SPROP(pcval)) { + JSScopeProperty* sprop = PCVAL_TO_SPROP(pcval); + + if (setflags && !SPROP_HAS_STUB_SETTER(sprop)) + ABORT_TRACE("non-stub setter"); + if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) { + // FIXME 450335: generalize this away from regexp built-in getters. + if (setflags == 0 && + sprop->getter == js_RegExpClass.getProperty && + sprop->shortid < 0) { + LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins }; + v_ins = lir->insCall(F_CallGetter, args); + if (!unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_FALSE, + v_ins)) { + ABORT_TRACE("unboxing"); + } + JS_ASSERT(cs.ndefs == 1); + stack(-cs.nuses, v_ins); + return true; + } + ABORT_TRACE("non-stub getter"); + } + if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj))) + ABORT_TRACE("no valid slot"); + slot = sprop->slot; + } else { + if (!PCVAL_IS_SLOT(pcval)) + ABORT_TRACE("PCE is not a slot"); + slot = PCVAL_TO_SLOT(pcval); + } + LIns* dslots_ins = NULL; v_ins = stobj_get_slot(obj_ins, slot, dslots_ins); if (!unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins)) diff --git a/js/src/nanojit/vm_fops.h b/js/src/nanojit/vm_fops.h index 7bc47ac6793..f2add1de59a 100644 --- a/js/src/nanojit/vm_fops.h +++ b/js/src/nanojit/vm_fops.h @@ -39,6 +39,7 @@ #define BUILTIN2(op, at0, at1, atr, tr, t0, t1, cse, fold) F_##op, #define BUILTIN3(op, at0, at1, at2, atr, tr, t0, t1, t2, cse, fold) F_##op, #define BUILTIN4(op, at0, at1, at2, at3, atr, tr, t0, t1, t2, t3, cse, fold) F_##op, +#define BUILTIN5(op, at0, at1, at2, at3, at4, atr, tr, t0, t1, t2, t3, t4, cse, fold) F_##op, INTERP_FOPCODE_LIST_BEGIN #include "builtins.tbl" @@ -48,3 +49,4 @@ INTERP_FOPCODE_LIST_END #undef BUILTIN2 #undef BUILTIN3 #undef BUILTIN4 +#undef BUILTIN5