Bug 1153978 - Part 2: Use the build id instead of the XDR_BYTECODE_VERSION constant. r=jandem
☠☠ backed out by ae1aebfd7287 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Fri, 01 Jan 2016 14:19:20 +0900
changeset 339791 8db1ac84d30e663f9487a9a54844dd044ebd934b
parent 339790 08ee8e76c7039e07c91f34477121787c845c2cca
child 339792 ae1aebfd7287fd54b036825143c6c6ce257df94e
push id12803
push userjbeich@FreeBSD.org
push dateSun, 13 Mar 2016 09:48:54 +0000
reviewersjandem
bugs1153978
milestone48.0a1
Bug 1153978 - Part 2: Use the build id instead of the XDR_BYTECODE_VERSION constant. r=jandem
js/src/frontend/SourceNotes.h
js/src/js.msg
js/src/jsapi-tests/testXDR.cpp
js/src/jsfriendapi.h
js/src/vm/Xdr.cpp
js/src/vm/Xdr.h
js/src/vm/make_opcode_doc.py
--- a/js/src/frontend/SourceNotes.h
+++ b/js/src/frontend/SourceNotes.h
@@ -28,19 +28,16 @@ namespace js {
  *              |note-type|delta|           |1 1| ext-delta |
  *              +---------+-----+           +---+-----------+
  *
  * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE,
  * SRC_COLSPAN, SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode.
  *
  * NB: the js_SrcNoteSpec array in BytecodeEmitter.cpp is indexed by this
  * enum, so its initializers need to match the order here.
- *
- * Don't forget to update XDR_BYTECODE_VERSION in vm/Xdr.h for all such
- * incompatible source note or other bytecode changes.
  */
 #define FOR_EACH_SRC_NOTE_TYPE(M)                                                                  \
     M(SRC_NULL,         "null",        0)  /* Terminates a note vector. */                         \
     M(SRC_IF,           "if",          0)  /* JSOP_IFEQ bytecode is from an if-then. */            \
     M(SRC_IF_ELSE,      "if-else",     1)  /* JSOP_IFEQ bytecode is from an if-then-else. */       \
     M(SRC_COND,         "cond",        1)  /* JSOP_IFEQ is from conditional ?: operator. */        \
     M(SRC_FOR,          "for",         3)  /* JSOP_NOP or JSOP_POP in for(;;) loop head. */        \
     M(SRC_WHILE,        "while",       1)  /* JSOP_GOTO to for or while loop condition from before \
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -158,19 +158,20 @@ MSG_DEF(JSMSG_UNWRAP_DENIED,           0
 // JSAPI-only (Not thrown as JS exceptions)
 MSG_DEF(JSMSG_BAD_CLONE_FUNOBJ_SCOPE,  0, JSEXN_TYPEERR, "bad cloned function scope chain")
 MSG_DEF(JSMSG_CANT_CLONE_OBJECT,       0, JSEXN_TYPEERR, "can't clone object")
 MSG_DEF(JSMSG_CANT_OPEN,               2, JSEXN_ERR, "can't open {0}: {1}")
 MSG_DEF(JSMSG_USER_DEFINED_ERROR,      0, JSEXN_ERR, "JS_ReportError was called")
 
 // Internal errors
 MSG_DEF(JSMSG_ALLOC_OVERFLOW,          0, JSEXN_INTERNALERR, "allocation size overflow")
+MSG_DEF(JSMSG_BAD_BUILD_ID,            0, JSEXN_INTERNALERR, "bad build ID for XDR script")
 MSG_DEF(JSMSG_BAD_BYTECODE,            1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}")
-MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC,        0, JSEXN_INTERNALERR, "bad script XDR magic number")
 MSG_DEF(JSMSG_BUFFER_TOO_SMALL,        0, JSEXN_INTERNALERR, "buffer too small")
+MSG_DEF(JSMSG_BUILD_ID_NOT_AVAILABLE,  0, JSEXN_INTERNALERR, "build ID is not available")
 MSG_DEF(JSMSG_BYTECODE_TOO_BIG,        2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})")
 MSG_DEF(JSMSG_NEED_DIET,               1, JSEXN_INTERNALERR, "{0} too large")
 MSG_DEF(JSMSG_OUT_OF_MEMORY,           0, JSEXN_INTERNALERR, "out of memory")
 MSG_DEF(JSMSG_OVER_RECURSED,           0, JSEXN_INTERNALERR, "too much recursion")
 MSG_DEF(JSMSG_TOO_BIG_TO_ENCODE,       0, JSEXN_INTERNALERR, "data are to big to encode")
 MSG_DEF(JSMSG_TOO_DEEP,                1, JSEXN_INTERNALERR, "{0} nested too deeply")
 MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION,      1, JSEXN_INTERNALERR, "uncaught exception: {0}")
 MSG_DEF(JSMSG_UNKNOWN_FORMAT,          1, JSEXN_INTERNALERR, "unknown bytecode format {0}")
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -7,19 +7,28 @@
 #include "jsfriendapi.h"
 #include "jsscript.h"
 #include "jsstr.h"
 
 #include "jsapi-tests/tests.h"
 
 #include "jsscriptinlines.h"
 
+static bool
+GetBuildId(JS::BuildIdCharVector* buildId)
+{
+    const char buildid[] = "testXDR";
+    return buildId->append(buildid, sizeof(buildid));
+}
+
 static JSScript*
 FreezeThaw(JSContext* cx, JS::HandleScript script)
 {
+    JS::SetBuildIdOp(cx->runtime(), GetBuildId);
+
     // freeze
     uint32_t nbytes;
     void* memory = JS_EncodeScript(cx, script, &nbytes);
     if (!memory)
         return nullptr;
 
     // thaw
     JSScript* script2 = JS_DecodeScript(cx, memory, nbytes);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -960,19 +960,16 @@ StringIsArrayIndex(JSLinearString* str, 
 
 JS_FRIEND_API(void)
 SetPreserveWrapperCallback(JSRuntime* rt, PreserveWrapperCallback callback);
 
 JS_FRIEND_API(bool)
 IsObjectInContextCompartment(JSObject* obj, const JSContext* cx);
 
 /*
- * NB: these flag bits are encoded into the bytecode stream in the immediate
- * operand of JSOP_ITER, so don't change them without advancing vm/Xdr.h's
- * XDR_BYTECODE_VERSION.
  * NB: keep these in sync with the copy in builtin/SelfHostingDefines.h.
  * The first three are omitted because they shouldn't be used in new code.
  */
 #define JSITER_ENUMERATE  0x1   /* for-in compatible hidden default iterator */
 #define JSITER_FOREACH    0x2   /* get obj[key] for each property */
 #define JSITER_KEYVALUE   0x4   /* obsolete destructuring for-in wants [key, value] */
 #define JSITER_OWNONLY    0x8   /* iterate over obj's own properties only */
 #define JSITER_HIDDEN     0x10  /* also enumerate non-enumerable properties */
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -1,25 +1,28 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/Xdr.h"
 
+#include "mozilla/PodOperations.h"
+
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsscript.h"
 
 #include "vm/Debugger.h"
 #include "vm/ScopeObject.h"
 
 using namespace js;
+using mozilla::PodEqual;
 
 void
 XDRBuffer::freeBuffer()
 {
     js_free(base);
 #ifdef DEBUG
     memset(this, 0xe2, sizeof *this);
 #endif
@@ -87,29 +90,58 @@ XDRState<mode>::codeChars(char16_t* char
     }
     return true;
 }
 
 template<XDRMode mode>
 static bool
 VersionCheck(XDRState<mode>* xdr)
 {
-    uint32_t bytecodeVer;
+    JS::BuildIdCharVector buildId;
+    if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId)) {
+        JS_ReportErrorNumber(xdr->cx(), GetErrorMessage, nullptr, JSMSG_BUILD_ID_NOT_AVAILABLE);
+        return false;
+    }
+    MOZ_ASSERT(!buildId.empty());
+
+    uint32_t buildIdLength;
     if (mode == XDR_ENCODE)
-        bytecodeVer = XDR_BYTECODE_VERSION;
+        buildIdLength = buildId.length();
 
-    if (!xdr->codeUint32(&bytecodeVer))
+    if (!xdr->codeUint32(&buildIdLength))
         return false;
 
-    if (mode == XDR_DECODE && bytecodeVer != XDR_BYTECODE_VERSION) {
-        /* We do not provide binary compatibility with older scripts. */
-        JS_ReportErrorNumber(xdr->cx(), GetErrorMessage, nullptr, JSMSG_BAD_SCRIPT_MAGIC);
+    if (mode == XDR_DECODE && buildIdLength != buildId.length()) {
+        JS_ReportErrorNumber(xdr->cx(), GetErrorMessage, nullptr, JSMSG_BAD_BUILD_ID);
         return false;
     }
 
+    if (mode == XDR_ENCODE) {
+        if (!xdr->codeBytes(buildId.begin(), buildIdLength))
+            return false;
+    } else {
+        JS::BuildIdCharVector decodedBuildId;
+
+        // buildIdLength is already checked against the length of current
+        // buildId.
+        if (!decodedBuildId.resize(buildIdLength)) {
+            ReportOutOfMemory(xdr->cx());
+            return false;
+        }
+
+        if (!xdr->codeBytes(decodedBuildId.begin(), buildIdLength))
+            return false;
+
+        if (!PodEqual(decodedBuildId.begin(), buildId.begin(), buildIdLength)) {
+            // We do not provide binary compatibility with older scripts.
+            JS_ReportErrorNumber(xdr->cx(), GetErrorMessage, nullptr, JSMSG_BAD_BUILD_ID);
+            return false;
+        }
+    }
+
     return true;
 }
 
 template<XDRMode mode>
 bool
 XDRState<mode>::codeFunction(MutableHandleFunction objp)
 {
     if (mode == XDR_DECODE)
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -10,42 +10,16 @@
 #include "mozilla/Endian.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jsatom.h"
 #include "jsfriendapi.h"
 
 namespace js {
 
-/*
- * Bytecode version number. Increment the subtrahend whenever JS bytecode
- * changes incompatibly.
- *
- * This version number is XDR'd near the front of xdr bytecode and aborts
- * deserialization if there is a mismatch between the current and saved
- * versions.  If deserialization fails, the data should be invalidated if
- * possible.
- *
- * When you change this, run make_opcode_doc.py and copy the new output into
- * this wiki page:
- *
- *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
- *
- * (If you're wondering, 0xb973c0de is used because it looks like "bytecode".)
- */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 351;
-static const uint32_t XDR_BYTECODE_VERSION =
-    uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
-
-static_assert(JSErr_Limit == 418,
-              "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
-              "removed MSG_DEFs from js.msg, you should increment "
-              "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
-              "expected JSErr_Limit value.");
-
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)
       : context(cx), base(nullptr), cursor(nullptr), limit(nullptr) { }
 
     JSContext* cx() const {
         return context;
     }
--- a/js/src/vm/make_opcode_doc.py
+++ b/js/src/vm/make_opcode_doc.py
@@ -1,14 +1,14 @@
 #!/usr/bin/python -B
 
 """ Usage: make_opcode_doc.py PATH_TO_MOZILLA_CENTRAL
 
     This script generates SpiderMonkey bytecode documentation
-    from js/src/vm/Opcodes.h and js/src/vm/Xdr.h.
+    from js/src/vm/Opcodes.h.
 
     Output is written to stdout and should be pasted into the following
     MDN page:
     https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
 """
 
 from __future__ import print_function
 import re
@@ -16,35 +16,16 @@ import sys
 from xml.sax.saxutils import escape
 
 SOURCE_BASE = 'http://mxr.mozilla.org/mozilla-central/source'
 
 def error(message):
     print("Error: {message}".format(message=message), file=sys.stderr)
     sys.exit(1)
 
-def get_xdr_version(dir):
-    subtrahend_pat = re.compile('XDR_BYTECODE_VERSION_SUBTRAHEND\s*=\s*(\d+);', re.S)
-    version_expr_pat = re.compile('XDR_BYTECODE_VERSION\s*=\s*uint32_t\(0xb973c0de\s*-\s*(.+?)\);', re.S)
-
-    with open('{dir}/js/src/vm/Xdr.h'.format(dir=dir), 'r') as f:
-        data = f.read()
-
-    m = subtrahend_pat.search(data)
-    if not m:
-        error('XDR_BYTECODE_VERSION_SUBTRAHEND is not recognized.')
-
-    subtrahend = int(m.group(1))
-
-    m = version_expr_pat.search(data)
-    if not m:
-        error('XDR_BYTECODE_VERSION is not recognized.')
-
-    return subtrahend
-
 quoted_pat = re.compile(r"([^A-Za-z0-9]|^)'([^']+)'")
 js_pat = re.compile(r"([^A-Za-z0-9]|^)(JS[A-Z0-9_\*]+)")
 def codify(text):
     text = re.sub(quoted_pat, '\\1<code>\\2</code>', text)
     text = re.sub(js_pat, '\\1<code>\\2</code>', text)
 
     return text
 
@@ -350,31 +331,25 @@ def make_element_id(category, type=''):
         id_count[id] += 1
         id = '{}_{}'.format(id, id_count[id])
     else:
         id_count[id] = 1
 
     id_cache[key] = id
     return id
 
-def print_doc(version, index):
+def print_doc(index):
     print("""<div>{{{{SpiderMonkeySidebar("Internals")}}}}</div>
 
 <h2 id="Bytecode_Listing">Bytecode Listing</h2>
 
 <p>This document is automatically generated from
-<a href="{source_base}/js/src/vm/Opcodes.h">Opcodes.h</a> and
-<a href="{source_base}/js/src/vm/Xdr.h">Xdr.h</a> by
+<a href="{source_base}/js/src/vm/Opcodes.h">Opcodes.h</a> by
 <a href="{source_base}/js/src/vm/make_opcode_doc.py">make_opcode_doc.py</a>.</p>
-
-<p>Bytecode version: <code>{version}</code>
-(<code>0x{actual_version:08x}</code>).</p>
-""".format(source_base=SOURCE_BASE,
-           version=version,
-           actual_version=0xb973c0de - version))
+""".format(source_base=SOURCE_BASE))
 
     for (category_name, types) in index:
         print('<h3 id="{id}">{name}</h3>'.format(name=category_name,
                                                  id=make_element_id(category_name)))
         for (type_name, opcodes) in types:
             if type_name:
                 print('<h4 id="{id}">{name}</h4>'.format(name=type_name,
                                                          id=make_element_id(category_name, type_name)))
@@ -385,11 +360,10 @@ def print_doc(version, index):
             print('</dl>')
 
 if __name__ == '__main__':
     if len(sys.argv) < 2:
         print("Usage: make_opcode_doc.py PATH_TO_MOZILLA_CENTRAL",
               file=sys.stderr)
         sys.exit(1)
     dir = sys.argv[1]
-    version = get_xdr_version(dir)
     index = get_opcodes(dir)
-    print_doc(version, index)
+    print_doc(index)