From 7226b2e716f6f2b9df3577f299ea647b09b3351d Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Mon, 20 Feb 2012 11:58:00 +0100 Subject: [PATCH] bug 737624 - memory-only encoding/decoding of scripts and functions. r=:luke The patch shrinks the API presented in jsxdrapi.h down to 4 functions to encode/decode scripts and interpreted functions to/from the memory. The newly introduced implementation header vm/Xdr.h replaces the former JSXDRState with the template class XDRState parametrized by the enum type with two constants, XDR_ENCODE and XDR_DECODE. This way a compiler can fully eliminate the former runtime checks for the decoding/encoding mode. As a drawback this required to explicitly instantiate the xdr implementation as I do not want to put all the xdr code to header files. The memory-only XDR allows to avoid coping filename and to-be-atomized chars to a temporary buffer as the code can just access the buffer directly. Another change is that new XDRScript takes as a parameter its parent script. This allowed to avoid keeping filename in XDRState and simplify the filename management. Another change is the removal of JS_HAS_HDR. As CloneScript uses XDR to copy a script, JS_HAS_XDR cannot be disabled. --HG-- rename : js/src/jsxdrapi.cpp => js/src/vm/Xdr.cpp --- caps/src/nsJSPrincipals.cpp | 1 - .../xul/document/src/nsXULPrototypeCache.cpp | 2 +- js/src/Makefile.in | 3 +- js/src/frontend/BytecodeEmitter.h | 2 +- js/src/js.msg | 10 +- .../jsapi-tests/testAddPropertyPropcache.cpp | 1 - js/src/jsapi-tests/testCloneScript.cpp | 2 - .../testDefineGetterSetterNonEnumerable.cpp | 1 - js/src/jsapi-tests/testIntTypesABI.cpp | 1 - js/src/jsapi-tests/testSetProperty.cpp | 1 - js/src/jsapi-tests/testXDR.cpp | 58 +- js/src/jsapi.cpp | 47 +- js/src/jsapi.h | 18 + js/src/jsatom.cpp | 63 ++ js/src/jsatom.h | 8 + js/src/jsfriendapi.h | 4 +- js/src/jsfun.cpp | 37 +- js/src/jsfun.h | 5 +- js/src/jsnum.h | 4 +- js/src/jsobj.cpp | 12 +- js/src/jsprvtd.h | 8 + js/src/jspubtd.h | 5 +- js/src/jsscript.cpp | 254 +++----- js/src/jsscript.h | 25 +- js/src/jsversion.h | 5 - js/src/jsxdrapi.cpp | 589 ------------------ js/src/jsxdrapi.h | 224 ------- js/src/vm/ArgumentsObject.cpp | 1 + js/src/vm/RegExpObject.cpp | 25 +- js/src/vm/RegExpObject.h | 3 +- js/src/vm/ScopeObject.cpp | 31 +- js/src/vm/ScopeObject.h | 5 +- js/src/vm/Xdr.cpp | 239 +++++++ js/src/vm/Xdr.h | 322 ++++++++++ js/xpconnect/loader/mozJSComponentLoader.cpp | 1 - js/xpconnect/loader/mozJSLoaderUtils.cpp | 44 +- js/xpconnect/src/nsXPConnect.cpp | 69 +- 37 files changed, 941 insertions(+), 1189 deletions(-) delete mode 100644 js/src/jsxdrapi.cpp delete mode 100644 js/src/jsxdrapi.h create mode 100644 js/src/vm/Xdr.cpp create mode 100644 js/src/vm/Xdr.h diff --git a/caps/src/nsJSPrincipals.cpp b/caps/src/nsJSPrincipals.cpp index 207c7a66101..075eedd832f 100644 --- a/caps/src/nsJSPrincipals.cpp +++ b/caps/src/nsJSPrincipals.cpp @@ -44,7 +44,6 @@ #include "nsXPIDLString.h" #include "nsCOMPtr.h" #include "jsapi.h" -#include "jsxdrapi.h" #include "nsIJSRuntimeService.h" #include "nsIServiceManager.h" #include "nsMemory.h" diff --git a/content/xul/document/src/nsXULPrototypeCache.cpp b/content/xul/document/src/nsXULPrototypeCache.cpp index 579df272dea..7ec1df04a89 100644 --- a/content/xul/document/src/nsXULPrototypeCache.cpp +++ b/content/xul/document/src/nsXULPrototypeCache.cpp @@ -60,7 +60,7 @@ #include "nsNetUtil.h" #include "nsAppDirectoryServiceDefs.h" -#include "jsxdrapi.h" +#include "jsapi.h" #include "mozilla/Preferences.h" #include "mozilla/scache/StartupCache.h" diff --git a/js/src/Makefile.in b/js/src/Makefile.in index cbd8305ae02..8473c5095c4 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -146,7 +146,6 @@ CPPSRCS = \ jswatchpoint.cpp \ jsweakmap.cpp \ jswrapper.cpp \ - jsxdrapi.cpp \ jsxml.cpp \ prmjtime.cpp \ sharkctl.cpp \ @@ -177,6 +176,7 @@ CPPSRCS = \ Statistics.cpp \ StringBuffer.cpp \ Unicode.cpp \ + Xdr.cpp \ $(NULL) # Changes to internal header files, used externally, massively slow down @@ -210,7 +210,6 @@ INSTALLED_HEADERS = \ jsutil.h \ jsversion.h \ jswrapper.h \ - jsxdrapi.h \ jsval.h \ $(NULL) diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index cdbfdac7f0c..37cc6607fdd 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -822,7 +822,7 @@ EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *body); * arity in JSSrcNoteSpec. This is why SRC_IF and SRC_INITPROP have the same * value below. * - * Don't forget to update JSXDR_BYTECODE_VERSION in jsxdrapi.h for all such + * Don't forget to update XDR_BYTECODE_VERSION in vm/Xdr.h for all such * incompatible source note or other bytecode changes. */ enum SrcNoteType { diff --git a/js/src/js.msg b/js/src/js.msg index 24bbb58639b..6690ac8bda1 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -146,11 +146,11 @@ MSG_DEF(JSMSG_NO_INPUT, 59, 5, JSEXN_SYNTAXERR, "no input for /{0 MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_ERR, "can't open {0}: {1}") MSG_DEF(JSMSG_TOO_MANY_FUN_APPLY_ARGS, 61, 0, JSEXN_RANGEERR, "arguments array passed to Function.prototype.apply is too large") MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 62, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") -MSG_DEF(JSMSG_END_OF_DATA, 63, 0, JSEXN_INTERNALERR, "unexpected end of data") -MSG_DEF(JSMSG_SEEK_BEYOND_START, 64, 0, JSEXN_INTERNALERR, "illegal seek beyond start") -MSG_DEF(JSMSG_SEEK_BEYOND_END, 65, 0, JSEXN_INTERNALERR, "illegal seek beyond end") -MSG_DEF(JSMSG_END_SEEK, 66, 0, JSEXN_INTERNALERR, "illegal end-based seek") -MSG_DEF(JSMSG_WHITHER_WHENCE, 67, 1, JSEXN_INTERNALERR, "unknown seek whence: {0}") +MSG_DEF(JSMSG_TOO_BIG_TO_ENCODE, 63, 0, JSEXN_INTERNALERR, "data are to big to encode") +MSG_DEF(JSMSG_UNUSED64, 64, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_UNUSED65, 65, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_UNUSED66, 66, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_UNUSED67, 67, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC, 68, 0, JSEXN_INTERNALERR, "bad script XDR magic number") MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL, 69, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters") MSG_DEF(JSMSG_MISSING_FORMAL, 70, 0, JSEXN_SYNTAXERR, "missing formal parameter") diff --git a/js/src/jsapi-tests/testAddPropertyPropcache.cpp b/js/src/jsapi-tests/testAddPropertyPropcache.cpp index b30437baebe..42c1a30a121 100644 --- a/js/src/jsapi-tests/testAddPropertyPropcache.cpp +++ b/js/src/jsapi-tests/testAddPropertyPropcache.cpp @@ -3,7 +3,6 @@ */ #include "tests.h" -#include "jsxdrapi.h" /* Do the test a bunch of times, because sometimes we seem to randomly miss the propcache */ diff --git a/js/src/jsapi-tests/testCloneScript.cpp b/js/src/jsapi-tests/testCloneScript.cpp index 06d832a80e5..3b489301013 100644 --- a/js/src/jsapi-tests/testCloneScript.cpp +++ b/js/src/jsapi-tests/testCloneScript.cpp @@ -5,9 +5,7 @@ */ #include "tests.h" -#include "jsapi.h" #include "jsdbgapi.h" -#include "jsxdrapi.h" BEGIN_TEST(test_cloneScript) { diff --git a/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp b/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp index 2c3f134d40c..423bd71ac18 100644 --- a/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp +++ b/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp @@ -3,7 +3,6 @@ */ #include "tests.h" -#include "jsxdrapi.h" static JSBool native(JSContext *cx, unsigned argc, jsval *vp) diff --git a/js/src/jsapi-tests/testIntTypesABI.cpp b/js/src/jsapi-tests/testIntTypesABI.cpp index 4a598326bd3..e33ac0c0486 100644 --- a/js/src/jsapi-tests/testIntTypesABI.cpp +++ b/js/src/jsapi-tests/testIntTypesABI.cpp @@ -13,7 +13,6 @@ #include "jspubtd.h" #include "jstypes.h" #include "jsval.h" -#include "jsxdrapi.h" #include "js/HashTable.h" #include "js/MemoryMetrics.h" diff --git a/js/src/jsapi-tests/testSetProperty.cpp b/js/src/jsapi-tests/testSetProperty.cpp index f4f960fc4d7..2d2fe88b522 100644 --- a/js/src/jsapi-tests/testSetProperty.cpp +++ b/js/src/jsapi-tests/testSetProperty.cpp @@ -3,7 +3,6 @@ */ #include "tests.h" -#include "jsxdrapi.h" static JSBool nativeGet(JSContext *cx, JSObject *obj, jsid id, jsval *vp) diff --git a/js/src/jsapi-tests/testXDR.cpp b/js/src/jsapi-tests/testXDR.cpp index 80ba09b69f2..d78f41ffca9 100644 --- a/js/src/jsapi-tests/testXDR.cpp +++ b/js/src/jsapi-tests/testXDR.cpp @@ -4,7 +4,6 @@ #include "tests.h" #include "jsscript.h" -#include "jsxdrapi.h" static JSScript * CompileScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj, @@ -28,44 +27,18 @@ CompileScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj, return script; } -template -T * -FreezeThawImpl(JSContext *cx, T *thing, JSBool (*xdrAction)(JSXDRState *xdr, T **)) +JSScript * +FreezeThaw(JSContext *cx, JSScript *script) { // freeze - JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE); - if (!w) - return NULL; - - void *memory = NULL; uint32_t nbytes; - if (xdrAction(w, &thing)) { - void *p = JS_XDRMemGetData(w, &nbytes); - if (p) { - memory = JS_malloc(cx, nbytes); - if (memory) - memcpy(memory, p, nbytes); - } - } - JS_XDRDestroy(w); + void *memory = JS_EncodeScript(cx, script, &nbytes); if (!memory) return NULL; // thaw - JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE); - JS_XDRMemSetData(r, memory, nbytes); - - JSScript *script = GetScript(cx, thing); - JS_XDRSetPrincipals(r, script->principals, script->originPrincipals); - if (!xdrAction(r, &thing)) - thing = NULL; - JS_XDRDestroy(r); // this frees `memory - return thing; -} - -static JSScript * -GetScript(JSContext *cx, JSScript *script) -{ + script = JS_DecodeScript(cx, memory, nbytes, script->principals, script->originPrincipals); + js_free(memory); return script; } @@ -75,16 +48,21 @@ GetScript(JSContext *cx, JSObject *funobj) return JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj)); } -static JSScript * -FreezeThaw(JSContext *cx, JSScript *script) -{ - return FreezeThawImpl(cx, script, JS_XDRScript); -} - -static JSObject * +JSObject * FreezeThaw(JSContext *cx, JSObject *funobj) { - return FreezeThawImpl(cx, funobj, JS_XDRFunctionObject); + // freeze + uint32_t nbytes; + void *memory = JS_EncodeInterpretedFunction(cx, funobj, &nbytes); + if (!memory) + return NULL; + + // thaw + JSScript *script = GetScript(cx, funobj); + funobj = JS_DecodeInterpretedFunction(cx, memory, nbytes, + script->principals, script->originPrincipals); + js_free(memory); + return funobj; } static JSPrincipals testPrincipals[] = { diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 788f4261f45..1fe16bd3b4a 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -82,6 +82,7 @@ #include "jsweakmap.h" #include "jswrapper.h" #include "jstypedarray.h" +#include "jsxml.h" #include "ds/LifoAlloc.h" #include "builtin/MapObject.h" @@ -90,10 +91,10 @@ #include "frontend/BytecodeEmitter.h" #include "gc/Memory.h" #include "js/MemoryMetrics.h" -#include "mozilla/Util.h" #include "yarr/BumpPointerAllocator.h" #include "vm/MethodGuard.h" #include "vm/StringBuffer.h" +#include "vm/Xdr.h" #include "jsatominlines.h" #include "jsinferinlines.h" @@ -112,10 +113,6 @@ #include "methodjit/Logging.h" #endif -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - using namespace js; using namespace js::gc; using namespace js::types; @@ -6638,3 +6635,43 @@ AutoEnumStateRooter::~AutoEnumStateRooter() } } // namespace JS + +JS_PUBLIC_API(void *) +JS_EncodeScript(JSContext *cx, JSScript *script, uint32_t *lengthp) +{ + XDREncoder encoder(cx); + if (!encoder.codeScript(&script)) + return NULL; + return encoder.forgetData(lengthp); +} + +JS_PUBLIC_API(void *) +JS_EncodeInterpretedFunction(JSContext *cx, JSObject *funobj, uint32_t *lengthp) +{ + XDREncoder encoder(cx); + if (!encoder.codeFunction(&funobj)) + return NULL; + return encoder.forgetData(lengthp); +} + +JS_PUBLIC_API(JSScript *) +JS_DecodeScript(JSContext *cx, const void *data, uint32_t length, + JSPrincipals *principals, JSPrincipals *originPrincipals) +{ + XDRDecoder decoder(cx, data, length, principals, originPrincipals); + JSScript *script; + if (!decoder.codeScript(&script)) + return NULL; + return script; +} + +JS_PUBLIC_API(JSObject *) +JS_DecodeInterpretedFunction(JSContext *cx, const void *data, uint32_t length, + JSPrincipals *principals, JSPrincipals *originPrincipals) +{ + XDRDecoder decoder(cx, data, length, principals, originPrincipals); + JSObject *funobj; + if (!decoder.codeFunction(&funobj)) + return NULL; + return funobj; +} diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 606cd86405f..8faf5df7c59 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5410,6 +5410,24 @@ extern JS_PUBLIC_API(JSBool) JS_DescribeScriptedCaller(JSContext *cx, JSScript **script, unsigned *lineno); +/* + * Encode/Decode interpreted scripts and functions to/from memory. + */ + +extern JS_PUBLIC_API(void *) +JS_EncodeScript(JSContext *cx, JSScript *script, uint32_t *lengthp); + +extern JS_PUBLIC_API(void *) +JS_EncodeInterpretedFunction(JSContext *cx, JSObject *funobj, uint32_t *lengthp); + +extern JS_PUBLIC_API(JSScript *) +JS_DecodeScript(JSContext *cx, const void *data, uint32_t length, + JSPrincipals *principals, JSPrincipals *originPrincipals); + +extern JS_PUBLIC_API(JSObject *) +JS_DecodeInterpretedFunction(JSContext *cx, const void *data, uint32_t length, + JSPrincipals *principals, JSPrincipals *originPrincipals); + JS_END_EXTERN_C #endif /* jsapi_h___ */ diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 02921f4dda0..5a306f3f554 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -68,6 +68,7 @@ #include "jsobjinlines.h" #include "vm/String-inl.h" +#include "vm/Xdr.h" using namespace mozilla; using namespace js; @@ -657,3 +658,65 @@ js_InternNonIntElementIdSlow(JSContext *cx, JSObject *obj, const Value &idval, return false; } #endif + +template +bool +js::XDRAtom(XDRState *xdr, JSAtom **atomp) +{ + if (mode == XDR_ENCODE) { + JSString *str = *atomp; + return xdr->codeString(&str); + } + + /* + * Inline XDRState::codeString when decoding to avoid JSString allocation + * for already existing atoms. See bug 321985. + */ + uint32_t nchars; + if (!xdr->codeUint32(&nchars)) + return false; + + JSContext *cx = xdr->cx(); + JSAtom *atom; +#if IS_LITTLE_ENDIAN + /* Directly access the little endian chars in the XDR buffer. */ + const jschar *chars = reinterpret_cast(xdr->buf.read(nchars * sizeof(jschar))); + atom = js_AtomizeChars(cx, chars, nchars); +#else + /* + * We must copy chars to a temporary buffer to convert between little and + * big endian data. + */ + jschar *chars; + jschar stackChars[256]; + if (nchars <= ArrayLength(stackChars)) { + chars = stackChars; + } else { + /* + * This is very uncommon. Don't use the tempLifoAlloc arena for this as + * most allocations here will be bigger than tempLifoAlloc's default + * chunk size. + */ + chars = static_cast(cx->runtime->malloc_(nchars * sizeof(jschar))); + if (!chars) + return false; + } + + JS_ALWAYS_TRUE(xdr->codeChars(chars, nchars)); + atom = js_AtomizeChars(cx, chars, nchars); + if (chars != stackChars) + Foreground::free_(chars); +#endif /* !IS_LITTLE_ENDIAN */ + + if (!atom) + return false; + *atomp = atom; + return true; +} + +template bool +js::XDRAtom(XDRState *xdr, JSAtom **atomp); + +template bool +js::XDRAtom(XDRState *xdr, JSAtom **atomp); + diff --git a/js/src/jsatom.h b/js/src/jsatom.h index 812654bec3f..24863f3d114 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -478,4 +478,12 @@ js_InternNonIntElementId(JSContext *cx, JSObject *obj, const js::Value &idval, extern void js_InitAtomMap(JSContext *cx, js::AtomIndexMap *indices, JSAtom **atoms); +namespace js { + +template +bool +XDRAtom(XDRState *xdr, JSAtom **atomp); + +} /* namespace js */ + #endif /* jsatom_h___ */ diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index e8c79020141..bef345bd270 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -521,8 +521,8 @@ IsObjectInContextCompartment(const JSObject *obj, const JSContext *cx); /* * NB: these flag bits are encoded into the bytecode stream in the immediate - * operand of JSOP_ITER, so don't change them without advancing jsxdrapi.h's - * JSXDR_BYTECODE_VERSION. + * operand of JSOP_ITER, so don't change them without advancing vm/Xdr.h's + * XDR_BYTECODE_VERSION. */ #define JSITER_ENUMERATE 0x1 /* for-in compatible hidden default iterator */ #define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 2cbbbc1d25f..e45418bd470 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -52,7 +52,7 @@ #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" -#include "jsversion.h" +#include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsgcmark.h" @@ -66,7 +66,6 @@ #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" -#include "jsexn.h" #include "frontend/BytecodeCompiler.h" #include "frontend/BytecodeEmitter.h" @@ -74,15 +73,12 @@ #include "vm/Debugger.h" #include "vm/MethodGuard.h" #include "vm/ScopeObject.h" +#include "vm/Xdr.h" #if JS_HAS_GENERATORS # include "jsiter.h" #endif -#if JS_HAS_XDR -# include "jsxdrapi.h" -#endif - #ifdef JS_METHODJIT #include "methodjit/MethodJIT.h" #endif @@ -480,13 +476,10 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, return true; } -#if JS_HAS_XDR - -/* XXX store parent and proto, if defined */ -JSBool -js::XDRFunctionObject(JSXDRState *xdr, JSObject **objp) +template +bool +js::XDRInterpretedFunction(XDRState *xdr, JSObject **objp, JSScript *parentScript) { - JSContext *cx; JSFunction *fun; JSAtom *atom; uint32_t firstword; /* flag telling whether fun->atom is non-null, @@ -494,9 +487,9 @@ js::XDRFunctionObject(JSXDRState *xdr, JSObject **objp) and 14 bits reserved for future use */ uint32_t flagsword; /* word for argument count and fun->flags */ - cx = xdr->cx; + JSContext *cx = xdr->cx(); JSScript *script; - if (xdr->mode == JSXDR_ENCODE) { + if (mode == XDR_ENCODE) { fun = (*objp)->toFunction(); if (!fun->isInterpreted()) { JSAutoByteString funNameBytes; @@ -523,17 +516,17 @@ js::XDRFunctionObject(JSXDRState *xdr, JSObject **objp) script = NULL; } - if (!JS_XDRUint32(xdr, &firstword)) + if (!xdr->codeUint32(&firstword)) return false; - if ((firstword & 1U) && !js_XDRAtom(xdr, &atom)) + if ((firstword & 1U) && !XDRAtom(xdr, &atom)) return false; - if (!JS_XDRUint32(xdr, &flagsword)) + if (!xdr->codeUint32(&flagsword)) return false; - if (!XDRScript(xdr, &script)) + if (!XDRScript(xdr, &script, parentScript)) return false; - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { fun->nargs = flagsword >> 16; JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED); fun->flags = uint16_t(flagsword); @@ -549,7 +542,11 @@ js::XDRFunctionObject(JSXDRState *xdr, JSObject **objp) return true; } -#endif /* JS_HAS_XDR */ +template bool +js::XDRInterpretedFunction(XDRState *xdr, JSObject **objp, JSScript *parentScript); + +template bool +js::XDRInterpretedFunction(XDRState *xdr, JSObject **objp, JSScript *parentScript); /* * [[HasInstance]] internal method for Function objects: fetch the .prototype diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 57a9f9211bc..652a4e77d60 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -356,8 +356,9 @@ js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->a namespace js { -extern JSBool -XDRFunctionObject(JSXDRState *xdr, JSObject **objp); +template +bool +XDRInterpretedFunction(XDRState *xdr, JSObject **objp, JSScript *parentScript); } /* namespace js */ diff --git a/js/src/jsnum.h b/js/src/jsnum.h index ae9b888756d..19b32aaa1ce 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -74,7 +74,7 @@ #define JSDOUBLE_HI32_EXPSHIFT 20 #define JSDOUBLE_EXPBIAS 1023 -typedef union jsdpun { +union jsdpun { struct { #if defined(IS_LITTLE_ENDIAN) && !defined(FPU_IS_ARM_FPA) uint32_t lo, hi; @@ -84,7 +84,7 @@ typedef union jsdpun { } s; uint64_t u64; double d; -} jsdpun; +}; static inline int JSDOUBLE_IS_NaN(double d) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 46f61f710b6..58a50c464dc 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -76,13 +76,15 @@ #include "json.h" #include "jswatchpoint.h" #include "jswrapper.h" +#include "jsxml.h" #include "builtin/MapObject.h" #include "frontend/BytecodeCompiler.h" #include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" -#include "vm/StringBuffer.h" #include "js/MemoryMetrics.h" +#include "vm/StringBuffer.h" +#include "vm/Xdr.h" #include "jsarrayinlines.h" #include "jsatominlines.h" @@ -93,14 +95,6 @@ #include "vm/MethodGuard-inl.h" -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#if JS_HAS_XDR -#include "jsxdrapi.h" -#endif - #include "jsautooplen.h" using namespace mozilla; diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index afa591ee98d..7af25c6bf08 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -316,6 +316,14 @@ typedef Handle HandleAtom; typedef Handle HandleId; typedef Handle HandleValue; +enum XDRMode { + XDR_ENCODE, + XDR_DECODE +}; + +template +class XDRState; + } /* namespace js */ namespace JSC { diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 0859351f15c..80c003da53a 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -226,7 +226,6 @@ typedef struct JSStructuredCloneCallbacks JSStructuredCloneCallbacks; typedef struct JSStructuredCloneReader JSStructuredCloneReader; typedef struct JSStructuredCloneWriter JSStructuredCloneWriter; typedef struct JSTracer JSTracer; -typedef struct JSXDRState JSXDRState; #ifdef __cplusplus class JSFlatString; @@ -234,7 +233,7 @@ class JSString; #else typedef struct JSFlatString JSFlatString; typedef struct JSString JSString; -#endif +#endif /* !__cplusplus */ #ifdef JS_THREADSAFE typedef struct PRCallOnceType JSCallOnceType; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 84bc3d2e869..6e86b974de3 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -62,9 +62,6 @@ #include "jsopcode.h" #include "jsscope.h" #include "jsscript.h" -#if JS_HAS_XDR -#include "jsxdrapi.h" -#endif #include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" @@ -72,6 +69,7 @@ #include "methodjit/MethodJIT.h" #include "methodjit/Retcon.h" #include "vm/Debugger.h" +#include "vm/Xdr.h" #include "jsinferinlines.h" #include "jsinterpinlines.h" @@ -296,10 +294,11 @@ Bindings::trace(JSTracer *trc) MarkShape(trc, &lastBinding, "shape"); } -#if JS_HAS_XDR +} /* namespace js */ +template static bool -XDRScriptConst(JSXDRState *xdr, HeapValue *vp) +XDRScriptConst(XDRState *xdr, HeapValue *vp) { /* * A script constant can be an arbitrary primitive value as they are used @@ -317,7 +316,7 @@ XDRScriptConst(JSXDRState *xdr, HeapValue *vp) }; uint32_t tag; - if (xdr->mode == JSXDR_ENCODE) { + if (mode == XDR_ENCODE) { if (vp->isInt32()) { tag = SCRIPT_INT; } else if (vp->isDouble()) { @@ -336,54 +335,54 @@ XDRScriptConst(JSXDRState *xdr, HeapValue *vp) } } - if (!JS_XDRUint32(xdr, &tag)) + if (!xdr->codeUint32(&tag)) return false; switch (tag) { case SCRIPT_INT: { uint32_t i; - if (xdr->mode == JSXDR_ENCODE) + if (mode == XDR_ENCODE) i = uint32_t(vp->toInt32()); - if (!JS_XDRUint32(xdr, &i)) + if (!xdr->codeUint32(&i)) return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) + if (mode == XDR_DECODE) vp->init(Int32Value(int32_t(i))); break; } case SCRIPT_DOUBLE: { double d; - if (xdr->mode == JSXDR_ENCODE) + if (mode == XDR_ENCODE) d = vp->toDouble(); - if (!JS_XDRDouble(xdr, &d)) + if (!xdr->codeDouble(&d)) return false; - if (xdr->mode == JSXDR_DECODE) + if (mode == XDR_DECODE) vp->init(DoubleValue(d)); break; } case SCRIPT_STRING: { JSString *str; - if (xdr->mode == JSXDR_ENCODE) + if (mode == XDR_ENCODE) str = vp->toString(); - if (!JS_XDRString(xdr, &str)) + if (!xdr->codeString(&str)) return false; - if (xdr->mode == JSXDR_DECODE) + if (mode == XDR_DECODE) vp->init(StringValue(str)); break; } case SCRIPT_TRUE: - if (xdr->mode == JSXDR_DECODE) + if (mode == XDR_DECODE) vp->init(BooleanValue(true)); break; case SCRIPT_FALSE: - if (xdr->mode == JSXDR_DECODE) + if (mode == XDR_DECODE) vp->init(BooleanValue(false)); break; case SCRIPT_NULL: - if (xdr->mode == JSXDR_DECODE) + if (mode == XDR_DECODE) vp->init(NullValue()); break; case SCRIPT_VOID: - if (xdr->mode == JSXDR_DECODE) + if (mode == XDR_DECODE) vp->init(UndefinedValue()); break; } @@ -393,8 +392,9 @@ XDRScriptConst(JSXDRState *xdr, HeapValue *vp) static const char * SaveScriptFilename(JSContext *cx, const char *filename); -JSBool -XDRScript(JSXDRState *xdr, JSScript **scriptp) +template +bool +js::XDRScript(XDRState *xdr, JSScript **scriptp, JSScript *parentScript) { enum ScriptBits { NoScriptRval, @@ -404,7 +404,7 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) MayNeedArgsObj, NeedsArgsObj, OwnFilename, - SharedFilename + ParentFilename }; uint32_t length, lineno, nslots; @@ -414,7 +414,7 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) uint32_t nTypeSets = 0; uint32_t scriptBits = 0; - JSContext *cx = xdr->cx; + JSContext *cx = xdr->cx(); JSScript *script; nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0; jssrcnote *notes = NULL; @@ -426,9 +426,10 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) nargs = nvars = Bindings::BINDING_COUNT_LIMIT; #endif uint32_t argsVars; - if (xdr->mode == JSXDR_ENCODE) { + if (mode == XDR_ENCODE) { script = *scriptp; - + JS_ASSERT_IF(parentScript, parentScript->compartment() == script->compartment()); + /* Should not XDR scripts optimized for a single global object. */ JS_ASSERT(!JSScript::isValidOffset(script->globalsOffset)); @@ -436,9 +437,9 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) nvars = script->bindings.countVars(); argsVars = (nargs << 16) | nvars; } - if (!JS_XDRUint32(xdr, &argsVars)) + if (!xdr->codeUint32(&argsVars)) return false; - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { nargs = argsVars >> 16; nvars = argsVars & 0xFFFF; } @@ -466,7 +467,7 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) } Vector names(cx); - if (xdr->mode == JSXDR_ENCODE) { + if (mode == XDR_ENCODE) { if (!script->bindings.getLocalNameArray(cx, &names)) return false; PodZero(bitmap, bitmapLength); @@ -476,7 +477,7 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) } } for (unsigned i = 0; i < bitmapLength; ++i) { - if (!JS_XDRUint32(xdr, &bitmap[i])) + if (!xdr->codeUint32(&bitmap[i])) return false; } @@ -484,7 +485,7 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) if (i < nargs && !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] & JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) { - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { uint16_t dummy; if (!bindings.addDestructuring(cx, &dummy)) return false; @@ -495,11 +496,11 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) } JSAtom *name; - if (xdr->mode == JSXDR_ENCODE) + if (mode == XDR_ENCODE) name = names[i]; - if (!js_XDRAtom(xdr, &name)) + if (!XDRAtom(xdr, &name)) return false; - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { BindingKind kind = (i < nargs) ? ARGUMENT : (bitmap[i >> JS_BITS_PER_UINT32_LOG2] & @@ -512,18 +513,18 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) } } - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { if (!bindings.ensureShape(cx)) return false; bindings.makeImmutable(); } - if (xdr->mode == JSXDR_ENCODE) + if (mode == XDR_ENCODE) length = script->length; - if (!JS_XDRUint32(xdr, &length)) + if (!xdr->codeUint32(&length)) return JS_FALSE; - if (xdr->mode == JSXDR_ENCODE) { + if (mode == XDR_ENCODE) { prologLength = script->mainOffset; JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN); version = (uint32_t)script->getVersion() | (script->nfixed << 16); @@ -569,44 +570,44 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) scriptBits |= (1 << NeedsArgsObj); } if (script->filename) { - scriptBits |= (script->filename != xdr->sharedFilename) - ? (1 << OwnFilename) - : (1 << SharedFilename); + scriptBits |= (parentScript && parentScript->filename == script->filename) + ? (1 << ParentFilename) + : (1 << OwnFilename); } JS_ASSERT(!script->compileAndGo); JS_ASSERT(!script->hasSingletons); } - if (!JS_XDRUint32(xdr, &prologLength)) + if (!xdr->codeUint32(&prologLength)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &version)) + if (!xdr->codeUint32(&version)) return JS_FALSE; /* * To fuse allocations, we need srcnote, atom, objects, regexp, and trynote * counts early. */ - if (!JS_XDRUint32(xdr, &natoms)) + if (!xdr->codeUint32(&natoms)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &nsrcnotes)) + if (!xdr->codeUint32(&nsrcnotes)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &ntrynotes)) + if (!xdr->codeUint32(&ntrynotes)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &nobjects)) + if (!xdr->codeUint32(&nobjects)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &nregexps)) + if (!xdr->codeUint32(&nregexps)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &nconsts)) + if (!xdr->codeUint32(&nconsts)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &encodedClosedCount)) + if (!xdr->codeUint32(&encodedClosedCount)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &nTypeSets)) + if (!xdr->codeUint32(&nTypeSets)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &scriptBits)) + if (!xdr->codeUint32(&scriptBits)) return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { nClosedArgs = encodedClosedCount >> 16; nClosedVars = encodedClosedCount & 0xFFFF; @@ -637,7 +638,7 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) if (scriptBits & (1 << UsesEval)) script->usesEval = true; if (scriptBits & (1 << MayNeedArgsObj)) { - script->mayNeedArgsObj_ = true; + script->setMayNeedArgsObj(); if (scriptBits & (1 << NeedsArgsObj)) script->setNeedsArgsObj(true); } else { @@ -645,56 +646,41 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) } } - if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode))) - return false; - - if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || - !JS_XDRUint32(xdr, &lineno) || - !JS_XDRUint32(xdr, &nslots)) { + JS_STATIC_ASSERT(sizeof(jsbytecode) == 1); + JS_STATIC_ASSERT(sizeof(jssrcnote) == 1); + if (!xdr->codeBytes(script->code, length) || + !xdr->codeBytes(notes, nsrcnotes) || + !xdr->codeUint32(&lineno) || + !xdr->codeUint32(&nslots)) { return false; } if (scriptBits & (1 << OwnFilename)) { - char *filename; - if (xdr->mode == JSXDR_ENCODE) - filename = const_cast(script->filename); - if (!JS_XDRCString(xdr, &filename)) + const char *filename; + if (mode == XDR_ENCODE) + filename = script->filename; + if (!xdr->codeCString(&filename)) return false; - if (xdr->mode == JSXDR_DECODE) { - script->filename = SaveScriptFilename(xdr->cx, filename); - Foreground::free_(filename); + if (mode == XDR_DECODE) { + script->filename = SaveScriptFilename(cx, filename); if (!script->filename) return false; } - if (!xdr->sharedFilename) - xdr->sharedFilename = script->filename; - } else if (scriptBits & (1 << SharedFilename)) { - JS_ASSERT(xdr->sharedFilename); - if (xdr->mode == JSXDR_DECODE) - script->filename = xdr->sharedFilename; + } else if (scriptBits & (1 << ParentFilename)) { + JS_ASSERT(parentScript); + if (mode == XDR_DECODE) + script->filename = parentScript->filename; } - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { script->lineno = lineno; script->nslots = uint16_t(nslots); script->staticLevel = uint16_t(nslots >> 16); - - /* The origin principals must be normalized at this point. */ - JS_ASSERT_IF(xdr->principals, xdr->originPrincipals); - JS_ASSERT(!script->principals); - JS_ASSERT(!script->originPrincipals); - if (xdr->principals) { - script->principals = xdr->principals; - JS_HoldPrincipals(xdr->principals); - } - if (xdr->originPrincipals) { - script->originPrincipals = xdr->originPrincipals; - JS_HoldPrincipals(xdr->originPrincipals); - } + xdr->initScriptPrincipals(script); } for (i = 0; i != natoms; ++i) { - if (!js_XDRAtom(xdr, &script->atoms[i])) + if (!XDRAtom(xdr, &script->atoms[i])) return false; } @@ -707,16 +693,16 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) for (i = 0; i != nobjects; ++i) { HeapPtr *objp = &script->objects()->vector[i]; uint32_t isBlock; - if (xdr->mode == JSXDR_ENCODE) { + if (mode == XDR_ENCODE) { JSObject *obj = *objp; JS_ASSERT(obj->isFunction() || obj->isStaticBlock()); isBlock = obj->isBlock() ? 1 : 0; } - if (!JS_XDRUint32(xdr, &isBlock)) + if (!xdr->codeUint32(&isBlock)) return false; if (isBlock == 0) { JSObject *tmp = *objp; - if (!XDRFunctionObject(xdr, &tmp)) + if (!XDRInterpretedFunction(xdr, &tmp, parentScript)) return false; *objp = tmp; } else { @@ -732,11 +718,11 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) return false; } for (i = 0; i != nClosedArgs; ++i) { - if (!JS_XDRUint32(xdr, &script->closedSlots[i])) + if (!xdr->codeUint32(&script->closedSlots[i])) return false; } for (i = 0; i != nClosedVars; ++i) { - if (!JS_XDRUint32(xdr, &script->closedSlots[nClosedArgs + i])) + if (!xdr->codeUint32(&script->closedSlots[nClosedArgs + i])) return false; } @@ -755,16 +741,16 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) tn = tnfirst + ntrynotes; do { --tn; - if (xdr->mode == JSXDR_ENCODE) { + if (mode == XDR_ENCODE) { kindAndDepth = (uint32_t(tn->kind) << 16) | uint32_t(tn->stackDepth); } - if (!JS_XDRUint32(xdr, &kindAndDepth) || - !JS_XDRUint32(xdr, &tn->start) || - !JS_XDRUint32(xdr, &tn->length)) { + if (!xdr->codeUint32(&kindAndDepth) || + !xdr->codeUint32(&tn->start) || + !xdr->codeUint32(&tn->length)) { return false; } - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { tn->kind = uint8_t(kindAndDepth >> 16); tn->stackDepth = uint16_t(kindAndDepth); } @@ -779,7 +765,7 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) } } - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { if (cx->hasRunOption(JSOPTION_PCCOUNT)) (void) script->initCounts(cx); *scriptp = script; @@ -788,9 +774,11 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp) return true; } -#endif /* JS_HAS_XDR */ +template bool +js::XDRScript(XDRState *xdr, JSScript **scriptp, JSScript *parentScript); -} /* namespace js */ +template bool +js::XDRScript(XDRState *xdr, JSScript **scriptp, JSScript *parentScript); bool JSScript::initCounts(JSContext *cx) @@ -844,8 +832,6 @@ JSScript::destroyCounts(JSContext *cx) } } -namespace js { - /* * Shared script filename management. */ @@ -924,8 +910,6 @@ FreeScriptFilenames(JSCompartment *comp) table.clear(); } -} /* namespace js */ - /* * JSScript data structures memory alignment: * @@ -1240,7 +1224,7 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce) if (bce->inFunction()) { bool needsArgsObj = bce->mayOverwriteArguments() || bce->needsEagerArguments(); if (needsArgsObj || bce->usesArguments()) { - script->mayNeedArgsObj_ = true; + script->setMayNeedArgsObj(); if (needsArgsObj) script->setNeedsArgsObj(true); } @@ -1626,74 +1610,32 @@ CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, unsigned *line *origin = script->originPrincipals; } -class AutoJSXDRState { - public: - AutoJSXDRState(JSXDRState *x - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : xdr(x) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - ~AutoJSXDRState() - { - JS_XDRDestroy(xdr); - } - - operator JSXDRState*() const - { - return xdr; - } - - JSXDRState* operator->() const - { - return xdr; - } - - private: - JSXDRState *const xdr; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; +} /* namespace js */ JSScript * -CloneScript(JSContext *cx, JSScript *script) +js::CloneScript(JSContext *cx, JSScript *script) { JS_ASSERT(cx->compartment != script->compartment()); /* Serialize script. */ - AutoJSXDRState w(JS_XDRNewMem(cx, JSXDR_ENCODE)); - if (!w) - return NULL; + XDREncoder encoder(cx); - if (!XDRScript(w, &script)) + if (!XDRScript(&encoder, &script, NULL)) return NULL; uint32_t nbytes; - void *p = JS_XDRMemGetData(w, &nbytes); - if (!p) - return NULL; + const void *p = encoder.getData(&nbytes); /* De-serialize script. */ - AutoJSXDRState r(JS_XDRNewMem(cx, JSXDR_DECODE)); - if (!r) - return NULL; + XDRDecoder decoder(cx, p, nbytes, cx->compartment->principals, script->originPrincipals); - /* - * Hand p off from w to r. Don't want them to share the data mem, lest - * they both try to free it in JS_XDRDestroy. - */ - JS_XDRMemSetData(r, p, nbytes); - JS_XDRMemSetData(w, NULL, 0); - - JS_XDRSetPrincipals(r, cx->compartment->principals, script->originPrincipals); - JSScript *newScript = NULL; - if (!XDRScript(r, &newScript)) + JSScript *newScript; + if (!XDRScript(&decoder, &newScript, NULL)) return NULL; return newScript; } -} /* namespace js */ - void JSScript::copyClosedSlotsTo(JSScript *other) { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index a0a7e5778f5..c9944db5d35 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -323,14 +323,6 @@ class DebugScript BreakpointSite *breakpoints[1]; }; -/* - * NB: after a successful JSXDR_DECODE, js_XDRScript callers must do any - * required subsequent set-up of owning function or script object and then call - * js_CallNewScriptHook. - */ -extern JSBool -XDRScript(JSXDRState *xdr, JSScript **scriptp); - } /* namespace js */ static const uint32_t JS_SCRIPT_COOKIE = 0xc00cee; @@ -356,8 +348,6 @@ struct JSScript : public js::gc::Cell static JSScript *NewScriptFromEmitter(JSContext *cx, js::BytecodeEmitter *bce); - friend JSBool js::XDRScript(JSXDRState *, JSScript **); - #ifdef JS_CRASH_DIAGNOSTICS /* * Make sure that the cookie size does not affect the GC alignment @@ -445,6 +435,10 @@ struct JSScript : public js::gc::Cell void setNeedsArgsObj(bool needsArgsObj); bool applySpeculationFailed(JSContext *cx); + void setMayNeedArgsObj() { + mayNeedArgsObj_ = true; + } + uint32_t natoms; /* length of atoms array */ uint16_t nslots; /* vars plus maximum stack depth */ uint16_t staticLevel;/* static level for display maintenance */ @@ -890,6 +884,15 @@ CurrentScriptFileLineOrigin(JSContext *cx, unsigned *linenop, LineOption = NOT_C extern JSScript * CloneScript(JSContext *cx, JSScript *script); -} +/* + * NB: after a successful XDR_DECODE, XDRScript callers must do any required + * subsequent set-up of owning function or script object and then call + * js_CallNewScriptHook. + */ +template +bool +XDRScript(XDRState *xdr, JSScript **scriptp, JSScript *parentScript); + +} /* namespace js */ #endif /* jsscript_h___ */ diff --git a/js/src/jsversion.h b/js/src/jsversion.h index 38f8ee0a7b3..94b5884f409 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -85,7 +85,6 @@ #define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ #endif #define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ -#define JS_HAS_XDR 0 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ #define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ @@ -108,7 +107,6 @@ #define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ @@ -127,7 +125,6 @@ #define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ @@ -146,7 +143,6 @@ #define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ @@ -165,7 +161,6 @@ #define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ diff --git a/js/src/jsxdrapi.cpp b/js/src/jsxdrapi.cpp deleted file mode 100644 index e4bbdb218fa..00000000000 --- a/js/src/jsxdrapi.cpp +++ /dev/null @@ -1,589 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "mozilla/Util.h" - -#include "jsversion.h" - -#if JS_HAS_XDR - -#include -#include "jstypes.h" -#include "jsutil.h" -#include "jsdhash.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jscntxt.h" -#include "jsnum.h" -#include "jsscript.h" /* js_XDRScript */ -#include "jsstr.h" -#include "jsxdrapi.h" -#include "vm/Debugger.h" - -#include "jsobjinlines.h" - -using namespace mozilla; -using namespace js; - -#ifdef DEBUG -#define DBG(x) x -#else -#define DBG(x) ((void)0) -#endif - -typedef struct JSXDRMemState { - JSXDRState state; - char *base; - uint32_t count; - uint32_t limit; -} JSXDRMemState; - -#define MEM_BLOCK 8192 -#define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr)) - -#define MEM_BASE(xdr) (MEM_PRIV(xdr)->base) -#define MEM_COUNT(xdr) (MEM_PRIV(xdr)->count) -#define MEM_LIMIT(xdr) (MEM_PRIV(xdr)->limit) - -#define MEM_LEFT(xdr, bytes) \ - JS_BEGIN_MACRO \ - if ((xdr)->mode == JSXDR_DECODE && \ - MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ - JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL, \ - JSMSG_END_OF_DATA); \ - return 0; \ - } \ - JS_END_MACRO - -#define MEM_NEED(xdr, bytes) \ - JS_BEGIN_MACRO \ - if ((xdr)->mode == JSXDR_ENCODE) { \ - if (MEM_LIMIT(xdr) && \ - MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ - uint32_t limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ - void *data_ = (xdr)->cx->realloc_(MEM_BASE(xdr), limit_); \ - if (!data_) \ - return 0; \ - MEM_BASE(xdr) = (char *) data_; \ - MEM_LIMIT(xdr) = limit_; \ - } \ - } else { \ - MEM_LEFT(xdr, bytes); \ - } \ - JS_END_MACRO - -#define MEM_DATA(xdr) ((void *)(MEM_BASE(xdr) + MEM_COUNT(xdr))) -#define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes)) - -static JSBool -mem_get32(JSXDRState *xdr, uint32_t *lp) -{ - MEM_LEFT(xdr, 4); - *lp = *(uint32_t *)MEM_DATA(xdr); - MEM_INCR(xdr, 4); - return JS_TRUE; -} - -static JSBool -mem_set32(JSXDRState *xdr, uint32_t *lp) -{ - MEM_NEED(xdr, 4); - *(uint32_t *)MEM_DATA(xdr) = *lp; - MEM_INCR(xdr, 4); - return JS_TRUE; -} - -static JSBool -mem_getbytes(JSXDRState *xdr, char *bytes, uint32_t len) -{ - MEM_LEFT(xdr, len); - js_memcpy(bytes, MEM_DATA(xdr), len); - MEM_INCR(xdr, len); - return JS_TRUE; -} - -static JSBool -mem_setbytes(JSXDRState *xdr, char *bytes, uint32_t len) -{ - MEM_NEED(xdr, len); - js_memcpy(MEM_DATA(xdr), bytes, len); - MEM_INCR(xdr, len); - return JS_TRUE; -} - -static void * -mem_raw(JSXDRState *xdr, uint32_t len) -{ - void *data; - if (xdr->mode == JSXDR_ENCODE) { - MEM_NEED(xdr, len); - } else if (xdr->mode == JSXDR_DECODE) { - MEM_LEFT(xdr, len); - } - data = MEM_DATA(xdr); - MEM_INCR(xdr, len); - return data; -} - -static JSBool -mem_seek(JSXDRState *xdr, int32_t offset, JSXDRWhence whence) -{ - switch (whence) { - case JSXDR_SEEK_CUR: - if ((int32_t)MEM_COUNT(xdr) + offset < 0) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_SEEK_BEYOND_START); - return JS_FALSE; - } - if (offset > 0) - MEM_NEED(xdr, offset); - MEM_COUNT(xdr) += offset; - return JS_TRUE; - case JSXDR_SEEK_SET: - if (offset < 0) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_SEEK_BEYOND_START); - return JS_FALSE; - } - if (xdr->mode == JSXDR_ENCODE) { - if ((uint32_t)offset > MEM_COUNT(xdr)) - MEM_NEED(xdr, offset - MEM_COUNT(xdr)); - MEM_COUNT(xdr) = offset; - } else { - if ((uint32_t)offset > MEM_LIMIT(xdr)) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_SEEK_BEYOND_END); - return JS_FALSE; - } - MEM_COUNT(xdr) = offset; - } - return JS_TRUE; - case JSXDR_SEEK_END: - if (offset >= 0 || - xdr->mode == JSXDR_ENCODE || - (int32_t)MEM_LIMIT(xdr) + offset < 0) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_END_SEEK); - return JS_FALSE; - } - MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset; - return JS_TRUE; - default: { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%d", whence); - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_WHITHER_WHENCE, numBuf); - return JS_FALSE; - } - } -} - -static uint32_t -mem_tell(JSXDRState *xdr) -{ - return MEM_COUNT(xdr); -} - -static void -mem_finalize(JSXDRState *xdr) -{ - xdr->cx->free_(MEM_BASE(xdr)); -} - -static JSXDROps xdrmem_ops = { - mem_get32, mem_set32, mem_getbytes, mem_setbytes, - mem_raw, mem_seek, mem_tell, mem_finalize -}; - -JS_PUBLIC_API(void) -JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx) -{ - xdr->mode = mode; - xdr->cx = cx; - xdr->sharedFilename = NULL; - xdr->principals = NULL; - xdr->originPrincipals = NULL; -} - -JS_PUBLIC_API(JSXDRState *) -JS_XDRNewMem(JSContext *cx, JSXDRMode mode) -{ - JSXDRState *xdr = (JSXDRState *) cx->malloc_(sizeof(JSXDRMemState)); - if (!xdr) - return NULL; - JS_XDRInitBase(xdr, mode, cx); - if (mode == JSXDR_ENCODE) { - if (!(MEM_BASE(xdr) = (char *) cx->malloc_(MEM_BLOCK))) { - cx->free_(xdr); - return NULL; - } - } else { - /* XXXbe ok, so better not deref MEM_BASE(xdr) if not ENCODE */ - MEM_BASE(xdr) = NULL; - } - xdr->ops = &xdrmem_ops; - MEM_COUNT(xdr) = 0; - MEM_LIMIT(xdr) = MEM_BLOCK; - return xdr; -} - -JS_PUBLIC_API(void *) -JS_XDRMemGetData(JSXDRState *xdr, uint32_t *lp) -{ - if (xdr->ops != &xdrmem_ops) - return NULL; - *lp = MEM_COUNT(xdr); - return MEM_BASE(xdr); -} - -JS_PUBLIC_API(void) -JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32_t len) -{ - if (xdr->ops != &xdrmem_ops) - return; - MEM_LIMIT(xdr) = len; - MEM_BASE(xdr) = (char *) data; - MEM_COUNT(xdr) = 0; -} - -JS_PUBLIC_API(uint32_t) -JS_XDRMemDataLeft(JSXDRState *xdr) -{ - if (xdr->ops != &xdrmem_ops) - return 0; - return MEM_LIMIT(xdr) - MEM_COUNT(xdr); -} - -JS_PUBLIC_API(void) -JS_XDRMemResetData(JSXDRState *xdr) -{ - if (xdr->ops != &xdrmem_ops) - return; - MEM_COUNT(xdr) = 0; -} - -JS_PUBLIC_API(void) -JS_XDRDestroy(JSXDRState *xdr) -{ - JSContext *cx = xdr->cx; - xdr->ops->finalize(xdr); - cx->free_(xdr); -} - -JS_PUBLIC_API(void) -JS_XDRSetPrincipals(JSXDRState *xdr, JSPrincipals *principals, JSPrincipals *originPrincipals) -{ - JS_ASSERT(xdr->mode == JSXDR_DECODE); - xdr->principals = principals; - xdr->originPrincipals = JSScript::normalizeOriginPrincipals(principals, originPrincipals); -} - -JS_PUBLIC_API(JSBool) -JS_XDRUint8(JSXDRState *xdr, uint8_t *b) -{ - uint32_t l = *b; - if (!JS_XDRUint32(xdr, &l)) - return JS_FALSE; - *b = (uint8_t) l; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRUint16(JSXDRState *xdr, uint16_t *s) -{ - uint32_t l = *s; - if (!JS_XDRUint32(xdr, &l)) - return JS_FALSE; - *s = (uint16_t) l; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRUint32(JSXDRState *xdr, uint32_t *lp) -{ - JSBool ok = JS_TRUE; - if (xdr->mode == JSXDR_ENCODE) { - uint32_t xl = JSXDR_SWAB32(*lp); - ok = xdr->ops->set32(xdr, &xl); - } else if (xdr->mode == JSXDR_DECODE) { - ok = xdr->ops->get32(xdr, lp); - *lp = JSXDR_SWAB32(*lp); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32_t len) -{ - uint32_t padlen; - static char padbuf[JSXDR_ALIGN-1]; - - if (xdr->mode == JSXDR_ENCODE) { - if (!xdr->ops->setbytes(xdr, bytes, len)) - return JS_FALSE; - } else { - if (!xdr->ops->getbytes(xdr, bytes, len)) - return JS_FALSE; - } - len = xdr->ops->tell(xdr); - if (len % JSXDR_ALIGN) { - padlen = JSXDR_ALIGN - (len % JSXDR_ALIGN); - if (xdr->mode == JSXDR_ENCODE) { - if (!xdr->ops->setbytes(xdr, padbuf, padlen)) - return JS_FALSE; - } else { - if (!xdr->ops->seek(xdr, padlen, JSXDR_SEEK_CUR)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -/** - * Convert between a C string and the XDR representation: - * leading 32-bit count, then counted vector of chars, - * then possibly \0 padding to multiple of 4. - */ -JS_PUBLIC_API(JSBool) -JS_XDRCString(JSXDRState *xdr, char **sp) -{ - uint32_t len; - - if (xdr->mode == JSXDR_ENCODE) - len = strlen(*sp); - JS_XDRUint32(xdr, &len); - if (xdr->mode == JSXDR_DECODE) { - if (!(*sp = (char *) xdr->cx->malloc_(len + 1))) - return JS_FALSE; - } - if (!JS_XDRBytes(xdr, *sp, len)) { - if (xdr->mode == JSXDR_DECODE) - xdr->cx->free_(*sp); - return JS_FALSE; - } - if (xdr->mode == JSXDR_DECODE) { - (*sp)[len] = '\0'; - } - return JS_TRUE; -} - -static JSBool -XDRChars(JSXDRState *xdr, jschar *chars, uint32_t nchars) -{ - uint32_t i, padlen, nbytes; - jschar *raw; - - nbytes = nchars * sizeof(jschar); - padlen = nbytes % JSXDR_ALIGN; - if (padlen) { - padlen = JSXDR_ALIGN - padlen; - nbytes += padlen; - } - if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) - return JS_FALSE; - if (xdr->mode == JSXDR_ENCODE) { - for (i = 0; i != nchars; i++) - raw[i] = JSXDR_SWAB16(chars[i]); - if (padlen) - memset((char *)raw + nbytes - padlen, 0, padlen); - } else if (xdr->mode == JSXDR_DECODE) { - for (i = 0; i != nchars; i++) - chars[i] = JSXDR_SWAB16(raw[i]); - } - return JS_TRUE; -} - -/* - * Convert between a JS (Unicode) string and the XDR representation. - */ -JS_PUBLIC_API(JSBool) -JS_XDRString(JSXDRState *xdr, JSString **strp) -{ - uint32_t nchars; - jschar *chars; - - if (xdr->mode == JSXDR_ENCODE) - nchars = (*strp)->length(); - if (!JS_XDRUint32(xdr, &nchars)) - return JS_FALSE; - - if (xdr->mode == JSXDR_DECODE) - chars = (jschar *) xdr->cx->malloc_((nchars + 1) * sizeof(jschar)); - else - chars = const_cast((*strp)->getChars(xdr->cx)); - if (!chars) - return JS_FALSE; - - if (!XDRChars(xdr, chars, nchars)) - goto bad; - if (xdr->mode == JSXDR_DECODE) { - chars[nchars] = 0; - *strp = JS_NewUCString(xdr->cx, chars, nchars); - if (!*strp) - goto bad; - } - return JS_TRUE; - -bad: - if (xdr->mode == JSXDR_DECODE) - xdr->cx->free_(chars); - return JS_FALSE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp) -{ - uint32_t null = (*strp == NULL); - if (!JS_XDRUint32(xdr, &null)) - return JS_FALSE; - if (null) { - *strp = NULL; - return JS_TRUE; - } - return JS_XDRString(xdr, strp); -} - -JS_PUBLIC_API(JSBool) -JS_XDRDouble(JSXDRState *xdr, double *dp) -{ - jsdpun u; - - u.d = (xdr->mode == JSXDR_ENCODE) ? *dp : 0.0; - if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi)) - return false; - if (xdr->mode == JSXDR_DECODE) - *dp = u.d; - return true; -} - -extern JSBool -js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) -{ - JSString *str; - uint32_t nchars; - JSAtom *atom; - JSContext *cx; - jschar *chars; - jschar stackChars[256]; - - if (xdr->mode == JSXDR_ENCODE) { - str = *atomp; - return JS_XDRString(xdr, &str); - } - - /* - * Inline JS_XDRString when decoding to avoid JSString allocation - * for already existing atoms. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &nchars)) - return JS_FALSE; - atom = NULL; - cx = xdr->cx; - if (nchars <= ArrayLength(stackChars)) { - chars = stackChars; - } else { - /* - * This is very uncommon. Don't use the tempLifoAlloc arena for this as - * most allocations here will be bigger than tempLifoAlloc's default - * chunk size. - */ - chars = (jschar *) cx->malloc_(nchars * sizeof(jschar)); - if (!chars) - return JS_FALSE; - } - - if (XDRChars(xdr, chars, nchars)) - atom = js_AtomizeChars(cx, chars, nchars); - if (chars != stackChars) - cx->free_(chars); - - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) -{ - return XDRFunctionObject(xdr, objp); -} - -JS_PUBLIC_API(JSBool) -JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) -{ - JSScript *script; - uint32_t magic; - uint32_t bytecodeVer; - if (xdr->mode == JSXDR_DECODE) { - script = NULL; - *scriptp = NULL; - } else { - script = *scriptp; - magic = JSXDR_MAGIC_SCRIPT_CURRENT; - bytecodeVer = JSXDR_BYTECODE_VERSION; - } - - if (!JS_XDRUint32(xdr, &magic)) - return false; - if (!JS_XDRUint32(xdr, &bytecodeVer)) - return false; - - if (magic != JSXDR_MAGIC_SCRIPT_CURRENT || - bytecodeVer != JSXDR_BYTECODE_VERSION) { - /* We do not provide binary compatibility with older scripts. */ - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_BAD_SCRIPT_MAGIC); - return false; - } - - if (!XDRScript(xdr, &script)) - return false; - - if (xdr->mode == JSXDR_DECODE) { - JS_ASSERT(!script->compileAndGo); - script->globalObject = GetCurrentGlobal(xdr->cx); - js_CallNewScriptHook(xdr->cx, script, NULL); - Debugger::onNewScript(xdr->cx, script, NULL); - *scriptp = script; - } - - return true; -} - -#endif /* JS_HAS_XDR */ diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h deleted file mode 100644 index ac5391c7a51..00000000000 --- a/js/src/jsxdrapi.h +++ /dev/null @@ -1,224 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsxdrapi_h___ -#define jsxdrapi_h___ - -/* - * JS external data representation interface API. - * - * The XDR system is comprised of three major parts: - * - * - the state serialization/deserialization APIs, which allow consumers - * of the API to serialize JS runtime state (script bytecodes, atom maps, - * object graphs, etc.) for later restoration. These portions - * are implemented in various appropriate files, such as jsscript.c - * for the script portions and jsobj.c for object state. - * - the callback APIs through which the runtime requests an opaque - * representation of a native object, and through which the runtime - * constructs a live native object from an opaque representation. These - * portions are the responsibility of the native object implementor. - * - utility functions for en/decoding of primitive types, such as - * JSStrings. This portion is implemented in jsxdrapi.c. - * - * Spiritually guided by Sun's XDR, where appropriate. - */ - -#include "jspubtd.h" -#include "jsprvtd.h" - -JS_BEGIN_EXTERN_C - -/* We use little-endian byteorder for all encoded data */ - -#if defined IS_LITTLE_ENDIAN -#define JSXDR_SWAB32(x) x -#define JSXDR_SWAB16(x) x -#elif defined IS_BIG_ENDIAN -#define JSXDR_SWAB32(x) (((uint32_t)(x) >> 24) | \ - (((uint32_t)(x) >> 8) & 0xff00) | \ - (((uint32_t)(x) << 8) & 0xff0000) | \ - ((uint32_t)(x) << 24)) -#define JSXDR_SWAB16(x) (((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8)) -#else -#error "unknown byte order" -#endif - -#define JSXDR_ALIGN 4 - -typedef enum JSXDRMode { - JSXDR_ENCODE, - JSXDR_DECODE -} JSXDRMode; - -typedef enum JSXDRWhence { - JSXDR_SEEK_SET, - JSXDR_SEEK_CUR, - JSXDR_SEEK_END -} JSXDRWhence; - -typedef struct JSXDROps { - JSBool (*get32)(JSXDRState *, uint32_t *); - JSBool (*set32)(JSXDRState *, uint32_t *); - JSBool (*getbytes)(JSXDRState *, char *, uint32_t); - JSBool (*setbytes)(JSXDRState *, char *, uint32_t); - void * (*raw)(JSXDRState *, uint32_t); - JSBool (*seek)(JSXDRState *, int32_t, JSXDRWhence); - uint32_t (*tell)(JSXDRState *); - void (*finalize)(JSXDRState *); -} JSXDROps; - -struct JSXDRState { - JSXDRMode mode; - JSXDROps *ops; - JSContext *cx; - const char *sharedFilename; - JSPrincipals *principals; - JSPrincipals *originPrincipals; -}; - -extern JS_PUBLIC_API(void) -JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx); - -extern JS_PUBLIC_API(JSXDRState *) -JS_XDRNewMem(JSContext *cx, JSXDRMode mode); - -extern JS_PUBLIC_API(void *) -JS_XDRMemGetData(JSXDRState *xdr, uint32_t *lp); - -extern JS_PUBLIC_API(void) -JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32_t len); - -extern JS_PUBLIC_API(uint32_t) -JS_XDRMemDataLeft(JSXDRState *xdr); - -extern JS_PUBLIC_API(void) -JS_XDRMemResetData(JSXDRState *xdr); - -extern JS_PUBLIC_API(void) -JS_XDRDestroy(JSXDRState *xdr); - -/* - * Set principals that should be assigned to decoded scripts and functions. - * The principals is not held via JS_HoldPrincipals/JS_DropPrincipals unless - * they are stored in a decoded script. Thus the caller must either ensure - * that the principals outlive the XDR instance or are explicitly set to NULL - * before they release by the caller. - */ -extern JS_PUBLIC_API(void) -JS_XDRSetPrincipals(JSXDRState *xdr, JSPrincipals *principals, JSPrincipals *originPrincipals); - -extern JS_PUBLIC_API(JSBool) -JS_XDRUint8(JSXDRState *xdr, uint8_t *b); - -extern JS_PUBLIC_API(JSBool) -JS_XDRUint16(JSXDRState *xdr, uint16_t *s); - -extern JS_PUBLIC_API(JSBool) -JS_XDRUint32(JSXDRState *xdr, uint32_t *lp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32_t len); - -extern JS_PUBLIC_API(JSBool) -JS_XDRCString(JSXDRState *xdr, char **sp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRString(JSXDRState *xdr, JSString **strp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRDouble(JSXDRState *xdr, double *dp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRFunctionObject(JSXDRState *xdr, JSObject **objp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRScript(JSXDRState *xdr, JSScript **scriptp); - -/* - * Magic numbers. - */ -#define JSXDR_MAGIC_SCRIPT_1 0xdead0001 -#define JSXDR_MAGIC_SCRIPT_2 0xdead0002 -#define JSXDR_MAGIC_SCRIPT_3 0xdead0003 -#define JSXDR_MAGIC_SCRIPT_4 0xdead0004 -#define JSXDR_MAGIC_SCRIPT_5 0xdead0005 -#define JSXDR_MAGIC_SCRIPT_6 0xdead0006 -#define JSXDR_MAGIC_SCRIPT_7 0xdead0007 -#define JSXDR_MAGIC_SCRIPT_8 0xdead0008 -#define JSXDR_MAGIC_SCRIPT_9 0xdead0009 -#define JSXDR_MAGIC_SCRIPT_10 0xdead000a -#define JSXDR_MAGIC_SCRIPT_11 0xdead000b -#define JSXDR_MAGIC_SCRIPT_12 0xdead000c -#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_12 - -/* - * Bytecode version number. Increment the subtrahend whenever JS bytecode - * changes incompatibly. - * - * This version number is XDR'd near the front of xdr bytecode and - * aborts deserialization if there is a mismatch between the current - * and saved versions. If deserialization fails, the data should be - * invalidated if possible. - */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 111) - -JS_END_EXTERN_C - -/* - * Library-private functions. - */ -extern JSBool -js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); - -/* - * Set principals that should be assigned to decoded scripts and functions. - * The principals is not held via JS_HoldPrincipals/JS_DropPrincipals unless - * they are stored in a decoded script. Thus the caller must either ensure - * that principal outlive the XDR instance or are explicitly set to NULL - * before they release by the caller. - */ -extern void -js_XDRSetPrincipals(JSXDRState *xdr, JSPrincipals *principals, JSPrincipals *originPrincipals); - -#endif /* ! jsxdrapi_h___ */ diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index d649a8e041b..7f80c1eb118 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -45,6 +45,7 @@ #include "vm/GlobalObject.h" #include "vm/MethodGuard.h" #include "vm/Stack.h" +#include "vm/Xdr.h" #include "jsobjinlines.h" diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index cca3ada40e1..dec36ff61cb 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -42,6 +42,7 @@ #include "vm/MatchPairs.h" #include "vm/RegExpStatics.h" #include "vm/StringBuffer.h" +#include "vm/Xdr.h" #include "jsobjinlines.h" @@ -746,36 +747,38 @@ js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut) return true; } -#if JS_HAS_XDR -# include "jsxdrapi.h" - +template bool -js::XDRScriptRegExpObject(JSXDRState *xdr, HeapPtrObject *objp) +js::XDRScriptRegExpObject(XDRState *xdr, HeapPtrObject *objp) { JSAtom *source = 0; uint32_t flagsword = 0; - if (xdr->mode == JSXDR_ENCODE) { + if (mode == XDR_ENCODE) { JS_ASSERT(objp); RegExpObject &reobj = (*objp)->asRegExp(); source = reobj.getSource(); flagsword = reobj.getFlags(); } - if (!js_XDRAtom(xdr, &source) || !JS_XDRUint32(xdr, &flagsword)) + if (!XDRAtom(xdr, &source) || !xdr->codeUint32(&flagsword)) return false; - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { RegExpFlag flags = RegExpFlag(flagsword); - RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, source, flags, NULL); + RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx(), source, flags, NULL); if (!reobj) return false; - if (!reobj->clearParent(xdr->cx)) + if (!reobj->clearParent(xdr->cx())) return false; - if (!reobj->clearType(xdr->cx)) + if (!reobj->clearType(xdr->cx())) return false; objp->init(reobj); } return true; } -#endif /* !JS_HAS_XDR */ +template bool +js::XDRScriptRegExpObject(XDRState *xdr, HeapPtrObject *objp); + +template bool +js::XDRScriptRegExpObject(XDRState *xdr, HeapPtrObject *objp); diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index ff888f0a4e2..901c73f4376 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -469,8 +469,9 @@ ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut); inline bool RegExpToShared(JSContext *cx, JSObject &obj, RegExpGuard *g); +template bool -XDRScriptRegExpObject(JSXDRState *xdr, HeapPtrObject *objp); +XDRScriptRegExpObject(XDRState *xdr, HeapPtrObject *objp); } /* namespace js */ diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 0b9bf89c1f7..051c9725899 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -42,12 +42,10 @@ #include "jscompartment.h" #include "jsiter.h" #include "jsscope.h" -#if JS_HAS_XDR -#include "jsxdrapi.h" -#endif #include "GlobalObject.h" #include "ScopeObject.h" +#include "Xdr.h" #include "jsatominlines.h" #include "jsobjinlines.h" @@ -936,8 +934,6 @@ Class js::BlockClass = { JS_ConvertStub }; -#if JS_HAS_XDR - #define NO_PARENT_INDEX UINT32_MAX static uint32_t @@ -957,16 +953,17 @@ FindObjectIndex(JSObjectArray *array, JSObject *obj) return NO_PARENT_INDEX; } +template bool -js::XDRStaticBlockObject(JSXDRState *xdr, JSScript *script, StaticBlockObject **objp) +js::XDRStaticBlockObject(XDRState *xdr, JSScript *script, StaticBlockObject **objp) { - JSContext *cx = xdr->cx; + JSContext *cx = xdr->cx(); StaticBlockObject *obj = NULL; uint32_t parentId = 0; uint32_t count = 0; uint32_t depthAndCount = 0; - if (xdr->mode == JSXDR_ENCODE) { + if (mode == XDR_ENCODE) { obj = *objp; parentId = JSScript::isValidOffset(script->objectsOffset) ? FindObjectIndex(script->objects(), obj->enclosingBlock()) @@ -979,10 +976,10 @@ js::XDRStaticBlockObject(JSXDRState *xdr, JSScript *script, StaticBlockObject ** } /* First, XDR the parent atomid. */ - if (!JS_XDRUint32(xdr, &parentId)) + if (!xdr->codeUint32(&parentId)) return false; - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { obj = StaticBlockObject::create(cx); if (!obj) return false; @@ -1000,10 +997,10 @@ js::XDRStaticBlockObject(JSXDRState *xdr, JSScript *script, StaticBlockObject ** AutoObjectRooter tvr(cx, obj); - if (!JS_XDRUint32(xdr, &depthAndCount)) + if (!xdr->codeUint32(&depthAndCount)) return false; - if (xdr->mode == JSXDR_DECODE) { + if (mode == XDR_DECODE) { uint32_t depth = uint16_t(depthAndCount >> 16); count = uint16_t(depthAndCount); obj->setStackDepth(depth); @@ -1014,7 +1011,7 @@ js::XDRStaticBlockObject(JSXDRState *xdr, JSScript *script, StaticBlockObject ** */ for (unsigned i = 0; i < count; i++) { JSAtom *atom; - if (!js_XDRAtom(xdr, &atom)) + if (!XDRAtom(xdr, &atom)) return false; /* The empty string indicates an int id. */ @@ -1054,11 +1051,15 @@ js::XDRStaticBlockObject(JSXDRState *xdr, JSScript *script, StaticBlockObject ** ? JSID_TO_ATOM(propid) : cx->runtime->emptyString; - if (!js_XDRAtom(xdr, &atom)) + if (!XDRAtom(xdr, &atom)) return false; } } return true; } -#endif /* JS_HAS_XDR */ +template bool +js::XDRStaticBlockObject(XDRState *xdr, JSScript *script, StaticBlockObject **objp); + +template bool +js::XDRStaticBlockObject(XDRState *xdr, JSScript *script, StaticBlockObject **objp); diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 4e3ea4ec085..f1937a9a3e3 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -276,8 +276,9 @@ class ClonedBlockObject : public BlockObject bool containsVar(PropertyName *name, Value *vp, JSContext *cx); }; -extern bool -XDRStaticBlockObject(JSXDRState *xdr, JSScript *script, StaticBlockObject **objp); +template +bool +XDRStaticBlockObject(XDRState *xdr, JSScript *script, StaticBlockObject **objp); } /* namespace js */ diff --git a/js/src/vm/Xdr.cpp b/js/src/vm/Xdr.cpp new file mode 100644 index 00000000000..9e75bc1f73a --- /dev/null +++ b/js/src/vm/Xdr.cpp @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "mozilla/Util.h" + +#include "jsversion.h" + +#include +#include "jstypes.h" +#include "jsutil.h" +#include "jsdhash.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jscntxt.h" +#include "jsnum.h" +#include "jsscript.h" +#include "jsstr.h" + +#include "Xdr.h" +#include "Debugger.h" + +#include "jsobjinlines.h" + +using namespace mozilla; +using namespace js; + +namespace js { + +void +XDRBuffer::freeBuffer() +{ + Foreground::free_(base); +#ifdef DEBUG + memset(this, 0xe2, sizeof *this); +#endif +} + +bool +XDRBuffer::grow(size_t n) +{ + JS_ASSERT(n > size_t(limit - cursor)); + + const size_t MEM_BLOCK = 8192; + size_t offset = cursor - base; + size_t newCapacity = JS_ROUNDUP(offset + n, MEM_BLOCK); + if (isUint32Overflow(newCapacity)) { + JS_ReportErrorNumber(cx(), js_GetErrorMessage, NULL, JSMSG_TOO_BIG_TO_ENCODE); + return false; + } + + void *data = OffTheBooks::realloc_(base, newCapacity); + if (!data) { + js_ReportOutOfMemory(cx()); + return false; + } + base = static_cast(data); + cursor = base + offset; + limit = base + newCapacity; + return true; +} + +template +bool +XDRState::codeChars(jschar *chars, size_t nchars) +{ + size_t nbytes = nchars * sizeof(jschar); + if (mode == XDR_ENCODE) { + uint8_t *ptr = buf.write(nbytes); + if (!ptr) + return false; +#ifdef IS_LITTLE_ENDIAN + memcpy(ptr, chars, nbytes); +#else + for (size_t i = 0; i != nchars; i++) { + uint16_t tmp = NormalizeByteOrder16(chars[i]); + memcpy(ptr, &tmp, sizeof tmp); + ptr += sizeof tmp; + } +#endif + } else { + const uint8_t *ptr = buf.read(nbytes); +#ifdef IS_LITTLE_ENDIAN + memcpy(chars, ptr, nbytes); +#else + for (size_t i = 0; i != nchars; i++) { + uint16_t tmp; + memcpy(&tmp, ptr, sizeof tmp); + chars[i] = NormalizeByteOrder16(tmp); + ptr += sizeof tmp; + } +#endif + } + return true; +} + +/* + * Convert between a JS (Unicode) string and the XDR representation. + */ +template +bool +XDRState::codeString(JSString **strp) +{ + uint32_t nchars; + jschar *chars; + + if (mode == XDR_ENCODE) + nchars = (*strp)->length(); + if (!codeUint32(&nchars)) + return false; + + if (mode == XDR_DECODE) + chars = (jschar *) cx()->malloc_((nchars + 1) * sizeof(jschar)); + else + chars = const_cast((*strp)->getChars(cx())); + if (!chars) + return false; + + if (!codeChars(chars, nchars)) + goto bad; + if (mode == XDR_DECODE) { + chars[nchars] = 0; + *strp = JS_NewUCString(cx(), chars, nchars); + if (!*strp) + goto bad; + } + return true; + +bad: + if (mode == XDR_DECODE) + Foreground::free_(chars); + return false; +} + +template +static bool +VersionCheck(XDRState *xdr) +{ + uint32_t bytecodeVer; + if (mode == XDR_ENCODE) + bytecodeVer = XDR_BYTECODE_VERSION; + + if (!xdr->codeUint32(&bytecodeVer)) + return false; + + if (mode == XDR_DECODE && bytecodeVer != XDR_BYTECODE_VERSION) { + /* We do not provide binary compatibility with older scripts. */ + JS_ReportErrorNumber(xdr->cx(), js_GetErrorMessage, NULL, JSMSG_BAD_SCRIPT_MAGIC); + return false; + } + + return true; +} + +template +bool +XDRState::codeFunction(JSObject **objp) +{ + if (mode == XDR_DECODE) + *objp = NULL; + + return VersionCheck(this) && XDRInterpretedFunction(this, objp, NULL); +} + +template +bool +XDRState::codeScript(JSScript **scriptp) +{ + JSScript *script; + if (mode == XDR_DECODE) { + script = NULL; + *scriptp = NULL; + } else { + script = *scriptp; + } + + if (!VersionCheck(this) || !XDRScript(this, &script, NULL)) + return false; + + if (mode == XDR_DECODE) { + JS_ASSERT(!script->compileAndGo); + script->globalObject = GetCurrentGlobal(cx()); + js_CallNewScriptHook(cx(), script, NULL); + Debugger::onNewScript(cx(), script, NULL); + *scriptp = script; + } + + return true; +} + +XDRDecoder::XDRDecoder(JSContext *cx, const void *data, uint32_t length, + JSPrincipals *principals, JSPrincipals *originPrincipals) + : XDRState(cx) +{ + buf.setData(data, length); + this->principals = principals; + this->originPrincipals = JSScript::normalizeOriginPrincipals(principals, originPrincipals); +} + +template class XDRState; +template class XDRState; + +} /* namespace js */ + diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h new file mode 100644 index 00000000000..3b0276fd284 --- /dev/null +++ b/js/src/vm/Xdr.h @@ -0,0 +1,322 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey string object code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef Xdr_h___ +#define Xdr_h___ + +#include "jsapi.h" +#include "jsprvtd.h" +#include "jsnum.h" + +namespace js { + +/* + * Bytecode version number. Increment the subtrahend whenever JS bytecode + * changes incompatibly. + * + * This version number is XDR'd near the front of xdr bytecode and + * aborts deserialization if there is a mismatch between the current + * and saved versions. If deserialization fails, the data should be + * invalidated if possible. + */ +static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 112); + +class XDRBuffer { + public: + XDRBuffer(JSContext *cx) + : context(cx), base(NULL), cursor(NULL), limit(NULL) { } + + JSContext *cx() const { + return context; + } + + void *getData(uint32_t *lengthp) const { + JS_ASSERT(size_t(cursor - base) <= size_t(UINT32_MAX)); + *lengthp = uint32_t(cursor - base); + return base; + } + + void setData(const void *data, uint32_t length) { + base = static_cast(const_cast(data)); + cursor = base; + limit = base + length; + } + + const uint8_t *read(size_t n) { + JS_ASSERT(n <= size_t(limit - cursor)); + uint8_t *ptr = cursor; + cursor += n; + return ptr; + } + + const char *readCString() { + char *ptr = reinterpret_cast(cursor); + cursor = reinterpret_cast(strchr(ptr, '\0')) + 1; + JS_ASSERT(base < cursor); + JS_ASSERT(cursor <= limit); + return ptr; + } + + uint8_t *write(size_t n) { + if (n > size_t(limit - cursor)) { + if (!grow(n)) + return NULL; + } + uint8_t *ptr = cursor; + cursor += n; + return ptr; + } + + static bool isUint32Overflow(size_t n) { + return size_t(-1) > size_t(UINT32_MAX) && n > size_t(UINT32_MAX); + } + + void freeBuffer(); + + private: + bool grow(size_t n); + + JSContext *const context; + uint8_t *base; + uint8_t *cursor; + uint8_t *limit; +}; + +/* We use little-endian byteorder for all encoded data */ + +#if defined IS_LITTLE_ENDIAN + +inline uint32_t +NormalizeByteOrder32(uint32_t x) +{ + return x; +} + +inline uint16_t +NormalizeByteOrder16(uint16_t x) +{ + return x; +} + +#elif defined IS_BIG_ENDIAN + +inline uint32_t +NormalizeByteOrder32(uint32_t x) +{ + return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); +} + +inline uint16_t +NormalizeByteOrder16(uint16_t x) +{ + return (x >> 8) | (x << 8); +} + +#else +#error "unknown byte order" +#endif + +template +class XDRState { + public: + XDRBuffer buf; + + protected: + JSPrincipals *principals; + JSPrincipals *originPrincipals; + + XDRState(JSContext *cx) + : buf(cx), principals(NULL), originPrincipals(NULL) { + } + + public: + JSContext *cx() const { + return buf.cx(); + } + + bool codeUint8(uint8_t *n) { + if (mode == XDR_ENCODE) { + uint8_t *ptr = buf.write(sizeof *n); + if (!ptr) + return false; + *ptr = *n; + } else { + *n = *buf.read(sizeof *n); + } + return true; + } + + bool codeUint16(uint16_t *n) { + uint16_t tmp; + if (mode == XDR_ENCODE) { + uint8_t *ptr = buf.write(sizeof tmp); + if (!ptr) + return false; + tmp = NormalizeByteOrder16(*n); + memcpy(ptr, &tmp, sizeof tmp); + } else { + memcpy(&tmp, buf.read(sizeof tmp), sizeof tmp); + *n = NormalizeByteOrder16(tmp); + } + return true; + } + + bool codeUint32(uint32_t *n) { + uint32_t tmp; + if (mode == XDR_ENCODE) { + uint8_t *ptr = buf.write(sizeof tmp); + if (!ptr) + return false; + tmp = NormalizeByteOrder32(*n); + memcpy(ptr, &tmp, sizeof tmp); + } else { + memcpy(&tmp, buf.read(sizeof tmp), sizeof tmp); + *n = NormalizeByteOrder32(tmp); + } + return true; + } + + bool codeDouble(double *dp) { + jsdpun tmp; + if (mode == XDR_ENCODE) { + uint8_t *ptr = buf.write(sizeof tmp); + if (!ptr) + return false; + tmp.d = *dp; + tmp.s.lo = NormalizeByteOrder32(tmp.s.lo); + tmp.s.hi = NormalizeByteOrder32(tmp.s.hi); + memcpy(ptr, &tmp.s.lo, sizeof tmp.s.lo); + memcpy(ptr + sizeof tmp.s.lo, &tmp.s.hi, sizeof tmp.s.hi); + } else { + const uint8_t *ptr = buf.read(sizeof tmp); + memcpy(&tmp.s.lo, ptr, sizeof tmp.s.lo); + memcpy(&tmp.s.hi, ptr + sizeof tmp.s.lo, sizeof tmp.s.hi); + tmp.s.lo = NormalizeByteOrder32(tmp.s.lo); + tmp.s.hi = NormalizeByteOrder32(tmp.s.hi); + *dp = tmp.d; + } + return true; + } + + bool codeBytes(void *bytes, size_t len) { + if (mode == XDR_ENCODE) { + uint8_t *ptr = buf.write(len); + if (!ptr) + return false; + memcpy(ptr, bytes, len); + } else { + memcpy(bytes, buf.read(len), len); + } + return true; + } + + /* + * During encoding the string is written into the buffer together with its + * terminating '\0'. During decoding the method returns a pointer into the + * decoding buffer and the caller must copy the string if it will outlive + * the decoding buffer. + */ + bool codeCString(const char **sp) { + if (mode == XDR_ENCODE) { + size_t n = strlen(*sp) + 1; + uint8_t *ptr = buf.write(n); + if (!ptr) + return false; + memcpy(ptr, *sp, n); + } else { + *sp = buf.readCString(); + } + return true; + } + + bool codeChars(jschar *chars, size_t nchars); + bool codeString(JSString **strp); + + bool codeFunction(JSObject **objp); + bool codeScript(JSScript **scriptp); + + void initScriptPrincipals(JSScript *script) { + JS_ASSERT(mode == XDR_DECODE); + + /* The origin principals must be normalized at this point. */ + JS_ASSERT_IF(principals, originPrincipals); + JS_ASSERT(!script->principals); + JS_ASSERT(!script->originPrincipals); + if (principals) { + script->principals = principals; + JS_HoldPrincipals(principals); + } + if (originPrincipals) { + script->originPrincipals = originPrincipals; + JS_HoldPrincipals(originPrincipals); + } + } +}; + +class XDREncoder : public XDRState { + public: + XDREncoder(JSContext *cx) + : XDRState(cx) { + } + + ~XDREncoder() { + buf.freeBuffer(); + } + + const void *getData(uint32_t *lengthp) const { + return buf.getData(lengthp); + } + + void *forgetData(uint32_t *lengthp) { + void *data = buf.getData(lengthp); + buf.setData(NULL, 0); + return data; + } +}; + +class XDRDecoder : public XDRState { + public: + XDRDecoder(JSContext *cx, const void *data, uint32_t length, + JSPrincipals *principals, JSPrincipals *originPrincipals); + +}; + +} /* namespace js */ + +#endif /* Xdr_h___ */ diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index 7e5153927fd..8ba78ff7ea0 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -78,7 +78,6 @@ #include "nsIJARURI.h" #include "nsNetUtil.h" #include "nsDOMFile.h" -#include "jsxdrapi.h" #include "jsprf.h" #include "nsJSPrincipals.h" // For reporting errors with the console service diff --git a/js/xpconnect/loader/mozJSLoaderUtils.cpp b/js/xpconnect/loader/mozJSLoaderUtils.cpp index d73bc342c1b..d50722ff366 100644 --- a/js/xpconnect/loader/mozJSLoaderUtils.cpp +++ b/js/xpconnect/loader/mozJSLoaderUtils.cpp @@ -40,7 +40,6 @@ #include "jsapi.h" #include "jsdbgapi.h" -#include "jsxdrapi.h" #include "nsJSPrincipals.h" @@ -54,31 +53,20 @@ using namespace mozilla::scache; // principals to the system principals. nsresult ReadCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, - nsIPrincipal *systemPrincipal, JSScript **script) + nsIPrincipal *systemPrincipal, JSScript **scriptp) { nsAutoArrayPtr buf; PRUint32 len; nsresult rv = cache->GetBuffer(PromiseFlatCString(uri).get(), getter_Transfers(buf), &len); - if (NS_FAILED(rv)) { + if (NS_FAILED(rv)) return rv; // don't warn since NOT_AVAILABLE is an ok error - } - JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE); - if (!xdr) { + JSScript *script = JS_DecodeScript(cx, buf, len, nsJSPrincipals::get(systemPrincipal), nsnull); + if (!script) return NS_ERROR_OUT_OF_MEMORY; - } - - ::JS_XDRMemSetData(xdr, buf, len); - ::JS_XDRSetPrincipals(xdr, nsJSPrincipals::get(systemPrincipal), nsnull); - - JSBool ok = ::JS_XDRScript(xdr, script); - - // Prevent XDR from automatically freeing the buffer. - ::JS_XDRMemSetData(xdr, NULL, 0); - ::JS_XDRDestroy(xdr); - - return ok ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + *scriptp = script; + return NS_OK; } nsresult @@ -88,21 +76,13 @@ WriteCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, MOZ_ASSERT(JS_GetScriptPrincipals(script) == nsJSPrincipals::get(systemPrincipal)); MOZ_ASSERT(JS_GetScriptOriginPrincipals(script) == nsJSPrincipals::get(systemPrincipal)); - JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE); - if (!xdr) { + uint32_t size; + void *data = JS_EncodeScript(cx, script, &size); + if (!data) return NS_ERROR_OUT_OF_MEMORY; - } - nsresult rv; - if (!::JS_XDRScript(xdr, &script)) { - rv = NS_ERROR_OUT_OF_MEMORY; - } else { - uint32_t size; - char* data = static_cast(::JS_XDRMemGetData(xdr, &size)); - MOZ_ASSERT(size); - rv = cache->PutBuffer(PromiseFlatCString(uri).get(), data, size); - } - - ::JS_XDRDestroy(xdr); + MOZ_ASSERT(size); + nsresult rv = cache->PutBuffer(PromiseFlatCString(uri).get(), static_cast(data), size); + js_free(data); return rv; } diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 5be80a6e297..87db075d5d1 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -53,7 +53,6 @@ #include "jsatom.h" #include "jsfriendapi.h" #include "jsgc.h" -#include "jsxdrapi.h" #include "dom_quickstubs.h" #include "nsNullPrincipal.h" #include "nsIURI.h" @@ -2839,34 +2838,23 @@ WriteScriptOrFunction(nsIObjectOutputStream *stream, JSContext *cx, return rv; } - JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); - if (!xdr) - return NS_ERROR_OUT_OF_MEMORY; - - JSBool ok; + uint32_t size; + void* data; { JSAutoRequest ar(cx); if (functionObj) - ok = JS_XDRFunctionObject(xdr, &functionObj); + data = JS_EncodeInterpretedFunction(cx, functionObj, &size); else - ok = JS_XDRScript(xdr, &script); + data = JS_EncodeScript(cx, script, &size); } - if (!ok) { - rv = NS_ERROR_OUT_OF_MEMORY; - } else { - // Get the encoded JSXDRState data and write it. The JSXDRState owns - // this buffer memory and will free it beneath JS_XDRDestroy. - uint32_t size; - const char* data = reinterpret_cast(::JS_XDRMemGetData(xdr, &size)); - NS_ASSERTION(data, "no decoded JSXDRState data!"); - - rv = stream->Write32(size); - if (NS_SUCCEEDED(rv)) - rv = stream->WriteBytes(data, size); - } - - JS_XDRDestroy(xdr); + if (!data) + return NS_ERROR_OUT_OF_MEMORY; + MOZ_ASSERT(size); + rv = stream->Write32(size); + if (NS_SUCCEEDED(rv)) + rv = stream->WriteBytes(static_cast(data), size); + js_free(data); return rv; } @@ -2911,31 +2899,26 @@ ReadScriptOrFunction(nsIObjectInputStream *stream, JSContext *cx, if (NS_FAILED(rv)) return rv; - JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE); - if (!xdr) { - nsMemory::Free(data); - return NS_ERROR_OUT_OF_MEMORY; - } - - JS_XDRMemSetData(xdr, data, size); - JS_XDRSetPrincipals(xdr, principal, originPrincipal); - - JSBool ok; { JSAutoRequest ar(cx); - if (scriptp) - ok = JS_XDRScript(xdr, scriptp); - else - ok = JS_XDRFunctionObject(xdr, functionObjp); + if (scriptp) { + JSScript *script = JS_DecodeScript(cx, data, size, principal, originPrincipal); + if (!script) + rv = NS_ERROR_OUT_OF_MEMORY; + else + *scriptp = script; + } else { + JSObject *funobj = JS_DecodeInterpretedFunction(cx, data, size, + principal, originPrincipal); + if (!funobj) + rv = NS_ERROR_OUT_OF_MEMORY; + else + *functionObjp = funobj; + } } - // We cannot rely on XDR automatically freeing the data memory as we must - // use nsMemory::Free to release it. - JS_XDRMemSetData(xdr, NULL, 0); - JS_XDRDestroy(xdr); nsMemory::Free(data); - - return ok ? NS_OK : NS_ERROR_FAILURE; + return rv; } NS_IMETHODIMP