author | Ms2ger <ms2ger@gmail.com> |
Thu, 22 Aug 2013 10:16:30 +0200 | |
changeset 156789 | d5c39687fffce41563045be1a7fc7d6ab20f0191 |
parent 156788 | c85332df4320446109b4673733f5bd211d3d3463 |
child 156790 | a25a71001d7e9af16f75f256c9d086814435c362 |
push id | 2961 |
push user | lsblakk@mozilla.com |
push date | Mon, 28 Oct 2013 21:59:28 +0000 |
treeherder | mozilla-beta@73ef4f13486f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 905017 |
milestone | 26.0a1 |
backs out | 7e6fb33fdf224bfb5d54decdcecfd18ab398ef4a |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -297,50 +297,28 @@ def check_style(): def module_name(name): '''Strip the trailing .cpp, .h, inlines.h or -inl.h from a filename.''' return name.replace('inlines.h', '').replace('-inl.h', '').replace('.h', '').replace('.cpp', '') -def is_module_header(enclosing_inclname, header_inclname): - '''Determine if an included name is the "module header", i.e. should be - first in the file.''' - - module = module_name(enclosing_inclname) - - # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h". - if module == module_name(header_inclname): - return True - - # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h". - m = re.match(r'js\/(.*)\.h', header_inclname) - if m is not None and module.endswith('/' + m.group(1)): - return True - - # A weird public header case. - if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h': - return True - - return False - - class Include(object): '''Important information for a single #include statement.''' def __init__(self, inclname, linenum, is_system): self.inclname = inclname self.linenum = linenum self.is_system = is_system def isLeaf(self): return True - def section(self, enclosing_inclname): + def section(self, module): '''Identify which section inclname belongs to. The section numbers are as follows. 0. Module header (e.g. jsfoo.h or jsfooinlines.h within jsfoo.cpp) 1. mozilla/Foo.h 2. <foo.h> or <foo> 3. jsfoo.h, prmjtime.h, etc 4. foo/Bar.h @@ -350,19 +328,21 @@ class Include(object): ''' if self.is_system: return 2 if not self.inclname.endswith('.h'): return 7 - # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need - # special handling. - if is_module_header(enclosing_inclname, self.inclname): + # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need special + # handling. + if module == module_name(self.inclname) or \ + module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ + module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h': return 0 if '/' in self.inclname: if self.inclname.startswith('mozilla/'): return 1 if self.inclname.endswith('-inl.h'): return 6 @@ -466,25 +446,27 @@ def do_file(filename, inclname, file_kin error(filename, include.linenum, 'vanilla header includes an inline-header file ' + include.quote()) # Check a file doesn't #include itself. (We do this here because the cycle # detection below doesn't detect this case.) if inclname == include.inclname: error(filename, include.linenum, 'the file includes itself') + module = module_name(inclname) + def check_includes_order(include1, include2): '''Check the ordering of two #include statements.''' if include1.inclname in oddly_ordered_inclnames or \ include2.inclname in oddly_ordered_inclnames: return - section1 = include1.section(inclname) - section2 = include2.section(inclname) + section1 = include1.section(module) + section2 = include2.section(module) if (section1 > section2) or \ ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())): error(filename, str(include1.linenum) + ':' + str(include2.linenum), include1.quote() + ' should be included after ' + include2.quote()) # The #include statements in the files in assembler/ and yarr/ have all manner of implicit # ordering requirements. Boo. Ignore them. skip_order_checking = inclname.startswith(('assembler/', 'yarr/'))
--- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -1,17 +1,15 @@ /* 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/. */ #ifndef StructuredCloneTags_h__ #define StructuredCloneTags_h__ -#include "js/StructuredClone.h" - namespace mozilla { namespace dom { // CHANGING THE ORDER/PLACEMENT OF EXISTING ENUM VALUES MAY BREAK INDEXEDDB. // PROCEED WITH EXTREME CAUTION. enum StructuredCloneTags { SCTAG_BASE = JS_SCTAG_USER_MIN,
--- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -9,17 +9,16 @@ #include "nsCOMPtr.h" #include "nsIScriptContext.h" #include "nsIVariant.h" #include "nsIXPConnect.h" #include "nsServiceManagerUtils.h" #include "nsContentUtils.h" #include "jsapi.h" -#include "js/StructuredClone.h" #include "mozilla/Base64.h" using namespace mozilla; NS_IMPL_ADDREF(nsStructuredCloneContainer) NS_IMPL_RELEASE(nsStructuredCloneContainer)
--- a/dom/bluetooth/BluetoothReplyRunnable.h +++ b/dom/bluetooth/BluetoothReplyRunnable.h @@ -5,17 +5,17 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_bluetooth_bluetoothreplyrunnable_h__ #define mozilla_dom_bluetooth_bluetoothreplyrunnable_h__ #include "mozilla/Attributes.h" #include "BluetoothCommon.h" #include "nsThreadUtils.h" -#include "js/Value.h" +#include "jsapi.h" class nsIDOMDOMRequest; BEGIN_BLUETOOTH_NAMESPACE class BluetoothReply; class BluetoothReplyRunnable : public nsRunnable
--- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -5,17 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_indexeddb_indexeddatabase_h__ #define mozilla_dom_indexeddb_indexeddatabase_h__ #include "nsIProgrammingLanguage.h" #include "mozilla/Attributes.h" -#include "js/StructuredClone.h" +#include "jsapi.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsDebug.h" #include "nsError.h" #include "nsStringGlue.h" #include "nsTArray.h" #define BEGIN_INDEXEDDB_NAMESPACE \
--- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -6,18 +6,16 @@ #ifndef mozilla_dom_indexeddb_key_h__ #define mozilla_dom_indexeddb_key_h__ #include "mozilla/dom/indexedDB/IndexedDatabase.h" #include "mozIStorageStatement.h" -#include "js/Value.h" - namespace IPC { template <typename T> struct ParamTraits; } // namespace IPC BEGIN_INDEXEDDB_NAMESPACE class Key { @@ -156,17 +154,17 @@ public: void SetFromInteger(int64_t aInt) { mBuffer.Truncate(); EncodeNumber(double(aInt), eFloat); TrimBuffer(); } nsresult SetFromJSVal(JSContext* aCx, - const JS::Value aVal) + const jsval aVal) { mBuffer.Truncate(); if (JSVAL_IS_NULL(aVal) || JSVAL_IS_VOID(aVal)) { Unset(); return NS_OK; } @@ -206,17 +204,17 @@ public: if (NS_SUCCEEDED(rv)) { aVal = value; } return rv; } nsresult AppendItem(JSContext* aCx, bool aFirstOfArray, - const JS::Value aVal) + const jsval aVal) { nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); if (NS_FAILED(rv)) { Unset(); return rv; } return NS_OK; @@ -300,17 +298,17 @@ private: while (!*end) { --end; } mBuffer.Truncate(end + 1 - mBuffer.BeginReading()); } // Encoding functions. These append the encoded value to the end of mBuffer - inline nsresult EncodeJSVal(JSContext* aCx, const JS::Value aVal, + inline nsresult EncodeJSVal(JSContext* aCx, const jsval aVal, uint8_t aTypeOffset) { return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); } void EncodeString(const nsAString& aString, uint8_t aTypeOffset); void EncodeNumber(double aFloat, uint8_t aType); // Decoding functions. aPos points into mBuffer and is adjusted to point @@ -326,17 +324,17 @@ private: const unsigned char* aEnd, nsString& aString); static double DecodeNumber(const unsigned char*& aPos, const unsigned char* aEnd); nsCString mBuffer; private: - nsresult EncodeJSValInternal(JSContext* aCx, const JS::Value aVal, + nsresult EncodeJSValInternal(JSContext* aCx, const jsval aVal, uint8_t aTypeOffset, uint16_t aRecursionDepth); static nsresult DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd, JSContext* aCx, uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth); };
--- a/dom/ipc/StructuredCloneUtils.cpp +++ b/dom/ipc/StructuredCloneUtils.cpp @@ -10,17 +10,16 @@ #include "nsIDOMDOMException.h" #include "nsIMutable.h" #include "nsIXPConnect.h" #include "nsContentUtils.h" #include "nsJSEnvironment.h" #include "nsThreadUtils.h" #include "StructuredCloneTags.h" -#include "jsapi.h" using namespace mozilla::dom; namespace { void Error(JSContext* aCx, uint32_t aErrorId) {
--- a/dom/ipc/StructuredCloneUtils.h +++ b/dom/ipc/StructuredCloneUtils.h @@ -6,17 +6,17 @@ #ifndef mozilla_dom_StructuredCloneUtils_h #define mozilla_dom_StructuredCloneUtils_h #include "nsCOMPtr.h" #include "nsTArray.h" #include "nsIDOMFile.h" -#include "js/StructuredClone.h" +#include "jsapi.h" namespace mozilla { struct SerializedStructuredCloneBuffer; namespace dom { struct
--- a/dom/workers/Events.h +++ b/dom/workers/Events.h @@ -3,17 +3,17 @@ * 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/. */ #ifndef mozilla_dom_workers_events_h__ #define mozilla_dom_workers_events_h__ #include "Workers.h" -#include "js/StructuredClone.h" +class JSAutoStructuredCloneBuffer; BEGIN_WORKERS_NAMESPACE namespace events { bool InitClasses(JSContext* aCx, JS::Handle<JSObject*> aGlobal, bool aMainRuntime);
--- a/dom/workers/XMLHttpRequest.h +++ b/dom/workers/XMLHttpRequest.h @@ -10,18 +10,16 @@ #include "mozilla/dom/workers/bindings/WorkerFeature.h" // Need this for XMLHttpRequestResponseType. #include "mozilla/dom/XMLHttpRequestBinding.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/TypedArray.h" -#include "js/StructuredClone.h" - BEGIN_WORKERS_NAMESPACE class Proxy; class XMLHttpRequestUpload; class WorkerPrivate; class XMLHttpRequest : public XMLHttpRequestEventTarget, public WorkerFeature
--- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -17,17 +17,17 @@ #include "mozilla/Util.h" #include <stdint.h> #include "nsID.h" #include "nsMemory.h" #include "nsStringGlue.h" #include "nsTArray.h" -#include "js/StructuredClone.h" +#include "jsapi.h" #include "nsCSSProperty.h" #ifdef _MSC_VER #pragma warning( disable : 4800 ) #endif #if !defined(OS_POSIX) // This condition must be kept in sync with the one in
--- a/js/ipc/JavaScriptParent.h +++ b/js/ipc/JavaScriptParent.h @@ -5,17 +5,16 @@ * 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/. */ #ifndef mozilla_jsipc_JavaScriptParent__ #define mozilla_jsipc_JavaScriptParent__ #include "JavaScriptShared.h" #include "mozilla/jsipc/PJavaScriptParent.h" -#include "jsclass.h" #ifdef XP_WIN #undef GetClassName #undef GetClassInfo #endif namespace mozilla { namespace jsipc {
deleted file mode 100644 --- a/js/public/ProfilingStack.h +++ /dev/null @@ -1,94 +0,0 @@ -/* -*- 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/. */ - -#ifndef js_ProfilingStack_h -#define js_ProfilingStack_h - -#include "jsbytecode.h" -#include "jstypes.h" - -#include "js/Utility.h" - -struct JSRuntime; -class JSScript; - -namespace js { - -// A call stack can be specified to the JS engine such that all JS entry/exits -// to functions push/pop an entry to/from the specified stack. -// -// For more detailed information, see vm/SPSProfiler.h. -// -class ProfileEntry -{ - // All fields are marked volatile to prevent the compiler from re-ordering - // instructions. Namely this sequence: - // - // entry[size] = ...; - // size++; - // - // If the size modification were somehow reordered before the stores, then - // if a sample were taken it would be examining bogus information. - // - // A ProfileEntry represents both a C++ profile entry and a JS one. Both use - // the string as a description, but JS uses the sp as NULL to indicate that - // it is a JS entry. The script_ is then only ever examined for a JS entry, - // and the idx is used by both, but with different meanings. - // - const char * volatile string; // Descriptive string of this entry - void * volatile sp; // Relevant stack pointer for the entry - JSScript * volatile script_; // if js(), non-null script which is running - int32_t volatile idx; // if js(), idx of pc, otherwise line number - - public: - // All of these methods are marked with the 'volatile' keyword because SPS's - // representation of the stack is stored such that all ProfileEntry - // instances are volatile. These methods would not be available unless they - // were marked as volatile as well. - - bool js() volatile { - JS_ASSERT_IF(sp == NULL, script_ != NULL); - return sp == NULL; - } - - uint32_t line() volatile { JS_ASSERT(!js()); return idx; } - JSScript *script() volatile { JS_ASSERT(js()); return script_; } - void *stackAddress() volatile { return sp; } - const char *label() volatile { return string; } - - void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; } - void setLabel(const char *aString) volatile { string = aString; } - void setStackAddress(void *aSp) volatile { sp = aSp; } - void setScript(JSScript *aScript) volatile { script_ = aScript; } - - // We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp. - JS_FRIEND_API(jsbytecode *) pc() volatile; - JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile; - - static size_t offsetOfString() { return offsetof(ProfileEntry, string); } - static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); } - static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); } - static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); } - - // The index used in the entry can either be a line number or the offset of - // a pc into a script's code. To signify a NULL pc, use a -1 index. This is - // checked against in pc() and setPC() to set/get the right pc. - static const int32_t NullPCIndex = -1; -}; - -JS_FRIEND_API(void) -SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, - uint32_t max); - -JS_FRIEND_API(void) -EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled); - -JS_FRIEND_API(jsbytecode*) -ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip); - -} // namespace js - -#endif /* js_ProfilingStack_h */
deleted file mode 100644 --- a/js/public/StructuredClone.h +++ /dev/null @@ -1,162 +0,0 @@ -/* -*- 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/. */ - -#ifndef js_StructuredClone_h -#define js_StructuredClone_h - -#include <stdint.h> - -#include "jstypes.h" - -struct JSContext; -class JSObject; -struct JSRuntime; -struct JSStructuredCloneReader; -struct JSStructuredCloneWriter; - -namespace JS { -template <typename T> class Handle; -class Value; -} - -// API for the HTML5 internal structured cloning algorithm. - -// Read structured data from the reader r. This hook is used to read a value -// previously serialized by a call to the WriteStructuredCloneOp hook. -// -// tag and data are the pair of uint32_t values from the header. The callback -// may use the JS_Read* APIs to read any other relevant parts of the object -// from the reader r. closure is any value passed to the JS_ReadStructuredClone -// function. Return the new object on success, NULL on error/exception. -typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, - uint32_t tag, uint32_t data, void *closure); - -// Structured data serialization hook. The engine can write primitive values, -// Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other -// type of object requires application support. This callback must first use -// the JS_WriteUint32Pair API to write an object header, passing a value -// greater than JS_SCTAG_USER to the tag parameter. Then it can use the -// JS_Write* APIs to write any other relevant parts of the value v to the -// writer w. closure is any value passed to the JS_WriteStructuredCLone function. -// -// Return true on success, false on error/exception. -typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, - JS::Handle<JSObject*> obj, void *closure); - -// This is called when JS_WriteStructuredClone is given an invalid transferable. -// To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException -// with error set to one of the JS_SCERR_* values. -typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); - -// The maximum supported structured-clone serialization format version. -#define JS_STRUCTURED_CLONE_VERSION 2 - -struct JSStructuredCloneCallbacks { - ReadStructuredCloneOp read; - WriteStructuredCloneOp write; - StructuredCloneErrorOp reportError; -}; - -// Note: if the *data contains transferable objects, it can be read only once. -JS_PUBLIC_API(bool) -JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, uint32_t version, - JS::Value *vp, const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure); - -// Note: On success, the caller is responsible for calling -// JS_ClearStructuredClone(*datap, nbytesp). -JS_PUBLIC_API(bool) -JS_WriteStructuredClone(JSContext *cx, JS::Value v, uint64_t **datap, size_t *nbytesp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure, JS::Value transferable); - -JS_PUBLIC_API(bool) -JS_ClearStructuredClone(const uint64_t *data, size_t nbytes); - -JS_PUBLIC_API(bool) -JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, bool *hasTransferable); - -JS_PUBLIC_API(bool) -JS_StructuredClone(JSContext *cx, JS::Value v, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, void *closure); - -// RAII sugar for JS_WriteStructuredClone. -class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { - uint64_t *data_; - size_t nbytes_; - uint32_t version_; - - public: - JSAutoStructuredCloneBuffer() - : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} - - ~JSAutoStructuredCloneBuffer() { clear(); } - - uint64_t *data() const { return data_; } - size_t nbytes() const { return nbytes_; } - - void clear(); - - // Copy some memory. It will be automatically freed by the destructor. - bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); - - // Adopt some memory. It will be automatically freed by the destructor. - // data must have been allocated by the JS engine (e.g., extracted via - // JSAutoStructuredCloneBuffer::steal). - void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); - - // Remove the buffer so that it will not be automatically freed. - // After this, the caller is responsible for feeding the memory back to - // JSAutoStructuredCloneBuffer::adopt. - void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=NULL); - - bool read(JSContext *cx, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); - - bool write(JSContext *cx, JS::Value v, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); - - bool write(JSContext *cx, JS::Value v, JS::Value transferable, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); - - // Swap ownership with another JSAutoStructuredCloneBuffer. - void swap(JSAutoStructuredCloneBuffer &other); - - private: - // Copy and assignment are not supported. - JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other); - JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other); -}; - -// The range of tag values the application may use for its own custom object types. -#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) -#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) - -#define JS_SCERR_RECURSION 0 -#define JS_SCERR_TRANSFERABLE 1 - -JS_PUBLIC_API(void) -JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); - -JS_PUBLIC_API(bool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); - -JS_PUBLIC_API(bool) -JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); - -JS_PUBLIC_API(bool) -JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp); - -JS_PUBLIC_API(bool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); - -JS_PUBLIC_API(bool) -JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); - -JS_PUBLIC_API(bool) -JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v); - -#endif /* js_StructuredClone_h */
--- a/js/public/Value.h +++ b/js/public/Value.h @@ -1891,30 +1891,12 @@ PRIVATE_TO_JSVAL(void *ptr) static inline void * JSVAL_TO_PRIVATE(jsval v) { MOZ_ASSERT(JSVAL_IS_DOUBLE(v)); return JSVAL_TO_PRIVATE_PTR_IMPL(JSVAL_TO_IMPL(v)); } -// JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and -// constructing values from scratch (e.g. Int32Value(0)). These constants are -// stored in memory and initialized at startup, so testing against them and -// using them requires memory loads and will be correspondingly slow. -extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL; -extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO; -extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; - -namespace JS { - -extern JS_PUBLIC_DATA(const Handle<Value>) NullHandleValue; -extern JS_PUBLIC_DATA(const Handle<Value>) UndefinedHandleValue; - -} - #undef JS_VALUE_IS_CONSTEXPR #undef JS_RETURN_LAYOUT_FROM_BITS #endif /* js_Value_h */
--- a/js/src/config/check_spidermonkey_style.py +++ b/js/src/config/check_spidermonkey_style.py @@ -297,50 +297,28 @@ def check_style(): def module_name(name): '''Strip the trailing .cpp, .h, inlines.h or -inl.h from a filename.''' return name.replace('inlines.h', '').replace('-inl.h', '').replace('.h', '').replace('.cpp', '') -def is_module_header(enclosing_inclname, header_inclname): - '''Determine if an included name is the "module header", i.e. should be - first in the file.''' - - module = module_name(enclosing_inclname) - - # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h". - if module == module_name(header_inclname): - return True - - # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h". - m = re.match(r'js\/(.*)\.h', header_inclname) - if m is not None and module.endswith('/' + m.group(1)): - return True - - # A weird public header case. - if module == 'jsmemorymetrics' and header_inclname == 'js/MemoryMetrics.h': - return True - - return False - - class Include(object): '''Important information for a single #include statement.''' def __init__(self, inclname, linenum, is_system): self.inclname = inclname self.linenum = linenum self.is_system = is_system def isLeaf(self): return True - def section(self, enclosing_inclname): + def section(self, module): '''Identify which section inclname belongs to. The section numbers are as follows. 0. Module header (e.g. jsfoo.h or jsfooinlines.h within jsfoo.cpp) 1. mozilla/Foo.h 2. <foo.h> or <foo> 3. jsfoo.h, prmjtime.h, etc 4. foo/Bar.h @@ -350,19 +328,21 @@ class Include(object): ''' if self.is_system: return 2 if not self.inclname.endswith('.h'): return 7 - # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need - # special handling. - if is_module_header(enclosing_inclname, self.inclname): + # A couple of modules have the .h file in js/ and the .cpp file elsewhere and so need special + # handling. + if module == module_name(self.inclname) or \ + module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ + module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h': return 0 if '/' in self.inclname: if self.inclname.startswith('mozilla/'): return 1 if self.inclname.endswith('-inl.h'): return 6 @@ -466,25 +446,27 @@ def do_file(filename, inclname, file_kin error(filename, include.linenum, 'vanilla header includes an inline-header file ' + include.quote()) # Check a file doesn't #include itself. (We do this here because the cycle # detection below doesn't detect this case.) if inclname == include.inclname: error(filename, include.linenum, 'the file includes itself') + module = module_name(inclname) + def check_includes_order(include1, include2): '''Check the ordering of two #include statements.''' if include1.inclname in oddly_ordered_inclnames or \ include2.inclname in oddly_ordered_inclnames: return - section1 = include1.section(inclname) - section2 = include2.section(inclname) + section1 = include1.section(module) + section2 = include2.section(module) if (section1 > section2) or \ ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())): error(filename, str(include1.linenum) + ':' + str(include2.linenum), include1.quote() + ' should be included after ' + include2.quote()) # The #include statements in the files in assembler/ and yarr/ have all manner of implicit # ordering requirements. Boo. Ignore them. skip_order_checking = inclname.startswith(('assembler/', 'yarr/'))
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -16,16 +16,17 @@ #include <ctype.h> #include <stdarg.h> #include <string.h> #include <sys/stat.h> #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" +#include "jsclone.h" #include "jscntxt.h" #include "jsdate.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsiter.h" #include "jslock.h" #include "jsmath.h" @@ -54,17 +55,16 @@ #include "builtin/ParallelArray.h" #include "builtin/RegExp.h" #include "frontend/BytecodeCompiler.h" #include "frontend/FullParseHandler.h" // for JS_BufferIsCompileableUnit #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit #include "gc/Marking.h" #include "jit/AsmJSLink.h" #include "js/CharacterEncoding.h" -#include "js/StructuredClone.h" #if ENABLE_INTL_API #include "unicode/uclean.h" #include "unicode/utypes.h" #endif // ENABLE_INTL_API #include "vm/DateObject.h" #include "vm/Debugger.h" #include "vm/ErrorObject.h" #include "vm/Interpreter.h" @@ -123,16 +123,27 @@ JS::detail::CallMethodIfWrapped(JSContex #define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) #endif #ifdef JS_USE_JSID_STRUCT_TYPES const jsid JSID_VOID = { size_t(JSID_TYPE_VOID) }; const jsid JSID_EMPTY = { size_t(JSID_TYPE_OBJECT) }; #endif +const jsval JSVAL_NULL = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0)); +const jsval JSVAL_ZERO = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 0)); +const jsval JSVAL_ONE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 1)); +const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, false)); +const jsval JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, true)); +const jsval JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); +const HandleValue JS::NullHandleValue = + HandleValue::fromMarkedLocation(&JSVAL_NULL); +const HandleValue JS::UndefinedHandleValue = + HandleValue::fromMarkedLocation(&JSVAL_VOID); + const jsid voidIdValue = JSID_VOID; const jsid emptyIdValue = JSID_EMPTY; const HandleId JS::JSID_VOIDHANDLE = HandleId::fromMarkedLocation(&voidIdValue); const HandleId JS::JSID_EMPTYHANDLE = HandleId::fromMarkedLocation(&emptyIdValue); /* Make sure that jschar is two bytes unsigned integer */ JS_STATIC_ASSERT((jschar)-1 > 0); JS_STATIC_ASSERT(sizeof(jschar) == 2); @@ -169,32 +180,28 @@ JS_GetEmptyStringValue(JSContext *cx) JS_PUBLIC_API(JSString *) JS_GetEmptyString(JSRuntime *rt) { JS_ASSERT(rt->hasContexts()); return rt->emptyString; } -namespace js { - -void +static void AssertHeapIsIdle(JSRuntime *rt) { JS_ASSERT(rt->heapState == js::Idle); } -void +static void AssertHeapIsIdle(JSContext *cx) { AssertHeapIsIdle(cx->runtime()); } -} - static void AssertHeapIsIdleOrIterating(JSRuntime *rt) { JS_ASSERT(!rt->isHeapCollecting()); } static void AssertHeapIsIdleOrIterating(JSContext *cx) @@ -5913,16 +5920,230 @@ JS_ParseJSONWithReviver(JSContext *cx, c RootedValue reviver(cx, reviverArg), value(cx); if (!ParseJSONWithReviver(cx, StableCharPtr(chars, len), len, reviver, &value)) return false; *vp = value; return true; } +JS_PUBLIC_API(bool) +JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes, + uint32_t version, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + + if (version > JS_STRUCTURED_CLONE_VERSION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION); + return false; + } + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure); +} + +JS_PUBLIC_API(bool) +JS_WriteStructuredClone(JSContext *cx, jsval valueArg, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure, jsval transferable) +{ + RootedValue value(cx, valueArg); + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, value); + + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + return WriteStructuredClone(cx, value, (uint64_t **) bufp, nbytesp, + callbacks, closure, transferable); +} + +JS_PUBLIC_API(bool) +JS_ClearStructuredClone(const uint64_t *data, size_t nbytes) +{ + return ClearStructuredClone(data, nbytes); +} + +JS_PUBLIC_API(bool) +JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, + bool *hasTransferable) +{ + bool transferable; + if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable)) + return false; + + *hasTransferable = transferable; + return true; +} + +JS_PUBLIC_API(bool) +JS_StructuredClone(JSContext *cx, jsval valueArg, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + RootedValue value(cx, valueArg); + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, value); + + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + JSAutoStructuredCloneBuffer buf; + return buf.write(cx, value, callbacks, closure) && + buf.read(cx, vp, callbacks, closure); +} + +void +JSAutoStructuredCloneBuffer::clear() +{ + if (data_) { + ClearStructuredClone(data_, nbytes_); + data_ = NULL; + nbytes_ = 0; + version_ = 0; + } +} + +void +JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version) +{ + clear(); + data_ = data; + nbytes_ = nbytes; + version_ = version; +} + +bool +JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version) +{ + // transferable objects cannot be copied + bool hasTransferable; + if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) || + hasTransferable) + return false; + + uint64_t *newData = static_cast<uint64_t *>(js_malloc(nbytes)); + if (!newData) + return false; + + js_memcpy(newData, srcData, nbytes); + + clear(); + data_ = newData; + nbytes_ = nbytes; + version_ = version; + return true; +} +void +JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp) +{ + *datap = data_; + *nbytesp = nbytes_; + if (versionp) + *versionp = version_; + + data_ = NULL; + nbytes_ = 0; + version_ = 0; +} + +bool +JSAutoStructuredCloneBuffer::read(JSContext *cx, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + JS_ASSERT(cx); + JS_ASSERT(data_); + return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, + optionalCallbacks, closure); +} + +bool +JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval valueArg, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + jsval transferable = JSVAL_VOID; + return write(cx, valueArg, transferable, optionalCallbacks, closure); +} + +bool +JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval valueArg, + jsval transferable, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + RootedValue value(cx, valueArg); + clear(); + bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_, + optionalCallbacks, closure, + transferable); + if (!ok) { + data_ = NULL; + nbytes_ = 0; + version_ = JS_STRUCTURED_CLONE_VERSION; + } + return ok; +} + +void +JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other) +{ + uint64_t *data = other.data_; + size_t nbytes = other.nbytes_; + uint32_t version = other.version_; + + other.data_ = this->data_; + other.nbytes_ = this->nbytes_; + other.version_ = this->version_; + + this->data_ = data; + this->nbytes_ = nbytes; + this->version_ = version; +} + +JS_PUBLIC_API(void) +JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks) +{ + rt->structuredCloneCallbacks = callbacks; +} + +JS_PUBLIC_API(bool) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2) +{ + return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); +} + +JS_PUBLIC_API(bool) +JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) +{ + return r->input().readBytes(p, len); +} + +JS_PUBLIC_API(bool) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data) +{ + return w->output().writePair(tag, data); +} + +JS_PUBLIC_API(bool) +JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len) +{ + return w->output().writeBytes(p, len); +} + /************************************************************************/ JS_PUBLIC_API(void) JS_ReportError(JSContext *cx, const char *format, ...) { va_list ap; AssertHeapIsIdle(cx);
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1142,18 +1142,64 @@ typedef JSObject * typedef void (* JSDestroyCompartmentCallback)(JSFreeOp *fop, JSCompartment *compartment); typedef void (* JSCompartmentNameCallback)(JSRuntime *rt, JSCompartment *compartment, char *buf, size_t bufsize); +/* + * Read structured data from the reader r. This hook is used to read a value + * previously serialized by a call to the WriteStructuredCloneOp hook. + * + * tag and data are the pair of uint32_t values from the header. The callback + * may use the JS_Read* APIs to read any other relevant parts of the object + * from the reader r. closure is any value passed to the JS_ReadStructuredClone + * function. Return the new object on success, NULL on error/exception. + */ +typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, + uint32_t tag, uint32_t data, void *closure); + +/* + * Structured data serialization hook. The engine can write primitive values, + * Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other + * type of object requires application support. This callback must first use + * the JS_WriteUint32Pair API to write an object header, passing a value + * greater than JS_SCTAG_USER to the tag parameter. Then it can use the + * JS_Write* APIs to write any other relevant parts of the value v to the + * writer w. closure is any value passed to the JS_WriteStructuredCLone function. + * + * Return true on success, false on error/exception. + */ +typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, + JS::Handle<JSObject*> obj, void *closure); + +/* + * This is called when JS_WriteStructuredClone is given an invalid transferable. + * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException + * with error set to one of the JS_SCERR_* values. + */ +typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); + /************************************************************************/ +/* + * JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and + * constructing values from scratch (e.g. Int32Value(0)). These constants are + * stored in memory and initialized at startup, so testing against them and + * using them requires memory loads and will be correspondingly slow. + */ +extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; + static JS_ALWAYS_INLINE jsval JS_NumberValue(double d) { int32_t i; d = JS_CANONICALIZE_NAN(d); if (mozilla::DoubleIsInt32(d, &i)) return INT_TO_JSVAL(i); return DOUBLE_TO_JSVAL(d); @@ -1825,22 +1871,16 @@ template <> struct GCMethods<jsid> static bool poisoned(jsid id) { return JS::IsPoisonedId(id); } static bool needsPostBarrier(jsid id) { return false; } #ifdef JSGC_GENERATIONAL static void postBarrier(jsid *idp) {} static void relocate(jsid *idp) {} #endif }; -void -AssertHeapIsIdle(JSRuntime *rt); - -void -AssertHeapIsIdle(JSContext *cx); - } /* namespace js */ class JSAutoRequest { public: JSAutoRequest(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : mContext(cx) @@ -4664,16 +4704,144 @@ JS_PUBLIC_API(bool) JS_ParseJSON(JSContext *cx, const jschar *chars, uint32_t len, JS::MutableHandle<JS::Value> vp); JS_PUBLIC_API(bool) JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval reviver, jsval *vp); /************************************************************************/ +/* API for the HTML5 internal structured cloning algorithm. */ + +/* The maximum supported structured-clone serialization format version. */ +#define JS_STRUCTURED_CLONE_VERSION 2 + +struct JSStructuredCloneCallbacks { + ReadStructuredCloneOp read; + WriteStructuredCloneOp write; + StructuredCloneErrorOp reportError; +}; + +/* Note: if the *data contains transferable objects, it can be read + * only once */ +JS_PUBLIC_API(bool) +JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, + uint32_t version, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure); + +/* Note: On success, the caller is responsible for calling + * JS_ClearStructuredClone(*datap, nbytesp). */ +JS_PUBLIC_API(bool) +JS_WriteStructuredClone(JSContext *cx, jsval v, uint64_t **datap, size_t *nbytesp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure, jsval transferable); + +JS_PUBLIC_API(bool) +JS_ClearStructuredClone(const uint64_t *data, size_t nbytes); + +JS_PUBLIC_API(bool) +JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, + bool *hasTransferable); + +JS_PUBLIC_API(bool) +JS_StructuredClone(JSContext *cx, jsval v, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure); + +/* RAII sugar for JS_WriteStructuredClone. */ +class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { + uint64_t *data_; + size_t nbytes_; + uint32_t version_; + + public: + JSAutoStructuredCloneBuffer() + : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} + + ~JSAutoStructuredCloneBuffer() { clear(); } + + uint64_t *data() const { return data_; } + size_t nbytes() const { return nbytes_; } + + void clear(); + + /* Copy some memory. It will be automatically freed by the destructor. */ + bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); + + /* + * Adopt some memory. It will be automatically freed by the destructor. + * data must have been allocated by the JS engine (e.g., extracted via + * JSAutoStructuredCloneBuffer::steal). + */ + void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); + + /* + * Remove the buffer so that it will not be automatically freed. + * After this, the caller is responsible for feeding the memory back to + * JSAutoStructuredCloneBuffer::adopt. + */ + void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=NULL); + + bool read(JSContext *cx, jsval *vp, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, + void *closure=NULL); + + bool write(JSContext *cx, jsval v, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, + void *closure=NULL); + + bool write(JSContext *cx, jsval v, + jsval transferable, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, + void *closure=NULL); + + /** + * Swap ownership with another JSAutoStructuredCloneBuffer. + */ + void swap(JSAutoStructuredCloneBuffer &other); + + private: + /* Copy and assignment are not supported. */ + JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other); + JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other); +}; + +/* API for implementing custom serialization behavior (for ImageData, File, etc.) */ + +/* The range of tag values the application may use for its own custom object types. */ +#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) +#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) + +#define JS_SCERR_RECURSION 0 +#define JS_SCERR_TRANSFERABLE 1 + +JS_PUBLIC_API(void) +JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); + +JS_PUBLIC_API(bool) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); + +JS_PUBLIC_API(bool) +JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); + +JS_PUBLIC_API(bool) +JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp); + +JS_PUBLIC_API(bool) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); + +JS_PUBLIC_API(bool) +JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); + +JS_PUBLIC_API(bool) +JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v); + +/************************************************************************/ + /* * The default locale for the ECMAScript Internationalization API * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat). * Note that the Internationalization API encourages clients to * specify their own locales. * The locale string remains owned by the caller. */ extern JS_PUBLIC_API(bool) @@ -5082,16 +5250,19 @@ JS_DecodeScript(JSContext *cx, const voi JSPrincipals *principals, JSPrincipals *originPrincipals); extern JS_PUBLIC_API(JSObject *) JS_DecodeInterpretedFunction(JSContext *cx, const void *data, uint32_t length, JSPrincipals *principals, JSPrincipals *originPrincipals); namespace JS { +extern JS_PUBLIC_DATA(const Handle<Value>) NullHandleValue; +extern JS_PUBLIC_DATA(const Handle<Value>) UndefinedHandleValue; + extern JS_PUBLIC_DATA(const Handle<jsid>) JSID_VOIDHANDLE; extern JS_PUBLIC_DATA(const Handle<jsid>) JSID_EMPTYHANDLE; } /* namespace JS */ namespace js { /*
new file mode 100644 --- /dev/null +++ b/js/src/jsclone.cpp @@ -0,0 +1,1282 @@ +/* -*- 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/. */ + +/* + * This file implements the structured clone algorithm of + * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#safe-passing-of-structured-data + * + * The implementation differs slightly in that it uses an explicit stack, and + * the "memory" maps source objects to sequential integer indexes rather than + * directly pointing to destination objects. As a result, the order in which + * things are added to the memory must exactly match the order in which they + * are placed into 'allObjs', an analogous array of back-referenceable + * destination objects constructed while reading. + * + * For the most part, this is easy: simply add objects to the memory when first + * encountering them. But reading in a typed array requires an ArrayBuffer for + * construction, so objects cannot just be added to 'allObjs' in the order they + * are created. If they were, ArrayBuffers would come before typed arrays when + * in fact the typed array was added to 'memory' first. + * + * So during writing, we add objects to the memory when first encountering + * them. When reading a typed array, a placeholder is pushed onto allObjs until + * the ArrayBuffer has been read, then it is updated with the actual typed + * array object. + */ + +#include "jsclone.h" + +#include "mozilla/Endian.h" +#include "mozilla/FloatingPoint.h" + +#include "jsdate.h" +#include "jswrapper.h" + +#include "vm/TypedArrayObject.h" +#include "vm/WrapperObject.h" + +#include "jscntxtinlines.h" +#include "jsobjinlines.h" + +using namespace js; + +using mozilla::IsNaN; +using mozilla::LittleEndian; +using mozilla::NativeEndian; + +enum StructuredDataType { + /* Structured data types provided by the engine */ + SCTAG_FLOAT_MAX = 0xFFF00000, + SCTAG_NULL = 0xFFFF0000, + SCTAG_UNDEFINED, + SCTAG_BOOLEAN, + SCTAG_INDEX, + SCTAG_STRING, + SCTAG_DATE_OBJECT, + SCTAG_REGEXP_OBJECT, + SCTAG_ARRAY_OBJECT, + SCTAG_OBJECT_OBJECT, + SCTAG_ARRAY_BUFFER_OBJECT, + SCTAG_BOOLEAN_OBJECT, + SCTAG_STRING_OBJECT, + SCTAG_NUMBER_OBJECT, + SCTAG_BACK_REFERENCE_OBJECT, + SCTAG_TRANSFER_MAP_HEADER, + SCTAG_TRANSFER_MAP, + SCTAG_TYPED_ARRAY_OBJECT, + SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100, + SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT8, + SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT8, + SCTAG_TYPED_ARRAY_V1_INT16 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT16, + SCTAG_TYPED_ARRAY_V1_UINT16 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT16, + SCTAG_TYPED_ARRAY_V1_INT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT32, + SCTAG_TYPED_ARRAY_V1_UINT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT32, + SCTAG_TYPED_ARRAY_V1_FLOAT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_FLOAT32, + SCTAG_TYPED_ARRAY_V1_FLOAT64 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_FLOAT64, + SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT8_CLAMPED, + SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_MAX - 1, + SCTAG_END_OF_BUILTIN_TYPES +}; + +enum TransferableMapHeader { + SCTAG_TM_NOT_MARKED = 0, + SCTAG_TM_MARKED +}; + +JS_FRIEND_API(uint64_t) +js_GetSCOffset(JSStructuredCloneWriter* writer) +{ + JS_ASSERT(writer); + return writer->output().count() * sizeof(uint64_t); +} + +JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN); +JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX); +JS_STATIC_ASSERT(TypedArrayObject::TYPE_INT8 == 0); + +bool +js::WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *cb, void *cbClosure, + jsval transferable) +{ + SCOutput out(cx); + JSStructuredCloneWriter w(out, cb, cbClosure, transferable); + return w.init() && w.write(v) && out.extractBuffer(bufp, nbytesp); +} + +bool +js::ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, + const JSStructuredCloneCallbacks *cb, void *cbClosure) +{ + SCInput in(cx, data, nbytes); + + /* XXX disallow callers from using internal pointers to GC things. */ + SkipRoot skip(cx, &in); + + JSStructuredCloneReader r(in, cb, cbClosure); + return r.read(vp); +} + +bool +js::ClearStructuredClone(const uint64_t *data, size_t nbytes) +{ + const uint64_t *point = data; + const uint64_t *end = data + nbytes / 8; + + uint64_t u = LittleEndian::readUint64(point++); + uint32_t tag = uint32_t(u >> 32); + if (tag == SCTAG_TRANSFER_MAP_HEADER) { + if ((TransferableMapHeader)uint32_t(u) == SCTAG_TM_NOT_MARKED) { + while (point != end) { + uint64_t u = LittleEndian::readUint64(point++); + uint32_t tag = uint32_t(u >> 32); + if (tag == SCTAG_TRANSFER_MAP) { + u = LittleEndian::readUint64(point++); + js_free(reinterpret_cast<void*>(u)); + } else { + // The only things in the transfer map should be + // SCTAG_TRANSFER_MAP tags paired with pointers. If we find + // any other tag, we've walked off the end of the transfer + // map. + break; + } + } + } + } + + js_free((void *)data); + return true; +} + +bool +js::StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable) +{ + *hasTransferable = false; + + if (data) { + uint64_t u = LittleEndian::readUint64(data); + uint32_t tag = uint32_t(u >> 32); + if (tag == SCTAG_TRANSFER_MAP_HEADER) { + *hasTransferable = true; + } + } + + return true; +} + +static inline uint64_t +PairToUInt64(uint32_t tag, uint32_t data) +{ + return uint64_t(data) | (uint64_t(tag) << 32); +} + +bool +SCInput::eof() +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "truncated"); + return false; +} + +SCInput::SCInput(JSContext *cx, uint64_t *data, size_t nbytes) + : cx(cx), point(data), end(data + nbytes / 8) +{ + JS_ASSERT((uintptr_t(data) & 7) == 0); + JS_ASSERT((nbytes & 7) == 0); +} + +bool +SCInput::read(uint64_t *p) +{ + if (point == end) { + *p = 0; /* initialize to shut GCC up */ + return eof(); + } + *p = LittleEndian::readUint64(point++); + return true; +} + +bool +SCInput::readPair(uint32_t *tagp, uint32_t *datap) +{ + uint64_t u; + bool ok = read(&u); + if (ok) { + *tagp = uint32_t(u >> 32); + *datap = uint32_t(u); + } + return ok; +} + +bool +SCInput::get(uint64_t *p) +{ + if (point == end) + return eof(); + *p = LittleEndian::readUint64(point); + return true; +} + +bool +SCInput::getPair(uint32_t *tagp, uint32_t *datap) +{ + uint64_t u; + if (!get(&u)) + return false; + + *tagp = uint32_t(u >> 32); + *datap = uint32_t(u); + return true; +} + +bool +SCInput::replace(uint64_t u) +{ + if (point == end) + return eof(); + LittleEndian::writeUint64(point, u); + return true; +} + +bool +SCInput::replacePair(uint32_t tag, uint32_t data) +{ + return replace(PairToUInt64(tag, data)); +} + +/* + * The purpose of this never-inlined function is to avoid a strange g++ build + * error on OS X 10.5 (see bug 624080). :-( + */ +static JS_NEVER_INLINE double +CanonicalizeNan(double d) +{ + return JS_CANONICALIZE_NAN(d); +} + +bool +SCInput::readDouble(double *p) +{ + union { + uint64_t u; + double d; + } pun; + if (!read(&pun.u)) + return false; + *p = CanonicalizeNan(pun.d); + return true; +} + +template <typename T> +static void +copyAndSwapFromLittleEndian(T *dest, const void *src, size_t nelems) +{ + NativeEndian::copyAndSwapFromLittleEndian(dest, src, nelems); +} + +template <> +void +copyAndSwapFromLittleEndian(uint8_t *dest, const void *src, size_t nelems) +{ + memcpy(dest, src, nelems); +} + +template <class T> +bool +SCInput::readArray(T *p, size_t nelems) +{ + JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0); + + /* + * Fail if nelems is so huge as to make JS_HOWMANY overflow or if nwords is + * larger than the remaining data. + */ + size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T)); + if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(end - point)) + return eof(); + + copyAndSwapFromLittleEndian(p, point, nelems); + point += nwords; + return true; +} + +bool +SCInput::readBytes(void *p, size_t nbytes) +{ + return readArray((uint8_t *) p, nbytes); +} + +bool +SCInput::readChars(jschar *p, size_t nchars) +{ + JS_ASSERT(sizeof(jschar) == sizeof(uint16_t)); + return readArray((uint16_t *) p, nchars); +} + +bool +SCInput::readPtr(void **p) +{ + // On a 32 bit system the void* variable we have to write to is only + // 32 bits, so we create a 64 temporary and discard the unused bits. + uint64_t tmp; + bool ret = read(&tmp); + *p = reinterpret_cast<void*>(tmp); + return ret; +} + +SCOutput::SCOutput(JSContext *cx) : cx(cx), buf(cx) {} + +bool +SCOutput::write(uint64_t u) +{ + return buf.append(NativeEndian::swapToLittleEndian(u)); +} + +bool +SCOutput::writePair(uint32_t tag, uint32_t data) +{ + /* + * As it happens, the tag word appears after the data word in the output. + * This is because exponents occupy the last 2 bytes of doubles on the + * little-endian platforms we care most about. + * + * For example, JSVAL_TRUE is written using writePair(SCTAG_BOOLEAN, 1). + * PairToUInt64 produces the number 0xFFFF000200000001. + * That is written out as the bytes 01 00 00 00 02 00 FF FF. + */ + return write(PairToUInt64(tag, data)); +} + +static inline uint64_t +ReinterpretDoubleAsUInt64(double d) +{ + union { + double d; + uint64_t u; + } pun; + pun.d = d; + return pun.u; +} + +static inline double +ReinterpretUInt64AsDouble(uint64_t u) +{ + union { + uint64_t u; + double d; + } pun; + pun.u = u; + return pun.d; +} + +static inline double +ReinterpretPairAsDouble(uint32_t tag, uint32_t data) +{ + return ReinterpretUInt64AsDouble(PairToUInt64(tag, data)); +} + +bool +SCOutput::writeDouble(double d) +{ + return write(ReinterpretDoubleAsUInt64(CanonicalizeNan(d))); +} + +template <typename T> +static void +copyAndSwapToLittleEndian(void *dest, const T *src, size_t nelems) +{ + NativeEndian::copyAndSwapToLittleEndian(dest, src, nelems); +} + +template <> +void +copyAndSwapToLittleEndian(void *dest, const uint8_t *src, size_t nelems) +{ + memcpy(dest, src, nelems); +} + +template <class T> +bool +SCOutput::writeArray(const T *p, size_t nelems) +{ + JS_ASSERT(8 % sizeof(T) == 0); + JS_ASSERT(sizeof(uint64_t) % sizeof(T) == 0); + + if (nelems == 0) + return true; + + if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems) { + js_ReportAllocationOverflow(context()); + return false; + } + size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T)); + size_t start = buf.length(); + if (!buf.growByUninitialized(nwords)) + return false; + + buf.back() = 0; /* zero-pad to an 8-byte boundary */ + + T *q = (T *) &buf[start]; + copyAndSwapToLittleEndian(q, p, nelems); + return true; +} + +bool +SCOutput::writeBytes(const void *p, size_t nbytes) +{ + return writeArray((const uint8_t *) p, nbytes); +} + +bool +SCOutput::writeChars(const jschar *p, size_t nchars) +{ + JS_ASSERT(sizeof(jschar) == sizeof(uint16_t)); + return writeArray((const uint16_t *) p, nchars); +} + +bool +SCOutput::writePtr(const void *p) +{ + return write(reinterpret_cast<uint64_t>(p)); +} + +bool +SCOutput::extractBuffer(uint64_t **datap, size_t *sizep) +{ + *sizep = buf.length() * sizeof(uint64_t); + return (*datap = buf.extractRawBuffer()) != NULL; +} + +JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX); + +bool +JSStructuredCloneWriter::parseTransferable() +{ + transferableObjects.clear(); + + if (JSVAL_IS_NULL(transferable) || JSVAL_IS_VOID(transferable)) + return true; + + if (!transferable.isObject()) { + reportErrorTransferable(); + return false; + } + + RootedObject array(context(), &transferable.toObject()); + if (!JS_IsArrayObject(context(), array)) { + reportErrorTransferable(); + return false; + } + + uint32_t length; + if (!JS_GetArrayLength(context(), array, &length)) { + return false; + } + + RootedValue v(context()); + + for (uint32_t i = 0; i < length; ++i) { + if (!JS_GetElement(context(), array, i, &v)) { + return false; + } + + if (!v.isObject()) { + reportErrorTransferable(); + return false; + } + + JSObject* tObj = CheckedUnwrap(&v.toObject()); + if (!tObj) { + JS_ReportError(context(), "Permission denied to access object"); + return false; + } + if (!tObj->is<ArrayBufferObject>()) { + reportErrorTransferable(); + return false; + } + + // No duplicate: + if (transferableObjects.has(tObj)) { + reportErrorTransferable(); + return false; + } + + if (!transferableObjects.putNew(tObj)) + return false; + } + + return true; +} + +void +JSStructuredCloneWriter::reportErrorTransferable() +{ + if (callbacks && callbacks->reportError) + return callbacks->reportError(context(), JS_SCERR_TRANSFERABLE); +} + +bool +JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str) +{ + size_t length = str->length(); + const jschar *chars = str->getChars(context()); + if (!chars) + return false; + return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length); +} + +bool +JSStructuredCloneWriter::writeId(jsid id) +{ + if (JSID_IS_INT(id)) + return out.writePair(SCTAG_INDEX, uint32_t(JSID_TO_INT(id))); + JS_ASSERT(JSID_IS_STRING(id)); + return writeString(SCTAG_STRING, JSID_TO_STRING(id)); +} + +inline void +JSStructuredCloneWriter::checkStack() +{ +#ifdef DEBUG + /* To avoid making serialization O(n^2), limit stack-checking at 10. */ + const size_t MAX = 10; + + size_t limit = Min(counts.length(), MAX); + JS_ASSERT(objs.length() == counts.length()); + size_t total = 0; + for (size_t i = 0; i < limit; i++) { + JS_ASSERT(total + counts[i] >= total); + total += counts[i]; + } + if (counts.length() <= MAX) + JS_ASSERT(total == ids.length()); + else + JS_ASSERT(total <= ids.length()); + + size_t j = objs.length(); + for (size_t i = 0; i < limit; i++) + JS_ASSERT(memory.has(&objs[--j].toObject())); +#endif +} + +JS_PUBLIC_API(bool) +JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v) +{ + JS_ASSERT(v.isObject()); + assertSameCompartment(w->context(), v); + RootedObject obj(w->context(), &v.toObject()); + + // If the object is a security wrapper, see if we're allowed to unwrap it. + // If we aren't, throw. + if (obj->is<WrapperObject>()) + obj = CheckedUnwrap(obj); + if (!obj) { + JS_ReportError(w->context(), "Permission denied to access object"); + return false; + } + return w->writeTypedArray(obj); +} + +/* + * Write out a typed array. Note that post-v1 structured clone buffers do not + * perform endianness conversion on stored data, so multibyte typed arrays + * cannot be deserialized into a different endianness machine. Endianness + * conversion would prevent sharing ArrayBuffers: if you have Int8Array and + * Int16Array views of the same ArrayBuffer, should the data bytes be + * byte-swapped when writing or not? The Int8Array requires them to not be + * swapped; the Int16Array requires that they are. + */ +bool +JSStructuredCloneWriter::writeTypedArray(HandleObject obj) +{ + Rooted<TypedArrayObject*> tarr(context(), &obj->as<TypedArrayObject>()); + if (!out.writePair(SCTAG_TYPED_ARRAY_OBJECT, tarr->length())) + return false; + uint64_t type = tarr->type(); + if (!out.write(type)) + return false; + + // Write out the ArrayBuffer tag and contents + if (!startWrite(TypedArrayObject::bufferValue(tarr))) + return false; + + return out.write(tarr->byteOffset()); +} + +bool +JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj) +{ + ArrayBufferObject &buffer = obj->as<ArrayBufferObject>(); + return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, buffer.byteLength()) && + out.writeBytes(buffer.dataPointer(), buffer.byteLength()); +} + +bool +JSStructuredCloneWriter::startObject(HandleObject obj, bool *backref) +{ + /* Handle cycles in the object graph. */ + CloneMemory::AddPtr p = memory.lookupForAdd(obj); + if ((*backref = p)) + return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value); + if (!memory.add(p, obj, memory.count())) + return false; + + if (memory.count() == UINT32_MAX) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, + JSMSG_NEED_DIET, "object graph to serialize"); + return false; + } + + return true; +} + +bool +JSStructuredCloneWriter::traverseObject(HandleObject obj) +{ + /* + * Get enumerable property ids and put them in reverse order so that they + * will come off the stack in forward order. + */ + size_t initialLength = ids.length(); + if (!GetPropertyNames(context(), obj, JSITER_OWNONLY, &ids)) + return false; + jsid *begin = ids.begin() + initialLength, *end = ids.end(); + size_t count = size_t(end - begin); + Reverse(begin, end); + + /* Push obj and count to the stack. */ + if (!objs.append(ObjectValue(*obj)) || !counts.append(count)) + return false; + checkStack(); + + /* Write the header for obj. */ + return out.writePair(obj->is<ArrayObject>() ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0); +} + +bool +JSStructuredCloneWriter::startWrite(const Value &v) +{ + assertSameCompartment(context(), v); + + if (v.isString()) { + return writeString(SCTAG_STRING, v.toString()); + } else if (v.isNumber()) { + return out.writeDouble(v.toNumber()); + } else if (v.isBoolean()) { + return out.writePair(SCTAG_BOOLEAN, v.toBoolean()); + } else if (v.isNull()) { + return out.writePair(SCTAG_NULL, 0); + } else if (v.isUndefined()) { + return out.writePair(SCTAG_UNDEFINED, 0); + } else if (v.isObject()) { + RootedObject obj(context(), &v.toObject()); + + // The object might be a security wrapper. See if we can clone what's + // behind it. If we can, unwrap the object. + obj = CheckedUnwrap(obj); + if (!obj) { + JS_ReportError(context(), "Permission denied to access object"); + return false; + } + + AutoCompartment ac(context(), obj); + + bool backref; + if (!startObject(obj, &backref)) + return false; + if (backref) + return true; + + if (obj->is<RegExpObject>()) { + RegExpObject &reobj = obj->as<RegExpObject>(); + return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) && + writeString(SCTAG_STRING, reobj.getSource()); + } else if (obj->is<DateObject>()) { + double d = js_DateGetMsecSinceEpoch(obj); + return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d); + } else if (obj->is<TypedArrayObject>()) { + return writeTypedArray(obj); + } else if (obj->is<ArrayBufferObject>() && obj->as<ArrayBufferObject>().hasData()) { + return writeArrayBuffer(obj); + } else if (obj->is<JSObject>() || obj->is<ArrayObject>()) { + return traverseObject(obj); + } else if (obj->is<BooleanObject>()) { + return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->as<BooleanObject>().unbox()); + } else if (obj->is<NumberObject>()) { + return out.writePair(SCTAG_NUMBER_OBJECT, 0) && + out.writeDouble(obj->as<NumberObject>().unbox()); + } else if (obj->is<StringObject>()) { + return writeString(SCTAG_STRING_OBJECT, obj->as<StringObject>().unbox()); + } + + if (callbacks && callbacks->write) + return callbacks->write(context(), this, obj, closure); + /* else fall through */ + } + + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_UNSUPPORTED_TYPE); + return false; +} + +bool +JSStructuredCloneWriter::writeTransferMap() +{ + if (!transferableObjects.empty()) { + if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_NOT_MARKED)) + return false; + + for (HashSet<JSObject*>::Range r = transferableObjects.all(); + !r.empty(); r.popFront()) { + JSObject *obj = r.front(); + + if (!memory.put(obj, memory.count())) + return false; + + void *content; + uint8_t *data; + if (!JS_StealArrayBufferContents(context(), obj, &content, &data)) + return false; + + if (!out.writePair(SCTAG_TRANSFER_MAP, 0) || !out.writePtr(content)) + return false; + } + } + + return true; +} + +bool +JSStructuredCloneWriter::write(const Value &v) +{ + if (!startWrite(v)) + return false; + + while (!counts.empty()) { + RootedObject obj(context(), &objs.back().toObject()); + AutoCompartment ac(context(), obj); + if (counts.back()) { + counts.back()--; + RootedId id(context(), ids.back()); + ids.popBack(); + checkStack(); + if (JSID_IS_STRING(id) || JSID_IS_INT(id)) { + /* + * If obj still has an own property named id, write it out. + * The cost of re-checking could be avoided by using + * NativeIterators. + */ + RootedObject obj2(context()); + RootedShape prop(context()); + if (!HasOwnProperty<CanGC>(context(), obj->getOps()->lookupGeneric, obj, id, + &obj2, &prop)) { + return false; + } + + if (prop) { + RootedValue val(context()); + if (!writeId(id) || + !JSObject::getGeneric(context(), obj, obj, id, &val) || + !startWrite(val)) + return false; + } + } + } else { + out.writePair(SCTAG_NULL, 0); + objs.popBack(); + counts.popBack(); + } + } + + memory.clear(); + + return true; +} + +bool +JSStructuredCloneReader::checkDouble(double d) +{ + jsval_layout l; + l.asDouble = d; + if (!JSVAL_IS_DOUBLE_IMPL(l)) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, "unrecognized NaN"); + return false; + } + return true; +} + +class Chars { + JSContext *cx; + jschar *p; + public: + Chars(JSContext *cx) : cx(cx), p(NULL) {} + ~Chars() { if (p) js_free(p); } + + bool allocate(size_t len) { + JS_ASSERT(!p); + // We're going to null-terminate! + p = cx->pod_malloc<jschar>(len + 1); + if (p) { + p[len] = jschar(0); + return true; + } + return false; + } + jschar *get() { return p; } + void forget() { p = NULL; } +}; + +JSString * +JSStructuredCloneReader::readString(uint32_t nchars) +{ + if (nchars > JSString::MAX_LENGTH) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, + "string length"); + return NULL; + } + Chars chars(context()); + if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars)) + return NULL; + JSString *str = js_NewString<CanGC>(context(), chars.get(), nchars); + if (str) + chars.forget(); + return str; +} + +static uint32_t +TagToV1ArrayType(uint32_t tag) +{ + JS_ASSERT(tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX); + return tag - SCTAG_TYPED_ARRAY_V1_MIN; +} + +JS_PUBLIC_API(bool) +JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp) +{ + uint32_t tag, nelems; + if (!r->input().readPair(&tag, &nelems)) + return false; + if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { + return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true); + } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) { + uint64_t arrayType; + if (!r->input().read(&arrayType)) + return false; + return r->readTypedArray(arrayType, nelems, vp); + } else { + JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array"); + return false; + } +} + +bool +JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Value *vp, + bool v1Read) +{ + if (arrayType > TypedArrayObject::TYPE_UINT8_CLAMPED) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, "unhandled typed array element type"); + return false; + } + + // Push a placeholder onto the allObjs list to stand in for the typed array + uint32_t placeholderIndex = allObjs.length(); + Value dummy = JSVAL_NULL; + if (!allObjs.append(dummy)) + return false; + + // Read the ArrayBuffer object and its contents (but no properties) + RootedValue v(context()); + uint32_t byteOffset; + if (v1Read) { + if (!readV1ArrayBuffer(arrayType, nelems, v.address())) + return false; + byteOffset = 0; + } else { + if (!startRead(v.address())) + return false; + uint64_t n; + if (!in.read(&n)) + return false; + byteOffset = n; + } + RootedObject buffer(context(), &v.toObject()); + RootedObject obj(context(), NULL); + + switch (arrayType) { + case TypedArrayObject::TYPE_INT8: + obj = JS_NewInt8ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + case TypedArrayObject::TYPE_UINT8: + obj = JS_NewUint8ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + case TypedArrayObject::TYPE_INT16: + obj = JS_NewInt16ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + case TypedArrayObject::TYPE_UINT16: + obj = JS_NewUint16ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + case TypedArrayObject::TYPE_INT32: + obj = JS_NewInt32ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + case TypedArrayObject::TYPE_UINT32: + obj = JS_NewUint32ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + case TypedArrayObject::TYPE_FLOAT32: + obj = JS_NewFloat32ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + case TypedArrayObject::TYPE_FLOAT64: + obj = JS_NewFloat64ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + case TypedArrayObject::TYPE_UINT8_CLAMPED: + obj = JS_NewUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + default: + MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type"); + } + + if (!obj) + return false; + vp->setObject(*obj); + + allObjs[placeholderIndex] = *vp; + + return true; +} + +bool +JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, Value *vp) +{ + JSObject *obj = ArrayBufferObject::create(context(), nbytes); + if (!obj) + return false; + vp->setObject(*obj); + ArrayBufferObject &buffer = obj->as<ArrayBufferObject>(); + JS_ASSERT(buffer.byteLength() == nbytes); + return in.readArray(buffer.dataPointer(), nbytes); +} + +static size_t +bytesPerTypedArrayElement(uint32_t arrayType) +{ + switch (arrayType) { + case TypedArrayObject::TYPE_INT8: + case TypedArrayObject::TYPE_UINT8: + case TypedArrayObject::TYPE_UINT8_CLAMPED: + return sizeof(uint8_t); + case TypedArrayObject::TYPE_INT16: + case TypedArrayObject::TYPE_UINT16: + return sizeof(uint16_t); + case TypedArrayObject::TYPE_INT32: + case TypedArrayObject::TYPE_UINT32: + case TypedArrayObject::TYPE_FLOAT32: + return sizeof(uint32_t); + case TypedArrayObject::TYPE_FLOAT64: + return sizeof(uint64_t); + default: + MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type"); + } +} + +/* + * Read in the data for a structured clone version 1 ArrayBuffer, performing + * endianness-conversion while reading. + */ +bool +JSStructuredCloneReader::readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, Value *vp) +{ + JS_ASSERT(arrayType <= TypedArrayObject::TYPE_UINT8_CLAMPED); + + uint32_t nbytes = nelems * bytesPerTypedArrayElement(arrayType); + JSObject *obj = ArrayBufferObject::create(context(), nbytes); + if (!obj) + return false; + vp->setObject(*obj); + ArrayBufferObject &buffer = obj->as<ArrayBufferObject>(); + JS_ASSERT(buffer.byteLength() == nbytes); + + switch (arrayType) { + case TypedArrayObject::TYPE_INT8: + case TypedArrayObject::TYPE_UINT8: + case TypedArrayObject::TYPE_UINT8_CLAMPED: + return in.readArray((uint8_t*) buffer.dataPointer(), nelems); + case TypedArrayObject::TYPE_INT16: + case TypedArrayObject::TYPE_UINT16: + return in.readArray((uint16_t*) buffer.dataPointer(), nelems); + case TypedArrayObject::TYPE_INT32: + case TypedArrayObject::TYPE_UINT32: + case TypedArrayObject::TYPE_FLOAT32: + return in.readArray((uint32_t*) buffer.dataPointer(), nelems); + case TypedArrayObject::TYPE_FLOAT64: + return in.readArray((uint64_t*) buffer.dataPointer(), nelems); + default: + MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type"); + } +} + +bool +JSStructuredCloneReader::startRead(Value *vp) +{ + uint32_t tag, data; + + if (!in.readPair(&tag, &data)) + return false; + switch (tag) { + case SCTAG_NULL: + vp->setNull(); + break; + + case SCTAG_UNDEFINED: + vp->setUndefined(); + break; + + case SCTAG_BOOLEAN: + case SCTAG_BOOLEAN_OBJECT: + vp->setBoolean(!!data); + if (tag == SCTAG_BOOLEAN_OBJECT && !js_PrimitiveToObject(context(), vp)) + return false; + break; + + case SCTAG_STRING: + case SCTAG_STRING_OBJECT: { + JSString *str = readString(data); + if (!str) + return false; + vp->setString(str); + if (tag == SCTAG_STRING_OBJECT && !js_PrimitiveToObject(context(), vp)) + return false; + break; + } + + case SCTAG_NUMBER_OBJECT: { + double d; + if (!in.readDouble(&d) || !checkDouble(d)) + return false; + vp->setDouble(d); + if (!js_PrimitiveToObject(context(), vp)) + return false; + break; + } + + case SCTAG_DATE_OBJECT: { + double d; + if (!in.readDouble(&d) || !checkDouble(d)) + return false; + if (!IsNaN(d) && d != TimeClip(d)) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, + "date"); + return false; + } + JSObject *obj = js_NewDateObjectMsec(context(), d); + if (!obj) + return false; + vp->setObject(*obj); + break; + } + + case SCTAG_REGEXP_OBJECT: { + RegExpFlag flags = RegExpFlag(data); + uint32_t tag2, nchars; + if (!in.readPair(&tag2, &nchars)) + return false; + if (tag2 != SCTAG_STRING) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, + "regexp"); + return false; + } + JSString *str = readString(nchars); + if (!str) + return false; + JSStableString *stable = str->ensureStable(context()); + if (!stable) + return false; + + size_t length = stable->length(); + const StableCharPtr chars = stable->chars(); + RegExpObject *reobj = RegExpObject::createNoStatics(context(), chars.get(), length, flags, NULL); + if (!reobj) + return false; + vp->setObject(*reobj); + break; + } + + case SCTAG_ARRAY_OBJECT: + case SCTAG_OBJECT_OBJECT: { + JSObject *obj = (tag == SCTAG_ARRAY_OBJECT) + ? NewDenseEmptyArray(context()) + : NewBuiltinClassInstance(context(), &JSObject::class_); + if (!obj || !objs.append(ObjectValue(*obj))) + return false; + vp->setObject(*obj); + break; + } + + case SCTAG_BACK_REFERENCE_OBJECT: { + if (data >= allObjs.length()) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, + "invalid back reference in input"); + return false; + } + *vp = allObjs[data]; + return true; + } + + case SCTAG_TRANSFER_MAP_HEADER: + // A map header cannot be here but just at the beginning of the buffer. + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, + "invalid input"); + return false; + + case SCTAG_TRANSFER_MAP: + // A map cannot be here but just at the beginning of the buffer. + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, + "invalid input"); + return false; + + case SCTAG_ARRAY_BUFFER_OBJECT: + if (!readArrayBuffer(data, vp)) + return false; + break; + + case SCTAG_TYPED_ARRAY_OBJECT: + // readTypedArray adds the array to allObjs + uint64_t arrayType; + if (!in.read(&arrayType)) + return false; + return readTypedArray(arrayType, data, vp); + break; + + default: { + if (tag <= SCTAG_FLOAT_MAX) { + double d = ReinterpretPairAsDouble(tag, data); + if (!checkDouble(d)) + return false; + vp->setNumber(d); + break; + } + + if (SCTAG_TYPED_ARRAY_V1_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { + // A v1-format typed array + // readTypedArray adds the array to allObjs + return readTypedArray(TagToV1ArrayType(tag), data, vp, true); + } + + if (!callbacks || !callbacks->read) { + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, + "unsupported type"); + return false; + } + JSObject *obj = callbacks->read(context(), this, tag, data, closure); + if (!obj) + return false; + vp->setObject(*obj); + } + } + + if (vp->isObject() && !allObjs.append(*vp)) + return false; + + return true; +} + +bool +JSStructuredCloneReader::readId(jsid *idp) +{ + uint32_t tag, data; + if (!in.readPair(&tag, &data)) + return false; + + if (tag == SCTAG_INDEX) { + *idp = INT_TO_JSID(int32_t(data)); + return true; + } + if (tag == SCTAG_STRING) { + JSString *str = readString(data); + if (!str) + return false; + JSAtom *atom = AtomizeString<CanGC>(context(), str); + if (!atom) + return false; + *idp = NON_INTEGER_ATOM_TO_JSID(atom); + return true; + } + if (tag == SCTAG_NULL) { + *idp = JSID_VOID; + return true; + } + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "id"); + return false; +} + +bool +JSStructuredCloneReader::readTransferMap() +{ + uint32_t tag, data; + if (!in.getPair(&tag, &data)) + return false; + + if (tag != SCTAG_TRANSFER_MAP_HEADER || + (TransferableMapHeader)data == SCTAG_TM_MARKED) + return true; + + if (!in.replacePair(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_MARKED)) + return false; + + if (!in.readPair(&tag, &data)) + return false; + + while (1) { + if (!in.getPair(&tag, &data)) + return false; + + if (tag != SCTAG_TRANSFER_MAP) + break; + + void *content; + + if (!in.readPair(&tag, &data) || !in.readPtr(&content)) + return false; + + JSObject *obj = JS_NewArrayBufferWithContents(context(), content); + if (!obj || !allObjs.append(ObjectValue(*obj))) + return false; + } + + return true; +} + +bool +JSStructuredCloneReader::read(Value *vp) +{ + if (!readTransferMap()) + return false; + + if (!startRead(vp)) + return false; + + while (objs.length() != 0) { + RootedObject obj(context(), &objs.back().toObject()); + + RootedId id(context()); + if (!readId(id.address())) + return false; + + if (JSID_IS_VOID(id)) { + objs.popBack(); + } else { + RootedValue v(context()); + if (!startRead(v.address()) || !JSObject::defineGeneric(context(), obj, id, v)) + return false; + } + } + + allObjs.clear(); + + return true; +}
new file mode 100644 --- /dev/null +++ b/js/src/jsclone.h @@ -0,0 +1,206 @@ +/* -*- 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/. */ + +#ifndef jsclone_h +#define jsclone_h + +#include "jsapi.h" +#include "jscntxt.h" + +#include "js/Vector.h" + +namespace js { + +bool +WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *cb, void *cbClosure, + jsval transferable); + +bool +ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, + const JSStructuredCloneCallbacks *cb, void *cbClosure); + +bool +ClearStructuredClone(const uint64_t *data, size_t nbytes); + +bool +StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, + bool *hasTransferable); + +struct SCOutput { + public: + explicit SCOutput(JSContext *cx); + + JSContext *context() const { return cx; } + + bool write(uint64_t u); + bool writePair(uint32_t tag, uint32_t data); + bool writeDouble(double d); + bool writeBytes(const void *p, size_t nbytes); + bool writeChars(const jschar *p, size_t nchars); + bool writePtr(const void *); + + template <class T> + bool writeArray(const T *p, size_t nbytes); + + bool extractBuffer(uint64_t **datap, size_t *sizep); + + uint64_t count() { return buf.length(); } + + private: + JSContext *cx; + js::Vector<uint64_t> buf; +}; + +struct SCInput { + public: + SCInput(JSContext *cx, uint64_t *data, size_t nbytes); + + JSContext *context() const { return cx; } + + bool read(uint64_t *p); + bool readPair(uint32_t *tagp, uint32_t *datap); + bool readDouble(double *p); + bool readBytes(void *p, size_t nbytes); + bool readChars(jschar *p, size_t nchars); + bool readPtr(void **); + + bool get(uint64_t *p); + bool getPair(uint32_t *tagp, uint32_t *datap); + + bool replace(uint64_t u); + bool replacePair(uint32_t tag, uint32_t data); + + template <class T> + bool readArray(T *p, size_t nelems); + + private: + bool eof(); + + void staticAssertions() { + JS_STATIC_ASSERT(sizeof(jschar) == 2); + JS_STATIC_ASSERT(sizeof(uint32_t) == 4); + JS_STATIC_ASSERT(sizeof(double) == 8); + } + + JSContext *cx; + uint64_t *point; + uint64_t *end; +}; + +} /* namespace js */ + +struct JSStructuredCloneReader { + public: + explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb, + void *cbClosure) + : in(in), objs(in.context()), allObjs(in.context()), + callbacks(cb), closure(cbClosure) { } + + js::SCInput &input() { return in; } + bool read(js::Value *vp); + + private: + JSContext *context() { return in.context(); } + + bool readTransferMap(); + + bool checkDouble(double d); + JSString *readString(uint32_t nchars); + bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false); + bool readArrayBuffer(uint32_t nbytes, js::Value *vp); + bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp); + bool readId(jsid *idp); + bool startRead(js::Value *vp); + + js::SCInput ∈ + + // Stack of objects with properties remaining to be read. + js::AutoValueVector objs; + + // Stack of all objects read during this deserialization + js::AutoValueVector allObjs; + + // The user defined callbacks that will be used for cloning. + const JSStructuredCloneCallbacks *callbacks; + + // Any value passed to JS_ReadStructuredClone. + void *closure; + + friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp); +}; + +struct JSStructuredCloneWriter { + public: + explicit JSStructuredCloneWriter(js::SCOutput &out, + const JSStructuredCloneCallbacks *cb, + void *cbClosure, + jsval tVal) + : out(out), objs(out.context()), + counts(out.context()), ids(out.context()), + memory(out.context()), callbacks(cb), closure(cbClosure), + transferable(out.context(), tVal), transferableObjects(out.context()) { } + + bool init() { return transferableObjects.init() && parseTransferable() && + memory.init() && writeTransferMap(); } + + bool write(const js::Value &v); + + js::SCOutput &output() { return out; } + + private: + JSContext *context() { return out.context(); } + + bool writeTransferMap(); + + bool writeString(uint32_t tag, JSString *str); + bool writeId(jsid id); + bool writeArrayBuffer(JS::HandleObject obj); + bool writeTypedArray(JS::HandleObject obj); + bool startObject(JS::HandleObject obj, bool *backref); + bool startWrite(const js::Value &v); + bool traverseObject(JS::HandleObject obj); + + bool parseTransferable(); + void reportErrorTransferable(); + + inline void checkStack(); + + js::SCOutput &out; + + // Vector of objects with properties remaining to be written. + // + // NB: These can span multiple compartments, so the compartment must be + // entered before any manipulation is performed. + js::AutoValueVector objs; + + // counts[i] is the number of properties of objs[i] remaining to be written. + // counts.length() == objs.length() and sum(counts) == ids.length(). + js::Vector<size_t> counts; + + // Ids of properties remaining to be written. + js::AutoIdVector ids; + + // The "memory" list described in the HTML5 internal structured cloning algorithm. + // memory is a superset of objs; items are never removed from Memory + // until a serialization operation is finished + typedef js::AutoObjectUnsigned32HashMap CloneMemory; + CloneMemory memory; + + // The user defined callbacks that will be used for cloning. + const JSStructuredCloneCallbacks *callbacks; + + // Any value passed to JS_WriteStructuredClone. + void *closure; + + // List of transferable objects + JS::RootedValue transferable; + js::AutoObjectHashSet transferableObjects; + + friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v); +}; + +#endif /* jsclone_h */
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -987,16 +987,34 @@ js::GetTestingFunctions(JSContext *cx) JS_FRIEND_API(unsigned) js::GetEnterCompartmentDepth(JSContext *cx) { return cx->getEnterCompartmentDepth(); } #endif JS_FRIEND_API(void) +js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max) +{ + rt->spsProfiler.setProfilingStack(stack, size, max); +} + +JS_FRIEND_API(void) +js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled) +{ + rt->spsProfiler.enable(enabled); +} + +JS_FRIEND_API(jsbytecode*) +js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip) +{ + return rt->spsProfiler.ipToPC(script, size_t(ip)); +} + +JS_FRIEND_API(void) js::SetDOMCallbacks(JSRuntime *rt, const DOMCallbacks *callbacks) { rt->DOMcallbacks = callbacks; } JS_FRIEND_API(const DOMCallbacks *) js::GetDOMCallbacks(JSRuntime *rt) {
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -664,16 +664,94 @@ JS_FRIEND_API(size_t) GetPCCountScriptCount(JSContext *cx); JS_FRIEND_API(JSString *) GetPCCountScriptSummary(JSContext *cx, size_t script); JS_FRIEND_API(JSString *) GetPCCountScriptContents(JSContext *cx, size_t script); +/* + * A call stack can be specified to the JS engine such that all JS entry/exits + * to functions push/pop an entry to/from the specified stack. + * + * For more detailed information, see vm/SPSProfiler.h + */ +class ProfileEntry +{ + /* + * All fields are marked volatile to prevent the compiler from re-ordering + * instructions. Namely this sequence: + * + * entry[size] = ...; + * size++; + * + * If the size modification were somehow reordered before the stores, then + * if a sample were taken it would be examining bogus information. + * + * A ProfileEntry represents both a C++ profile entry and a JS one. Both use + * the string as a description, but JS uses the sp as NULL to indicate that + * it is a JS entry. The script_ is then only ever examined for a JS entry, + * and the idx is used by both, but with different meanings. + */ + const char * volatile string; // Descriptive string of this entry + void * volatile sp; // Relevant stack pointer for the entry + JSScript * volatile script_; // if js(), non-null script which is running + int32_t volatile idx; // if js(), idx of pc, otherwise line number + + public: + /* + * All of these methods are marked with the 'volatile' keyword because SPS's + * representation of the stack is stored such that all ProfileEntry + * instances are volatile. These methods would not be available unless they + * were marked as volatile as well + */ + + bool js() volatile { + JS_ASSERT_IF(sp == NULL, script_ != NULL); + return sp == NULL; + } + + uint32_t line() volatile { JS_ASSERT(!js()); return idx; } + JSScript *script() volatile { JS_ASSERT(js()); return script_; } + void *stackAddress() volatile { return sp; } + const char *label() volatile { return string; } + + void setLine(uint32_t aLine) volatile { JS_ASSERT(!js()); idx = aLine; } + void setLabel(const char *aString) volatile { string = aString; } + void setStackAddress(void *aSp) volatile { sp = aSp; } + void setScript(JSScript *aScript) volatile { script_ = aScript; } + + /* we can't know the layout of JSScript, so look in vm/SPSProfiler.cpp */ + JS_FRIEND_API(jsbytecode *) pc() volatile; + JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile; + + static size_t offsetOfString() { return offsetof(ProfileEntry, string); } + static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); } + static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); } + static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); } + + /* + * The index used in the entry can either be a line number or the offset of + * a pc into a script's code. To signify a NULL pc, use a -1 index. This is + * checked against in pc() and setPC() to set/get the right pc. + */ + static const int32_t NullPCIndex = -1; +}; + +JS_FRIEND_API(void) +SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, + uint32_t max); + +JS_FRIEND_API(void) +EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled); + +JS_FRIEND_API(jsbytecode*) +ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip); + #ifdef JS_THREADSAFE JS_FRIEND_API(bool) ContextHasOutstandingRequests(const JSContext *cx); #endif JS_FRIEND_API(bool) HasUnrootedGlobal(const JSContext *cx);
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -63,21 +63,19 @@ EXPORTS.js += [ '../public/CallArgs.h', '../public/CharacterEncoding.h', '../public/Date.h', '../public/GCAPI.h', '../public/HashTable.h', '../public/HeapAPI.h', '../public/LegacyIntTypes.h', '../public/MemoryMetrics.h', - '../public/ProfilingStack.h', '../public/PropertyKey.h', '../public/RequiredDefines.h', '../public/RootingAPI.h', - '../public/StructuredClone.h', '../public/Utility.h', '../public/Value.h', '../public/Vector.h', ] CPP_SOURCES += [ 'ArgumentsObject.cpp', 'BinaryData.cpp', @@ -122,36 +120,35 @@ CPP_SOURCES += [ 'ScopeObject.cpp', 'SelfHosting.cpp', 'Shape.cpp', 'Stack.cpp', 'Statistics.cpp', 'StoreBuffer.cpp', 'String.cpp', 'StringBuffer.cpp', - 'StructuredClone.cpp', 'TestingFunctions.cpp', 'ThreadPool.cpp', 'TokenStream.cpp', 'TypedArrayObject.cpp', 'Unicode.cpp', - 'Value.cpp', 'Verifier.cpp', 'Xdr.cpp', 'YarrCanonicalizeUCS2.cpp', 'YarrInterpreter.cpp', 'YarrPattern.cpp', 'YarrSyntaxChecker.cpp', 'Zone.cpp', 'jsalloc.cpp', 'jsanalyze.cpp', 'jsapi.cpp', 'jsarray.cpp', 'jsatom.cpp', 'jsbool.cpp', + 'jsclone.cpp', 'jscntxt.cpp', 'jscompartment.cpp', 'jscrashreport.cpp', 'jsdate.cpp', 'jsdbgapi.cpp', 'jsdtoa.cpp', 'jsexn.cpp', 'jsfriendapi.cpp',
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -57,17 +57,16 @@ #if JS_TRACE_LOGGING #include "TraceLogging.h" #endif #include "builtin/TestingFunctions.h" #include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" #include "jit/Ion.h" -#include "js/StructuredClone.h" #include "perf/jsperf.h" #include "shell/jsheaptools.h" #include "shell/jsoptparse.h" #include "vm/Shape.h" #include "vm/TypedArrayObject.h" #include "vm/WrapperObject.h" #include "jsfuninlines.h"
--- a/js/src/vm/CharacterEncoding.cpp +++ b/js/src/vm/CharacterEncoding.cpp @@ -1,19 +1,19 @@ /* -*- 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 "js/CharacterEncoding.h" - #include "jscntxt.h" #include "jsprf.h" +#include "js/CharacterEncoding.h" + using namespace JS; Latin1CharsZ JS::LossyTwoByteCharsToNewLatin1CharsZ(js::ThreadSafeContext *cx, TwoByteChars tbchars) { JS_ASSERT(cx); size_t len = tbchars.length(); unsigned char *latin1 = cx->pod_malloc<unsigned char>(len + 1);
--- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -260,38 +260,19 @@ SPSEntryMarker::~SPSEntryMarker() { if (profiler != NULL) { profiler->pop(); JS_ASSERT(size_before == *profiler->size_); } } JS_FRIEND_API(jsbytecode*) -ProfileEntry::pc() volatile -{ +ProfileEntry::pc() volatile { JS_ASSERT_IF(idx != NullPCIndex, idx >= 0 && uint32_t(idx) < script()->length); return idx == NullPCIndex ? NULL : script()->code + idx; } JS_FRIEND_API(void) -ProfileEntry::setPC(jsbytecode *pc) volatile -{ - JS_ASSERT_IF(pc != NULL, script()->code <= pc && pc < script()->code + script()->length); +ProfileEntry::setPC(jsbytecode *pc) volatile { + JS_ASSERT_IF(pc != NULL, script()->code <= pc && + pc < script()->code + script()->length); idx = pc == NULL ? NullPCIndex : pc - script()->code; } - -JS_FRIEND_API(void) -js::SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size, uint32_t max) -{ - rt->spsProfiler.setProfilingStack(stack, size, max); -} - -JS_FRIEND_API(void) -js::EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled) -{ - rt->spsProfiler.enable(enabled); -} - -JS_FRIEND_API(jsbytecode*) -js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip) -{ - return rt->spsProfiler.ipToPC(script, size_t(ip)); -}
--- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -9,18 +9,16 @@ #include "mozilla/DebugOnly.h" #include "mozilla/GuardObjects.h" #include <stddef.h> #include "jsscript.h" -#include "js/ProfilingStack.h" - /* * SPS Profiler integration with the JS Engine * https://developer.mozilla.org/en/Performance/Profiling_with_the_Built-in_Profiler * * The SPS profiler (found in tools/profiler) is an implementation of a profiler * which has the ability to walk the C++ stack as well as use instrumentation to * gather information. When dealing with JS, however, SPS needs integration * with the engine because otherwise it is very difficult to figure out what
deleted file mode 100644 --- a/js/src/vm/StructuredClone.cpp +++ /dev/null @@ -1,1679 +0,0 @@ -/* -*- 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/. */ - -/* - * This file implements the structured clone algorithm of - * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#safe-passing-of-structured-data - * - * The implementation differs slightly in that it uses an explicit stack, and - * the "memory" maps source objects to sequential integer indexes rather than - * directly pointing to destination objects. As a result, the order in which - * things are added to the memory must exactly match the order in which they - * are placed into 'allObjs', an analogous array of back-referenceable - * destination objects constructed while reading. - * - * For the most part, this is easy: simply add objects to the memory when first - * encountering them. But reading in a typed array requires an ArrayBuffer for - * construction, so objects cannot just be added to 'allObjs' in the order they - * are created. If they were, ArrayBuffers would come before typed arrays when - * in fact the typed array was added to 'memory' first. - * - * So during writing, we add objects to the memory when first encountering - * them. When reading a typed array, a placeholder is pushed onto allObjs until - * the ArrayBuffer has been read, then it is updated with the actual typed - * array object. - */ - -#include "js/StructuredClone.h" - -#include "mozilla/Endian.h" -#include "mozilla/FloatingPoint.h" - -#include "jsapi.h" -#include "jscntxt.h" -#include "jsdate.h" -#include "jswrapper.h" - -#include "vm/TypedArrayObject.h" -#include "vm/WrapperObject.h" - -#include "jscntxtinlines.h" -#include "jsobjinlines.h" - -using namespace js; - -using mozilla::IsNaN; -using mozilla::LittleEndian; -using mozilla::NativeEndian; - -enum StructuredDataType { - /* Structured data types provided by the engine */ - SCTAG_FLOAT_MAX = 0xFFF00000, - SCTAG_NULL = 0xFFFF0000, - SCTAG_UNDEFINED, - SCTAG_BOOLEAN, - SCTAG_INDEX, - SCTAG_STRING, - SCTAG_DATE_OBJECT, - SCTAG_REGEXP_OBJECT, - SCTAG_ARRAY_OBJECT, - SCTAG_OBJECT_OBJECT, - SCTAG_ARRAY_BUFFER_OBJECT, - SCTAG_BOOLEAN_OBJECT, - SCTAG_STRING_OBJECT, - SCTAG_NUMBER_OBJECT, - SCTAG_BACK_REFERENCE_OBJECT, - SCTAG_TRANSFER_MAP_HEADER, - SCTAG_TRANSFER_MAP, - SCTAG_TYPED_ARRAY_OBJECT, - SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100, - SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT8, - SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT8, - SCTAG_TYPED_ARRAY_V1_INT16 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT16, - SCTAG_TYPED_ARRAY_V1_UINT16 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT16, - SCTAG_TYPED_ARRAY_V1_INT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_INT32, - SCTAG_TYPED_ARRAY_V1_UINT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT32, - SCTAG_TYPED_ARRAY_V1_FLOAT32 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_FLOAT32, - SCTAG_TYPED_ARRAY_V1_FLOAT64 = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_FLOAT64, - SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_UINT8_CLAMPED, - SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_MIN + TypedArrayObject::TYPE_MAX - 1, - SCTAG_END_OF_BUILTIN_TYPES -}; - -enum TransferableMapHeader { - SCTAG_TM_NOT_MARKED = 0, - SCTAG_TM_MARKED -}; - -namespace js { - -struct SCOutput { - public: - explicit SCOutput(JSContext *cx); - - JSContext *context() const { return cx; } - - bool write(uint64_t u); - bool writePair(uint32_t tag, uint32_t data); - bool writeDouble(double d); - bool writeBytes(const void *p, size_t nbytes); - bool writeChars(const jschar *p, size_t nchars); - bool writePtr(const void *); - - template <class T> - bool writeArray(const T *p, size_t nbytes); - - bool extractBuffer(uint64_t **datap, size_t *sizep); - - uint64_t count() { return buf.length(); } - - private: - JSContext *cx; - js::Vector<uint64_t> buf; -}; - -struct SCInput { - public: - SCInput(JSContext *cx, uint64_t *data, size_t nbytes); - - JSContext *context() const { return cx; } - - bool read(uint64_t *p); - bool readPair(uint32_t *tagp, uint32_t *datap); - bool readDouble(double *p); - bool readBytes(void *p, size_t nbytes); - bool readChars(jschar *p, size_t nchars); - bool readPtr(void **); - - bool get(uint64_t *p); - bool getPair(uint32_t *tagp, uint32_t *datap); - - bool replace(uint64_t u); - bool replacePair(uint32_t tag, uint32_t data); - - template <class T> - bool readArray(T *p, size_t nelems); - - private: - bool eof(); - - void staticAssertions() { - JS_STATIC_ASSERT(sizeof(jschar) == 2); - JS_STATIC_ASSERT(sizeof(uint32_t) == 4); - JS_STATIC_ASSERT(sizeof(double) == 8); - } - - JSContext *cx; - uint64_t *point; - uint64_t *end; -}; - -} - -struct JSStructuredCloneReader { - public: - explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb, - void *cbClosure) - : in(in), objs(in.context()), allObjs(in.context()), - callbacks(cb), closure(cbClosure) { } - - js::SCInput &input() { return in; } - bool read(js::Value *vp); - - private: - JSContext *context() { return in.context(); } - - bool readTransferMap(); - - bool checkDouble(double d); - JSString *readString(uint32_t nchars); - bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false); - bool readArrayBuffer(uint32_t nbytes, js::Value *vp); - bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp); - bool readId(jsid *idp); - bool startRead(js::Value *vp); - - js::SCInput ∈ - - // Stack of objects with properties remaining to be read. - js::AutoValueVector objs; - - // Stack of all objects read during this deserialization - js::AutoValueVector allObjs; - - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks *callbacks; - - // Any value passed to JS_ReadStructuredClone. - void *closure; - - friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp); -}; - -struct JSStructuredCloneWriter { - public: - explicit JSStructuredCloneWriter(js::SCOutput &out, - const JSStructuredCloneCallbacks *cb, - void *cbClosure, - jsval tVal) - : out(out), objs(out.context()), - counts(out.context()), ids(out.context()), - memory(out.context()), callbacks(cb), closure(cbClosure), - transferable(out.context(), tVal), transferableObjects(out.context()) { } - - bool init() { return transferableObjects.init() && parseTransferable() && - memory.init() && writeTransferMap(); } - - bool write(const js::Value &v); - - js::SCOutput &output() { return out; } - - private: - JSContext *context() { return out.context(); } - - bool writeTransferMap(); - - bool writeString(uint32_t tag, JSString *str); - bool writeId(jsid id); - bool writeArrayBuffer(JS::HandleObject obj); - bool writeTypedArray(JS::HandleObject obj); - bool startObject(JS::HandleObject obj, bool *backref); - bool startWrite(const js::Value &v); - bool traverseObject(JS::HandleObject obj); - - bool parseTransferable(); - void reportErrorTransferable(); - - inline void checkStack(); - - js::SCOutput &out; - - // Vector of objects with properties remaining to be written. - // - // NB: These can span multiple compartments, so the compartment must be - // entered before any manipulation is performed. - js::AutoValueVector objs; - - // counts[i] is the number of properties of objs[i] remaining to be written. - // counts.length() == objs.length() and sum(counts) == ids.length(). - js::Vector<size_t> counts; - - // Ids of properties remaining to be written. - js::AutoIdVector ids; - - // The "memory" list described in the HTML5 internal structured cloning algorithm. - // memory is a superset of objs; items are never removed from Memory - // until a serialization operation is finished - typedef js::AutoObjectUnsigned32HashMap CloneMemory; - CloneMemory memory; - - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks *callbacks; - - // Any value passed to JS_WriteStructuredClone. - void *closure; - - // List of transferable objects - JS::RootedValue transferable; - js::AutoObjectHashSet transferableObjects; - - friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v); -}; - -JS_FRIEND_API(uint64_t) -js_GetSCOffset(JSStructuredCloneWriter* writer) -{ - JS_ASSERT(writer); - return writer->output().count() * sizeof(uint64_t); -} - -JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN); -JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX); -JS_STATIC_ASSERT(TypedArrayObject::TYPE_INT8 == 0); - -namespace js { - -bool -WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *cb, void *cbClosure, - jsval transferable) -{ - SCOutput out(cx); - JSStructuredCloneWriter w(out, cb, cbClosure, transferable); - return w.init() && w.write(v) && out.extractBuffer(bufp, nbytesp); -} - -bool -ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, - const JSStructuredCloneCallbacks *cb, void *cbClosure) -{ - SCInput in(cx, data, nbytes); - - /* XXX disallow callers from using internal pointers to GC things. */ - SkipRoot skip(cx, &in); - - JSStructuredCloneReader r(in, cb, cbClosure); - return r.read(vp); -} - -bool -ClearStructuredClone(const uint64_t *data, size_t nbytes) -{ - const uint64_t *point = data; - const uint64_t *end = data + nbytes / 8; - - uint64_t u = LittleEndian::readUint64(point++); - uint32_t tag = uint32_t(u >> 32); - if (tag == SCTAG_TRANSFER_MAP_HEADER) { - if ((TransferableMapHeader)uint32_t(u) == SCTAG_TM_NOT_MARKED) { - while (point != end) { - uint64_t u = LittleEndian::readUint64(point++); - uint32_t tag = uint32_t(u >> 32); - if (tag == SCTAG_TRANSFER_MAP) { - u = LittleEndian::readUint64(point++); - js_free(reinterpret_cast<void*>(u)); - } else { - // The only things in the transfer map should be - // SCTAG_TRANSFER_MAP tags paired with pointers. If we find - // any other tag, we've walked off the end of the transfer - // map. - break; - } - } - } - } - - js_free((void *)data); - return true; -} - -bool -StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable) -{ - *hasTransferable = false; - - if (data) { - uint64_t u = LittleEndian::readUint64(data); - uint32_t tag = uint32_t(u >> 32); - if (tag == SCTAG_TRANSFER_MAP_HEADER) { - *hasTransferable = true; - } - } - - return true; -} - -} - -static inline uint64_t -PairToUInt64(uint32_t tag, uint32_t data) -{ - return uint64_t(data) | (uint64_t(tag) << 32); -} - -bool -SCInput::eof() -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "truncated"); - return false; -} - -SCInput::SCInput(JSContext *cx, uint64_t *data, size_t nbytes) - : cx(cx), point(data), end(data + nbytes / 8) -{ - JS_ASSERT((uintptr_t(data) & 7) == 0); - JS_ASSERT((nbytes & 7) == 0); -} - -bool -SCInput::read(uint64_t *p) -{ - if (point == end) { - *p = 0; /* initialize to shut GCC up */ - return eof(); - } - *p = LittleEndian::readUint64(point++); - return true; -} - -bool -SCInput::readPair(uint32_t *tagp, uint32_t *datap) -{ - uint64_t u; - bool ok = read(&u); - if (ok) { - *tagp = uint32_t(u >> 32); - *datap = uint32_t(u); - } - return ok; -} - -bool -SCInput::get(uint64_t *p) -{ - if (point == end) - return eof(); - *p = LittleEndian::readUint64(point); - return true; -} - -bool -SCInput::getPair(uint32_t *tagp, uint32_t *datap) -{ - uint64_t u; - if (!get(&u)) - return false; - - *tagp = uint32_t(u >> 32); - *datap = uint32_t(u); - return true; -} - -bool -SCInput::replace(uint64_t u) -{ - if (point == end) - return eof(); - LittleEndian::writeUint64(point, u); - return true; -} - -bool -SCInput::replacePair(uint32_t tag, uint32_t data) -{ - return replace(PairToUInt64(tag, data)); -} - -/* - * The purpose of this never-inlined function is to avoid a strange g++ build - * error on OS X 10.5 (see bug 624080). :-( - */ -static JS_NEVER_INLINE double -CanonicalizeNan(double d) -{ - return JS_CANONICALIZE_NAN(d); -} - -bool -SCInput::readDouble(double *p) -{ - union { - uint64_t u; - double d; - } pun; - if (!read(&pun.u)) - return false; - *p = CanonicalizeNan(pun.d); - return true; -} - -template <typename T> -static void -copyAndSwapFromLittleEndian(T *dest, const void *src, size_t nelems) -{ - NativeEndian::copyAndSwapFromLittleEndian(dest, src, nelems); -} - -template <> -void -copyAndSwapFromLittleEndian(uint8_t *dest, const void *src, size_t nelems) -{ - memcpy(dest, src, nelems); -} - -template <class T> -bool -SCInput::readArray(T *p, size_t nelems) -{ - JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0); - - /* - * Fail if nelems is so huge as to make JS_HOWMANY overflow or if nwords is - * larger than the remaining data. - */ - size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T)); - if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(end - point)) - return eof(); - - copyAndSwapFromLittleEndian(p, point, nelems); - point += nwords; - return true; -} - -bool -SCInput::readBytes(void *p, size_t nbytes) -{ - return readArray((uint8_t *) p, nbytes); -} - -bool -SCInput::readChars(jschar *p, size_t nchars) -{ - JS_ASSERT(sizeof(jschar) == sizeof(uint16_t)); - return readArray((uint16_t *) p, nchars); -} - -bool -SCInput::readPtr(void **p) -{ - // On a 32 bit system the void* variable we have to write to is only - // 32 bits, so we create a 64 temporary and discard the unused bits. - uint64_t tmp; - bool ret = read(&tmp); - *p = reinterpret_cast<void*>(tmp); - return ret; -} - -SCOutput::SCOutput(JSContext *cx) : cx(cx), buf(cx) {} - -bool -SCOutput::write(uint64_t u) -{ - return buf.append(NativeEndian::swapToLittleEndian(u)); -} - -bool -SCOutput::writePair(uint32_t tag, uint32_t data) -{ - /* - * As it happens, the tag word appears after the data word in the output. - * This is because exponents occupy the last 2 bytes of doubles on the - * little-endian platforms we care most about. - * - * For example, JSVAL_TRUE is written using writePair(SCTAG_BOOLEAN, 1). - * PairToUInt64 produces the number 0xFFFF000200000001. - * That is written out as the bytes 01 00 00 00 02 00 FF FF. - */ - return write(PairToUInt64(tag, data)); -} - -static inline uint64_t -ReinterpretDoubleAsUInt64(double d) -{ - union { - double d; - uint64_t u; - } pun; - pun.d = d; - return pun.u; -} - -static inline double -ReinterpretUInt64AsDouble(uint64_t u) -{ - union { - uint64_t u; - double d; - } pun; - pun.u = u; - return pun.d; -} - -static inline double -ReinterpretPairAsDouble(uint32_t tag, uint32_t data) -{ - return ReinterpretUInt64AsDouble(PairToUInt64(tag, data)); -} - -bool -SCOutput::writeDouble(double d) -{ - return write(ReinterpretDoubleAsUInt64(CanonicalizeNan(d))); -} - -template <typename T> -static void -copyAndSwapToLittleEndian(void *dest, const T *src, size_t nelems) -{ - NativeEndian::copyAndSwapToLittleEndian(dest, src, nelems); -} - -template <> -void -copyAndSwapToLittleEndian(void *dest, const uint8_t *src, size_t nelems) -{ - memcpy(dest, src, nelems); -} - -template <class T> -bool -SCOutput::writeArray(const T *p, size_t nelems) -{ - JS_ASSERT(8 % sizeof(T) == 0); - JS_ASSERT(sizeof(uint64_t) % sizeof(T) == 0); - - if (nelems == 0) - return true; - - if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems) { - js_ReportAllocationOverflow(context()); - return false; - } - size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T)); - size_t start = buf.length(); - if (!buf.growByUninitialized(nwords)) - return false; - - buf.back() = 0; /* zero-pad to an 8-byte boundary */ - - T *q = (T *) &buf[start]; - copyAndSwapToLittleEndian(q, p, nelems); - return true; -} - -bool -SCOutput::writeBytes(const void *p, size_t nbytes) -{ - return writeArray((const uint8_t *) p, nbytes); -} - -bool -SCOutput::writeChars(const jschar *p, size_t nchars) -{ - JS_ASSERT(sizeof(jschar) == sizeof(uint16_t)); - return writeArray((const uint16_t *) p, nchars); -} - -bool -SCOutput::writePtr(const void *p) -{ - return write(reinterpret_cast<uint64_t>(p)); -} - -bool -SCOutput::extractBuffer(uint64_t **datap, size_t *sizep) -{ - *sizep = buf.length() * sizeof(uint64_t); - return (*datap = buf.extractRawBuffer()) != NULL; -} - -JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX); - -bool -JSStructuredCloneWriter::parseTransferable() -{ - transferableObjects.clear(); - - if (JSVAL_IS_NULL(transferable) || JSVAL_IS_VOID(transferable)) - return true; - - if (!transferable.isObject()) { - reportErrorTransferable(); - return false; - } - - RootedObject array(context(), &transferable.toObject()); - if (!JS_IsArrayObject(context(), array)) { - reportErrorTransferable(); - return false; - } - - uint32_t length; - if (!JS_GetArrayLength(context(), array, &length)) { - return false; - } - - RootedValue v(context()); - - for (uint32_t i = 0; i < length; ++i) { - if (!JS_GetElement(context(), array, i, &v)) { - return false; - } - - if (!v.isObject()) { - reportErrorTransferable(); - return false; - } - - JSObject* tObj = CheckedUnwrap(&v.toObject()); - if (!tObj) { - JS_ReportError(context(), "Permission denied to access object"); - return false; - } - if (!tObj->is<ArrayBufferObject>()) { - reportErrorTransferable(); - return false; - } - - // No duplicate: - if (transferableObjects.has(tObj)) { - reportErrorTransferable(); - return false; - } - - if (!transferableObjects.putNew(tObj)) - return false; - } - - return true; -} - -void -JSStructuredCloneWriter::reportErrorTransferable() -{ - if (callbacks && callbacks->reportError) - return callbacks->reportError(context(), JS_SCERR_TRANSFERABLE); -} - -bool -JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str) -{ - size_t length = str->length(); - const jschar *chars = str->getChars(context()); - if (!chars) - return false; - return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length); -} - -bool -JSStructuredCloneWriter::writeId(jsid id) -{ - if (JSID_IS_INT(id)) - return out.writePair(SCTAG_INDEX, uint32_t(JSID_TO_INT(id))); - JS_ASSERT(JSID_IS_STRING(id)); - return writeString(SCTAG_STRING, JSID_TO_STRING(id)); -} - -inline void -JSStructuredCloneWriter::checkStack() -{ -#ifdef DEBUG - /* To avoid making serialization O(n^2), limit stack-checking at 10. */ - const size_t MAX = 10; - - size_t limit = Min(counts.length(), MAX); - JS_ASSERT(objs.length() == counts.length()); - size_t total = 0; - for (size_t i = 0; i < limit; i++) { - JS_ASSERT(total + counts[i] >= total); - total += counts[i]; - } - if (counts.length() <= MAX) - JS_ASSERT(total == ids.length()); - else - JS_ASSERT(total <= ids.length()); - - size_t j = objs.length(); - for (size_t i = 0; i < limit; i++) - JS_ASSERT(memory.has(&objs[--j].toObject())); -#endif -} - -/* - * Write out a typed array. Note that post-v1 structured clone buffers do not - * perform endianness conversion on stored data, so multibyte typed arrays - * cannot be deserialized into a different endianness machine. Endianness - * conversion would prevent sharing ArrayBuffers: if you have Int8Array and - * Int16Array views of the same ArrayBuffer, should the data bytes be - * byte-swapped when writing or not? The Int8Array requires them to not be - * swapped; the Int16Array requires that they are. - */ -bool -JSStructuredCloneWriter::writeTypedArray(HandleObject obj) -{ - Rooted<TypedArrayObject*> tarr(context(), &obj->as<TypedArrayObject>()); - if (!out.writePair(SCTAG_TYPED_ARRAY_OBJECT, tarr->length())) - return false; - uint64_t type = tarr->type(); - if (!out.write(type)) - return false; - - // Write out the ArrayBuffer tag and contents - if (!startWrite(TypedArrayObject::bufferValue(tarr))) - return false; - - return out.write(tarr->byteOffset()); -} - -bool -JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj) -{ - ArrayBufferObject &buffer = obj->as<ArrayBufferObject>(); - return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, buffer.byteLength()) && - out.writeBytes(buffer.dataPointer(), buffer.byteLength()); -} - -bool -JSStructuredCloneWriter::startObject(HandleObject obj, bool *backref) -{ - /* Handle cycles in the object graph. */ - CloneMemory::AddPtr p = memory.lookupForAdd(obj); - if ((*backref = p)) - return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value); - if (!memory.add(p, obj, memory.count())) - return false; - - if (memory.count() == UINT32_MAX) { - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, - JSMSG_NEED_DIET, "object graph to serialize"); - return false; - } - - return true; -} - -bool -JSStructuredCloneWriter::traverseObject(HandleObject obj) -{ - /* - * Get enumerable property ids and put them in reverse order so that they - * will come off the stack in forward order. - */ - size_t initialLength = ids.length(); - if (!GetPropertyNames(context(), obj, JSITER_OWNONLY, &ids)) - return false; - jsid *begin = ids.begin() + initialLength, *end = ids.end(); - size_t count = size_t(end - begin); - Reverse(begin, end); - - /* Push obj and count to the stack. */ - if (!objs.append(ObjectValue(*obj)) || !counts.append(count)) - return false; - checkStack(); - - /* Write the header for obj. */ - return out.writePair(obj->is<ArrayObject>() ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0); -} - -bool -JSStructuredCloneWriter::startWrite(const Value &v) -{ - assertSameCompartment(context(), v); - - if (v.isString()) { - return writeString(SCTAG_STRING, v.toString()); - } else if (v.isNumber()) { - return out.writeDouble(v.toNumber()); - } else if (v.isBoolean()) { - return out.writePair(SCTAG_BOOLEAN, v.toBoolean()); - } else if (v.isNull()) { - return out.writePair(SCTAG_NULL, 0); - } else if (v.isUndefined()) { - return out.writePair(SCTAG_UNDEFINED, 0); - } else if (v.isObject()) { - RootedObject obj(context(), &v.toObject()); - - // The object might be a security wrapper. See if we can clone what's - // behind it. If we can, unwrap the object. - obj = CheckedUnwrap(obj); - if (!obj) { - JS_ReportError(context(), "Permission denied to access object"); - return false; - } - - AutoCompartment ac(context(), obj); - - bool backref; - if (!startObject(obj, &backref)) - return false; - if (backref) - return true; - - if (obj->is<RegExpObject>()) { - RegExpObject &reobj = obj->as<RegExpObject>(); - return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) && - writeString(SCTAG_STRING, reobj.getSource()); - } else if (obj->is<DateObject>()) { - double d = js_DateGetMsecSinceEpoch(obj); - return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d); - } else if (obj->is<TypedArrayObject>()) { - return writeTypedArray(obj); - } else if (obj->is<ArrayBufferObject>() && obj->as<ArrayBufferObject>().hasData()) { - return writeArrayBuffer(obj); - } else if (obj->is<JSObject>() || obj->is<ArrayObject>()) { - return traverseObject(obj); - } else if (obj->is<BooleanObject>()) { - return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->as<BooleanObject>().unbox()); - } else if (obj->is<NumberObject>()) { - return out.writePair(SCTAG_NUMBER_OBJECT, 0) && - out.writeDouble(obj->as<NumberObject>().unbox()); - } else if (obj->is<StringObject>()) { - return writeString(SCTAG_STRING_OBJECT, obj->as<StringObject>().unbox()); - } - - if (callbacks && callbacks->write) - return callbacks->write(context(), this, obj, closure); - /* else fall through */ - } - - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_UNSUPPORTED_TYPE); - return false; -} - -bool -JSStructuredCloneWriter::writeTransferMap() -{ - if (!transferableObjects.empty()) { - if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_NOT_MARKED)) - return false; - - for (HashSet<JSObject*>::Range r = transferableObjects.all(); - !r.empty(); r.popFront()) { - JSObject *obj = r.front(); - - if (!memory.put(obj, memory.count())) - return false; - - void *content; - uint8_t *data; - if (!JS_StealArrayBufferContents(context(), obj, &content, &data)) - return false; - - if (!out.writePair(SCTAG_TRANSFER_MAP, 0) || !out.writePtr(content)) - return false; - } - } - - return true; -} - -bool -JSStructuredCloneWriter::write(const Value &v) -{ - if (!startWrite(v)) - return false; - - while (!counts.empty()) { - RootedObject obj(context(), &objs.back().toObject()); - AutoCompartment ac(context(), obj); - if (counts.back()) { - counts.back()--; - RootedId id(context(), ids.back()); - ids.popBack(); - checkStack(); - if (JSID_IS_STRING(id) || JSID_IS_INT(id)) { - /* - * If obj still has an own property named id, write it out. - * The cost of re-checking could be avoided by using - * NativeIterators. - */ - RootedObject obj2(context()); - RootedShape prop(context()); - if (!HasOwnProperty<CanGC>(context(), obj->getOps()->lookupGeneric, obj, id, - &obj2, &prop)) { - return false; - } - - if (prop) { - RootedValue val(context()); - if (!writeId(id) || - !JSObject::getGeneric(context(), obj, obj, id, &val) || - !startWrite(val)) - return false; - } - } - } else { - out.writePair(SCTAG_NULL, 0); - objs.popBack(); - counts.popBack(); - } - } - - memory.clear(); - - return true; -} - -bool -JSStructuredCloneReader::checkDouble(double d) -{ - jsval_layout l; - l.asDouble = d; - if (!JSVAL_IS_DOUBLE_IMPL(l)) { - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, - JSMSG_SC_BAD_SERIALIZED_DATA, "unrecognized NaN"); - return false; - } - return true; -} - -class Chars { - JSContext *cx; - jschar *p; - public: - Chars(JSContext *cx) : cx(cx), p(NULL) {} - ~Chars() { if (p) js_free(p); } - - bool allocate(size_t len) { - JS_ASSERT(!p); - // We're going to null-terminate! - p = cx->pod_malloc<jschar>(len + 1); - if (p) { - p[len] = jschar(0); - return true; - } - return false; - } - jschar *get() { return p; } - void forget() { p = NULL; } -}; - -JSString * -JSStructuredCloneReader::readString(uint32_t nchars) -{ - if (nchars > JSString::MAX_LENGTH) { - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, - "string length"); - return NULL; - } - Chars chars(context()); - if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars)) - return NULL; - JSString *str = js_NewString<CanGC>(context(), chars.get(), nchars); - if (str) - chars.forget(); - return str; -} - -static uint32_t -TagToV1ArrayType(uint32_t tag) -{ - JS_ASSERT(tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX); - return tag - SCTAG_TYPED_ARRAY_V1_MIN; -} - -bool -JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Value *vp, - bool v1Read) -{ - if (arrayType > TypedArrayObject::TYPE_UINT8_CLAMPED) { - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, - JSMSG_SC_BAD_SERIALIZED_DATA, "unhandled typed array element type"); - return false; - } - - // Push a placeholder onto the allObjs list to stand in for the typed array - uint32_t placeholderIndex = allObjs.length(); - Value dummy = JSVAL_NULL; - if (!allObjs.append(dummy)) - return false; - - // Read the ArrayBuffer object and its contents (but no properties) - RootedValue v(context()); - uint32_t byteOffset; - if (v1Read) { - if (!readV1ArrayBuffer(arrayType, nelems, v.address())) - return false; - byteOffset = 0; - } else { - if (!startRead(v.address())) - return false; - uint64_t n; - if (!in.read(&n)) - return false; - byteOffset = n; - } - RootedObject buffer(context(), &v.toObject()); - RootedObject obj(context(), NULL); - - switch (arrayType) { - case TypedArrayObject::TYPE_INT8: - obj = JS_NewInt8ArrayWithBuffer(context(), buffer, byteOffset, nelems); - break; - case TypedArrayObject::TYPE_UINT8: - obj = JS_NewUint8ArrayWithBuffer(context(), buffer, byteOffset, nelems); - break; - case TypedArrayObject::TYPE_INT16: - obj = JS_NewInt16ArrayWithBuffer(context(), buffer, byteOffset, nelems); - break; - case TypedArrayObject::TYPE_UINT16: - obj = JS_NewUint16ArrayWithBuffer(context(), buffer, byteOffset, nelems); - break; - case TypedArrayObject::TYPE_INT32: - obj = JS_NewInt32ArrayWithBuffer(context(), buffer, byteOffset, nelems); - break; - case TypedArrayObject::TYPE_UINT32: - obj = JS_NewUint32ArrayWithBuffer(context(), buffer, byteOffset, nelems); - break; - case TypedArrayObject::TYPE_FLOAT32: - obj = JS_NewFloat32ArrayWithBuffer(context(), buffer, byteOffset, nelems); - break; - case TypedArrayObject::TYPE_FLOAT64: - obj = JS_NewFloat64ArrayWithBuffer(context(), buffer, byteOffset, nelems); - break; - case TypedArrayObject::TYPE_UINT8_CLAMPED: - obj = JS_NewUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems); - break; - default: - MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type"); - } - - if (!obj) - return false; - vp->setObject(*obj); - - allObjs[placeholderIndex] = *vp; - - return true; -} - -bool -JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, Value *vp) -{ - JSObject *obj = ArrayBufferObject::create(context(), nbytes); - if (!obj) - return false; - vp->setObject(*obj); - ArrayBufferObject &buffer = obj->as<ArrayBufferObject>(); - JS_ASSERT(buffer.byteLength() == nbytes); - return in.readArray(buffer.dataPointer(), nbytes); -} - -static size_t -bytesPerTypedArrayElement(uint32_t arrayType) -{ - switch (arrayType) { - case TypedArrayObject::TYPE_INT8: - case TypedArrayObject::TYPE_UINT8: - case TypedArrayObject::TYPE_UINT8_CLAMPED: - return sizeof(uint8_t); - case TypedArrayObject::TYPE_INT16: - case TypedArrayObject::TYPE_UINT16: - return sizeof(uint16_t); - case TypedArrayObject::TYPE_INT32: - case TypedArrayObject::TYPE_UINT32: - case TypedArrayObject::TYPE_FLOAT32: - return sizeof(uint32_t); - case TypedArrayObject::TYPE_FLOAT64: - return sizeof(uint64_t); - default: - MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type"); - } -} - -/* - * Read in the data for a structured clone version 1 ArrayBuffer, performing - * endianness-conversion while reading. - */ -bool -JSStructuredCloneReader::readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, Value *vp) -{ - JS_ASSERT(arrayType <= TypedArrayObject::TYPE_UINT8_CLAMPED); - - uint32_t nbytes = nelems * bytesPerTypedArrayElement(arrayType); - JSObject *obj = ArrayBufferObject::create(context(), nbytes); - if (!obj) - return false; - vp->setObject(*obj); - ArrayBufferObject &buffer = obj->as<ArrayBufferObject>(); - JS_ASSERT(buffer.byteLength() == nbytes); - - switch (arrayType) { - case TypedArrayObject::TYPE_INT8: - case TypedArrayObject::TYPE_UINT8: - case TypedArrayObject::TYPE_UINT8_CLAMPED: - return in.readArray((uint8_t*) buffer.dataPointer(), nelems); - case TypedArrayObject::TYPE_INT16: - case TypedArrayObject::TYPE_UINT16: - return in.readArray((uint16_t*) buffer.dataPointer(), nelems); - case TypedArrayObject::TYPE_INT32: - case TypedArrayObject::TYPE_UINT32: - case TypedArrayObject::TYPE_FLOAT32: - return in.readArray((uint32_t*) buffer.dataPointer(), nelems); - case TypedArrayObject::TYPE_FLOAT64: - return in.readArray((uint64_t*) buffer.dataPointer(), nelems); - default: - MOZ_ASSUME_UNREACHABLE("unknown TypedArrayObject type"); - } -} - -bool -JSStructuredCloneReader::startRead(Value *vp) -{ - uint32_t tag, data; - - if (!in.readPair(&tag, &data)) - return false; - switch (tag) { - case SCTAG_NULL: - vp->setNull(); - break; - - case SCTAG_UNDEFINED: - vp->setUndefined(); - break; - - case SCTAG_BOOLEAN: - case SCTAG_BOOLEAN_OBJECT: - vp->setBoolean(!!data); - if (tag == SCTAG_BOOLEAN_OBJECT && !js_PrimitiveToObject(context(), vp)) - return false; - break; - - case SCTAG_STRING: - case SCTAG_STRING_OBJECT: { - JSString *str = readString(data); - if (!str) - return false; - vp->setString(str); - if (tag == SCTAG_STRING_OBJECT && !js_PrimitiveToObject(context(), vp)) - return false; - break; - } - - case SCTAG_NUMBER_OBJECT: { - double d; - if (!in.readDouble(&d) || !checkDouble(d)) - return false; - vp->setDouble(d); - if (!js_PrimitiveToObject(context(), vp)) - return false; - break; - } - - case SCTAG_DATE_OBJECT: { - double d; - if (!in.readDouble(&d) || !checkDouble(d)) - return false; - if (!IsNaN(d) && d != TimeClip(d)) { - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, - "date"); - return false; - } - JSObject *obj = js_NewDateObjectMsec(context(), d); - if (!obj) - return false; - vp->setObject(*obj); - break; - } - - case SCTAG_REGEXP_OBJECT: { - RegExpFlag flags = RegExpFlag(data); - uint32_t tag2, nchars; - if (!in.readPair(&tag2, &nchars)) - return false; - if (tag2 != SCTAG_STRING) { - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, - "regexp"); - return false; - } - JSString *str = readString(nchars); - if (!str) - return false; - JSStableString *stable = str->ensureStable(context()); - if (!stable) - return false; - - size_t length = stable->length(); - const StableCharPtr chars = stable->chars(); - RegExpObject *reobj = RegExpObject::createNoStatics(context(), chars.get(), length, flags, NULL); - if (!reobj) - return false; - vp->setObject(*reobj); - break; - } - - case SCTAG_ARRAY_OBJECT: - case SCTAG_OBJECT_OBJECT: { - JSObject *obj = (tag == SCTAG_ARRAY_OBJECT) - ? NewDenseEmptyArray(context()) - : NewBuiltinClassInstance(context(), &JSObject::class_); - if (!obj || !objs.append(ObjectValue(*obj))) - return false; - vp->setObject(*obj); - break; - } - - case SCTAG_BACK_REFERENCE_OBJECT: { - if (data >= allObjs.length()) { - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, - JSMSG_SC_BAD_SERIALIZED_DATA, - "invalid back reference in input"); - return false; - } - *vp = allObjs[data]; - return true; - } - - case SCTAG_TRANSFER_MAP_HEADER: - // A map header cannot be here but just at the beginning of the buffer. - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, - JSMSG_SC_BAD_SERIALIZED_DATA, - "invalid input"); - return false; - - case SCTAG_TRANSFER_MAP: - // A map cannot be here but just at the beginning of the buffer. - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, - JSMSG_SC_BAD_SERIALIZED_DATA, - "invalid input"); - return false; - - case SCTAG_ARRAY_BUFFER_OBJECT: - if (!readArrayBuffer(data, vp)) - return false; - break; - - case SCTAG_TYPED_ARRAY_OBJECT: - // readTypedArray adds the array to allObjs - uint64_t arrayType; - if (!in.read(&arrayType)) - return false; - return readTypedArray(arrayType, data, vp); - break; - - default: { - if (tag <= SCTAG_FLOAT_MAX) { - double d = ReinterpretPairAsDouble(tag, data); - if (!checkDouble(d)) - return false; - vp->setNumber(d); - break; - } - - if (SCTAG_TYPED_ARRAY_V1_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { - // A v1-format typed array - // readTypedArray adds the array to allObjs - return readTypedArray(TagToV1ArrayType(tag), data, vp, true); - } - - if (!callbacks || !callbacks->read) { - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, - "unsupported type"); - return false; - } - JSObject *obj = callbacks->read(context(), this, tag, data, closure); - if (!obj) - return false; - vp->setObject(*obj); - } - } - - if (vp->isObject() && !allObjs.append(*vp)) - return false; - - return true; -} - -bool -JSStructuredCloneReader::readId(jsid *idp) -{ - uint32_t tag, data; - if (!in.readPair(&tag, &data)) - return false; - - if (tag == SCTAG_INDEX) { - *idp = INT_TO_JSID(int32_t(data)); - return true; - } - if (tag == SCTAG_STRING) { - JSString *str = readString(data); - if (!str) - return false; - JSAtom *atom = AtomizeString<CanGC>(context(), str); - if (!atom) - return false; - *idp = NON_INTEGER_ATOM_TO_JSID(atom); - return true; - } - if (tag == SCTAG_NULL) { - *idp = JSID_VOID; - return true; - } - JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "id"); - return false; -} - -bool -JSStructuredCloneReader::readTransferMap() -{ - uint32_t tag, data; - if (!in.getPair(&tag, &data)) - return false; - - if (tag != SCTAG_TRANSFER_MAP_HEADER || - (TransferableMapHeader)data == SCTAG_TM_MARKED) - return true; - - if (!in.replacePair(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_MARKED)) - return false; - - if (!in.readPair(&tag, &data)) - return false; - - while (1) { - if (!in.getPair(&tag, &data)) - return false; - - if (tag != SCTAG_TRANSFER_MAP) - break; - - void *content; - - if (!in.readPair(&tag, &data) || !in.readPtr(&content)) - return false; - - JSObject *obj = JS_NewArrayBufferWithContents(context(), content); - if (!obj || !allObjs.append(ObjectValue(*obj))) - return false; - } - - return true; -} - -bool -JSStructuredCloneReader::read(Value *vp) -{ - if (!readTransferMap()) - return false; - - if (!startRead(vp)) - return false; - - while (objs.length() != 0) { - RootedObject obj(context(), &objs.back().toObject()); - - RootedId id(context()); - if (!readId(id.address())) - return false; - - if (JSID_IS_VOID(id)) { - objs.popBack(); - } else { - RootedValue v(context()); - if (!startRead(v.address()) || !JSObject::defineGeneric(context(), obj, id, v)) - return false; - } - } - - allObjs.clear(); - - return true; -} -using namespace js; - -JS_PUBLIC_API(bool) -JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes, - uint32_t version, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - - if (version > JS_STRUCTURED_CLONE_VERSION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION); - return false; - } - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure); -} - -JS_PUBLIC_API(bool) -JS_WriteStructuredClone(JSContext *cx, JS::Value valueArg, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure, JS::Value transferable) -{ - RootedValue value(cx, valueArg); - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value); - - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable); -} - -JS_PUBLIC_API(bool) -JS_ClearStructuredClone(const uint64_t *data, size_t nbytes) -{ - return ClearStructuredClone(data, nbytes); -} - -JS_PUBLIC_API(bool) -JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, - bool *hasTransferable) -{ - bool transferable; - if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable)) - return false; - - *hasTransferable = transferable; - return true; -} - -JS_PUBLIC_API(bool) -JS_StructuredClone(JSContext *cx, JS::Value valueArg, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - RootedValue value(cx, valueArg); - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value); - - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - JSAutoStructuredCloneBuffer buf; - return buf.write(cx, value, callbacks, closure) && - buf.read(cx, vp, callbacks, closure); -} - -void -JSAutoStructuredCloneBuffer::clear() -{ - if (data_) { - ClearStructuredClone(data_, nbytes_); - data_ = NULL; - nbytes_ = 0; - version_ = 0; - } -} - -bool -JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version) -{ - // transferable objects cannot be copied - bool hasTransferable; - if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) || - hasTransferable) - return false; - - uint64_t *newData = static_cast<uint64_t *>(js_malloc(nbytes)); - if (!newData) - return false; - - js_memcpy(newData, srcData, nbytes); - - clear(); - data_ = newData; - nbytes_ = nbytes; - version_ = version; - return true; -} - -void -JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version) -{ - clear(); - data_ = data; - nbytes_ = nbytes; - version_ = version; -} - -void -JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp) -{ - *datap = data_; - *nbytesp = nbytes_; - if (versionp) - *versionp = version_; - - data_ = NULL; - nbytes_ = 0; - version_ = 0; -} - -bool -JSAutoStructuredCloneBuffer::read(JSContext *cx, JS::Value *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - JS_ASSERT(cx); - JS_ASSERT(data_); - return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, - optionalCallbacks, closure); -} - -bool -JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - JS::Value transferable = JSVAL_VOID; - return write(cx, valueArg, transferable, optionalCallbacks, closure); -} - -bool -JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg, - JS::Value transferable, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - RootedValue value(cx, valueArg); - clear(); - bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_, - optionalCallbacks, closure, - transferable); - if (!ok) { - data_ = NULL; - nbytes_ = 0; - version_ = JS_STRUCTURED_CLONE_VERSION; - } - return ok; -} - -void -JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other) -{ - uint64_t *data = other.data_; - size_t nbytes = other.nbytes_; - uint32_t version = other.version_; - - other.data_ = this->data_; - other.nbytes_ = this->nbytes_; - other.version_ = this->version_; - - this->data_ = data; - this->nbytes_ = nbytes; - this->version_ = version; -} - -JS_PUBLIC_API(void) -JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks) -{ - rt->structuredCloneCallbacks = callbacks; -} - -JS_PUBLIC_API(bool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2) -{ - return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); -} - -JS_PUBLIC_API(bool) -JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) -{ - return r->input().readBytes(p, len); -} - -JS_PUBLIC_API(bool) -JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp) -{ - uint32_t tag, nelems; - if (!r->input().readPair(&tag, &nelems)) - return false; - if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { - return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true); - } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) { - uint64_t arrayType; - if (!r->input().read(&arrayType)) - return false; - return r->readTypedArray(arrayType, nelems, vp); - } else { - JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL, - JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array"); - return false; - } -} - -JS_PUBLIC_API(bool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data) -{ - return w->output().writePair(tag, data); -} - -JS_PUBLIC_API(bool) -JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len) -{ - return w->output().writeBytes(p, len); -} - -JS_PUBLIC_API(bool) -JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v) -{ - JS_ASSERT(v.isObject()); - assertSameCompartment(w->context(), v); - RootedObject obj(w->context(), &v.toObject()); - - // If the object is a security wrapper, see if we're allowed to unwrap it. - // If we aren't, throw. - if (obj->is<WrapperObject>()) - obj = CheckedUnwrap(obj); - if (!obj) { - JS_ReportError(w->context(), "Permission denied to access object"); - return false; - } - return w->writeTypedArray(obj); -} -
deleted file mode 100644 --- a/js/src/vm/Value.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- 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 "js/Value.h" - -const jsval JSVAL_NULL = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0)); -const jsval JSVAL_ZERO = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 0)); -const jsval JSVAL_ONE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 1)); -const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, false)); -const jsval JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, true)); -const jsval JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); - -namespace JS { - -const HandleValue NullHandleValue = HandleValue::fromMarkedLocation(&JSVAL_NULL); -const HandleValue UndefinedHandleValue = HandleValue::fromMarkedLocation(&JSVAL_VOID); - -} // namespace JS
--- a/js/xpconnect/public/nsAutoJSValHolder.h +++ b/js/xpconnect/public/nsAutoJSValHolder.h @@ -2,20 +2,19 @@ /* 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/. */ #ifndef __NSAUTOJSVALHOLDER_H__ #define __NSAUTOJSVALHOLDER_H__ #include "nsDebug.h" -#include "jsapi.h" /** - * Simple class that looks and acts like a JS::Value except that it unroots + * Simple class that looks and acts like a jsval except that it unroots * itself automatically if Root() is ever called. Designed to be rooted on the * context or runtime (but not both!). */ class nsAutoJSValHolder { public: nsAutoJSValHolder() : mVal(JSVAL_NULL), mRt(nullptr) @@ -40,17 +39,17 @@ public: if (this != &aOther) { if (aOther.IsHeld()) { // XXX No error handling here... this->Hold(aOther.mRt); } else { this->Release(); } - *this = static_cast<JS::Value>(aOther); + *this = static_cast<jsval>(aOther); } return *this; } /** * Hold by rooting on the context's runtime. */ bool Hold(JSContext* aCx) { @@ -72,20 +71,20 @@ public: mRt = aRt; } return !!mRt; } /** * Manually release, nullifying mVal, and mRt, but returning - * the original JS::Value. + * the original jsval. */ - JS::Value Release() { - JS::Value oldval = mVal; + jsval Release() { + jsval oldval = mVal; if (mRt) { JS_RemoveValueRootRT(mRt, &mVal); // infallible mRt = nullptr; } mVal = JSVAL_NULL; @@ -103,37 +102,37 @@ public: * Explicit JSObject* conversion. */ JSObject* ToJSObject() const { return mVal.isObject() ? &mVal.toObject() : nullptr; } - JS::Value* ToJSValPtr() { + jsval* ToJSValPtr() { return &mVal; } /** - * Pretend to be a JS::Value. + * Pretend to be a jsval. */ - operator JS::Value() const { return mVal; } + operator jsval() const { return mVal; } nsAutoJSValHolder &operator=(JSObject* aOther) { return *this = OBJECT_TO_JSVAL(aOther); } - nsAutoJSValHolder &operator=(JS::Value aOther) { + nsAutoJSValHolder &operator=(jsval aOther) { #ifdef DEBUG if (JSVAL_IS_GCTHING(aOther) && !JSVAL_IS_NULL(aOther)) { MOZ_ASSERT(IsHeld(), "Not rooted!"); } #endif mVal = aOther; return *this; } private: - JS::Value mVal; + jsval mVal; JSRuntime* mRt; }; #endif /* __NSAUTOJSVALHOLDER_H__ */
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -2,17 +2,16 @@ * * 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 "nsNSSComponent.h" #include "nsNSSIOLayer.h" -#include "mozilla/Casting.h" #include "mozilla/DebugOnly.h" #include "mozilla/Telemetry.h" #include "prlog.h" #include "prnetdb.h" #include "nsIPrefService.h" #include "nsIClientAuthDialogs.h" #include "nsClientAuthRemember.h"
--- a/tools/profiler/PseudoStack.h +++ b/tools/profiler/PseudoStack.h @@ -3,17 +3,17 @@ * 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/. */ #ifndef PROFILER_PSEUDO_STACK_H_ #define PROFILER_PSEUDO_STACK_H_ #include "mozilla/NullPtr.h" #include <stdint.h> -#include "js/ProfilingStack.h" +#include "jsfriendapi.h" #include <stdlib.h> #include <algorithm> /* we duplicate this code here to avoid header dependencies * which make it more difficult to include in other places */ #if defined(_M_X64) || defined(__x86_64__) #define V8_HOST_ARCH_X64 1 #elif defined(_M_IX86) || defined(__i386__) || defined(__i386)