From cc941ff66876de4a6ac849e5b7c458983d2cc2b0 Mon Sep 17 00:00:00 2001 From: "jwalden@mit.edu" Date: Sat, 9 Feb 2008 19:30:07 -0800 Subject: [PATCH] Bug 410571 - Yield and let expressions disappear in decompilation of object literal due to mismanagement of the sprintstack; just sprint all at once instead of in two steps. r+a=brendan --- js/src/jsopcode.c | 155 ++++++++--------- .../js1_7/decompilation/regress-410571.js | 159 ++++++++++++++++++ 2 files changed, 230 insertions(+), 84 deletions(-) create mode 100644 js/tests/js1_7/decompilation/regress-410571.js diff --git a/js/src/jsopcode.c b/js/src/jsopcode.c index 65c1461b950a..a79c43aa8b08 100644 --- a/js/src/jsopcode.c +++ b/js/src/jsopcode.c @@ -4192,87 +4192,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; } - case JSOP_INITPROP: { JSBool isFirst; - - LOAD_ATOM(0); - xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), - (jschar) - (ATOM_IS_IDENTIFIER(atom) ? 0 : '\'')); - if (!xval) - return NULL; - isFirst = (ss->opcodes[ss->top - 2] == JSOP_NEWINIT); - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s%s", - lval, - isFirst ? "" : ", "); - } - - do_initprop: - /* - * NB: From here onward we must not overwrite todo, which must - * be the offset just before we print the [ or { which starts - * this literal. This is important both for JSOP_INITPROP and - * for JSOP_INITELEM (whose control flow can extend through - * here via a goto). - */ -#ifdef OLD_GETTER_SETTER - if (Sprint(&ss->sprinter, "%s%s%s%s%s:%s", - xval, - (lastop == JSOP_GETTER || lastop == JSOP_SETTER) - ? " " : "", - (lastop == JSOP_GETTER) ? js_getter_str : - (lastop == JSOP_SETTER) ? js_setter_str : - "", - rval) < 0) { - return NULL; - } -#else - if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { - if (!atom || - !ATOM_IS_STRING(atom) || - !ATOM_IS_IDENTIFIER(atom) || - ATOM_IS_KEYWORD(atom) || - (ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ && - ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) { - if (Sprint(&ss->sprinter, "%s %s: %s", xval, - (lastop == JSOP_GETTER) ? js_getter_str : - (lastop == JSOP_SETTER) ? js_setter_str : - "", - rval) < 0) { - return NULL; - } - } else { - const char *end = rval + strlen(rval); - - if (*rval == '(') - ++rval, --end; - LOCAL_ASSERT(strncmp(rval, js_function_str, 8) == 0); - LOCAL_ASSERT(rval[8] == ' '); - rval += 8 + 1; - LOCAL_ASSERT(*end ? *end == ')' : end[-1] == '}'); - if (Sprint(&ss->sprinter, "%s %s%s%.*s", - (lastop == JSOP_GETTER) - ? js_get_str : js_set_str, - xval, - (rval[0] != '(') ? " " : "", - end - rval, rval) < 0) { - return NULL; - } - } - } else { - if (Sprint(&ss->sprinter, "%s: %s", xval, rval) < 0) - return NULL; - } -#endif - break; + const char *maybeComma; case JSOP_INITELEM: - { - JSBool isFirst; - /* Turn off most parens (all if there's only one initialiser). */ LOCAL_ASSERT(pc + len < endpc); isFirst = (ss->opcodes[ss->top - 3] == JSOP_NEWINIT); @@ -4289,18 +4213,81 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) lval = POP_STR(); sn = js_GetSrcNote(jp->script, pc); - /* NB: must not overwrite todo after this! */ - todo = Sprint(&ss->sprinter, "%s%s", - lval, - isFirst ? "" : ", "); - if (todo < 0) - break; if (sn && SN_TYPE(sn) == SRC_INITPROP) { atom = NULL; goto do_initprop; } - if (SprintCString(&ss->sprinter, rval) < 0) + maybeComma = isFirst ? "" : ", "; + todo = Sprint(&ss->sprinter, "%s%s%s", + lval, + maybeComma, + rval); + break; + + case JSOP_INITPROP: + LOAD_ATOM(0); + xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), + (jschar) + (ATOM_IS_IDENTIFIER(atom) ? 0 : '\'')); + if (!xval) return NULL; + isFirst = (ss->opcodes[ss->top - 2] == JSOP_NEWINIT); + rval = POP_STR(); + lval = POP_STR(); + /* fall through */ + + do_initprop: + maybeComma = isFirst ? "" : ", "; +#ifdef OLD_GETTER_SETTER + todo = Sprint(&ss->sprinter, "%s%s%s%s%s%s%s:%s", + lval, + maybeComma, + xval, + (lastop == JSOP_GETTER || lastop == JSOP_SETTER) + ? " " : "", + (lastop == JSOP_GETTER) ? js_getter_str : + (lastop == JSOP_SETTER) ? js_setter_str : + "", + rval); +#else + if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { + if (!atom || + !ATOM_IS_STRING(atom) || + !ATOM_IS_IDENTIFIER(atom) || + ATOM_IS_KEYWORD(atom) || + (ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ && + ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) { + todo = Sprint(&ss->sprinter, "%s%s%s %s: %s", + lval, + maybeComma, + xval, + (lastop == JSOP_GETTER) ? js_getter_str : + (lastop == JSOP_SETTER) ? js_setter_str : + "", + rval); + } else { + const char *end = rval + strlen(rval); + + if (*rval == '(') + ++rval, --end; + LOCAL_ASSERT(strncmp(rval, js_function_str, 8) == 0); + LOCAL_ASSERT(rval[8] == ' '); + rval += 8 + 1; + LOCAL_ASSERT(*end ? *end == ')' : end[-1] == '}'); + todo = Sprint(&ss->sprinter, "%s%s%s %s%s%.*s", + lval, + maybeComma, + (lastop == JSOP_GETTER) + ? js_get_str : js_set_str, + xval, + (rval[0] != '(') ? " " : "", + end - rval, rval); + } + } else { + todo = Sprint(&ss->sprinter, "%s%s%s: %s", + lval, maybeComma, xval, rval); + } +#endif break; } diff --git a/js/tests/js1_7/decompilation/regress-410571.js b/js/tests/js1_7/decompilation/regress-410571.js new file mode 100644 index 000000000000..047a76910fe6 --- /dev/null +++ b/js/tests/js1_7/decompilation/regress-410571.js @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is JavaScript Engine testing utilities. + * + * The Initial Developer of the Original Code is + * Jeff Walden . + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +var gTestfile = 'regress-410571.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 410571; +var summary = 'incorrect decompilation of last element of object literals'; +var actual, expect; + +printBugNumber(BUGNUMBER); +printStatus(summary); + +/************** + * BEGIN TEST * + **************/ + +function getProps(o) +{ + var props = []; + for (var i in o) + props.push(i); + return props.sort().join(","); +} + +var tests = + [ + { + fun: function() + { + yield { x: 1, y: let (z = 7) z }; + }, + generates: + [ + function(rv) + { + return typeof rv === "object" && + rv.x === 1 && + rv.y === 7 && + getProps(rv) === "x,y"; + }, + ] + }, + { + fun: function() + { + var t = { x: 3, y: yield 4 }; + yield t; + }, + generates: + [ + function(rv) { return rv == 4; }, + function(rv) + { + return typeof rv === "object" && + rv.x === 3 && + rv.y === undefined && + getProps(rv) === "x,y"; + }, + ] + }, + { + fun: function() + { + var t = { x: 3, get y() { return 17; } }; + yield t; + }, + generates: + [ + function(rv) + { + return typeof rv === "object" && + rv.x === 3 && + rv.y === 17 && + getProps(rv) === "x,y"; + } + ] + }, + { + fun: function() + { + function q() { return 32; } + var x = { x getter: q }; + yield x; + }, + generates: + [ + function(rv) + { + return typeof rv === "object" && + getProps(rv) === "x" && + rv.x === 32; + } + ] + }, + ]; + +function checkItems(name, gen) +{ + var i = 0; + for (var item in gen) + { + if (!test.generates[i](item)) + throw "wrong generated value (" + item + ") " + + "for test " + name + ", item " + i; + i++; + } + if (i !== test.generates.length) + throw "Didn't iterate all of test " + name; +} + +for (var i = 0, sz = tests.length; i < sz; i++) +{ + var test = tests[i]; + + var fun = test.fun; + checkItems(i, fun()); + + var dec = fun.toString(); + var rec = eval(dec); + checkItems("recompiled " + i, rec()); +} + +expect = actual = "no exception thrown"; + +reportCompare(expect, actual, summary);