From e2bd5b1b0a88a1a74aabbe4940e5ba9a08ff73dd Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 5 Jan 2010 12:48:09 -0600 Subject: [PATCH] Backed out changeset 3862a7e48e79 due to tinderbox failures on js1_5/GC/regress-324278.js. --- js/src/jsemit.cpp | 23 ++----- js/src/jsfun.cpp | 7 +- js/src/jsinterp.h | 7 +- js/src/jsobj.cpp | 2 +- js/src/jsops.cpp | 110 ++++++++++++++++++++++++++++++-- js/src/jsparse.cpp | 4 +- js/src/jsscript.cpp | 2 +- js/src/tests/ecma_5/RegExp/7.8.5-1.js | 27 -------- js/src/tests/ecma_5/RegExp/jstests.list | 2 - 9 files changed, 126 insertions(+), 58 deletions(-) delete mode 100644 js/src/tests/ecma_5/RegExp/7.8.5-1.js delete mode 100644 js/src/tests/ecma_5/RegExp/jstests.list diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 6a96533125..2b198acc30 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -6699,24 +6699,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ok = EmitNumberOp(cx, pn->pn_dval, cg); break; - case TOK_REGEXP: { + case TOK_REGEXP: /* - * If the regexp's script is one-shot and the regexp is not used in a - * loop, we can avoid the extra fork-on-exec costs of JSOP_REGEXP by - * selecting JSOP_OBJECT. Otherwise, to avoid incorrect proto, parent, - * and lastIndex sharing, select JSOP_REGEXP. + * If the regexp's script is one-shot, we can avoid the extra + * fork-on-exec costs of JSOP_REGEXP by selecting JSOP_OBJECT. + * Otherwise, to avoid incorrect proto, parent, and lastIndex + * sharing among threads and sequentially across re-execution, + * select JSOP_REGEXP. */ JS_ASSERT(pn->pn_op == JSOP_REGEXP); - bool singleton = !cg->fun && (cg->flags & TCF_COMPILE_N_GO); - if (singleton) { - for (JSStmtInfo *stmt = cg->topStmt; stmt; stmt = stmt->down) { - if (STMT_IS_LOOP(stmt)) { - singleton = false; - break; - } - } - } - if (singleton) { + if (cg->flags & TCF_COMPILE_N_GO) { ok = EmitObjectOp(cx, pn->pn_objbox, JSOP_OBJECT, cg); } else { ok = EmitIndexOp(cx, JSOP_REGEXP, @@ -6724,7 +6716,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) cg); } break; - } #if JS_HAS_XML_SUPPORT case TOK_ANYNAME: diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 64c15ae777..821bb348f2 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1785,7 +1785,12 @@ JSFunction::countInterpretedReservedSlots() const { JS_ASSERT(FUN_INTERPRETED(this)); - return (u.i.nupvars == 0) ? 0 : u.i.script->upvars()->length; + uint32 nslots = (u.i.nupvars == 0) + ? 0 + : u.i.script->upvars()->length; + if (u.i.script->regexpsOffset != 0) + nslots += u.i.script->regexps()->length; + return nslots; } static uint32 diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index a7e5726734..07b707778d 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -181,8 +181,13 @@ JSStackFrame::assertValidStackDepth(uintN depth) static JS_INLINE uintN GlobalVarCount(JSStackFrame *fp) { + uintN n; + JS_ASSERT(!fp->fun); - return fp->script->nfixed; + n = fp->script->nfixed; + if (fp->script->regexpsOffset != 0) + n -= fp->script->regexps()->length; + return n; } typedef struct JSInlineFrame { diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 6b15c193df..d7f9b0f3db 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2284,7 +2284,7 @@ class AutoDescriptorArray : private JSTempValueRooter JS_POP_TEMP_ROOT(cx, this); } - PropertyDescriptor *append() { + PropertyDescriptor * append() { if (!descriptors.append(PropertyDescriptor())) return NULL; return &descriptors.back(); diff --git a/js/src/jsops.cpp b/js/src/jsops.cpp index f8b4b7c0dd..aea0786a42 100644 --- a/js/src/jsops.cpp +++ b/js/src/jsops.cpp @@ -2438,21 +2438,117 @@ BEGIN_CASE(JSOP_OBJECT) END_CASE(JSOP_OBJECT) BEGIN_CASE(JSOP_REGEXP) +{ + JSObject *funobj; + /* - * Push a regexp object cloned from the regexp literal object mapped by the - * bytecode at pc. ES5 finally fixed this bad old ES3 design flaw which was - * flouted by many browser-based implementations. + * Push a regexp object for the atom mapped by the bytecode at pc, cloning + * the literal's regexp object if necessary, to simulate in the + * pre-compile/execute-later case what ECMA specifies for the + * compile-and-go case: that scanning each regexp literal creates a single + * corresponding RegExp object. + * + * To support pre-compilation transparently, we must handle the case where + * a regexp object literal is used in a different global at execution time + * from the global with which it was scanned at compile time. We do this + * by re-wrapping the JSRegExp private data struct with a cloned object + * having the right prototype and parent, and having its own lastIndex + * property value storage. + * + * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone literal + * objects, we don't want to pay a script prolog execution price for all + * regexp literals in a script (many may not be used by a particular + * execution of that script, depending on control flow), so we initialize + * lazily here. * * XXX This code is specific to regular expression objects. If we need a * similar op for other kinds of object literals, we should push cloning * down under JSObjectOps and reuse code here. */ index = GET_FULL_INDEX(0); - obj = js_CloneRegExpObject(cx, script->getRegExp(index), NULL); - if (!obj) - goto error; - rval = OBJECT_TO_JSVAL(obj); + JS_ASSERT(index < script->regexps()->length); + + slot = index; + if (fp->fun) { + /* + * We're in function code, not global or eval code (in eval code, + * JSOP_REGEXP is never emitted). The cloned funobj contains + * script->regexps()->length reserved slots for the cloned regexps; see + * fun_reserveSlots, jsfun.c. + */ + funobj = JSVAL_TO_OBJECT(fp->argv[-2]); + slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass); + if (script->upvarsOffset != 0) + slot += script->upvars()->length; + if (!JS_GetReservedSlot(cx, funobj, slot, &rval)) + goto error; + if (JSVAL_IS_VOID(rval)) + rval = JSVAL_NULL; + } else { + /* + * We're in global code. The code generator reserved a slot for the + * regexp among script->nfixed slots. All such slots are initialized to + * null, not void, for faster testing in JSOP_*GVAR cases. To simplify + * index calculations we count regexps in the reverse order down from + * script->nslots - 1. + */ + JS_ASSERT(slot < script->nfixed); + slot = script->nfixed - slot - 1; + rval = fp->slots[slot]; +#ifdef __GNUC__ + funobj = NULL; /* suppress bogus gcc warnings */ +#endif + } + + if (JSVAL_IS_NULL(rval)) { + /* Compute the current global object in obj2. */ + obj2 = fp->scopeChain; + while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) + obj2 = parent; + + /* + * If obj's parent is not obj2, we must clone obj so that it has the + * right parent, and therefore, the right prototype. + * + * Yes, this means we assume that the correct RegExp.prototype to which + * regexp instances (including literals) delegate can be distinguished + * solely by the instance's parent, which was set to the parent of the + * RegExp constructor function object when the instance was created. + * In other words, + * + * (/x/.__parent__ == RegExp.__parent__) implies + * (/x/.__proto__ == RegExp.prototype) + * + * (unless you assign a different object to RegExp.prototype at + * runtime, in which case, ECMA doesn't specify operation, and you get + * what you deserve). + * + * This same coupling between instance parent and constructor parent + * turns up everywhere (see jsobj.c's FindClassObject, + * js_ConstructObject, and js_NewObject). It's fundamental to the + * design of the language when you consider multiple global objects and + * separate compilation and execution, even though it is not specified + * fully in ECMA. + */ + obj = script->getRegExp(index); + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneRegExpObject(cx, obj, obj2); + if (!obj) + goto error; + } + rval = OBJECT_TO_JSVAL(obj); + + /* Store the regexp object value in its cloneIndex slot. */ + if (fp->fun) { + if (!JS_SetReservedSlot(cx, funobj, slot, rval)) + goto error; + } else { + fp->slots[slot] = rval; + } + } + PUSH_OPND(rval); +} END_CASE(JSOP_REGEXP) BEGIN_CASE(JSOP_ZERO) diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 2459979f74..2e0fbcd4b4 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -951,11 +951,11 @@ JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *cal #endif /* - * Global variables (gvars) share the atom index space with locals. Due to + * Global variables and regexps share the index space with locals. Due to * incremental code generation we need to patch the bytecode to adjust the * local references to skip the globals. */ - scriptGlobals = cg.ngvars; + scriptGlobals = cg.ngvars + cg.regexpList.length; if (scriptGlobals != 0 || cg.hasSharps()) { jsbytecode *code, *end; JSOp op; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 5448d16885..b785887fdf 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1596,7 +1596,7 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); nfixed = (cg->flags & TCF_IN_FUNCTION) ? cg->fun->u.i.nvars - : cg->ngvars + cg->sharpSlots(); + : cg->ngvars + cg->regexpList.length + cg->sharpSlots(); JS_ASSERT(nfixed < SLOTNO_LIMIT); script->nfixed = (uint16) nfixed; js_InitAtomMap(cx, &script->atomMap, &cg->atomList); diff --git a/js/src/tests/ecma_5/RegExp/7.8.5-1.js b/js/src/tests/ecma_5/RegExp/7.8.5-1.js deleted file mode 100644 index b2b21b1423..0000000000 --- a/js/src/tests/ecma_5/RegExp/7.8.5-1.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -var gTestfile = '7.8.5-1.js'; -var BUGNUMBER = 98409; -var summary = 'regular expression literal evaluates to new RegExp instance'; - -printBugNumber(BUGNUMBER); -printStatus(summary); - -function flip(x) { - return /hi/g.test(x); -} -function flop(x) { - return r.test(x); -} -var r = /hi/g; -var s = "hi there"; - -var actual = [flip(s), flip(s), flip(s), flip(s), flop(s), flop(s), flop(s), flop(s)].join(','); -var expect = "true,true,true,true,true,false,true,false"; - -reportCompare(expect, actual, summary); - -printStatus("All tests passed!"); diff --git a/js/src/tests/ecma_5/RegExp/jstests.list b/js/src/tests/ecma_5/RegExp/jstests.list deleted file mode 100644 index 439b1549ef..0000000000 --- a/js/src/tests/ecma_5/RegExp/jstests.list +++ /dev/null @@ -1,2 +0,0 @@ -url-prefix ../../jsreftest.html?test=ecma_5/RegExp/ -script 7.8.5-1.js -- 2.11.4.GIT