From 22ba09591c8415cd52ad86911bf033267d8f6828 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 1 Oct 2010 09:57:22 -0500 Subject: [PATCH 001/284] Bug 601040 - Revise StructuredClone API. r=gal. --- js/src/jsclone.cpp | 32 +++++++++++++++----------------- js/src/jspubtd.h | 21 ++++++++++----------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/js/src/jsclone.cpp b/js/src/jsclone.cpp index 13a8799f66ec..2239674e6862 100644 --- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -304,7 +304,7 @@ SCOutput::writeArray(const T *p, size_t nelems) js_ReportAllocationOverflow(context()); return false; } - uint64_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T)); + size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T)); size_t start = buf.length(); if (!buf.growByUninitialized(nwords)) return false; @@ -463,7 +463,7 @@ JSStructuredCloneWriter::startObject(JSObject *obj) if (p) { JSContext *cx = context(); const JSStructuredCloneCallbacks *cb = cx->runtime->structuredCloneCallbacks; - if (cb && cb->reportError) + if (cb) cb->reportError(cx, JS_SCERR_RECURSION); else JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SC_RECURSION); @@ -521,20 +521,14 @@ JSStructuredCloneWriter::startWrite(const js::Value &v) } else if (js_IsArrayBuffer(obj) && ArrayBuffer::fromJSObject(obj)) { return writeArrayBuffer(obj); } + + const JSStructuredCloneCallbacks *cb = context()->runtime->structuredCloneCallbacks; + if (cb) + return cb->write(context(), this, obj); /* else fall through */ } - /* - * v is either an object or some strange value like JSVAL_HOLE. Even in the - * latter case, we call the write op anyway, to let the application throw - * the NOT_SUPPORTED_ERR exception. - */ - JSContext *cx = context(); - JSRuntime *rt = cx->runtime; - if (rt->structuredCloneCallbacks) - return rt->structuredCloneCallbacks->write(cx, this, Jsvalify(v)); - - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SC_UNSUPPORTED_TYPE); + JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_UNSUPPORTED_TYPE); return false; } @@ -735,7 +729,7 @@ JSStructuredCloneReader::startRead(Value *vp) case SCTAG_ARRAY_BUFFER_OBJECT: return readArrayBuffer(data, vp); - default: + default: { if (tag <= SCTAG_FLOAT_MAX) { jsdouble d = ReinterpretPairAsDouble(tag, data); if (IsNonCanonicalizedNaN(d)) { @@ -750,13 +744,17 @@ JSStructuredCloneReader::startRead(Value *vp) if (SCTAG_TYPED_ARRAY_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_MAX) return readTypedArray(tag, data, vp); - JSRuntime *rt = context()->runtime; - if (!rt->structuredCloneCallbacks) { + const JSStructuredCloneCallbacks *cb = context()->runtime->structuredCloneCallbacks; + if (!cb) { JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "unsupported type"); return false; } - return rt->structuredCloneCallbacks->read(context(), this, tag, data, Jsvalify(vp)); + JSObject *obj = cb->read(context(), this, tag, data); + if (!obj) + return false; + vp->setObject(*obj); + } } return true; } diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 95ae6abba92e..8a8d1a170c6f 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -575,23 +575,22 @@ typedef JSBool * * tag and data are the pair of uint32 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. On success, it stores an object in *vp and returns JS_TRUE. + * the reader r. Return the new object on success, NULL on error/exception. */ -typedef JSBool (*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, - uint32 tag, uint32 data, jsval *vp); +typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, + uint32 tag, uint32 data); /* * Structured data serialization hook. The engine can write primitive values, - * Objects, Arrays, Dates, and RegExps. Any other type of object requires - * application support. This callback must first use the JS_WritePair 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. + * Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other + * type of object requires application support. This callback must first use + * the JS_WritePair 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. * - * If !JSVAL_IS_OBJECT(v), then the callback is expected to report an - * appropriate (application-specific) error and return JS_FALSE. + * Return true on success, false on error/exception. */ -typedef JSBool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, jsval v); +typedef JSBool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, JSObject *obj); /* * This is called when JS_WriteStructuredClone finds that the object to be From 28a60376f5bf7f21e89500199f445cf5e4812fc5 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 1 Oct 2010 09:47:44 -0700 Subject: [PATCH 003/284] Clean up exec pool and LinkBuffer use (bug 601041, r=dmandelin). --- js/src/assembler/assembler/LinkBuffer.h | 19 +- js/src/jsclone.h | 2 +- js/src/jscntxt.h | 2 +- js/src/methodjit/BaseAssembler.h | 21 +- .../{CompilerBase.h => BaseCompiler.h} | 82 +++++- js/src/methodjit/Compiler.cpp | 36 +-- js/src/methodjit/Compiler.h | 18 +- js/src/methodjit/MethodJIT.cpp | 10 +- js/src/methodjit/MonoIC.cpp | 12 +- js/src/methodjit/PolyIC.cpp | 240 ++++++------------ js/src/methodjit/StubCompiler.cpp | 6 - js/src/methodjit/StubCompiler.h | 1 - js/src/methodjit/TrampolineCompiler.cpp | 2 +- 13 files changed, 203 insertions(+), 248 deletions(-) rename js/src/methodjit/{CompilerBase.h => BaseCompiler.h} (53%) diff --git a/js/src/assembler/assembler/LinkBuffer.h b/js/src/assembler/assembler/LinkBuffer.h index d663d8d18772..5bd5d6ed326d 100644 --- a/js/src/assembler/assembler/LinkBuffer.h +++ b/js/src/assembler/assembler/LinkBuffer.h @@ -67,7 +67,7 @@ public: // m_code uses m_executablePool, *not* executablePool, since this is no longer valid. LinkBuffer(MacroAssembler* masm, ExecutablePool* executablePool) : m_executablePool(executablePool) - , m_code(masm->m_assembler.executableCopy(m_executablePool)) + , m_code(executableCopy(*masm, executablePool)) , m_size(masm->m_assembler.size()) #ifndef NDEBUG , m_completed(false) @@ -75,6 +75,16 @@ public: { } + LinkBuffer() + : m_executablePool(NULL) + , m_code(NULL) + , m_size(0) +#ifndef NDEBUG + , m_completed(false) +#endif + { + } + LinkBuffer(uint8* ncode, size_t size) : m_executablePool(NULL) , m_code(ncode) @@ -179,7 +189,7 @@ public: return CodeLocationLabel(code()); } -private: +protected: // Keep this private! - the underlying code should only be obtained externally via // finalizeCode() or finalizeCodeAddendum(). void* code() @@ -187,6 +197,11 @@ private: return m_code; } + void *executableCopy(MacroAssembler &masm, ExecutablePool *pool) + { + return masm.m_assembler.executableCopy(pool); + } + void performFinalization() { #ifndef NDEBUG diff --git a/js/src/jsclone.h b/js/src/jsclone.h index bc543f1648e4..df17a68578de 100644 --- a/js/src/jsclone.h +++ b/js/src/jsclone.h @@ -1,5 +1,5 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- -/* ***** BEGIN LICENSE BLOCK ***** + * ***** 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 diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index e7672334e8db..ec2c7ba5ea58 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -244,7 +244,7 @@ namespace mjit { struct ThreadData { - JSC::ExecutableAllocator *execPool; + JSC::ExecutableAllocator *execAlloc; // Trampolines for JIT code. Trampolines trampolines; diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index c6d5f98f8767..110c53b57c83 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -45,7 +45,7 @@ #include "jstl.h" #include "assembler/assembler/MacroAssemblerCodeRef.h" #include "assembler/assembler/MacroAssembler.h" -#include "assembler/assembler/RepatchBuffer.h" +#include "assembler/assembler/LinkBuffer.h" #include "assembler/moco/MocoStubs.h" #include "methodjit/MethodJIT.h" #include "methodjit/MachineRegs.h" @@ -91,11 +91,11 @@ struct ImmIntPtr : public JSC::MacroAssembler::ImmPtr class BaseAssembler : public JSC::MacroAssembler { struct CallPatch { - CallPatch(ptrdiff_t distance, void *fun) - : distance(distance), fun(fun) + CallPatch(Call cl, void *fun) + : call(cl), fun(fun) { } - ptrdiff_t distance; + Call call; JSC::FunctionPtr fun; }; @@ -326,8 +326,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste Call call(void *fun) { Call cl = JSC::MacroAssembler::call(); - - callPatches.append(CallPatch(differenceBetween(startLabel, cl), fun)); + callPatches.append(CallPatch(cl, fun)); return cl; } @@ -335,14 +334,10 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste return MacroAssembler::call(reg); } - void finalize(uint8 *ncode) { - JSC::JITCode jc(ncode, size()); - JSC::CodeBlock cb(jc); - JSC::RepatchBuffer repatchBuffer(&cb); - + void finalize(JSC::LinkBuffer &linker) { for (size_t i = 0; i < callPatches.length(); i++) { - JSC::MacroAssemblerCodePtr cp(ncode + callPatches[i].distance); - repatchBuffer.relink(JSC::CodeLocationCall(cp), callPatches[i].fun); + CallPatch &patch = callPatches[i]; + linker.link(patch.call, JSC::FunctionPtr(patch.fun)); } } }; diff --git a/js/src/methodjit/CompilerBase.h b/js/src/methodjit/BaseCompiler.h similarity index 53% rename from js/src/methodjit/CompilerBase.h rename to js/src/methodjit/BaseCompiler.h index e3696b261f42..ad1ff5ad8045 100644 --- a/js/src/methodjit/CompilerBase.h +++ b/js/src/methodjit/BaseCompiler.h @@ -43,31 +43,89 @@ #include "jscntxt.h" #include "jstl.h" #include "assembler/assembler/MacroAssembler.h" +#include "assembler/assembler/LinkBuffer.h" namespace js { namespace mjit { -class CompilerBase +class BaseCompiler { protected: - typedef JSC::MacroAssembler::Label Label; - typedef JSC::MacroAssembler::ImmPtr ImmPtr; - typedef JSC::MacroAssembler::RegisterID RegisterID; - typedef JSC::MacroAssembler::Address Address; - typedef JSC::MacroAssembler MacroAssembler; - - private: JSContext *cx; public: - CompilerBase(JSContext *cx) - : cx(cx) + BaseCompiler() : cx(NULL) + { } + + BaseCompiler(JSContext *cx) : cx(cx) { } protected: - JSC::ExecutablePool *getExecPool(size_t size) { + typedef JSC::MacroAssembler::Label Label; + typedef JSC::MacroAssembler::Imm32 Imm32; + typedef JSC::MacroAssembler::ImmPtr ImmPtr; + typedef JSC::MacroAssembler::RegisterID RegisterID; + typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; + typedef JSC::MacroAssembler::Address Address; + typedef JSC::MacroAssembler::BaseIndex BaseIndex; + typedef JSC::MacroAssembler::AbsoluteAddress AbsoluteAddress; + typedef JSC::MacroAssembler MacroAssembler; + typedef JSC::MacroAssembler::Jump Jump; + typedef JSC::MacroAssembler::JumpList JumpList; + typedef JSC::MacroAssembler::Call Call; + typedef JSC::MacroAssembler::DataLabelPtr DataLabelPtr; + typedef JSC::MacroAssembler::DataLabel32 DataLabel32; + typedef JSC::FunctionPtr FunctionPtr; + typedef JSC::RepatchBuffer RepatchBuffer; + typedef JSC::CodeBlock CodeBlock; + typedef JSC::CodeLocationLabel CodeLocationLabel; + typedef JSC::JITCode JITCode; + typedef JSC::ReturnAddressPtr ReturnAddressPtr; + typedef JSC::MacroAssemblerCodePtr MacroAssemblerCodePtr; + + JSC::ExecutablePool * + getExecPool(size_t size) { + return BaseCompiler::GetExecPool(cx, size); + } + + public: + static JSC::ExecutablePool * + GetExecPool(JSContext *cx, size_t size) { ThreadData *jaegerData = &JS_METHODJIT_DATA(cx); - return jaegerData->execPool->poolForSize(size); + JSC::ExecutablePool *pool = jaegerData->execAlloc->poolForSize(size); + if (!pool) + js_ReportOutOfMemory(cx); + return pool; + } +}; + +// This class wraps JSC::LinkBuffer for Mozilla-specific memory handling. +// Every return |false| guarantees an OOM that has been correctly propagated, +// and should continue to propagate. +class LinkerHelper : public JSC::LinkBuffer +{ + protected: + JSContext *cx; + + public: + LinkerHelper(JSContext *cx) : cx(cx) + { } + + JSC::ExecutablePool *init(Assembler &masm) { + // The pool is incref'd after this call, so it's necessary to release() + // on any failure. + JSC::ExecutablePool *ep = BaseCompiler::GetExecPool(cx, masm.size()); + if (!ep) + return ep; + + m_size = masm.size(); + m_code = executableCopy(masm, ep); + if (!m_code) { + ep->release(); + js_ReportOutOfMemory(cx); + return NULL; + } + return ep; } }; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index da3be784667f..208edf2fa435 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -75,7 +75,11 @@ static const char *OpcodeNames[] = { #endif mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) - : cx(cx), script(script), scopeChain(scopeChain), globalObj(scopeChain->getGlobal()), fun(fun), + : BaseCompiler(cx), + script(script), + scopeChain(scopeChain), + globalObj(scopeChain->getGlobal()), + fun(fun), analysis(cx, script), jumpMap(NULL), frame(cx, script, masm), branchPatches(ContextAllocPolicy(cx)), #if defined JS_MONOIC @@ -450,21 +454,11 @@ mjit::Compiler::finishThisUp() #endif /* JS_MONOIC */ for (size_t i = 0; i < callPatches.length(); i++) { - void *joinPoint = fullCode.locationOf(callPatches[i].joinPoint).executableAddress(); + CallPatchInfo &patch = callPatches[i]; - /* Patch the write of ncode in the hot path. */ - JSC::CodeLocationDataLabelPtr fastNcode = - fullCode.locationOf(callPatches[i].fastNcodePatch); - JSC::RepatchBuffer fastRepatch((uint8*)fastNcode.executableAddress() - 32, 64, false); - fastRepatch.repatch(fastNcode, joinPoint); - - /* Patch the write of ncode in the slow path. */ - if (callPatches[i].hasSlowNcode) { - JSC::CodeLocationDataLabelPtr slowNcode = - stubCode.locationOf(callPatches[i].slowNcodePatch); - JSC::RepatchBuffer slowRepatch((uint8*)slowNcode.executableAddress() - 32, 64, false); - slowRepatch.repatch(slowNcode, joinPoint); - } + fullCode.patch(patch.fastNcodePatch, fullCode.locationOf(patch.joinPoint)); + if (patch.hasSlowNcode) + stubCode.patch(patch.slowNcodePatch, fullCode.locationOf(patch.joinPoint)); } #if defined JS_POLYIC @@ -525,8 +519,8 @@ mjit::Compiler::finishThisUp() } /* Patch all outgoing calls. */ - masm.finalize(result); - stubcc.finalize(result + masm.size()); + masm.finalize(fullCode); + stubcc.masm.finalize(stubCode); JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size()); @@ -534,6 +528,7 @@ mjit::Compiler::finishThisUp() script->ncode = (uint8 *)(result + masm.distanceOf(invokeLabel)); /* Build the table of call sites. */ + script->jit->nCallSites = callSites.length(); if (callSites.length()) { CallSite *callSiteList = (CallSite *)cursor; cursor += sizeof(CallSite) * callSites.length(); @@ -1691,13 +1686,6 @@ mjit::Compiler::labelOf(jsbytecode *pc) return jumpMap[offs]; } -JSC::ExecutablePool * -mjit::Compiler::getExecPool(size_t size) -{ - ThreadData *jaegerData = &JS_METHODJIT_DATA(cx); - return jaegerData->execPool->poolForSize(size); -} - uint32 mjit::Compiler::fullAtomIndex(jsbytecode *pc) { diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 2c132b1129ef..be20c21df323 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -45,6 +45,7 @@ #include "BytecodeAnalyzer.h" #include "MethodJIT.h" #include "CodeGenIncludes.h" +#include "BaseCompiler.h" #include "StubCompiler.h" #include "MonoIC.h" #include "PolyIC.h" @@ -52,21 +53,8 @@ namespace js { namespace mjit { -class Compiler +class Compiler : public BaseCompiler { - typedef JSC::MacroAssembler::Label Label; - typedef JSC::MacroAssembler::Imm32 Imm32; - typedef JSC::MacroAssembler::ImmPtr ImmPtr; - typedef JSC::MacroAssembler::RegisterID RegisterID; - typedef JSC::MacroAssembler::FPRegisterID FPRegisterID; - typedef JSC::MacroAssembler::Address Address; - typedef JSC::MacroAssembler::AbsoluteAddress AbsoluteAddress; - typedef JSC::MacroAssembler::BaseIndex BaseIndex; - typedef JSC::MacroAssembler::Jump Jump; - typedef JSC::MacroAssembler::JumpList JumpList; - typedef JSC::MacroAssembler::Call Call; - typedef JSC::MacroAssembler::DataLabelPtr DataLabelPtr; - typedef JSC::MacroAssembler::DataLabel32 DataLabel32; struct BranchPatch { BranchPatch(const Jump &j, jsbytecode *pc) @@ -208,7 +196,6 @@ class Compiler bool ool; }; - JSContext *cx; JSScript *script; JSObject *scopeChain; JSObject *globalObj; @@ -259,7 +246,6 @@ class Compiler /* Non-emitting helpers. */ uint32 fullAtomIndex(jsbytecode *pc); void jumpInScript(Jump j, jsbytecode *pc); - JSC::ExecutablePool *getExecPool(size_t size); bool compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const Value &rhs); void addCallSite(uint32 id, bool stub); diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index bca47771b46c..cd7143851f12 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -685,13 +685,13 @@ JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL); bool ThreadData::Initialize() { - execPool = new JSC::ExecutableAllocator(); - if (!execPool) + execAlloc = new JSC::ExecutableAllocator(); + if (!execAlloc) return false; - TrampolineCompiler tc(execPool, &trampolines); + TrampolineCompiler tc(execAlloc, &trampolines); if (!tc.compile()) { - delete execPool; + delete execAlloc; return false; } @@ -709,7 +709,7 @@ void ThreadData::Finish() { TrampolineCompiler::release(&trampolines); - delete execPool; + delete execAlloc; #ifdef JS_METHODJIT_PROFILE_STUBS FILE *fp = fopen("/tmp/stub-profiling", "wt"); # define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) \ diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 1a23505dba86..c6b47b3f7e60 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -277,28 +277,24 @@ SlowNewFromIC(VMFrame &f, uint32 index) * scripted native, then a small stub is generated which inlines the native * invocation. */ -class CallCompiler +class CallCompiler : public BaseCompiler { VMFrame &f; - JSContext *cx; CallICInfo ⁣ Value *vp; bool callingNew; public: CallCompiler(VMFrame &f, CallICInfo &ic, bool callingNew) - : f(f), cx(f.cx), ic(ic), vp(f.regs.sp - (ic.argc + 2)), callingNew(callingNew) + : BaseCompiler(f.cx), f(f), ic(ic), vp(f.regs.sp - (ic.argc + 2)), callingNew(callingNew) { } JSC::ExecutablePool *poolForSize(size_t size, CallICInfo::PoolIndex index) { - mjit::ThreadData *jm = &JS_METHODJIT_DATA(cx); - JSC::ExecutablePool *ep = jm->execPool->poolForSize(size); - if (!ep) { - js_ReportOutOfMemory(f.cx); + JSC::ExecutablePool *ep = getExecPool(size); + if (!ep) return NULL; - } JS_ASSERT(!ic.pools[index]); ic.pools[index] = ep; return ep; diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 695e5b57d4e1..0709f6857138 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -40,7 +40,9 @@ #include "StubCalls.h" #include "CodeGenIncludes.h" #include "StubCalls-inl.h" +#include "BaseCompiler.h" #include "assembler/assembler/LinkBuffer.h" +#include "assembler/assembler/RepatchBuffer.h" #include "jsscope.h" #include "jsnum.h" #include "jsobjinlines.h" @@ -58,21 +60,32 @@ using namespace js::mjit::ic; /* Rough over-estimate of how much memory we need to unprotect. */ static const uint32 INLINE_PATH_LENGTH = 64; -typedef JSC::FunctionPtr FunctionPtr; -typedef JSC::RepatchBuffer RepatchBuffer; -typedef JSC::CodeBlock CodeBlock; -typedef JSC::CodeLocationLabel CodeLocationLabel; -typedef JSC::JITCode JITCode; -typedef JSC::MacroAssembler::Jump Jump; -typedef JSC::MacroAssembler::RegisterID RegisterID; -typedef JSC::MacroAssembler::Label Label; -typedef JSC::MacroAssembler::Imm32 Imm32; -typedef JSC::MacroAssembler::ImmPtr ImmPtr; -typedef JSC::MacroAssembler::Address Address; -typedef JSC::ReturnAddressPtr ReturnAddressPtr; -typedef JSC::MacroAssemblerCodePtr MacroAssemblerCodePtr; +// Helper class to simplify LinkBuffer usage in PIC stub generators. +// This guarantees correct OOM and refcount handling for buffers while they +// are instantiated and rooted. +class PICLinker : public LinkerHelper +{ + ic::PICInfo &pic; -class PICStubCompiler + public: + PICLinker(JSContext *cx, ic::PICInfo &pic) + : LinkerHelper(cx), pic(pic) + { } + + bool init(Assembler &masm) { + JSC::ExecutablePool *pool = LinkerHelper::init(masm); + if (!pool) + return false; + if (!pic.execPools.append(pool)) { + pool->release(); + js_ReportOutOfMemory(cx); + return false; + } + return true; + } +}; + +class PICStubCompiler : public BaseCompiler { protected: const char *type; @@ -82,7 +95,7 @@ class PICStubCompiler public: PICStubCompiler(const char *type, VMFrame &f, JSScript *script, ic::PICInfo &pic) - : type(type), f(f), script(script), pic(pic) + : BaseCompiler(f.cx), type(type), f(f), script(script), pic(pic) { } bool isCallOp() const @@ -115,19 +128,13 @@ class PICStubCompiler return true; } - JSC::ExecutablePool *getExecPool(size_t size) - { - mjit::ThreadData *jd = &JS_METHODJIT_DATA(f.cx); - return jd->execPool->poolForSize(size); - } - protected: void spew(const char *event, const char *op) { #ifdef JS_METHODJIT_SPEW JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n", type, event, op, script->filename, - js_FramePCToLineNumber(f.cx, f.fp())); + js_FramePCToLineNumber(cx, f.fp())); #endif } }; @@ -308,8 +315,8 @@ class SetPropCompiler : public PICStubCompiler bool generateStub(uint32 initialShape, const Shape *shape, bool adding) { /* Exits to the slow path. */ - Vector slowExits(f.cx); - Vector otherGuards(f.cx); + Vector slowExits(cx); + Vector otherGuards(cx); Assembler masm; @@ -488,15 +495,10 @@ class SetPropCompiler : public PICStubCompiler pic.secondShapeGuard = 0; } - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep || !pic.execPools.append(ep)) { - if (ep) - ep->release(); - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - JSC::LinkBuffer buffer(&masm, ep); buffer.link(shapeGuard, pic.slowPathStart); if (slowExit.isSet()) buffer.link(slowExit.get(), pic.slowPathStart); @@ -556,7 +558,7 @@ class SetPropCompiler : public PICStubCompiler return disable("ops set property hook"); #ifdef JS_THREADSAFE - if (!CX_OWNS_OBJECT_TITLE(f.cx, obj)) + if (!CX_OWNS_OBJECT_TITLE(cx, obj)) return disable("shared object"); #endif @@ -564,12 +566,12 @@ class SetPropCompiler : public PICStubCompiler JSObject *holder; JSProperty *prop = NULL; - if (!obj->lookupProperty(f.cx, id, &holder, &prop)) + if (!obj->lookupProperty(cx, id, &holder, &prop)) return false; /* If the property exists but is on a prototype, treat as addprop. */ if (prop && holder != obj) { - AutoPropertyDropper dropper(f.cx, holder, prop); + AutoPropertyDropper dropper(cx, holder, prop); const Shape *shape = (const Shape *) prop; if (!holder->isNative()) @@ -603,7 +605,7 @@ class SetPropCompiler : public PICStubCompiler uint32 initialShape = obj->shape(); - if (!obj->ensureClassReservedSlots(f.cx)) + if (!obj->ensureClassReservedSlots(cx)) return false; uint32 slots = obj->numSlots(); @@ -623,7 +625,7 @@ class SetPropCompiler : public PICStubCompiler } const Shape *shape = - obj->putProperty(f.cx, id, getter, clasp->setProperty, + obj->putProperty(cx, id, getter, clasp->setProperty, SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, flags, 0); if (!shape) @@ -660,7 +662,7 @@ class SetPropCompiler : public PICStubCompiler return generateStub(initialShape, shape, true); } - AutoPropertyDropper dropper(f.cx, holder, prop); + AutoPropertyDropper dropper(cx, holder, prop); const Shape *shape = (const Shape *) prop; if (pic.kind == ic::PICInfo::SETMETHOD && !shape->isMethod()) @@ -803,15 +805,10 @@ class GetPropCompiler : public PICStubCompiler masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep || !pic.execPools.append(ep)) { - if (ep) - ep->release(); - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - JSC::LinkBuffer buffer(&masm, ep); buffer.link(notArgs, pic.slowPathStart); buffer.link(overridden, pic.slowPathStart); buffer.link(done, pic.storeBack); @@ -846,15 +843,10 @@ class GetPropCompiler : public PICStubCompiler masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep || !pic.execPools.append(ep)) { - if (ep) - ep->release(); - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - JSC::LinkBuffer buffer(&masm, ep); buffer.link(notArray, pic.slowPathStart); buffer.link(oob, pic.slowPathStart); buffer.link(done, pic.storeBack); @@ -881,12 +873,12 @@ class GetPropCompiler : public PICStubCompiler JSObject *holder; JSProperty *prop; - if (!obj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop)) + if (!obj->lookupProperty(cx, ATOM_TO_JSID(atom), &holder, &prop)) return false; if (!prop) return disable("property not found"); - AutoPropertyDropper dropper(f.cx, holder, prop); + AutoPropertyDropper dropper(cx, holder, prop); const Shape *shape = (const Shape *)prop; if (holder != obj) return disable("proto walk on String.prototype"); @@ -937,21 +929,15 @@ class GetPropCompiler : public PICStubCompiler Jump done = masm.jump(); - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep || !pic.execPools.append(ep)) { - if (ep) - ep->release(); - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - JSC::LinkBuffer patchBuffer(&masm, ep); + buffer.link(notString, pic.slowPathStart.labelAtOffset(pic.u.get.typeCheckOffset)); + buffer.link(shapeMismatch, pic.slowPathStart); + buffer.link(done, pic.storeBack); - patchBuffer.link(notString, pic.slowPathStart.labelAtOffset(pic.u.get.typeCheckOffset)); - patchBuffer.link(shapeMismatch, pic.slowPathStart); - patchBuffer.link(done, pic.storeBack); - - CodeLocationLabel cs = patchBuffer.finalizeCodeAddendum(); + CodeLocationLabel cs = buffer.finalizeCodeAddendum(); JaegerSpew(JSpew_PICs, "generate string call stub at %p\n", cs.executableAddress()); @@ -980,19 +966,14 @@ class GetPropCompiler : public PICStubCompiler masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep || !pic.execPools.append(ep)) { - if (ep) - ep->release(); - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - JSC::LinkBuffer patchBuffer(&masm, ep); - patchBuffer.link(notString, pic.slowPathStart.labelAtOffset(pic.u.get.typeCheckOffset)); - patchBuffer.link(done, pic.storeBack); + buffer.link(notString, pic.slowPathStart.labelAtOffset(pic.u.get.typeCheckOffset)); + buffer.link(done, pic.storeBack); - CodeLocationLabel start = patchBuffer.finalizeCodeAddendum(); + CodeLocationLabel start = buffer.finalizeCodeAddendum(); JaegerSpew(JSpew_PICs, "generate string length stub at %p\n", start.executableAddress()); @@ -1048,7 +1029,7 @@ class GetPropCompiler : public PICStubCompiler bool generateStub(JSObject *holder, const Shape *shape) { - Vector shapeMismatches(f.cx); + Vector shapeMismatches(cx); Assembler masm; @@ -1143,20 +1124,9 @@ class GetPropCompiler : public PICStubCompiler } Jump done = masm.jump(); - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep) { - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - - // :TODO: this can OOM - JSC::LinkBuffer buffer(&masm, ep); - - if (!pic.execPools.append(ep)) { - ep->release(); - js_ReportOutOfMemory(f.cx); - return false; - } // The guard exit jumps to the original slow case. for (Jump *pj = shapeMismatches.begin(); pj != shapeMismatches.end(); ++pj) @@ -1219,13 +1189,13 @@ class GetPropCompiler : public PICStubCompiler JSObject *holder; JSProperty *prop; - if (!aobj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop)) + if (!aobj->lookupProperty(cx, ATOM_TO_JSID(atom), &holder, &prop)) return false; if (!prop) return disable("lookup failed"); - AutoPropertyDropper dropper(f.cx, holder, prop); + AutoPropertyDropper dropper(cx, holder, prop); if (!holder->isNative()) return disable("non-native holder"); @@ -1414,7 +1384,7 @@ class GetElemCompiler : public PICStubCompiler bool generateStub(JSObject *holder, const Shape *shape) { JS_ASSERT(pic.u.get.idReg != pic.shapeReg); - Vector shapeMismatches(f.cx); + Vector shapeMismatches(cx); Assembler masm; @@ -1506,20 +1476,9 @@ class GetElemCompiler : public PICStubCompiler } Jump done = masm.jump(); - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep) { - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - - // :TODO: this can OOM - JSC::LinkBuffer buffer(&masm, ep); - - if (!pic.execPools.append(ep)) { - ep->release(); - js_ReportOutOfMemory(f.cx); - return false; - } // The guard exit jumps to the original slow case. for (Jump *pj = shapeMismatches.begin(); pj != shapeMismatches.end(); ++pj) @@ -1529,11 +1488,11 @@ class GetElemCompiler : public PICStubCompiler buffer.link(done, pic.storeBack); CodeLocationLabel cs = buffer.finalizeCodeAddendum(); #if DEBUG - char *chars = js_DeflateString(f.cx, id->chars(), id->length()); + char *chars = js_DeflateString(cx, id->chars(), id->length()); JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n", type, cs.executableAddress(), id, chars, holder->shape(), script->filename, - js_FramePCToLineNumber(f.cx, f.fp())); - f.cx->free(chars); + js_FramePCToLineNumber(cx, f.fp())); + cx->free(chars); #endif PICRepatchBuffer repatcher(pic, pic.lastPathStart()); @@ -1563,18 +1522,18 @@ class GetElemCompiler : public PICStubCompiler return true; } - JSAtom *atom = js_AtomizeString(f.cx, id, 0); + JSAtom *atom = js_AtomizeString(cx, id, 0); if (!atom) return false; JSObject *holder; JSProperty *prop; - if (!obj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop)) + if (!obj->lookupProperty(cx, ATOM_TO_JSID(atom), &holder, &prop)) return false; if (!prop) return disable("lookup failed"); - AutoPropertyDropper dropper(f.cx, holder, prop); + AutoPropertyDropper dropper(cx, holder, prop); if (!obj->isNative()) return disable("non-native obj"); @@ -1681,7 +1640,7 @@ class ScopeNameCompiler : public PICStubCompiler bool generateGlobalStub() { Assembler masm; - JumpList fails(f.cx); + JumpList fails(cx); /* For GETXPROP, the object is already in objReg. */ if (pic.kind == ic::PICInfo::NAME) @@ -1724,20 +1683,9 @@ class ScopeNameCompiler : public PICStubCompiler JS_ASSERT(masm.differenceBetween(failLabel, dbgJumpOffset) == SCOPENAME_JUMP_OFFSET); - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep) { - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - - // :TODO: this can OOM - JSC::LinkBuffer buffer(&masm, ep); - - if (!pic.execPools.append(ep)) { - ep->release(); - js_ReportOutOfMemory(f.cx); - return false; - } buffer.link(failJump, pic.slowPathStart); buffer.link(done, pic.storeBack); @@ -1765,7 +1713,7 @@ class ScopeNameCompiler : public PICStubCompiler bool generateCallStub() { Assembler masm; - Vector fails(f.cx); + Vector fails(cx); /* For GETXPROP, the object is already in objReg. */ if (pic.kind == ic::PICInfo::NAME) @@ -1839,20 +1787,9 @@ class ScopeNameCompiler : public PICStubCompiler Label failLabel = masm.label(); Jump failJump = masm.jump(); - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep) { - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - - // :TODO: this can OOM - JSC::LinkBuffer buffer(&masm, ep); - - if (!pic.execPools.append(ep)) { - ep->release(); - js_ReportOutOfMemory(f.cx); - return false; - } buffer.link(failJump, pic.slowPathStart); buffer.link(done, pic.storeBack); @@ -1873,7 +1810,7 @@ class ScopeNameCompiler : public PICStubCompiler bool updateForName() { - if (!js_FindProperty(f.cx, ATOM_TO_JSID(atom), &obj, &holder, &prop)) + if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &holder, &prop)) return false; return update(); @@ -1883,7 +1820,7 @@ class ScopeNameCompiler : public PICStubCompiler { obj = scopeChain; - if (!obj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop)) + if (!obj->lookupProperty(cx, ATOM_TO_JSID(atom), &holder, &prop)) return false; return update(); @@ -1924,8 +1861,6 @@ class ScopeNameCompiler : public PICStubCompiler bool retrieve(Value *vp) { - JSContext *cx = f.cx; - if (prop && (!obj->isNative() || !holder->isNative())) { holder->dropProperty(cx, prop); if (!obj->getProperty(cx, ATOM_TO_JSID(atom), vp)) @@ -2001,7 +1936,7 @@ class BindNameCompiler : public PICStubCompiler bool generateStub(JSObject *obj) { Assembler masm; - js::Vector fails(f.cx); + js::Vector fails(cx); /* Guard on the shape of the scope chain. */ masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfScopeChain()), pic.objReg); @@ -2039,20 +1974,9 @@ class BindNameCompiler : public PICStubCompiler JS_ASSERT(masm.differenceBetween(failLabel, dbgStubJumpOffset) == BINDNAME_STUB_JUMP_OFFSET); - JSC::ExecutablePool *ep = getExecPool(masm.size()); - if (!ep) { - js_ReportOutOfMemory(f.cx); + PICLinker buffer(cx, pic); + if (!buffer.init(masm)) return false; - } - - // :TODO: this can OOM - JSC::LinkBuffer buffer(&masm, ep); - - if (!pic.execPools.append(ep)) { - ep->release(); - js_ReportOutOfMemory(f.cx); - return false; - } buffer.link(failJump, pic.slowPathStart); buffer.link(done, pic.storeBack); @@ -2078,7 +2002,7 @@ class BindNameCompiler : public PICStubCompiler { JS_ASSERT(scopeChain->getParent()); - JSObject *obj = js_FindIdentifierBase(f.cx, scopeChain, ATOM_TO_JSID(atom)); + JSObject *obj = js_FindIdentifierBase(cx, scopeChain, ATOM_TO_JSID(atom)); if (!obj) return obj; diff --git a/js/src/methodjit/StubCompiler.cpp b/js/src/methodjit/StubCompiler.cpp index bbda96ad55a4..d2f2d972ccd3 100644 --- a/js/src/methodjit/StubCompiler.cpp +++ b/js/src/methodjit/StubCompiler.cpp @@ -199,12 +199,6 @@ StubCompiler::fixCrossJumps(uint8 *ncode, size_t offset, size_t total) slow.link(joins[i].from, fast.locationOf(joins[i].to)); } -void -StubCompiler::finalize(uint8 *ncode) -{ - masm.finalize(ncode); -} - JSC::MacroAssembler::Call StubCompiler::vpInc(JSOp op, uint32 depth) { diff --git a/js/src/methodjit/StubCompiler.h b/js/src/methodjit/StubCompiler.h index 5e293674969b..bdfaff63bd70 100644 --- a/js/src/methodjit/StubCompiler.h +++ b/js/src/methodjit/StubCompiler.h @@ -158,7 +158,6 @@ class StubCompiler /* Finish all native code patching. */ void fixCrossJumps(uint8 *ncode, size_t offset, size_t total); - void finalize(uint8 *ncode); void jumpInScript(Jump j, jsbytecode *target); void crossJump(Jump j, Label l); diff --git a/js/src/methodjit/TrampolineCompiler.cpp b/js/src/methodjit/TrampolineCompiler.cpp index e7dd63ee2364..bffa17e9fb58 100644 --- a/js/src/methodjit/TrampolineCompiler.cpp +++ b/js/src/methodjit/TrampolineCompiler.cpp @@ -100,8 +100,8 @@ TrampolineCompiler::compileTrampoline(Trampolines::TrampolinePtr *where, JSC::Ex return false; JSC::LinkBuffer buffer(&masm, *pool); + masm.finalize(buffer); uint8 *result = (uint8*)buffer.finalizeCodeAddendum().dataLocation(); - masm.finalize(result); *where = JS_DATA_TO_FUNC_PTR(Trampolines::TrampolinePtr, result + masm.distanceOf(entry)); return true; From 8572cdcca198111c9f507ecd305ea8cf5a8ddd5e Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 1 Oct 2010 12:50:52 -0500 Subject: [PATCH 004/284] Fix stray /* inside a comment to silence GCC warning. no_r=me. --HG-- extra : rebase_source : 980456c167a368e2c25a6d4312cbad3cff87fbbd --- js/src/jsclone.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsclone.cpp b/js/src/jsclone.cpp index 2239674e6862..20121586ee6f 100644 --- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -1,5 +1,5 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- -/* ***** BEGIN LICENSE BLOCK ***** + * ***** 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 From 18200db96c216dac5dae9b1da07d669f88727994 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Fri, 1 Oct 2010 11:10:30 -0700 Subject: [PATCH 005/284] Bug 594455 Add assertions for defaultCompartments. r=jorendorff --- js/src/jsapi.cpp | 33 +++++++++++++++++++++++++++++++++ js/src/jsgcinlines.h | 5 +++++ js/src/jsutil.h | 7 +++++++ 3 files changed, 45 insertions(+) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 41c8e6f401c8..b1c583beff74 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1277,6 +1277,7 @@ private: JSObject * js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); JSObject *fun_proto, *obj_proto; /* If cx has no global object, use obj so prototypes can be found. */ @@ -1327,6 +1328,7 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) JS_PUBLIC_API(JSBool) JS_InitStandardClasses(JSContext *cx, JSObject *obj) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); /* @@ -2908,6 +2910,7 @@ JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) JS_PUBLIC_API(JSObject *) JS_NewGlobalObject(JSContext *cx, JSClass *clasp) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL); JSObject *obj = NewNonFunction(cx, Valueify(clasp), NULL, NULL); @@ -2947,6 +2950,7 @@ JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *pr JS_PUBLIC_API(JSObject *) JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); assertSameCompartment(cx, proto, parent); @@ -2966,6 +2970,7 @@ JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent) JS_PUBLIC_API(JSObject *) JS_NewObjectWithGivenProto(JSContext *cx, JSClass *jsclasp, JSObject *proto, JSObject *parent) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); assertSameCompartment(cx, proto, parent); @@ -3979,6 +3984,7 @@ JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) JS_PUBLIC_API(JSObject *) JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); /* NB: jsuint cast does ToUint32. */ assertSameCompartment(cx, JSValueArray(vector, vector ? (jsuint)length : 0)); @@ -4080,6 +4086,7 @@ JS_PUBLIC_API(JSFunction *) JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags, JSObject *parent, const char *name) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); JSAtom *atom; CHECK_REQUEST(cx); @@ -4255,6 +4262,7 @@ js_generic_native_method_dispatcher(JSContext *cx, uintN argc, Value *vp) JS_PUBLIC_API(JSBool) JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); uintN flags; JSObject *ctor; JSFunction *fun; @@ -4304,6 +4312,7 @@ JS_PUBLIC_API(JSFunction *) JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, uintN nargs, uintN attrs) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); @@ -4315,6 +4324,7 @@ JS_DefineUCFunction(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSNative call, uintN nargs, uintN attrs) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); @@ -4359,6 +4369,7 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *prin const jschar *chars, size_t length, const char *filename, uintN lineno) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, principals); @@ -4377,6 +4388,7 @@ JS_PUBLIC_API(JSScript *) JS_CompileUCScript(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); return JS_CompileUCScriptForPrincipals(cx, obj, NULL, chars, length, filename, lineno); } @@ -4386,6 +4398,7 @@ JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, const char *bytes, size_t length, const char *filename, uintN lineno) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); jschar *chars = js_InflateString(cx, bytes, &length); @@ -4400,6 +4413,7 @@ JS_PUBLIC_API(JSScript *) JS_CompileScript(JSContext *cx, JSObject *obj, const char *bytes, size_t length, const char *filename, uintN lineno) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); return JS_CompileScriptForPrincipals(cx, obj, NULL, bytes, length, filename, lineno); } @@ -4447,6 +4461,7 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, const char *bytes, size_ JS_PUBLIC_API(JSScript *) JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); FILE *fp; uint32 tcflags; JSScript *script; @@ -4481,6 +4496,7 @@ JS_PUBLIC_API(JSScript *) JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, const char *filename, FILE *file, JSPrincipals *principals) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); uint32 tcflags; JSScript *script; @@ -4500,12 +4516,14 @@ JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, const char *file JS_PUBLIC_API(JSScript *) JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, FILE *file) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); return JS_CompileFileHandleForPrincipals(cx, obj, filename, file, NULL); } JS_PUBLIC_API(JSObject *) JS_NewScriptObject(JSContext *cx, JSScript *script) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); assertSameCompartment(cx, script); if (!script) @@ -4569,6 +4587,7 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); JSFunction *fun; JSAtom *funAtom, *argAtom; uintN i; @@ -4639,6 +4658,7 @@ JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, const jschar *chars, size_t length, const char *filename, uintN lineno) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); return JS_CompileUCFunctionForPrincipals(cx, obj, NULL, name, nargs, argnames, chars, length, filename, lineno); } @@ -4650,6 +4670,7 @@ JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, const char *bytes, size_t length, const char *filename, uintN lineno) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); jschar *chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; @@ -4666,6 +4687,7 @@ JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, const char *bytes, size_t length, const char *filename, uintN lineno) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); return JS_CompileFunctionForPrincipals(cx, obj, NULL, name, nargs, argnames, bytes, length, filename, lineno); } @@ -4673,6 +4695,7 @@ JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, JS_PUBLIC_API(JSString *) JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN indent) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); JSPrinter *jp; JSString *str; @@ -4695,6 +4718,7 @@ JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN inde JS_PUBLIC_API(JSString *) JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); assertSameCompartment(cx, fun); return js_DecompileToString(cx, "JS_DecompileFunction", fun, @@ -4706,6 +4730,7 @@ JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent) JS_PUBLIC_API(JSString *) JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); assertSameCompartment(cx, fun); return js_DecompileToString(cx, "JS_DecompileFunctionBody", fun, @@ -4717,6 +4742,7 @@ JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) JS_PUBLIC_API(JSBool) JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); JSBool ok; CHECK_REQUEST(cx); @@ -4747,6 +4773,7 @@ JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, const char *filename, uintN lineno, jsval *rval) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); JSScript *script; JSBool ok; @@ -4770,6 +4797,7 @@ JS_PUBLIC_API(JSBool) JS_EvaluateUCScript(JSContext *cx, JSObject *obj, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); return JS_EvaluateUCScriptForPrincipals(cx, obj, NULL, chars, length, filename, lineno, rval); } @@ -4779,6 +4807,7 @@ JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *princ const char *bytes, uintN nbytes, const char *filename, uintN lineno, jsval *rval) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); size_t length = nbytes; jschar *chars = js_InflateString(cx, bytes, &length); if (!chars) @@ -4793,6 +4822,7 @@ JS_PUBLIC_API(JSBool) JS_EvaluateScript(JSContext *cx, JSObject *obj, const char *bytes, uintN nbytes, const char *filename, uintN lineno, jsval *rval) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); return JS_EvaluateScriptForPrincipals(cx, obj, NULL, bytes, nbytes, filename, lineno, rval); } @@ -4800,6 +4830,7 @@ JS_PUBLIC_API(JSBool) JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval *argv, jsval *rval) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); JSBool ok; CHECK_REQUEST(cx); @@ -4813,6 +4844,7 @@ JS_PUBLIC_API(JSBool) JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *argv, jsval *rval) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, JSValueArray(argv, argc)); @@ -4829,6 +4861,7 @@ JS_PUBLIC_API(JSBool) JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, jsval *rval) { + JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->defaultCompartment); JSBool ok; CHECK_REQUEST(cx); diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 7841169c1d6a..73702207b4d4 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -67,6 +67,11 @@ JS_ALWAYS_INLINE T * NewFinalizableGCThing(JSContext *cx, unsigned thingKind) { JS_ASSERT(thingKind < js::gc::FINALIZE_LIMIT); +#ifdef JS_THREADSAFE + JS_ASSERT_IF((cx->compartment == cx->runtime->defaultCompartment), + (thingKind == js::gc::FINALIZE_STRING) || + (thingKind == js::gc::FINALIZE_SHORT_STRING)); +#endif METER(cx->compartment->compartmentStats[thingKind].alloc++); do { diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 19a05f09da45..31587781ad0f 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -69,12 +69,19 @@ JS_Assert(const char *s, const char *file, JSIntn ln); #define JS_ALWAYS_TRUE(expr) JS_ASSERT(expr) +# ifdef JS_THREADSAFE +# define JS_THREADSAFE_ASSERT(expr) JS_ASSERT(expr) +# else +# define JS_THREADSAFE_ASSERT(expr) ((void) 0) +# endif + #else #define JS_ASSERT(expr) ((void) 0) #define JS_ASSERT_IF(cond,expr) ((void) 0) #define JS_NOT_REACHED(reason) #define JS_ALWAYS_TRUE(expr) ((void) (expr)) +#define JS_THREADSAFE_ASSERT(expr) ((void) 0) #endif /* defined(DEBUG) */ From 99d33bc1905e6c67dc5d867e0aa4d3c7f015118d Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Fri, 1 Oct 2010 11:12:01 -0700 Subject: [PATCH 006/284] Bug 595963: notify iterators about property deletion in array_splice, r=gal --- js/src/jsarray.cpp | 4 ++ js/src/jsiter.cpp | 65 ++++++++++++++++---- js/src/jsiter.h | 3 + js/src/trace-test/tests/basic/bug595963-1.js | 19 ++++++ js/src/trace-test/tests/basic/bug595963-2.js | 19 ++++++ 5 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 js/src/trace-test/tests/basic/bug595963-1.js create mode 100644 js/src/trace-test/tests/basic/bug595963-2.js diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index c5e1806f88e8..af63217d8825 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2338,6 +2338,7 @@ array_splice(JSContext *cx, uintN argc, Value *vp) JSObject *obj = ComputeThisFromVp(cx, vp); if (!obj || !js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; + jsuint origlength = length; /* Convert the first argument into a starting index. */ jsdouble d; @@ -2452,6 +2453,9 @@ array_splice(JSContext *cx, uintN argc, Value *vp) length -= delta; } + if (length < origlength && !js_SuppressDeletedIndexProperties(cx, obj, length, origlength)) + return JS_FALSE; + /* * Copy from argv into the hole to complete the splice, and update length in * case we deleted elements from the end. diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 33e569b508b6..c2bad94bb81e 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -871,17 +871,25 @@ js_CloseIterator(JSContext *cx, JSObject *obj) } /* - * Suppress enumeration of deleted properties. We maintain a list of all active - * non-escaping for-in enumerators. Whenever a property is deleted, we check - * whether any active enumerator contains the (obj, id) pair and has not - * enumerated id yet. If so, we delete the id from the list (or advance the - * cursor if it is the next id to be enumerated). + * Suppress enumeration of deleted properties. This function must be called + * when a property is deleted and there might be active enumerators. + * + * We maintain a list of active non-escaping for-in enumerators. To suppress + * a property, we check whether each active enumerator contains the (obj, id) + * pair and has not yet enumerated |id|. If so, and |id| is the next property, + * we simply advance the cursor. Otherwise, we delete |id| from the list. * * We do not suppress enumeration of a property deleted along an object's * prototype chain. Only direct deletions on the object are handled. + * + * This function can suppress multiple properties at once. The |predicate| + * argument is an object which can be called on an id and returns true or + * false. It also must have a method |matchesAtMostOne| which allows us to + * stop searching after the first deletion if true. */ -bool -js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) +template +static bool +SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicate) { JSObject *iterobj = cx->enumerators; while (iterobj) { @@ -893,7 +901,7 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) jsid *props_cursor = ni->currentKey(); jsid *props_end = ni->endKey(); for (jsid *idp = props_cursor; idp < props_end; ++idp) { - if (*idp == id) { + if (predicate(*idp)) { /* * Check whether another property along the prototype chain * became visible as a result of this deletion. @@ -902,14 +910,14 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) AutoObjectRooter proto(cx, obj->getProto()); AutoObjectRooter obj2(cx); JSProperty *prop; - if (!proto.object()->lookupProperty(cx, id, obj2.addr(), &prop)) + if (!proto.object()->lookupProperty(cx, *idp, obj2.addr(), &prop)) return false; if (prop) { uintN attrs; if (obj2.object()->isNative()) { attrs = ((Shape *) prop)->attributes(); JS_UNLOCK_OBJ(cx, obj2.object()); - } else if (!obj2.object()->getAttributes(cx, id, &attrs)) { + } else if (!obj2.object()->getAttributes(cx, *idp, &attrs)) { return false; } if (attrs & JSPROP_ENUMERATE) @@ -925,7 +933,7 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) goto again; /* - * No property along the prototype chain steppeded in to take the + * No property along the prototype chain stepped in to take the * property's place, so go ahead and delete id from the list. * If it is the next property to be enumerated, just skip it. */ @@ -935,7 +943,8 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) memmove(idp, idp + 1, (props_end - (idp + 1)) * sizeof(jsid)); ni->props_end = ni->endKey() - 1; } - break; + if (predicate.matchesAtMostOne()) + break; } } } @@ -944,6 +953,38 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) return true; } +class SingleIdPredicate { + jsid id; +public: + SingleIdPredicate(jsid id) : id(id) {} + + bool operator()(jsid id) { return id == this->id; } + bool matchesAtMostOne() { return true; } +}; + +bool +js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) +{ + return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id)); +} + +class IndexRangePredicate { + jsint begin, end; +public: + IndexRangePredicate(jsint begin, jsint end) : begin(begin), end(end) {} + + bool operator()(jsid id) { + return JSID_IS_INT(id) && begin <= JSID_TO_INT(id) && JSID_TO_INT(id) < end; + } + bool matchesAtMostOne() { return false; } +}; + +bool +js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end) +{ + return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end)); +} + JSBool js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) { diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 022369e8fe5e..d47cb565f284 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -171,6 +171,9 @@ js_CloseIterator(JSContext *cx, JSObject *iterObj); bool js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id); +bool +js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end); + /* * IteratorMore() indicates whether another value is available. It might * internally call iterobj.next() and then cache the value until its diff --git a/js/src/trace-test/tests/basic/bug595963-1.js b/js/src/trace-test/tests/basic/bug595963-1.js new file mode 100644 index 000000000000..2cccfac400d1 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug595963-1.js @@ -0,0 +1,19 @@ +function remove(k, L) { + for (var i in k) { + if (i == L) + k.splice(L, 1); + } +} +function f(k) { + var L = 0; + for (var i in k) { + if (L == 1) + remove(k, L); + L++; + assertEq(k[i], 3); + } + assertEq(L, 6); +} + +var a = [3, 3, 3, 3, 3, 3, 3]; +f(a); diff --git a/js/src/trace-test/tests/basic/bug595963-2.js b/js/src/trace-test/tests/basic/bug595963-2.js new file mode 100644 index 000000000000..651a38064ea3 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug595963-2.js @@ -0,0 +1,19 @@ +function remove(k, L) { + for (var i in k) { + if (i == L) + k.splice(L, 3); + } +} +function f(k) { + var L = 0; + for (var i in k) { + if (L == 1) + remove(k, L); + L++; + assertEq(k[i], 3); + } + assertEq(L, 4); +} + +var a = [3, 3, 3, 3, 3, 3, 3]; +f(a); From 965a815ffc1a681328a93b2fd9451b2f733a84cb Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Fri, 1 Oct 2010 12:29:39 -0700 Subject: [PATCH 007/284] Bug 601197: fix typo in js_GetBlockChain, r=wmccloskey --- js/src/jsinterp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 2103f9d5ed98..bf60f475d605 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -175,7 +175,7 @@ js_GetBlockChain(JSContext *cx, JSStackFrame *fp) return NULL; JSScript *script = fp->script(); - jsbytecode *start = script->main; + jsbytecode *start = script->code; /* Assume that imacros don't affect blockChain */ jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx); From cbd39a3782fd3effb1a128664c91f9df74cb9512 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 1 Oct 2010 13:05:03 -0700 Subject: [PATCH 008/284] Bug 600943: Correctly compute |this| in Date.prototype.toJSON. r=waldo --- js/src/jsdate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 5b30834e9692..135b6b9643c4 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -2026,7 +2026,7 @@ JSBool date_toJSON(JSContext *cx, uintN argc, Value *vp) { /* Step 1. */ - JSObject *obj = ComputeThisFromVp(cx, vp); + JSObject *obj = js_ValueToNonNullObject(cx, vp[1]); if (!obj) return false; From 78322bd9ebcccc52746ab6ce0a9267c99085d120 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 1 Oct 2010 16:09:21 -0500 Subject: [PATCH 009/284] Bug 600639, part 1: Allow overriding gOutFile and gErrFile in js shell, and add a -g option to sleep on startup so that a debugger can be attached. r=dmandelin --- js/src/shell/js.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index be9c30310b13..2da24e7af25c 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -79,6 +79,7 @@ #include "jsxml.h" #include "jsperf.h" +#include "prenv.h" #include "prmjtime.h" #ifdef JSDEBUGGER @@ -559,7 +560,7 @@ static int usage(void) { fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); - fprintf(gErrFile, "usage: js [-zKPswWxCijmd] [-t timeoutSeconds] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] " + fprintf(gErrFile, "usage: js [-zKPswWxCijmd] [-t timeoutSeconds] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] [-g sleep-seconds-on-startup]" #ifdef JS_GC_ZEAL "[-Z gczeal] " #endif @@ -660,6 +661,7 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) #ifdef MOZ_TRACEVIS case 'T': #endif + case 'g': ++i; break; default:; @@ -859,6 +861,13 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) StartTraceVis(argv[i]); break; #endif + case 'g': + if (++i == argc) + return usage(); + + PR_Sleep(PR_SecondsToInterval(atoi(argv[i]))); + break; + default: return usage(); } @@ -5322,6 +5331,17 @@ shell(JSContext *cx, int argc, char **argv, char **envp) return result; } +static void +MaybeOverrideOutFileFromEnv(const char* const envVar, + FILE* defaultOut, + FILE** outFile) +{ + const char* outPath = PR_GetEnv(envVar); + if (!outPath || !*outPath || !(*outFile = fopen(outPath, "w"))) { + *outFile = defaultOut; + } +} + int main(int argc, char **argv, char **envp) { @@ -5370,8 +5390,8 @@ main(int argc, char **argv, char **envp) setbuf(stderr,0); #endif - gErrFile = stderr; - gOutFile = stdout; + MaybeOverrideOutFileFromEnv("JS_STDERR", stderr, &gErrFile); + MaybeOverrideOutFileFromEnv("JS_STDOUT", stdout, &gOutFile); argc--; argv++; From 0f2650f7d0ccc6909f5174b2a4686903b8d64b80 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 1 Oct 2010 16:09:21 -0500 Subject: [PATCH 010/284] Bug 600639, part 2: Make trace_test.py importable and add support for avoiding stdio with js. r=dmandelin --HG-- rename : js/src/trace-test/trace-test.py => js/src/trace-test/trace_test.py --- js/src/Makefile.in | 4 +- js/src/trace-test/README | 6 +- .../{trace-test.py => trace_test.py} | 103 ++++++++++++++---- 3 files changed, 87 insertions(+), 26 deletions(-) rename js/src/trace-test/{trace-test.py => trace_test.py} (82%) diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 58827dbca03f..291135c22757 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -569,11 +569,11 @@ endif ifdef ENABLE_TRACEJIT ifndef WINCE check:: - $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/trace-test/trace-test.py \ + $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/trace-test/trace_test.py \ --no-slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX) check-valgrind:: - $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/trace-test/trace-test.py \ + $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/trace-test/trace_test.py \ --valgrind --no-slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX) endif endif diff --git a/js/src/trace-test/README b/js/src/trace-test/README index d523f526525e..b5f87b137c5d 100644 --- a/js/src/trace-test/README +++ b/js/src/trace-test/README @@ -13,7 +13,7 @@ Python 2.5. This is already a standard requirement for building our tree. Basic usage: - python trace-test.py + python trace_test.py The progress bar shows [#tests passed, #tests failed, #tests run] at the left. If all tests pass, the output is 'PASSED ALL'. The test suite can be interrupted @@ -21,11 +21,11 @@ at any time with Ctrl+C and partial results will be printed. To run only the basic tests, not including the slow tests: - python trace-test.py basic + python trace_test.py basic For more options: - python trace-test.py -h + python trace_test.py -h * CREATING NEW TESTS diff --git a/js/src/trace-test/trace-test.py b/js/src/trace-test/trace_test.py similarity index 82% rename from js/src/trace-test/trace-test.py rename to js/src/trace-test/trace_test.py index 23968f605c7a..79f27275e68f 100644 --- a/js/src/trace-test/trace-test.py +++ b/js/src/trace-test/trace_test.py @@ -1,6 +1,6 @@ -# trace-test.py -- Python harness for JavaScript trace tests. +# trace_test.py -- Python harness for JavaScript trace tests. -import datetime, os, re, sys, traceback +import datetime, os, re, sys, tempfile, traceback import subprocess from subprocess import * @@ -124,12 +124,39 @@ def get_test_cmd(path, jitflags, lib_dir): return [ JS ] + jitflags + [ '-e', expr, '-f', os.path.join(lib_dir, 'prolog.js'), '-f', path ] +def run_cmd(cmdline, env): + # close_fds is not supported on Windows and will cause a ValueError. + close_fds = sys.platform != 'win32' + p = Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=close_fds, env=env) + out, err = p.communicate() + return out.decode(), err.decode(), p.returncode + +def tmppath(token): + fd, path = tempfile.mkstemp(prefix=token) + os.close(fd) + return path + +def read_and_unlink(path): + f = open(path) + d = f.read() + f.close() + os.unlink(path) + return d + +def run_cmd_avoid_stdio(cmdline, env): + stdoutPath, stderrPath = tmppath('jsstdout'), tmppath('jsstderr') + env['JS_STDOUT'] = stdoutPath + env['JS_STDERR'] = stderrPath + # close_fds is not supported on Windows and will cause a ValueError. + close_fds = sys.platform != 'win32' + p = Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=close_fds, env=env) + _, __ = p.communicate() + return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), p.returncode + def run_test(test, lib_dir): + env = os.environ.copy() if test.tmflags: - env = os.environ.copy() env['TMFLAGS'] = test.tmflags - else: - env = None cmd = get_test_cmd(test.path, test.jitflags, lib_dir) if (test.valgrind and @@ -146,19 +173,20 @@ def run_test(test, lib_dir): if OPTIONS.show_cmd: print(subprocess.list2cmdline(cmd)) - # close_fds is not supported on Windows and will cause a ValueError. - close_fds = sys.platform != 'win32' - p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=close_fds, env=env) - out, err = p.communicate() - out, err = out.decode(), err.decode() + + if OPTIONS.avoid_stdio: + out, err, code = run_cmd_avoid_stdio(cmd, env) + else: + out, err, code = run_cmd(cmd, env) + if OPTIONS.show_output: sys.stdout.write(out) sys.stdout.write(err) sys.stdout.write('Exit code: ' + str(p.returncode) + "\n") if test.valgrind: sys.stdout.write(err) - return (check_output(out, err, p.returncode, test.allow_oom, test.error), - out, err) + return (check_output(out, err, code, test.allow_oom, test.error), + out, err, code) def check_output(out, err, rc, allow_oom, expectedError): if expectedError: @@ -194,15 +222,15 @@ def run_tests(tests, test_dir, lib_dir): try: for i, test in enumerate(tests): doing = 'on %s'%test.path - ok, out, err = run_test(test, lib_dir) + ok, out, err, code = run_test(test, lib_dir) doing = 'after %s'%test.path if not ok: - failures.append(test) + failures.append([ test, out, err, code ]) if OPTIONS.tinderbox: if ok: - print('TEST-PASS | trace-test.py | %s'%test.path) + print('TEST-PASS | trace_test.py | %s'%test.path) else: lines = [ _ for _ in out.split('\n') + err.split('\n') if _ != '' ] @@ -210,7 +238,7 @@ def run_tests(tests, test_dir, lib_dir): msg = lines[-1] else: msg = '' - print('TEST-UNEXPECTED-FAIL | trace-test.py | %s: %s'% + print('TEST-UNEXPECTED-FAIL | trace_test.py | %s: %s'% (test.path, msg)) n = i + 1 @@ -219,7 +247,7 @@ def run_tests(tests, test_dir, lib_dir): pb.update(n) complete = True except KeyboardInterrupt: - print('TEST-UNEXPECTED_FAIL | trace-test.py | %s'%test.path) + print('TEST-UNEXPECTED_FAIL | trace_test.py | %s'%test.path) if pb: pb.finish() @@ -230,7 +258,7 @@ def run_tests(tests, test_dir, lib_dir): out = open(OPTIONS.write_failures, 'w') # Don't write duplicate entries when we are doing multiple failures per job. written = set() - for test in failures: + for test, fout, ferr, fcode in failures: if test.path not in written: out.write(os.path.relpath(test.path, test_dir) + '\n') written.add(test.path) @@ -242,7 +270,7 @@ def run_tests(tests, test_dir, lib_dir): sys.stderr.write('---\n') print('FAILURES:') - for test in failures: + for test, _, __, ___ in failures: if OPTIONS.show_failed: print(' ' + subprocess.list2cmdline(get_test_cmd(test.path, test.jitflags, lib_dir))) else: @@ -262,7 +290,25 @@ def parse_jitflags(): sys.exit(1) return jitflags -if __name__ == '__main__': +def platform_might_be_android(): + try: + # The python package for SL4A provides an |android| module. + # If that module is present, we're likely in SL4A-python on + # device. False positives and negatives are possible, + # however. + import android + return True + except ImportError: + return False + +def stdio_might_be_broken(): + return platform_might_be_android() + +JS = None +OPTIONS = None +def main(argv): + global JS, OPTIONS + script_path = os.path.abspath(__file__) script_dir = os.path.dirname(script_path) test_dir = os.path.join(script_dir, 'tests') @@ -301,13 +347,25 @@ if __name__ == '__main__': help='Run all tests with valgrind, if valgrind is in $PATH.') op.add_option('--jitflags', dest='jitflags', default='j', help='Example: --jitflags=j,mj to run each test with -j and -m -j') - (OPTIONS, args) = op.parse_args() + op.add_option('--avoid-stdio', dest='avoid_stdio', action='store_true', + help='Use js-shell file indirection instead of piping stdio.') + (OPTIONS, args) = op.parse_args(argv) if len(args) < 1: op.error('missing JS_SHELL argument') # We need to make sure we are using backslashes on Windows. JS, test_args = os.path.normpath(args[0]), args[1:] JS = os.path.realpath(JS) # Burst through the symlinks! + if stdio_might_be_broken(): + # Prefer erring on the side of caution and not using stdio if + # it might be broken on this platform. The file-redirect + # fallback should work on any platform, so at worst by + # guessing wrong we might have slowed down the tests a bit. + # + # XXX technically we could check for broken stdio, but it + # really seems like overkill. + OPTIONS.avoid_stdio = True + if OPTIONS.retest: OPTIONS.read_tests = OPTIONS.retest OPTIONS.write_failures = OPTIONS.retest @@ -386,3 +444,6 @@ if __name__ == '__main__': sys.exit(1) else: raise + +if __name__ == '__main__': + main(sys.argv[1:]) From a48b42642c415ef0fae152268f17e75c8257907d Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 1 Oct 2010 16:09:21 -0500 Subject: [PATCH 011/284] Bug 600639, part 3: Add a --write-failure-output option to trace-test to additionally log output from failed tests. r=dmandelin a=sayrer --- js/src/trace-test/trace_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/js/src/trace-test/trace_test.py b/js/src/trace-test/trace_test.py index 79f27275e68f..e432d4d65f19 100644 --- a/js/src/trace-test/trace_test.py +++ b/js/src/trace-test/trace_test.py @@ -261,6 +261,10 @@ def run_tests(tests, test_dir, lib_dir): for test, fout, ferr, fcode in failures: if test.path not in written: out.write(os.path.relpath(test.path, test_dir) + '\n') + if OPTIONS.write_failure_output: + out.write(fout) + out.write(ferr) + out.write('Exit code: ' + str(fcode) + "\n") written.add(test.path) out.close() except IOError: @@ -349,6 +353,8 @@ def main(argv): help='Example: --jitflags=j,mj to run each test with -j and -m -j') op.add_option('--avoid-stdio', dest='avoid_stdio', action='store_true', help='Use js-shell file indirection instead of piping stdio.') + op.add_option('--write-failure-output', dest='write_failure_output', action='store_true', + help='With --write-failures=FILE, additionally write the output of failed tests to [FILE]') (OPTIONS, args) = op.parse_args(argv) if len(args) < 1: op.error('missing JS_SHELL argument') From 52fc02d2d1151d03aef2c4e8f5991c716ed8fb3a Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 1 Oct 2010 16:09:21 -0500 Subject: [PATCH 012/284] Bug 600304: Add a PrintJSStack() friend of DumpJSStack() that returns a newly-allocated string instead of printing to stdout. r=jorendorff a=sayrer --- js/src/xpconnect/idl/nsIXPConnect.idl | 3 +++ js/src/xpconnect/src/nsXPConnect.cpp | 25 +++++++++++++++++++++++++ js/src/xpconnect/src/xpcdebug.cpp | 21 ++++++++++++++------- js/src/xpconnect/src/xpcprivate.h | 12 ++++++++++++ 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/js/src/xpconnect/idl/nsIXPConnect.idl b/js/src/xpconnect/idl/nsIXPConnect.idl index e443f4aba7ee..60afa69072fb 100644 --- a/js/src/xpconnect/idl/nsIXPConnect.idl +++ b/js/src/xpconnect/idl/nsIXPConnect.idl @@ -881,6 +881,9 @@ interface nsIXPConnect : nsISupports virtual nsIPrincipal* GetPrincipal(JSObject* obj, PRBool allowShortCircuit) const = 0; #endif + virtual char* DebugPrintJSStack(PRBool showArgs, + PRBool showLocals, + PRBool showThisProps) = 0; %} [notxpcom] void getNativeWrapperGetPropertyOp(out JSPropertyOp getProperty); diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 321dcb425fe8..b0de4108dc13 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -2228,6 +2228,22 @@ nsXPConnect::DebugDumpJSStack(PRBool showArgs, return NS_OK; } +char* +nsXPConnect::DebugPrintJSStack(PRBool showArgs, + PRBool showLocals, + PRBool showThisProps) +{ + JSContext* cx; + if(NS_FAILED(Peek(&cx))) + printf("failed to peek into nsIThreadJSContextStack service!\n"); + else if(!cx) + printf("there is no JSContext on the nsIThreadJSContextStack!\n"); + else + return xpc_PrintJSStack(cx, showArgs, showLocals, showThisProps); + + return nsnull; +} + /* void debugDumpEvalInJSStackFrame (in PRUint32 aFrameNumber, in string aSourceText); */ NS_IMETHODIMP nsXPConnect::DebugDumpEvalInJSStackFrame(PRUint32 aFrameNumber, const char *aSourceText) @@ -2738,6 +2754,15 @@ JS_EXPORT_API(void) DumpJSStack() printf("failed to get XPConnect service!\n"); } +JS_EXPORT_API(char*) PrintJSStack() +{ + nsresult rv; + nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); + return (NS_SUCCEEDED(rv) && xpc) ? + xpc->DebugPrintJSStack(PR_TRUE, PR_TRUE, PR_FALSE) : + nsnull; +} + JS_EXPORT_API(void) DumpJSEval(PRUint32 frameno, const char* text) { nsresult rv; diff --git a/js/src/xpconnect/src/xpcdebug.cpp b/js/src/xpconnect/src/xpcdebug.cpp index 1c37911f015f..15a9ac30a9d7 100644 --- a/js/src/xpconnect/src/xpcdebug.cpp +++ b/js/src/xpconnect/src/xpcdebug.cpp @@ -286,6 +286,18 @@ static char* FormatJSStackDump(JSContext* cx, char* buf, JSBool xpc_DumpJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, JSBool showThisProps) +{ + if(char* buf = xpc_PrintJSStack(cx, showArgs, showLocals, showThisProps)) + { + fputs(buf, stdout); + JS_smprintf_free(buf); + } + return JS_TRUE; +} + +char* +xpc_PrintJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, + JSBool showThisProps) { char* buf; JSExceptionState *state = JS_SaveExceptionState(cx); @@ -295,16 +307,11 @@ xpc_DumpJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, JSBool showTh JS_ClearPendingException(cx); buf = FormatJSStackDump(cx, nsnull, showArgs, showLocals, showThisProps); - if(buf) - { - fputs(buf, stdout); - JS_smprintf_free(buf); - } - else + if(!buf) puts("Failed to format JavaScript stack for dump"); JS_RestoreExceptionState(cx, state); - return JS_TRUE; + return buf; } /***************************************************************************/ diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index ce674d13e209..bc77e6c6d213 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -512,6 +512,11 @@ public: void RecordTraversal(void *p, nsISupports *s); #endif + virtual char* DebugPrintJSStack(PRBool showArgs, + PRBool showLocals, + PRBool showThisProps); + + static PRBool ReportAllJSExceptions() { return gReportAllJSExceptions > 0; @@ -3909,6 +3914,13 @@ extern JSBool xpc_DumpJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, JSBool showThisProps); +// Return a newly-allocated string containing a representation of the +// current JS stack. It is the *caller's* responsibility to free this +// string with JS_smprintf_free(). +extern char* +xpc_PrintJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, + JSBool showThisProps); + extern JSBool xpc_DumpEvalInJSStackFrame(JSContext* cx, JSUint32 frameno, const char* text); From 332fe2d51e9f5e2a46715c08ccf8c8ca1778be33 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 1 Oct 2010 16:09:21 -0500 Subject: [PATCH 013/284] Follow-up to bug 579479: Fix --disable-tracejit --enable-methodjit compiles. irc-r=sstangl a=bustage --- js/src/methodjit/Compiler.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 208edf2fa435..4cbd33702270 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -4184,8 +4184,10 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne, Jump *sl { #ifndef JS_TRACER jumpInScript(j, target); - if (slow) - stubcc.jumpInScript(*slow, target); + if (slowOne) + stubcc.jumpInScript(*slowOne, target); + if (slowTwo) + stubcc.jumpInScript(*slowTwo, target); #else if (!addTraceHints || target >= PC || JSOp(*target) != JSOP_TRACE) { jumpInScript(j, target); From 52851fbf4e0d9b8da4d83ae0aa5291fe22de7f39 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Fri, 1 Oct 2010 15:34:32 -0700 Subject: [PATCH 014/284] Bug 600639 followup: unbreak non-threadsafe shell builds, r=cjones --- js/src/shell/js.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 2da24e7af25c..dea6d8c0c79d 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -79,7 +79,6 @@ #include "jsxml.h" #include "jsperf.h" -#include "prenv.h" #include "prmjtime.h" #ifdef JSDEBUGGER @@ -861,12 +860,14 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) StartTraceVis(argv[i]); break; #endif +#ifdef JS_THREADSAFE case 'g': if (++i == argc) return usage(); PR_Sleep(PR_SecondsToInterval(atoi(argv[i]))); break; +#endif default: return usage(); @@ -5336,7 +5337,7 @@ MaybeOverrideOutFileFromEnv(const char* const envVar, FILE* defaultOut, FILE** outFile) { - const char* outPath = PR_GetEnv(envVar); + const char* outPath = getenv(envVar); if (!outPath || !*outPath || !(*outFile = fopen(outPath, "w"))) { *outFile = defaultOut; } From 6b5de12b7654074bc4466c0a8d958146f577187c Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Fri, 1 Oct 2010 16:01:49 -0700 Subject: [PATCH 015/284] Backed out changeset 97d2c33271e8 due to crashtest failures --- js/src/jsarray.cpp | 4 -- js/src/jsiter.cpp | 65 ++++---------------- js/src/jsiter.h | 3 - js/src/trace-test/tests/basic/bug595963-1.js | 19 ------ js/src/trace-test/tests/basic/bug595963-2.js | 19 ------ 5 files changed, 12 insertions(+), 98 deletions(-) delete mode 100644 js/src/trace-test/tests/basic/bug595963-1.js delete mode 100644 js/src/trace-test/tests/basic/bug595963-2.js diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index af63217d8825..c5e1806f88e8 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2338,7 +2338,6 @@ array_splice(JSContext *cx, uintN argc, Value *vp) JSObject *obj = ComputeThisFromVp(cx, vp); if (!obj || !js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; - jsuint origlength = length; /* Convert the first argument into a starting index. */ jsdouble d; @@ -2453,9 +2452,6 @@ array_splice(JSContext *cx, uintN argc, Value *vp) length -= delta; } - if (length < origlength && !js_SuppressDeletedIndexProperties(cx, obj, length, origlength)) - return JS_FALSE; - /* * Copy from argv into the hole to complete the splice, and update length in * case we deleted elements from the end. diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index c2bad94bb81e..33e569b508b6 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -871,25 +871,17 @@ js_CloseIterator(JSContext *cx, JSObject *obj) } /* - * Suppress enumeration of deleted properties. This function must be called - * when a property is deleted and there might be active enumerators. - * - * We maintain a list of active non-escaping for-in enumerators. To suppress - * a property, we check whether each active enumerator contains the (obj, id) - * pair and has not yet enumerated |id|. If so, and |id| is the next property, - * we simply advance the cursor. Otherwise, we delete |id| from the list. + * Suppress enumeration of deleted properties. We maintain a list of all active + * non-escaping for-in enumerators. Whenever a property is deleted, we check + * whether any active enumerator contains the (obj, id) pair and has not + * enumerated id yet. If so, we delete the id from the list (or advance the + * cursor if it is the next id to be enumerated). * * We do not suppress enumeration of a property deleted along an object's * prototype chain. Only direct deletions on the object are handled. - * - * This function can suppress multiple properties at once. The |predicate| - * argument is an object which can be called on an id and returns true or - * false. It also must have a method |matchesAtMostOne| which allows us to - * stop searching after the first deletion if true. */ -template -static bool -SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicate) +bool +js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) { JSObject *iterobj = cx->enumerators; while (iterobj) { @@ -901,7 +893,7 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicat jsid *props_cursor = ni->currentKey(); jsid *props_end = ni->endKey(); for (jsid *idp = props_cursor; idp < props_end; ++idp) { - if (predicate(*idp)) { + if (*idp == id) { /* * Check whether another property along the prototype chain * became visible as a result of this deletion. @@ -910,14 +902,14 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicat AutoObjectRooter proto(cx, obj->getProto()); AutoObjectRooter obj2(cx); JSProperty *prop; - if (!proto.object()->lookupProperty(cx, *idp, obj2.addr(), &prop)) + if (!proto.object()->lookupProperty(cx, id, obj2.addr(), &prop)) return false; if (prop) { uintN attrs; if (obj2.object()->isNative()) { attrs = ((Shape *) prop)->attributes(); JS_UNLOCK_OBJ(cx, obj2.object()); - } else if (!obj2.object()->getAttributes(cx, *idp, &attrs)) { + } else if (!obj2.object()->getAttributes(cx, id, &attrs)) { return false; } if (attrs & JSPROP_ENUMERATE) @@ -933,7 +925,7 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicat goto again; /* - * No property along the prototype chain stepped in to take the + * No property along the prototype chain steppeded in to take the * property's place, so go ahead and delete id from the list. * If it is the next property to be enumerated, just skip it. */ @@ -943,8 +935,7 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicat memmove(idp, idp + 1, (props_end - (idp + 1)) * sizeof(jsid)); ni->props_end = ni->endKey() - 1; } - if (predicate.matchesAtMostOne()) - break; + break; } } } @@ -953,38 +944,6 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicat return true; } -class SingleIdPredicate { - jsid id; -public: - SingleIdPredicate(jsid id) : id(id) {} - - bool operator()(jsid id) { return id == this->id; } - bool matchesAtMostOne() { return true; } -}; - -bool -js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) -{ - return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id)); -} - -class IndexRangePredicate { - jsint begin, end; -public: - IndexRangePredicate(jsint begin, jsint end) : begin(begin), end(end) {} - - bool operator()(jsid id) { - return JSID_IS_INT(id) && begin <= JSID_TO_INT(id) && JSID_TO_INT(id) < end; - } - bool matchesAtMostOne() { return false; } -}; - -bool -js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end) -{ - return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end)); -} - JSBool js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) { diff --git a/js/src/jsiter.h b/js/src/jsiter.h index d47cb565f284..022369e8fe5e 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -171,9 +171,6 @@ js_CloseIterator(JSContext *cx, JSObject *iterObj); bool js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id); -bool -js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end); - /* * IteratorMore() indicates whether another value is available. It might * internally call iterobj.next() and then cache the value until its diff --git a/js/src/trace-test/tests/basic/bug595963-1.js b/js/src/trace-test/tests/basic/bug595963-1.js deleted file mode 100644 index 2cccfac400d1..000000000000 --- a/js/src/trace-test/tests/basic/bug595963-1.js +++ /dev/null @@ -1,19 +0,0 @@ -function remove(k, L) { - for (var i in k) { - if (i == L) - k.splice(L, 1); - } -} -function f(k) { - var L = 0; - for (var i in k) { - if (L == 1) - remove(k, L); - L++; - assertEq(k[i], 3); - } - assertEq(L, 6); -} - -var a = [3, 3, 3, 3, 3, 3, 3]; -f(a); diff --git a/js/src/trace-test/tests/basic/bug595963-2.js b/js/src/trace-test/tests/basic/bug595963-2.js deleted file mode 100644 index 651a38064ea3..000000000000 --- a/js/src/trace-test/tests/basic/bug595963-2.js +++ /dev/null @@ -1,19 +0,0 @@ -function remove(k, L) { - for (var i in k) { - if (i == L) - k.splice(L, 3); - } -} -function f(k) { - var L = 0; - for (var i in k) { - if (L == 1) - remove(k, L); - L++; - assertEq(k[i], 3); - } - assertEq(L, 4); -} - -var a = [3, 3, 3, 3, 3, 3, 3]; -f(a); From faf46059c83496621af6b8ed9373c2a0c28329f9 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 1 Oct 2010 18:46:54 -0500 Subject: [PATCH 016/284] Finally get rid of /* Added by JSIFY */ comments. r=Waldo. --- js/src/jsapi.cpp | 4 ++-- js/src/jsarena.cpp | 4 ++-- js/src/jsarray.cpp | 2 +- js/src/jsatom.cpp | 4 ++-- js/src/jsbool.cpp | 2 +- js/src/jscntxt.cpp | 4 ++-- js/src/jscntxt.h | 2 +- js/src/jsdate.cpp | 2 +- js/src/jsdbgapi.cpp | 2 +- js/src/jsemit.cpp | 4 ++-- js/src/jsexn.cpp | 2 +- js/src/jsfun.cpp | 2 +- js/src/jsgc.cpp | 4 ++-- js/src/jshash.cpp | 4 ++-- js/src/jsinterp.cpp | 4 ++-- js/src/jslock.cpp | 2 +- js/src/jsnum.cpp | 2 +- js/src/jsobj.cpp | 6 +++--- js/src/jsobj.h | 2 +- js/src/jsopcode.cpp | 4 ++-- js/src/jsparse.cpp | 4 ++-- js/src/jsprf.cpp | 2 +- js/src/jsscan.cpp | 4 ++-- js/src/jsscope.cpp | 2 +- js/src/jsscript.cpp | 2 +- js/src/jsstr.cpp | 4 ++-- js/src/jsxdrapi.cpp | 2 +- 27 files changed, 41 insertions(+), 41 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index b1c583beff74..1047748c9c71 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -47,8 +47,8 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ +#include "jsarena.h" +#include "jsutil.h" #include "jsclist.h" #include "jsdhash.h" #include "jsprf.h" diff --git a/js/src/jsarena.cpp b/js/src/jsarena.cpp index b6f661c602ab..80440bcda1bc 100644 --- a/js/src/jsarena.cpp +++ b/js/src/jsarena.cpp @@ -47,8 +47,8 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsbit.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ +#include "jsarena.h" +#include "jsutil.h" #ifdef JS_ARENAMETER static JSArenaStats *arena_stats_list; diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index c5e1806f88e8..3b95c57d4ec8 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -79,7 +79,7 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index ff248ab411e9..3d6007bcd810 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -44,8 +44,8 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ +#include "jsutil.h" +#include "jshash.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp index 9f1bec4fc778..63c2c2f17516 100644 --- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -42,7 +42,7 @@ */ #include "jstypes.h" #include "jsstdint.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsapi.h" #include "jsatom.h" #include "jsbool.h" diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 42e9c73b0a8b..c9a896b8ce7b 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -49,8 +49,8 @@ #include "jsstdint.h" #include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ +#include "jsarena.h" +#include "jsutil.h" #include "jsclist.h" #include "jsprf.h" #include "jsatom.h" diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index ec2c7ba5ea58..894a599dafcd 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -53,7 +53,7 @@ #endif #include "jsprvtd.h" -#include "jsarena.h" /* Added by JSIFY */ +#include "jsarena.h" #include "jsclist.h" #include "jslong.h" #include "jsatom.h" diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 135b6b9643c4..85bcc0086991 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -60,7 +60,7 @@ #include "jsstdint.h" #include "jsprf.h" #include "prmjtime.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsapi.h" #include "jsversion.h" #include "jsbuiltins.h" diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 64f6862683b3..0b92a81b6218 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -44,7 +44,7 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsclist.h" #include "jsapi.h" #include "jscntxt.h" diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 2410e494d6ed..7d4db58b24f0 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -48,8 +48,8 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ +#include "jsarena.h" +#include "jsutil.h" #include "jsbit.h" #include "jsprf.h" #include "jsapi.h" diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 38ff961fffa0..c21b0c396560 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -46,7 +46,7 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsprf.h" #include "jsapi.h" #include "jscntxt.h" diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 0ff818461ada..2eec2313b2a5 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -45,7 +45,7 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index fb3a09beb632..fd122508ec9f 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -53,8 +53,8 @@ #include /* for memset used when DEBUG */ #include "jstypes.h" #include "jsstdint.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ +#include "jsutil.h" +#include "jshash.h" #include "jsbit.h" #include "jsclist.h" #include "jsprf.h" diff --git a/js/src/jshash.cpp b/js/src/jshash.cpp index 15a8a460ab5c..3d546e312e0a 100644 --- a/js/src/jshash.cpp +++ b/js/src/jshash.cpp @@ -45,8 +45,8 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ +#include "jsutil.h" +#include "jshash.h" /* Compute the number of buckets in ht */ #define NBUCKETS(ht) JS_BIT(JS_HASH_BITS - (ht)->shift) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index bf60f475d605..2879e8094d0c 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -46,8 +46,8 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ +#include "jsarena.h" +#include "jsutil.h" #include "jsprf.h" #include "jsapi.h" #include "jsarray.h" diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp index 39df1a03b6a4..807951ff86b1 100644 --- a/js/src/jslock.cpp +++ b/js/src/jslock.cpp @@ -45,7 +45,7 @@ #include #include #include "jspubtd.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jstypes.h" #include "jsstdint.h" #include "jsbit.h" diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 0976d495f646..1056c9ef7010 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -53,7 +53,7 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsapi.h" #include "jsatom.h" #include "jsbuiltins.h" diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 62acdb517336..de4b2c6bfcbc 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -45,10 +45,10 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" /* Added by JSIFY */ +#include "jsarena.h" #include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ +#include "jsutil.h" +#include "jshash.h" #include "jsdhash.h" #include "jsprf.h" #include "jsapi.h" diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 62c03b418eb2..5b4b66b66cd2 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -49,7 +49,7 @@ * is reference counted and the slot vector is malloc'ed. */ #include "jsapi.h" -#include "jshash.h" /* Added by JSIFY */ +#include "jshash.h" #include "jspubtd.h" #include "jsprvtd.h" #include "jslock.h" diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index f711ff48aa47..efbb1ac9d8fa 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -50,8 +50,8 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ +#include "jsarena.h" +#include "jsutil.h" #include "jsprf.h" #include "jsapi.h" #include "jsarray.h" diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index b56214333785..176ac0f38f9e 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -56,8 +56,8 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ +#include "jsarena.h" +#include "jsutil.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" diff --git a/js/src/jsprf.cpp b/js/src/jsprf.cpp index 6e02bc2d1857..edbc8a9bd225 100644 --- a/js/src/jsprf.cpp +++ b/js/src/jsprf.cpp @@ -48,7 +48,7 @@ #include "jsprf.h" #include "jsstdint.h" #include "jslong.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jspubtd.h" #include "jsstr.h" diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index f5c7c3318518..7bce38c35fa2 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -53,9 +53,9 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" /* Added by JSIFY */ +#include "jsarena.h" #include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 4319422d5a8c..1a79c88ab8f8 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -50,7 +50,7 @@ #include "jsbit.h" #include "jsclist.h" #include "jsdhash.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 9ca7bf3fc796..ef2649c5ebc7 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -44,7 +44,7 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index f6311df25913..fd5b63aa628f 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -52,8 +52,8 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ +#include "jsutil.h" +#include "jshash.h" #include "jsprf.h" #include "jsapi.h" #include "jsarray.h" diff --git a/js/src/jsxdrapi.cpp b/js/src/jsxdrapi.cpp index 8e3f3f266717..7c70d11ed025 100644 --- a/js/src/jsxdrapi.cpp +++ b/js/src/jsxdrapi.cpp @@ -44,7 +44,7 @@ #include #include "jstypes.h" #include "jsstdint.h" -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsdhash.h" #include "jsprf.h" #include "jsapi.h" From 028602bb7a7ed8b769b1cb173868c73fa7a2ba39 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 1 Oct 2010 17:53:08 -0700 Subject: [PATCH 017/284] Bug 587914 - js::Valueify in static data constructors causes runtime initialization (r=igor) --HG-- extra : rebase_source : 09f2d8b620985eff96468ef7875cab8751a93d18 --- js/ipc/ObjectWrapperParent.cpp | 18 ++--- js/src/ctypes/CTypes.cpp | 2 +- js/src/jsvalue.h | 10 ++- .../xpconnect/src/XPCChromeObjectWrapper.cpp | 14 ++-- js/src/xpconnect/src/XPCNativeWrapper.cpp | 38 +++++----- .../xpconnect/src/XPCSafeJSObjectWrapper.cpp | 18 ++--- js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp | 16 ++--- .../xpconnect/src/xpcwrappednativejsops.cpp | 72 +++++++++---------- modules/plugin/base/src/nsJSNPRuntime.cpp | 2 +- 9 files changed, 98 insertions(+), 92 deletions(-) diff --git a/js/ipc/ObjectWrapperParent.cpp b/js/ipc/ObjectWrapperParent.cpp index 4e8209f31d8c..5f375febff9b 100644 --- a/js/ipc/ObjectWrapperParent.cpp +++ b/js/ipc/ObjectWrapperParent.cpp @@ -173,23 +173,23 @@ const js::Class ObjectWrapperParent::sCPOW_JSClass = { "CrossProcessObjectWrapper", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(sNumSlots), - js::Valueify(ObjectWrapperParent::CPOW_AddProperty), - js::Valueify(ObjectWrapperParent::CPOW_DelProperty), - js::Valueify(ObjectWrapperParent::CPOW_GetProperty), - js::Valueify(ObjectWrapperParent::CPOW_SetProperty), + JS_VALUEIFY(js::PropertyOp, ObjectWrapperParent::CPOW_AddProperty), + JS_VALUEIFY(js::PropertyOp, ObjectWrapperParent::CPOW_DelProperty), + JS_VALUEIFY(js::PropertyOp, ObjectWrapperParent::CPOW_GetProperty), + JS_VALUEIFY(js::PropertyOp, ObjectWrapperParent::CPOW_SetProperty), (JSEnumerateOp) ObjectWrapperParent::CPOW_NewEnumerate, (JSResolveOp) ObjectWrapperParent::CPOW_NewResolve, - js::Valueify(ObjectWrapperParent::CPOW_Convert), + JS_VALUEIFY(js::ConvertOp, ObjectWrapperParent::CPOW_Convert), ObjectWrapperParent::CPOW_Finalize, nsnull, // reserved1 nsnull, // checkAccess - js::Valueify(ObjectWrapperParent::CPOW_Call), - js::Valueify(ObjectWrapperParent::CPOW_Construct), + JS_VALUEIFY(js::CallOp, ObjectWrapperParent::CPOW_Call), + JS_VALUEIFY(js::CallOp, ObjectWrapperParent::CPOW_Construct), nsnull, // xdrObject - js::Valueify(ObjectWrapperParent::CPOW_HasInstance), + JS_VALUEIFY(js::HasInstanceOp, ObjectWrapperParent::CPOW_HasInstance), nsnull, // mark { - js::Valueify(ObjectWrapperParent::CPOW_Equality), + JS_VALUEIFY(js::EqualityOp, ObjectWrapperParent::CPOW_Equality), nsnull, // outerObject nsnull, // innerObject nsnull, // iteratorObject diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 05dcb456ef21..157090979e09 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -5362,7 +5362,7 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) for (JSUint32 i = 0; i < cif->nargs; ++i) argv[i] = JSVAL_VOID; - js::AutoArrayRooter roots(cx, argv.length(), Valueify(argv.begin())); + js::AutoArrayRooter roots(cx, argv.length(), argv.begin()); for (JSUint32 i = 0; i < cif->nargs; ++i) { // Convert each argument, and have any CData objects created depend on // the existing buffers. diff --git a/js/src/jsvalue.h b/js/src/jsvalue.h index 65354aa94c4d..42b0ce7edf62 100644 --- a/js/src/jsvalue.h +++ b/js/src/jsvalue.h @@ -1052,6 +1052,9 @@ static JS_ALWAYS_INLINE PropertyDescriptor * Valueify(JSPropertyDescriptor *p) */ #ifdef DEBUG +# define JS_VALUEIFY(type, v) js::Valueify(v) +# define JS_JSVALIFY(type, v) js::Jsvalify(v) + static inline JSNative JsvalifyNative(Native n) { return (JSNative)n; } static inline JSNative JsvalifyNative(JSNative n) { return n; } static inline Native ValueifyNative(JSNative n) { return (Native)n; } @@ -1062,8 +1065,11 @@ static inline Native ValueifyNative(Native n) { return n; } #else -# define JS_VALUEIFY_NATIVE(n) ((js::Native)n) -# define JS_JSVALIFY_NATIVE(n) ((JSNative)n) +# define JS_VALUEIFY(type, v) ((type)(v)) +# define JS_JSVALIFY(type, v) ((type)(v)) + +# define JS_VALUEIFY_NATIVE(n) ((js::Native)(n)) +# define JS_JSVALIFY_NATIVE(n) ((JSNative)(n)) #endif diff --git a/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp b/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp index fb36a15b027b..43ef81281e4b 100644 --- a/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp +++ b/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp @@ -255,16 +255,16 @@ js::Class COWClass = { "ChromeObjectWrapper", JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(XPCWrapper::sNumSlots + 1), - js::Valueify(XPC_COW_AddProperty), - js::Valueify(XPC_COW_DelProperty), - js::Valueify(XPC_COW_GetProperty), - js::Valueify(XPC_COW_SetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_COW_AddProperty), + JS_VALUEIFY(js::PropertyOp, XPC_COW_DelProperty), + JS_VALUEIFY(js::PropertyOp, XPC_COW_GetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_COW_SetProperty), XPC_COW_Enumerate, (JSResolveOp)XPC_COW_NewResolve, - js::Valueify(XPC_COW_Convert), + JS_VALUEIFY(js::ConvertOp, XPC_COW_Convert), JS_FinalizeStub, nsnull, // reserved0 - js::Valueify(XPC_COW_CheckAccess), + JS_VALUEIFY(js::CheckAccessOp, XPC_COW_CheckAccess), nsnull, // call nsnull, // construct nsnull, // xdrObject @@ -273,7 +273,7 @@ js::Class COWClass = { // ClassExtension { - js::Valueify(XPC_COW_Equality), + JS_VALUEIFY(js::EqualityOp, XPC_COW_Equality), nsnull, // outerObject nsnull, // innerObject XPC_COW_Iterator, diff --git a/js/src/xpconnect/src/XPCNativeWrapper.cpp b/js/src/xpconnect/src/XPCNativeWrapper.cpp index 67d6a84dc0ab..f5a059acf6e5 100644 --- a/js/src/xpconnect/src/XPCNativeWrapper.cpp +++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp @@ -112,25 +112,25 @@ js::Class NW_NoCall_Class = { // Our one reserved slot holds a jsint of flag bits JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_MARK_IS_TRACE | JSCLASS_CONSTRUCT_PROTOTYPE, - js::Valueify(XPC_NW_AddProperty), - js::Valueify(XPC_NW_DelProperty), - js::Valueify(XPC_NW_GetProperty), - js::Valueify(XPC_NW_SetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_NW_AddProperty), + JS_VALUEIFY(js::PropertyOp, XPC_NW_DelProperty), + JS_VALUEIFY(js::PropertyOp, XPC_NW_GetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_NW_SetProperty), XPC_NW_Enumerate, (JSResolveOp)XPC_NW_NewResolve, - js::Valueify(XPC_NW_Convert), + JS_VALUEIFY(js::ConvertOp, XPC_NW_Convert), XPC_NW_Finalize, nsnull, // reserved0 - js::Valueify(XPC_NW_CheckAccess), + JS_VALUEIFY(js::CheckAccessOp, XPC_NW_CheckAccess), nsnull, // call - js::Valueify(XPC_NW_Construct), + JS_VALUEIFY(js::CallOp, XPC_NW_Construct), nsnull, // xdrObject - js::Valueify(XPC_NW_HasInstance), + JS_VALUEIFY(js::HasInstanceOp, XPC_NW_HasInstance), JS_CLASS_TRACE(XPC_NW_Trace), // ClassExtension { - js::Valueify(XPC_NW_Equality), + JS_VALUEIFY(js::EqualityOp, XPC_NW_Equality), nsnull, // outerObject nsnull, // innerObject XPC_NW_Iterator, @@ -144,25 +144,25 @@ js::Class NW_Call_Class = { // Our one reserved slot holds a jsint of flag bits JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_MARK_IS_TRACE | JSCLASS_CONSTRUCT_PROTOTYPE, - js::Valueify(XPC_NW_AddProperty), - js::Valueify(XPC_NW_DelProperty), - js::Valueify(XPC_NW_GetProperty), - js::Valueify(XPC_NW_SetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_NW_AddProperty), + JS_VALUEIFY(js::PropertyOp, XPC_NW_DelProperty), + JS_VALUEIFY(js::PropertyOp, XPC_NW_GetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_NW_SetProperty), XPC_NW_Enumerate, (JSResolveOp)XPC_NW_NewResolve, - js::Valueify(XPC_NW_Convert), + JS_VALUEIFY(js::ConvertOp, XPC_NW_Convert), XPC_NW_Finalize, nsnull, // reserved0 - js::Valueify(XPC_NW_CheckAccess), - js::Valueify(XPC_NW_Call), - js::Valueify(XPC_NW_Construct), + JS_VALUEIFY(js::CheckAccessOp, XPC_NW_CheckAccess), + JS_VALUEIFY(js::CallOp, XPC_NW_Call), + JS_VALUEIFY(js::CallOp, XPC_NW_Construct), nsnull, // xdrObject - js::Valueify(XPC_NW_HasInstance), + JS_VALUEIFY(js::HasInstanceOp, XPC_NW_HasInstance), JS_CLASS_TRACE(XPC_NW_Trace), // ClassExtension { - js::Valueify(XPC_NW_Equality), + JS_VALUEIFY(js::EqualityOp, XPC_NW_Equality), nsnull, // outerObject nsnull, // innerObject XPC_NW_Iterator, diff --git a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp index bf881d8c337f..e5baced3b6a8 100644 --- a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp +++ b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp @@ -253,25 +253,25 @@ js::Class SJOWClass = { "XPCSafeJSObjectWrapper", JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(sSJOWSlots), - js::Valueify(XPC_SJOW_AddProperty), - js::Valueify(XPC_SJOW_DelProperty), - js::Valueify(XPC_SJOW_GetProperty), - js::Valueify(XPC_SJOW_SetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_SJOW_AddProperty), + JS_VALUEIFY(js::PropertyOp, XPC_SJOW_DelProperty), + JS_VALUEIFY(js::PropertyOp, XPC_SJOW_GetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_SJOW_SetProperty), XPC_SJOW_Enumerate, (JSResolveOp)XPC_SJOW_NewResolve, - js::Valueify(XPC_SJOW_Convert), + JS_VALUEIFY(js::ConvertOp, XPC_SJOW_Convert), XPC_SJOW_Finalize, nsnull, // reserved0 - js::Valueify(XPC_SJOW_CheckAccess), - js::Valueify(XPC_SJOW_Call), - js::Valueify(XPC_SJOW_Create), + JS_VALUEIFY(js::CheckAccessOp, XPC_SJOW_CheckAccess), + JS_VALUEIFY(js::CallOp, XPC_SJOW_Call), + JS_VALUEIFY(js::CallOp, XPC_SJOW_Create), nsnull, // xdrObject nsnull, // hasInstance nsnull, // mark // ClassExtension { - js::Valueify(XPC_SJOW_Equality), + JS_VALUEIFY(js::EqualityOp, XPC_SJOW_Equality), nsnull, // outerObject nsnull, // innerObject XPC_SJOW_Iterator, diff --git a/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp b/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp index 1fdb2d7b937d..c9d1a8b92b96 100644 --- a/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp +++ b/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp @@ -102,25 +102,25 @@ js::Class SOWClass = { "SystemOnlyWrapper", JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(XPCWrapper::sNumSlots), - js::Valueify(XPC_SOW_AddProperty), - js::Valueify(XPC_SOW_DelProperty), - js::Valueify(XPC_SOW_GetProperty), - js::Valueify(XPC_SOW_SetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_SOW_AddProperty), + JS_VALUEIFY(js::PropertyOp, XPC_SOW_DelProperty), + JS_VALUEIFY(js::PropertyOp, XPC_SOW_GetProperty), + JS_VALUEIFY(js::PropertyOp, XPC_SOW_SetProperty), XPC_SOW_Enumerate, (JSResolveOp)XPC_SOW_NewResolve, - js::Valueify(XPC_SOW_Convert), + JS_VALUEIFY(js::ConvertOp, XPC_SOW_Convert), nsnull, // finalize nsnull, // reserved0 - js::Valueify(XPC_SOW_CheckAccess), + JS_VALUEIFY(js::CheckAccessOp, XPC_SOW_CheckAccess), nsnull, // call nsnull, // construct nsnull, // xdrObject - js::Valueify(XPC_SOW_HasInstance), + JS_VALUEIFY(js::HasInstanceOp, XPC_SOW_HasInstance), nsnull, // mark // ClassExtension { - js::Valueify(XPC_SOW_Equality), + JS_VALUEIFY(js::EqualityOp, XPC_SOW_Equality), nsnull, // outerObject nsnull, // innerObject XPC_SOW_Iterator, diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index eea9e23348c1..8aa2aa4a9406 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -924,15 +924,15 @@ js::Class XPC_WN_NoHelper_JSClass = { JSCLASS_MARK_IS_TRACE, // flags; /* Mandatory non-null function pointer members. */ - js::Valueify(XPC_WN_OnlyIWrite_PropertyStub), // addProperty - js::Valueify(XPC_WN_CannotModifyPropertyStub),// delProperty - js::PropertyStub, // getProperty - js::Valueify(XPC_WN_OnlyIWrite_PropertyStub), // setProperty + JS_VALUEIFY(js::PropertyOp, XPC_WN_OnlyIWrite_PropertyStub), // addProperty + JS_VALUEIFY(js::PropertyOp, XPC_WN_CannotModifyPropertyStub),// delProperty + js::PropertyStub, // getProperty + JS_VALUEIFY(js::PropertyOp, XPC_WN_OnlyIWrite_PropertyStub), // setProperty - XPC_WN_Shared_Enumerate, // enumerate - XPC_WN_NoHelper_Resolve, // resolve - js::Valueify(XPC_WN_Shared_Convert), // convert - XPC_WN_NoHelper_Finalize, // finalize + XPC_WN_Shared_Enumerate, // enumerate + XPC_WN_NoHelper_Resolve, // resolve + JS_VALUEIFY(js::ConvertOp, XPC_WN_Shared_Convert), // convert + XPC_WN_NoHelper_Finalize, // finalize /* Optionally non-null members start here. */ nsnull, // reserved0 @@ -945,7 +945,7 @@ js::Class XPC_WN_NoHelper_JSClass = { // ClassExtension { - js::Valueify(XPC_WN_Equality), + JS_VALUEIFY(js::EqualityOp, XPC_WN_Equality), XPC_WN_OuterObject, XPC_WN_InnerObject, nsnull, // iteratorObject @@ -961,7 +961,7 @@ js::Class XPC_WN_NoHelper_JSClass = { nsnull, // getAttributes nsnull, // setAttributes nsnull, // deleteProperty - js::Valueify(XPC_WN_JSOp_Enumerate), + JS_VALUEIFY(js::NewEnumerateOp, XPC_WN_JSOp_Enumerate), XPC_WN_JSOp_TypeOf_Object, nsnull, // trace nsnull, // fix @@ -1911,7 +1911,7 @@ js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = { js::PropertyStub, // setProperty; XPC_WN_Shared_Proto_Enumerate, // enumerate; XPC_WN_ModsAllowed_Proto_Resolve, // resolve; - js::Valueify(XPC_WN_Shared_Proto_Convert), // convert; + JS_VALUEIFY(js::ConvertOp, XPC_WN_Shared_Proto_Convert), // convert; XPC_WN_Shared_Proto_Finalize, // finalize; /* Optionally non-null members start here. */ @@ -1938,7 +1938,7 @@ js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = { js::PropertyStub, // setProperty; XPC_WN_Shared_Proto_Enumerate, // enumerate; XPC_WN_ModsAllowed_Proto_Resolve,// resolve; - js::Valueify(XPC_WN_Shared_Proto_Convert), // convert; + JS_VALUEIFY(js::ConvertOp, XPC_WN_Shared_Proto_Convert), // convert; XPC_WN_Shared_Proto_Finalize, // finalize; /* Optionally non-null members start here. */ @@ -2013,14 +2013,14 @@ js::Class XPC_WN_NoMods_WithCall_Proto_JSClass = { WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE, // flags; /* Mandatory non-null function pointer members. */ - js::Valueify(XPC_WN_OnlyIWrite_Proto_PropertyStub), // addProperty; - js::Valueify(XPC_WN_CannotModifyPropertyStub), // delProperty; - js::PropertyStub, // getProperty; - js::Valueify(XPC_WN_OnlyIWrite_Proto_PropertyStub), // setProperty; - XPC_WN_Shared_Proto_Enumerate, // enumerate; - XPC_WN_NoMods_Proto_Resolve, // resolve; - js::Valueify(XPC_WN_Shared_Proto_Convert), // convert; - XPC_WN_Shared_Proto_Finalize, // finalize; + JS_VALUEIFY(js::PropertyOp, XPC_WN_OnlyIWrite_Proto_PropertyStub), // addProperty; + JS_VALUEIFY(js::PropertyOp, XPC_WN_CannotModifyPropertyStub), // delProperty; + js::PropertyStub, // getProperty; + JS_VALUEIFY(js::PropertyOp, XPC_WN_OnlyIWrite_Proto_PropertyStub), // setProperty; + XPC_WN_Shared_Proto_Enumerate, // enumerate; + XPC_WN_NoMods_Proto_Resolve, // resolve; + JS_VALUEIFY(js::ConvertOp, XPC_WN_Shared_Proto_Convert), // convert; + XPC_WN_Shared_Proto_Finalize, // finalize; /* Optionally non-null members start here. */ nsnull, // reserved0; @@ -2040,14 +2040,14 @@ js::Class XPC_WN_NoMods_NoCall_Proto_JSClass = { WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE, // flags; /* Mandatory non-null function pointer members. */ - js::Valueify(XPC_WN_OnlyIWrite_Proto_PropertyStub), // addProperty; - js::Valueify(XPC_WN_CannotModifyPropertyStub), // delProperty; - js::PropertyStub, // getProperty; - js::Valueify(XPC_WN_OnlyIWrite_Proto_PropertyStub), // setProperty; - XPC_WN_Shared_Proto_Enumerate, // enumerate; - XPC_WN_NoMods_Proto_Resolve, // resolve; - js::Valueify(XPC_WN_Shared_Proto_Convert), // convert; - XPC_WN_Shared_Proto_Finalize, // finalize; + JS_VALUEIFY(js::PropertyOp, XPC_WN_OnlyIWrite_Proto_PropertyStub), // addProperty; + JS_VALUEIFY(js::PropertyOp, XPC_WN_CannotModifyPropertyStub), // delProperty; + js::PropertyStub, // getProperty; + JS_VALUEIFY(js::PropertyOp, XPC_WN_OnlyIWrite_Proto_PropertyStub), // setProperty; + XPC_WN_Shared_Proto_Enumerate, // enumerate; + XPC_WN_NoMods_Proto_Resolve, // resolve; + JS_VALUEIFY(js::ConvertOp, XPC_WN_Shared_Proto_Convert), // convert; + XPC_WN_Shared_Proto_Finalize, // finalize; /* Optionally non-null members start here. */ nsnull, // reserved0; @@ -2124,12 +2124,12 @@ js::Class XPC_WN_Tearoff_JSClass = { "WrappedNative_TearOff", // name; WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE, // flags; - js::Valueify(XPC_WN_OnlyIWrite_PropertyStub), // addProperty; - js::Valueify(XPC_WN_CannotModifyPropertyStub), // delProperty; - js::PropertyStub, // getProperty; - js::Valueify(XPC_WN_OnlyIWrite_PropertyStub), // setProperty; - XPC_WN_TearOff_Enumerate, // enumerate; - XPC_WN_TearOff_Resolve, // resolve; - js::Valueify(XPC_WN_Shared_Convert), // convert; - XPC_WN_TearOff_Finalize // finalize; + JS_VALUEIFY(js::PropertyOp, XPC_WN_OnlyIWrite_PropertyStub), // addProperty; + JS_VALUEIFY(js::PropertyOp, XPC_WN_CannotModifyPropertyStub), // delProperty; + js::PropertyStub, // getProperty; + JS_VALUEIFY(js::PropertyOp, XPC_WN_OnlyIWrite_PropertyStub), // setProperty; + XPC_WN_TearOff_Enumerate, // enumerate; + XPC_WN_TearOff_Resolve, // resolve; + JS_VALUEIFY(js::ConvertOp, XPC_WN_Shared_Convert), // convert; + XPC_WN_TearOff_Finalize // finalize; }; diff --git a/modules/plugin/base/src/nsJSNPRuntime.cpp b/modules/plugin/base/src/nsJSNPRuntime.cpp index 856835f95eb5..68c25acd5366 100644 --- a/modules/plugin/base/src/nsJSNPRuntime.cpp +++ b/modules/plugin/base/src/nsJSNPRuntime.cpp @@ -855,7 +855,7 @@ nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier id, AutoJSExceptionReporter reporter(cx); jsval v = NPVariantToJSVal(npp, cx, value); - js::AutoValueRooter tvr(cx, js::Valueify(v)); + js::AutoValueRooter tvr(cx, v); if (NPIdentifierIsString(id)) { JSString *str = NPIdentifierToString(id); From 55b397bc07fbc24659ff75e692f8fe8d19863059 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Fri, 1 Oct 2010 21:00:55 -0700 Subject: [PATCH 018/284] Header changes for sixgill, annotations. bug 601129, r=jorendorff --- js/src/ctypes/CTypes.cpp | 1 + js/src/jsapi.cpp | 1 + js/src/jsarena.cpp | 2 +- js/src/jsarena.h | 2 ++ js/src/jscntxt.cpp | 1 + js/src/jscntxtinlines.h | 3 ++ js/src/jsfun.cpp | 4 +-- js/src/jsgc.h | 1 + js/src/jsinterp.h | 1 + js/src/jsprvtd.h | 1 + js/src/jsstaticcheck.h | 56 ++++++++++++++++++++++++++++++ js/src/jstl.h | 1 + js/src/jsvector.h | 2 ++ js/src/methodjit/InvokeHelpers.cpp | 2 +- js/src/methodjit/MonoIC.cpp | 3 +- xpcom/glue/nsDebug.h | 48 +++++++++++++++---------- 16 files changed, 105 insertions(+), 24 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 157090979e09..8c07d0885fbc 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -4289,6 +4289,7 @@ StructType::ConstructData(JSContext* cx, if (argc == fields->count()) { for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { const FieldInfo& field = r.front().value; + STATIC_ASSUME(field.mIndex < fields->count()); /* Quantified invariant */ if (!ImplicitConvert(cx, argv[field.mIndex], field.mType, buffer + field.mOffset, false, NULL)) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 1047748c9c71..b31f104d3230 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3955,6 +3955,7 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) /* Non-native case: use the ida enumerated when iterobj was created. */ ida = (JSIdArray *) iterobj->getPrivate(); JS_ASSERT(i <= ida->length); + STATIC_ASSUME(i <= ida->length); if (i == 0) { *idp = JSID_VOID; } else { diff --git a/js/src/jsarena.cpp b/js/src/jsarena.cpp index 80440bcda1bc..dd1b8a650d13 100644 --- a/js/src/jsarena.cpp +++ b/js/src/jsarena.cpp @@ -48,7 +48,7 @@ #include "jsstdint.h" #include "jsbit.h" #include "jsarena.h" -#include "jsutil.h" +#include "jsprvtd.h" #ifdef JS_ARENAMETER static JSArenaStats *arena_stats_list; diff --git a/js/src/jsarena.h b/js/src/jsarena.h index ad2abd19ac6d..6eeb04a8cfa9 100644 --- a/js/src/jsarena.h +++ b/js/src/jsarena.h @@ -126,6 +126,7 @@ struct JSArenaPool { else \ _a->avail = _p + _nb; \ p = (type) _p; \ + STATIC_ASSUME(!p || ubound((char *)p) >= nb) \ JS_ArenaCountAllocation(pool, nb); \ JS_END_MACRO @@ -149,6 +150,7 @@ struct JSArenaPool { } else { \ p = (type) JS_ArenaGrow(pool, p, size, incr); \ } \ + STATIC_ASSUME(!p || ubound((char *)p) >= size + incr); \ JS_ArenaCountGrowth(pool, size, incr); \ JS_END_MACRO diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index c9a896b8ce7b..c9626bbb27fb 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -221,6 +221,7 @@ StackSpace::mark(JSTracer *trc) */ Value *end = firstUnused(); for (StackSegment *seg = currentSegment; seg; seg = seg->getPreviousInMemory()) { + STATIC_ASSERT(ubound(end) >= 0); if (seg->inContext()) { /* This may be the only pointer to the initialVarObj. */ if (seg->hasInitialVarObj()) diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index c5134c05387e..a06cbf0253b8 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -139,6 +139,7 @@ StackSpace::isCurrentAndActive(JSContext *cx) const currentSegment == cx->getCurrentSegment(); } +STATIC_POSTCONDITION(!return || ubound(from) >= nvals) JS_ALWAYS_INLINE bool StackSpace::ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const { @@ -642,6 +643,7 @@ assertSameCompartment(JSContext *cx, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) #undef START_ASSERT_SAME_COMPARTMENT +STATIC_PRECONDITION_ASSUME(ubound(vp) >= argc + 2) JS_ALWAYS_INLINE bool CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp) { @@ -657,6 +659,7 @@ CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp) return ok; } +STATIC_PRECONDITION(ubound(vp) >= argc + 2) JS_ALWAYS_INLINE bool CallJSNativeConstructor(JSContext *cx, js::Native native, uintN argc, js::Value *vp) { diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 2eec2313b2a5..4963789c88b6 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -210,7 +210,7 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee) namespace { -struct PutArg +struct STATIC_SKIP_INFERENCE PutArg { PutArg(Value *dst) : dst(dst) {} Value *dst; @@ -2288,7 +2288,7 @@ js_fun_call(JSContext *cx, uintN argc, Value *vp) namespace { -struct CopyNonHoleArgs +struct STATIC_SKIP_INFERENCE CopyNonHoleArgs { CopyNonHoleArgs(JSObject *aobj, Value *dst) : aobj(aobj), dst(dst) {} JSObject *aobj; diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 20679c45411b..dc01d92aed18 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -328,6 +328,7 @@ Cell::bitmap() const return &chunk()->bitmaps[arena()->arenaIndex()]; } +STATIC_POSTCONDITION_ASSUME(return < ArenaBitmap::BitCount) size_t Cell::cellIndex() const { diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 95799c5cae84..690cf876b0ec 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -52,6 +52,7 @@ struct JSFrameRegs { + STATIC_SKIP_INFERENCE js::Value *sp; /* stack pointer */ jsbytecode *pc; /* program counter */ JSStackFrame *fp; /* active frame */ diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index b20c555e9317..a79cbbeaa95b 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -55,6 +55,7 @@ */ #include "jspubtd.h" +#include "jsstaticcheck.h" #include "jsutil.h" JS_BEGIN_EXTERN_C diff --git a/js/src/jsstaticcheck.h b/js/src/jsstaticcheck.h index db38ebf2f166..c605d2f0848a 100644 --- a/js/src/jsstaticcheck.h +++ b/js/src/jsstaticcheck.h @@ -66,4 +66,60 @@ JS_ASSERT_NOT_ON_TRACE(JSContext *cx) #endif #define VOUCH_HAVE_STACK VOUCH_DOES_NOT_REQUIRE_STACK +/* sixgill annotation defines */ + +/* Avoid name collision if included with other headers defining annotations. */ +#ifndef HAVE_STATIC_ANNOTATIONS +#define HAVE_STATIC_ANNOTATIONS + +#ifdef XGILL_PLUGIN + +#define STATIC_PRECONDITION(COND) __attribute__((precondition(#COND))) +#define STATIC_PRECONDITION_ASSUME(COND) __attribute__((precondition_assume(#COND))) +#define STATIC_POSTCONDITION(COND) __attribute__((postcondition(#COND))) +#define STATIC_POSTCONDITION_ASSUME(COND) __attribute__((postcondition_assume(#COND))) +#define STATIC_INVARIANT(COND) __attribute__((invariant(#COND))) +#define STATIC_INVARIANT_ASSUME(COND) __attribute__((invariant_assume(#COND))) + +/* Used to make identifiers for assert/assume annotations in a function. */ +#define STATIC_PASTE2(X,Y) X ## Y +#define STATIC_PASTE1(X,Y) STATIC_PASTE2(X,Y) + +#define STATIC_ASSERT(COND) \ + JS_BEGIN_MACRO \ + __attribute__((assert_static(#COND), unused)) \ + int STATIC_PASTE1(assert_static_, __COUNTER__); \ + JS_END_MACRO + +#define STATIC_ASSUME(COND) \ + JS_BEGIN_MACRO \ + __attribute__((assume_static(#COND), unused)) \ + int STATIC_PASTE1(assume_static_, __COUNTER__); \ + JS_END_MACRO + +#define STATIC_ASSERT_RUNTIME(COND) \ + JS_BEGIN_MACRO \ + __attribute__((assert_static_runtime(#COND), unused)) \ + int STATIC_PASTE1(assert_static_runtime_, __COUNTER__); \ + JS_END_MACRO + +#else /* XGILL_PLUGIN */ + +#define STATIC_PRECONDITION(COND) /* nothing */ +#define STATIC_PRECONDITION_ASSUME(COND) /* nothing */ +#define STATIC_POSTCONDITION(COND) /* nothing */ +#define STATIC_POSTCONDITION_ASSUME(COND) /* nothing */ +#define STATIC_INVARIANT(COND) /* nothing */ +#define STATIC_INVARIANT_ASSUME(COND) /* nothing */ + +#define STATIC_ASSERT(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO +#define STATIC_ASSUME(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO +#define STATIC_ASSERT_RUNTIME(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO + +#endif /* XGILL_PLUGIN */ + +#define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference()) + +#endif /* HAVE_STATIC_ANNOTATIONS */ + #endif /* jsstaticcheck_h___ */ diff --git a/js/src/jstl.h b/js/src/jstl.h index 967930b69dd6..d05914289b12 100644 --- a/js/src/jstl.h +++ b/js/src/jstl.h @@ -212,6 +212,7 @@ class ReentrancyGuard * Round x up to the nearest power of 2. This function assumes that the most * significant bit of x is not set, which would lead to overflow. */ +STATIC_POSTCONDITION_ASSUME(return >= x) JS_ALWAYS_INLINE size_t RoundUpPow2(size_t x) { diff --git a/js/src/jsvector.h b/js/src/jsvector.h index cb80a0f178c5..2450e1236c59 100644 --- a/js/src/jsvector.h +++ b/js/src/jsvector.h @@ -467,6 +467,7 @@ Vector::~Vector() * curLength and check for overflow. */ template +STATIC_POSTCONDITION(!return || newCap >= curLength + lengthInc) inline bool Vector::calculateNewCapacity(size_t curLength, size_t lengthInc, size_t &newCap) @@ -622,6 +623,7 @@ Vector::growByUninitialized(size_t incr) } template +STATIC_POSTCONDITION(!return || ubound(this->begin()) >= newLength) inline bool Vector::resize(size_t newLength) { diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index 1f9cd245e9f6..b24a9da0ecdd 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -530,7 +530,7 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr) } if (ucr->fun->isNative()) { - if (!ucr->fun->u.n.native(cx, argc, vp)) + if (!CallJSNative(cx, ucr->fun->u.n.native, argc, vp)) THROW(); return; } diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index c6b47b3f7e60..ce12f4a9f3d2 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -442,8 +442,7 @@ class CallCompiler : public BaseCompiler if (callingNew) vp[1].setMagicWithObjectOrNullPayload(NULL); - Native fn = fun->u.n.native; - if (!fn(cx, ic.argc, vp)) + if (!CallJSNative(cx, fun->u.n.native, ic.argc, vp)) THROWV(true); /* Right now, take slow-path for IC misses or multiple stubs. */ diff --git a/xpcom/glue/nsDebug.h b/xpcom/glue/nsDebug.h index 3eeddb669d90..5d43c3d3b022 100644 --- a/xpcom/glue/nsDebug.h +++ b/xpcom/glue/nsDebug.h @@ -189,6 +189,10 @@ ** When the tool is not running these macros are no-ops. ******************************************************************************/ +/* Avoid name collision if included with other headers defining annotations. */ +#ifndef HAVE_STATIC_ANNOTATIONS +#define HAVE_STATIC_ANNOTATIONS + #ifdef XGILL_PLUGIN #define STATIC_PRECONDITION(COND) __attribute__((precondition(#COND))) @@ -204,34 +208,22 @@ #define STATIC_ASSERT(COND) \ PR_BEGIN_MACRO \ - __attribute__((assert(#COND), unused)) \ - int STATIC_PASTE1(static_assert_, __COUNTER__); \ + __attribute__((assert_static(#COND), unused)) \ + int STATIC_PASTE1(assert_static_, __COUNTER__); \ PR_END_MACRO #define STATIC_ASSUME(COND) \ PR_BEGIN_MACRO \ - __attribute__((assume(#COND), unused)) \ - int STATIC_PASTE1(static_assume_, __COUNTER__); \ + __attribute__((assume_static(#COND), unused)) \ + int STATIC_PASTE1(assume_static_, __COUNTER__); \ PR_END_MACRO #define STATIC_ASSERT_RUNTIME(COND) \ PR_BEGIN_MACRO \ - __attribute__((assert_runtime(#COND), unused)) \ - int STATIC_PASTE1(static_assert_runtime_, __COUNTER__); \ + __attribute__((assert_static_runtime(#COND), unused)) \ + int STATIC_PASTE1(assert_static_runtime_, __COUNTER__); \ PR_END_MACRO -/* Redefine runtime assertion macros to perform static assertions, for both - * debug and release builds. Don't include the original runtime assertions; - * this ensures the tool will consider cases where the assertion fails. */ - -#undef NS_PRECONDITION -#undef NS_ASSERTION -#undef NS_POSTCONDITION - -#define NS_PRECONDITION(expr, str) STATIC_ASSERT_RUNTIME(expr) -#define NS_ASSERTION(expr, str) STATIC_ASSERT_RUNTIME(expr) -#define NS_POSTCONDITION(expr, str) STATIC_ASSERT_RUNTIME(expr) - #else /* XGILL_PLUGIN */ #define STATIC_PRECONDITION(COND) /* nothing */ @@ -247,6 +239,26 @@ #endif /* XGILL_PLUGIN */ +#define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference()) + +#endif /* HAVE_STATIC_ANNOTATIONS */ + +#ifdef XGILL_PLUGIN + +/* Redefine runtime assertion macros to perform static assertions, for both + * debug and release builds. Don't include the original runtime assertions; + * this ensures the tool will consider cases where the assertion fails. */ + +#undef NS_PRECONDITION +#undef NS_ASSERTION +#undef NS_POSTCONDITION + +#define NS_PRECONDITION(expr, str) STATIC_ASSERT_RUNTIME(expr) +#define NS_ASSERTION(expr, str) STATIC_ASSERT_RUNTIME(expr) +#define NS_POSTCONDITION(expr, str) STATIC_ASSERT_RUNTIME(expr) + +#endif /* XGILL_PLUGIN */ + /****************************************************************************** ** Macros for terminating execution when an unrecoverable condition is ** reached. These need to be compiled regardless of the NS_DEBUG flag. From 335710920dfc125bb2593ad6852be31e3a26712b Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Sat, 2 Oct 2010 09:41:18 -0500 Subject: [PATCH 019/284] Rename JS_{Read,Write}Pair to JS_{Read,Write}Uint32Pair as suggested by Brendan. Followup to bug 595297. --- js/src/jsapi.cpp | 4 ++-- js/src/jsapi.h | 4 ++-- js/src/jspubtd.h | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index b31f104d3230..5b6917660ea0 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5306,7 +5306,7 @@ JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks * } JS_PUBLIC_API(JSBool) -JS_ReadPair(JSStructuredCloneReader *r, uint32 *p1, uint32 *p2) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32 *p1, uint32 *p2) { return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); } @@ -5318,7 +5318,7 @@ JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) } JS_PUBLIC_API(JSBool) -JS_WritePair(JSStructuredCloneWriter *w, uint32 tag, uint32 data) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32 tag, uint32 data) { return w->output().writePair(tag, data); } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 2ba04e67f9c4..93886a105e0f 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2937,13 +2937,13 @@ JS_PUBLIC_API(void) JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); JS_PUBLIC_API(JSBool) -JS_ReadPair(JSStructuredCloneReader *r, uint32 *p1, uint32 *p2); +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32 *p1, uint32 *p2); JS_PUBLIC_API(JSBool) JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); JS_PUBLIC_API(JSBool) -JS_WritePair(JSStructuredCloneWriter *w, uint32 tag, uint32 data); +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32 tag, uint32 data); JS_PUBLIC_API(JSBool) JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 8a8d1a170c6f..e0184fcdf6ac 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -584,9 +584,10 @@ typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReade * 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_WritePair 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. + * 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. * * Return true on success, false on error/exception. */ From 57857fccb8d9a3bb8ea1e86925b83484eb35c794 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Sat, 2 Oct 2010 10:08:59 -0500 Subject: [PATCH 020/284] Bug 600287 - Indirect eval should use callee's global, not the this-argument. r=brendan. --- js/src/jsobj.cpp | 49 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index de4b2c6bfcbc..1159881e6a6f 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1016,30 +1016,29 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) jsbytecode *callerPC = caller->pc(cx); bool indirectCall = (callerPC && *callerPC != JSOP_EVAL); - /* - * This call to JSObject::wrappedObject is safe because of the security - * checks we do below. However, the control flow below is confusing, so we - * double check. There are two cases: - * - Direct call: This object is never used. So unwrapping can't hurt. - * - Indirect call: If this object isn't already the scope chain (which - * we're guaranteed to be allowed to access) then we do a security - * check. - */ - Value *argv = JS_ARGV(cx, vp); - JSObject *obj = ComputeThisFromVp(cx, vp); - if (!obj) - return JS_FALSE; - obj = obj->wrappedObject(cx); - - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - /* * Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....)) * that attempt to use a non-global object as the scope object. + * + * This ban is a bit silly, since we could just disregard the this-argument + * entirely and comply with ES5, which supports indirect eval. See bug + * 592664. */ { + JSObject *obj = ComputeThisFromVp(cx, vp); + if (!obj) + return JS_FALSE; + + /* + * This call to JSObject::wrappedObject is safe because the result is + * only used for this check. + */ + obj = obj->wrappedObject(cx); + + OBJ_TO_INNER_OBJECT(cx, obj); + if (!obj) + return JS_FALSE; + JSObject *parent = obj->getParent(); if (indirectCall || parent) { uintN flags = parent @@ -1053,6 +1052,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) } } + Value *argv = JS_ARGV(cx, vp); if (!argv[0].isString()) { *vp = argv[0]; return JS_TRUE; @@ -1102,16 +1102,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) if (indirectCall) { /* Pretend that we're top level. */ staticLevel = 0; - - if (!js_CheckPrincipalsAccess(cx, obj, - js_StackFramePrincipals(cx, caller), - cx->runtime->atomState.evalAtom)) { - return JS_FALSE; - } - - /* NB: We know inner is a global object here. */ - JS_ASSERT(!obj->getParent()); - scopeobj = obj; + scopeobj = vp[0].toObject().getGlobal(); } else { /* * Compile using the caller's current scope object. From 817c40ae522e7c107bb6ffb5ed9224f06f33d7c0 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 29 Sep 2010 10:00:52 -0700 Subject: [PATCH 021/284] Bug 600193 - trace-test/tests/jaeger/bug588363-1.js asserts with CompartmentChecker enabled. r=gal. This makes eval(s) an indirect eval if the eval-function being called is in a different compartment from the caller's scope chain. This caused the eval in trace-test/tests/jaeger/bug588363-2.js to become indirect, which caused the test to fail with a ReferenceError. So I changed the test to delete the sandbox's eval, revealing the global eval underneath. --- js/src/jsobj.cpp | 11 +++++++++++ js/src/trace-test/tests/jaeger/bug588363-2.js | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 1159881e6a6f..32a581e9d271 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1016,6 +1016,13 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) jsbytecode *callerPC = caller->pc(cx); bool indirectCall = (callerPC && *callerPC != JSOP_EVAL); + /* + * If the callee was originally a cross-compartment wrapper, this should + * be an indirect call. + */ + if (caller->scopeChain().compartment() != vp[0].toObject().compartment()) + indirectCall = true; + /* * Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....)) * that attempt to use a non-global object as the scope object. @@ -1250,6 +1257,8 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; } + assertSameCompartment(cx, scopeobj, script); + /* * Belt-and-braces: check that the lesser of eval's principals and the * caller's principals has access to scopeobj. @@ -3114,6 +3123,8 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) if (!thisp) return NULL; + assertSameCompartment(cx, obj, thisp); + obj->setWithThis(thisp); return obj; } diff --git a/js/src/trace-test/tests/jaeger/bug588363-2.js b/js/src/trace-test/tests/jaeger/bug588363-2.js index 319f6355d47a..5550be3ea8d3 100644 --- a/js/src/trace-test/tests/jaeger/bug588363-2.js +++ b/js/src/trace-test/tests/jaeger/bug588363-2.js @@ -1,5 +1,6 @@ with(evalcx('')) { - eval("x", this.__defineGetter__("x", Function)) + delete eval; + eval("x", this.__defineGetter__("x", Function)); } /* Don't assert or crash. */ From 5742cdd087fb8c502b9e60da73dd15606484372c Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 30 Sep 2010 17:50:40 -0700 Subject: [PATCH 022/284] Bug 539766 - Object.defineProperty sets arguments.length without setting the length-overridden bit. r=brendan --- js/src/jsobj.cpp | 20 ++++++ js/src/tests/ecma_5/Function/jstests.list | 1 + .../Function/redefine-arguments-length.js | 65 +++++++++++++++++++ .../tests/arguments/args-redefine-length-1.js | 7 ++ .../tests/arguments/args-redefine-length-2.js | 8 +++ 5 files changed, 101 insertions(+) create mode 100644 js/src/tests/ecma_5/Function/redefine-arguments-length.js create mode 100644 js/src/trace-test/tests/arguments/args-redefine-length-1.js create mode 100644 js/src/trace-test/tests/arguments/args-redefine-length-2.js diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 32a581e9d271..72c857b6b7b0 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2163,6 +2163,8 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, } } + bool callDelProperty = false; + if (desc.isGenericDescriptor()) { /* 8.12.9 step 8, no validation required */ } else if (desc.isDataDescriptor() != shape->isDataDescriptor()) { @@ -2179,6 +2181,8 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, rval); } } + + callDelProperty = !shape->hasDefaultGetter() || !shape->hasDefaultSetter(); } else { /* 8.12.9 step 11. */ JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor()); @@ -2269,6 +2273,22 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, *rval = true; obj2->dropProperty(cx, current); + + /* + * Since "data" properties implemented using native C functions may rely on + * side effects during setting, we must make them aware that they have been + * "assigned"; deleting the property before redefining it does the trick. + * See bug 539766, where we ran into problems when we redefined + * arguments.length without making the property aware that its value had + * been changed (which would have happened if we had deleted it before + * redefining it or we had invoked its setter to change its value). + */ + if (callDelProperty) { + Value dummy; + if (!CallJSPropertyOp(cx, obj2->getClass()->delProperty, obj2, desc.id, &dummy)) + return false; + } + return js_DefineProperty(cx, obj, desc.id, &v, getter, setter, attrs); } diff --git a/js/src/tests/ecma_5/Function/jstests.list b/js/src/tests/ecma_5/Function/jstests.list index 320a171637bf..d13af73580f2 100644 --- a/js/src/tests/ecma_5/Function/jstests.list +++ b/js/src/tests/ecma_5/Function/jstests.list @@ -5,3 +5,4 @@ script function-caller.js script strict-arguments.js script arguments-property-attributes.js script function-bind.js +script redefine-arguments-length.js diff --git a/js/src/tests/ecma_5/Function/redefine-arguments-length.js b/js/src/tests/ecma_5/Function/redefine-arguments-length.js new file mode 100644 index 000000000000..ad9ae742ff28 --- /dev/null +++ b/js/src/tests/ecma_5/Function/redefine-arguments-length.js @@ -0,0 +1,65 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = 'redefine-arguments-length.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 539766; +var summary = + "Object.defineProperty sets arguments.length without setting the " + + "length-overridden bit"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function test_JSOP_ARGCNT() +{ + var length = "length"; + Object.defineProperty(arguments, length, { value: 17 }); + assertEq(arguments.length, 17); + assertEq(arguments[length], 17); +} +test_JSOP_ARGCNT(); + +function test_js_fun_apply() +{ + var length = "length"; + Object.defineProperty(arguments, length, { value: 17 }); + + function fun() + { + assertEq(arguments.length, 17); + assertEq(arguments[length], 17); + assertEq(arguments[0], "foo"); + for (var i = 1; i < 17; i++) + assertEq(arguments[i], undefined); + } + fun.apply(null, arguments); +} +test_js_fun_apply("foo"); + +function test_array_toString_sub_1() +{ + Object.defineProperty(arguments, "length", { value: 1 }); + arguments.join = [].join; + assertEq([].toString.call(arguments), "1"); +} +test_array_toString_sub_1(1, 2); + +function test_array_toString_sub_2() +{ + Object.defineProperty(arguments, "length", { value: 1 }); + assertEq([].toLocaleString.call(arguments), "1"); +} +test_array_toString_sub_2(1, 2); + + +/******************************************************************************/ + +reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/trace-test/tests/arguments/args-redefine-length-1.js b/js/src/trace-test/tests/arguments/args-redefine-length-1.js new file mode 100644 index 000000000000..c71c97b1cb83 --- /dev/null +++ b/js/src/trace-test/tests/arguments/args-redefine-length-1.js @@ -0,0 +1,7 @@ +function t() +{ + Object.defineProperty(arguments, "length", { value: 17 }); + for (var i = 0; i < 5; i++) + assertEq(arguments.length, 17); +} +t(); diff --git a/js/src/trace-test/tests/arguments/args-redefine-length-2.js b/js/src/trace-test/tests/arguments/args-redefine-length-2.js new file mode 100644 index 000000000000..fe1c9cf2fe17 --- /dev/null +++ b/js/src/trace-test/tests/arguments/args-redefine-length-2.js @@ -0,0 +1,8 @@ +function t() +{ + var a = arguments; + Object.defineProperty(a, "length", { value: 17 }); + for (var i = 0; i < 5; i++) + assertEq(a.length, 17); +} +t(); From 1d67cb842964d818a8603c084155ef75c79b9730 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 30 Sep 2010 21:03:47 -0700 Subject: [PATCH 023/284] Bug 577757 - array[-1073741824] != array["-1073741824"]. r=igor --- js/src/jsobj.cpp | 17 ++++++++++++----- js/src/tests/js1_5/Regress/regress-57043.js | 10 +++++++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 72c857b6b7b0..1f04385b6726 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4196,11 +4196,18 @@ js_CheckForStringIndex(jsid id) if (cp != end || (negative && index == 0)) return id; - if (oldIndex < JSID_INT_MAX / 10 || - (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10))) { - if (negative) - index = 0 - index; - id = INT_TO_JSID((jsint)index); + if (negative) { + if (oldIndex < -(JSID_INT_MIN / 10) || + (oldIndex == -(JSID_INT_MIN / 10) && c <= (-JSID_INT_MIN % 10))) + { + id = INT_TO_JSID(jsint(-index)); + } + } else { + if (oldIndex < JSID_INT_MAX / 10 || + (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10))) + { + id = INT_TO_JSID(jsint(index)); + } } return id; diff --git a/js/src/tests/js1_5/Regress/regress-57043.js b/js/src/tests/js1_5/Regress/regress-57043.js index 9eae290b1d27..ca30f1a2ae4c 100644 --- a/js/src/tests/js1_5/Regress/regress-57043.js +++ b/js/src/tests/js1_5/Regress/regress-57043.js @@ -63,7 +63,8 @@ var status = ''; var actual = ''; var expect = ''; var value = ''; // various indices to try - -var index = Array(-5000, -507, -3, -2, -1, 0, 1, 2, 3); +var index = + [-1073741825, -1073741824, -1073741823, -5000, -507, -3, -2, -1, -0, 0, 1, 2, 3, 1073741823, 1073741824, 1073741825]; //------------------------------------------------------------------------------------------------- @@ -77,7 +78,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - for (j in index) {testProperty(index[j]);} + for (var j in index) {testProperty(index[j]);} exitFunc ('test'); } @@ -101,8 +102,11 @@ function testProperty(i) reportCompare(expect, actual, status); } +function positive(n) { return 1 / n > 0; } function getStatus(i) { - return (statprefix + i + statsuffix); + return statprefix + + (positive(i) ? i : "-" + -i) + + statsuffix; } From 621b28b9de58fa839b9e1ee037f3c6edfaeaa76f Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Sun, 3 Oct 2010 08:21:38 -0700 Subject: [PATCH 024/284] Lazify fp->scopeChain, JM call path cleanup. bug 593882, r=lw,dvander. --- js/src/jsinterp.h | 55 +++++++++++-------------- js/src/jsinterpinlines.h | 24 ++++------- js/src/jstracer.cpp | 1 - js/src/methodjit/BytecodeAnalyzer.cpp | 12 ++++++ js/src/methodjit/BytecodeAnalyzer.h | 6 ++- js/src/methodjit/Compiler.cpp | 28 ++++++++----- js/src/methodjit/InlineFrameAssembler.h | 2 - js/src/methodjit/InvokeHelpers.cpp | 13 +++++- js/src/methodjit/MethodJIT.cpp | 8 ++-- js/src/methodjit/StubCalls.cpp | 4 +- js/src/methodjit/TrampolineCompiler.cpp | 2 +- 11 files changed, 86 insertions(+), 69 deletions(-) diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 690cf876b0ec..04708a494bdd 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -91,23 +91,14 @@ enum JSFrameFlags JSFRAME_UNDERFLOW_ARGS = 0x4000, /* numActualArgs < numFormalArgs */ /* Lazy frame initialization */ - JSFRAME_HAS_IMACRO_PC = 0x8000, /* frame has imacpc value available */ - JSFRAME_HAS_CALL_OBJ = 0x10000, /* frame has a callobj reachable from scopeChain_ */ - JSFRAME_HAS_ARGS_OBJ = 0x20000, /* frame has an argsobj in JSStackFrame::args */ - JSFRAME_HAS_HOOK_DATA = 0x40000, /* frame has hookData_ set */ - JSFRAME_HAS_ANNOTATION = 0x80000, /* frame has annotation_ set */ - - /* - * Whether the prevpc_ value is valid. If not set, the ncode_ value is - * valid and prevpc_ can be recovered using it. - */ - JSFRAME_HAS_PREVPC = 0x100000, - - /* - * For use by compiled functions, at function exit indicates whether rval_ - * has been assigned to. Otherwise the return value is carried in registers. - */ - JSFRAME_RVAL_ASSIGNED = 0x200000 + JSFRAME_HAS_IMACRO_PC = 0x8000, /* frame has imacpc value available */ + JSFRAME_HAS_CALL_OBJ = 0x10000, /* frame has a callobj reachable from scopeChain_ */ + JSFRAME_HAS_ARGS_OBJ = 0x20000, /* frame has an argsobj in JSStackFrame::args */ + JSFRAME_HAS_HOOK_DATA = 0x40000, /* frame has hookData_ set */ + JSFRAME_HAS_ANNOTATION = 0x80000, /* frame has annotation_ set */ + JSFRAME_HAS_RVAL = 0x100000, /* frame has rval_ set */ + JSFRAME_HAS_SCOPECHAIN = 0x200000, /* frame has scopeChain_ set */ + JSFRAME_HAS_PREVPC = 0x400000 /* frame has prevpc_ set */ }; /* @@ -117,7 +108,7 @@ enum JSFrameFlags struct JSStackFrame { private: - uint32 flags_; /* bits described by JSFrameFlags */ + mutable uint32 flags_; /* bits described by JSFrameFlags */ union { /* describes what code is executing in a */ JSScript *script; /* global frame */ JSFunction *fun; /* function frame, pre GetScopeChain */ @@ -127,7 +118,7 @@ struct JSStackFrame JSObject *obj; /* post GetArgumentsObject */ JSScript *script; /* eval has no args, but needs a script */ } args; - JSObject *scopeChain_; /* current scope chain */ + mutable JSObject *scopeChain_; /* current scope chain */ JSStackFrame *prev_; /* previous cx->regs->fp */ void *ncode_; /* return address for method JIT */ @@ -199,8 +190,7 @@ struct JSStackFrame uint32 nactual, uint32 flags); /* Called by method-jit stubs and serve as a specification for jit-code. */ - inline void initCallFrameCallerHalf(JSContext *cx, JSObject &scopeChain, - uint32 nactual, uint32 flags); + inline void initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flags); inline void initCallFrameEarlyPrologue(JSFunction *fun, void *ncode); inline void initCallFrameLatePrologue(); @@ -497,6 +487,11 @@ struct JSStackFrame */ JSObject &scopeChain() const { + JS_ASSERT_IF(!(flags_ & JSFRAME_HAS_SCOPECHAIN), isFunctionFrame()); + if (!(flags_ & JSFRAME_HAS_SCOPECHAIN)) { + scopeChain_ = callee().getParent(); + flags_ |= JSFRAME_HAS_SCOPECHAIN; + } return *scopeChain_; } @@ -576,24 +571,23 @@ struct JSStackFrame /* Return value */ const js::Value& returnValue() { + if (!(flags_ & JSFRAME_HAS_RVAL)) + rval_.setUndefined(); return rval_; } + void markReturnValue() { + flags_ |= JSFRAME_HAS_RVAL; + } + void setReturnValue(const js::Value &v) { rval_ = v; + markReturnValue(); } void clearReturnValue() { rval_.setUndefined(); - } - - js::Value* addressReturnValue() { - return &rval_; - } - - void setAssignedReturnValue(const js::Value &v) { - flags_ |= JSFRAME_RVAL_ASSIGNED; - setReturnValue(v); + markReturnValue(); } /* Native-code return address */ @@ -740,6 +734,7 @@ struct JSStackFrame } JSObject **addressOfScopeChain() { + JS_ASSERT(flags_ & JSFRAME_HAS_SCOPECHAIN); return &scopeChain_; } diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 87eb5dd9162e..87d5f3d87ad5 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -50,22 +50,20 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun, JS_ASSERT(fun == callee.getFunctionPrivate()); /* Initialize stack frame members. */ - flags_ = JSFRAME_FUNCTION | JSFRAME_HAS_PREVPC | flagsArg; + flags_ = JSFRAME_FUNCTION | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN | flagsArg; exec.fun = fun; args.nactual = nactual; /* only need to write if over/under-flow */ scopeChain_ = callee.getParent(); /* prevpc_, prev_ initialized by push*Frame */ JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); - rval_.setUndefined(); JS_ASSERT(annotation() == NULL); JS_ASSERT(!hasCallObj()); } inline void -JSStackFrame::initCallFrameCallerHalf(JSContext *cx, JSObject &scopeChain, - uint32 nactual, uint32 flagsArg) +JSStackFrame::initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flagsArg) { JS_ASSERT((flagsArg & ~(JSFRAME_CONSTRUCTING | JSFRAME_FUNCTION | @@ -76,7 +74,6 @@ JSStackFrame::initCallFrameCallerHalf(JSContext *cx, JSObject &scopeChain, /* Initialize the caller half of the stack frame members. */ flags_ = JSFRAME_FUNCTION | flagsArg; args.nactual = nactual; /* only need to write if over/under-flow */ - scopeChain_ = &scopeChain; prev_ = regs->fp; JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); @@ -104,8 +101,6 @@ JSStackFrame::initCallFrameEarlyPrologue(JSFunction *fun, void *ncode) inline void JSStackFrame::initCallFrameLatePrologue() { - rval_.setUndefined(); - SetValueRangeToUndefined(slots(), script()->nfixed); } @@ -128,7 +123,7 @@ JSStackFrame::initEvalFrame(JSScript *script, JSStackFrame *prev, dstvp[0].toObject().isFunction()); /* Initialize stack frame members. */ - flags_ = flagsArg | JSFRAME_HAS_PREVPC | + flags_ = flagsArg | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN | (prev->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL | JSFRAME_HAS_CALL_OBJ)); @@ -144,7 +139,6 @@ JSStackFrame::initEvalFrame(JSScript *script, JSStackFrame *prev, setPrev(prev, prevpc); JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); - rval_.setUndefined(); setAnnotation(prev->annotation()); } @@ -159,7 +153,7 @@ JSStackFrame::initGlobalFrame(JSScript *script, JSObject &chain, uint32 flagsArg vp[1].setUndefined(); /* Set after frame pushed using thisObject */ /* Initialize stack frame members. */ - flags_ = flagsArg | JSFRAME_GLOBAL | JSFRAME_HAS_PREVPC; + flags_ = flagsArg | JSFRAME_GLOBAL | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN; exec.script = script; args.script = (JSScript *)0xbad; scopeChain_ = &chain; @@ -167,7 +161,6 @@ JSStackFrame::initGlobalFrame(JSScript *script, JSObject &chain, uint32 flagsArg prev_ = NULL; JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); - rval_.setUndefined(); JS_ASSERT(annotation() == NULL); } @@ -175,7 +168,7 @@ inline void JSStackFrame::initDummyFrame(JSContext *cx, JSObject &chain) { js::PodZero(this); - flags_ = JSFRAME_DUMMY | JSFRAME_HAS_PREVPC; + flags_ = JSFRAME_DUMMY | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN; setPrev(cx->regs); chain.isGlobal(); setScopeChainNoCallObj(chain); @@ -335,12 +328,13 @@ JSStackFrame::setScopeChainNoCallObj(JSObject &obj) #ifdef DEBUG JS_ASSERT(&obj != NULL); JSObject *callObjBefore = maybeCallObj(); - if (!hasCallObj() && scopeChain_ != sInvalidScopeChain) { - for (JSObject *pobj = scopeChain_; pobj; pobj = pobj->getParent()) + if (!hasCallObj() && &scopeChain() != sInvalidScopeChain) { + for (JSObject *pobj = &scopeChain(); pobj; pobj = pobj->getParent()) JS_ASSERT_IF(pobj->isCall(), pobj->getPrivate() != this); } #endif scopeChain_ = &obj; + flags_ |= JSFRAME_HAS_SCOPECHAIN; JS_ASSERT(callObjBefore == maybeCallObj()); } @@ -350,7 +344,7 @@ JSStackFrame::setScopeChainAndCallObj(JSObject &obj) JS_ASSERT(&obj != NULL); JS_ASSERT(!hasCallObj() && obj.isCall() && obj.getPrivate() == this); scopeChain_ = &obj; - flags_ |= JSFRAME_HAS_CALL_OBJ; + flags_ |= JSFRAME_HAS_SCOPECHAIN | JSFRAME_HAS_CALL_OBJ; } inline void diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 6962e5cef1c8..81cdb7e15213 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -5739,7 +5739,6 @@ SynthesizeFrame(JSContext* cx, const FrameInfo& fi, JSObject* callee) ? JSFRAME_CONSTRUCTING | JSFRAME_CONSTRUCTING : 0; - /* Get pointer to new/frame/slots, prepare arguments. */ StackSpace &stack = cx->stack(); JSStackFrame *newfp = stack.getInlineFrame(cx, regs->sp, argc, newfun, diff --git a/js/src/methodjit/BytecodeAnalyzer.cpp b/js/src/methodjit/BytecodeAnalyzer.cpp index d5586ae243e6..3332911ef752 100644 --- a/js/src/methodjit/BytecodeAnalyzer.cpp +++ b/js/src/methodjit/BytecodeAnalyzer.cpp @@ -116,6 +116,18 @@ BytecodeAnalyzer::analyze(uint32 index) usesRval = true; break; + case JSOP_NAME: + case JSOP_CALLNAME: + case JSOP_BINDNAME: + case JSOP_SETNAME: + case JSOP_DELNAME: + case JSOP_INCNAME: + case JSOP_DECNAME: + case JSOP_NAMEINC: + case JSOP_NAMEDEC: + usesScope = true; + break; + case JSOP_DEFAULT: case JSOP_GOTO: offs = (pc + JSOP_GOTO_LENGTH) - script->code; diff --git a/js/src/methodjit/BytecodeAnalyzer.h b/js/src/methodjit/BytecodeAnalyzer.h index fa672a240900..911219fe03e7 100644 --- a/js/src/methodjit/BytecodeAnalyzer.h +++ b/js/src/methodjit/BytecodeAnalyzer.h @@ -68,11 +68,14 @@ namespace js /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */ bool usesRval; + /* Whether there are NAME bytecodes which can access the frame's scope chain. */ + bool usesScope; + public: BytecodeAnalyzer(JSContext *cx, JSScript *script) : cx(cx), script(script), ops(NULL), doList(ContextAllocPolicy(cx)), - usesRval(false) + usesRval(false), usesScope(false) { } ~BytecodeAnalyzer(); @@ -83,6 +86,7 @@ namespace js public: bool usesReturnValue() const { return usesRval; } + bool usesScopeChain() const { return usesScope; } inline const OpcodeStatus & operator [](uint32 offs) const { JS_ASSERT(offs < script->length); diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 4cbd33702270..83f70832be29 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -250,13 +250,7 @@ mjit::Compiler::generatePrologue() stubcc.crossJump(stubcc.masm.jump(), masm.label()); } - /* Fill in the members that initCallFrameLatePrologue does. */ - masm.storeValue(UndefinedValue(), Address(JSFrameReg, JSStackFrame::offsetOfReturnValue())); - - /* Set cx->fp */ - masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), Registers::ReturnReg); - - /* Set locals to undefined. */ + /* Set locals to undefined, as in initCallFrameLatePrologue */ for (uint32 i = 0; i < script->nfixed; i++) { Address local(JSFrameReg, sizeof(JSStackFrame) + i * sizeof(Value)); masm.storeValue(UndefinedValue(), local); @@ -269,6 +263,21 @@ mjit::Compiler::generatePrologue() } j.linkTo(masm.label(), &masm); + + if (analysis.usesScopeChain() && !fun->isHeavyweight()) { + /* + * Load the scope chain into the frame if necessary. The scope chain + * is always set for global and eval frames, and will have been set by + * GetCallObject for heavyweight function frames. + */ + RegisterID t0 = Registers::ReturnReg; + Jump hasScope = masm.branchTest32(Assembler::NonZero, + FrameFlagsAddress(), Imm32(JSFRAME_HAS_SCOPECHAIN)); + masm.loadPayload(Address(JSFrameReg, JSStackFrame::offsetOfCallee(fun)), t0); + masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0); + masm.storePtr(t0, Address(JSFrameReg, JSStackFrame::offsetOfScopeChain())); + hasScope.linkTo(masm.label(), &masm); + } } return Compile_Okay; @@ -647,7 +656,7 @@ mjit::Compiler::generateMethod() { RegisterID reg = frame.allocReg(); masm.load32(FrameFlagsAddress(), reg); - masm.or32(Imm32(JSFRAME_RVAL_ASSIGNED), reg); + masm.or32(Imm32(JSFRAME_HAS_RVAL), reg); masm.store32(reg, FrameFlagsAddress()); frame.freeReg(reg); @@ -1782,8 +1791,7 @@ mjit::Compiler::loadReturnValue(Assembler &masm) masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); if (analysis.usesReturnValue()) { Jump rvalClear = masm.branchTest32(Assembler::Zero, - FrameFlagsAddress(), - Imm32(JSFRAME_RVAL_ASSIGNED)); + FrameFlagsAddress(), Imm32(JSFRAME_HAS_RVAL)); Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); masm.loadValueAsComponents(rvalAddress, JSReturnReg_Type, JSReturnReg_Data); diff --git a/js/src/methodjit/InlineFrameAssembler.h b/js/src/methodjit/InlineFrameAssembler.h index 833d3a40d689..23d91923a9bc 100644 --- a/js/src/methodjit/InlineFrameAssembler.h +++ b/js/src/methodjit/InlineFrameAssembler.h @@ -114,8 +114,6 @@ class InlineFrameAssembler { AdjustedFrame adj(sizeof(JSStackFrame) + frameDepth * sizeof(Value)); masm.store32(Imm32(JSFRAME_FUNCTION | flags), adj.addrOf(JSStackFrame::offsetOfFlags())); - masm.loadPtr(Address(funObjReg, offsetof(JSObject, parent)), t0); - masm.storePtr(t0, adj.addrOf(JSStackFrame::offsetOfScopeChain())); masm.storePtr(JSFrameReg, adj.addrOf(JSStackFrame::offsetOfPrev())); DataLabelPtr ncodePatch = diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index b24a9da0ecdd..a7e3eadf9b38 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -311,7 +311,6 @@ stubs::FixupArity(VMFrame &f, uint32 nactual) * early prologue. */ uint32 flags = oldfp->isConstructingFlag(); - JSObject &scopeChain = oldfp->scopeChain(); JSFunction *fun = oldfp->fun(); void *ncode = oldfp->nativeReturnAddress(); @@ -327,7 +326,7 @@ stubs::FixupArity(VMFrame &f, uint32 nactual) THROWV(NULL); /* Reset the part of the stack frame set by the caller. */ - newfp->initCallFrameCallerHalf(cx, scopeChain, nactual, flags); + newfp->initCallFrameCallerHalf(cx, nactual, flags); /* Reset the part of the stack frame set by the prologue up to now. */ newfp->initCallFrameEarlyPrologue(fun, ncode); @@ -822,6 +821,16 @@ RunTracer(VMFrame &f) if (!cx->traceJitEnabled) return NULL; + /* + * Force initialization of the entry frame's scope chain and return value, + * if necessary. The tracer can query the scope chain without needing to + * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the + * correct return value stored if we trace/interpret through to the end + * of the frame. + */ + entryFrame->scopeChain(); + entryFrame->returnValue(); + bool blacklist; uintN inlineCallCount = 0; tpa = MonitorTracePoint(f.cx, inlineCallCount, blacklist); diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index cd7143851f12..191c9c549604 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -738,10 +738,6 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) prof.start(); #endif -#ifdef DEBUG - JSStackFrame *checkFp = fp; -#endif - Value *stackLimit = cx->stack().getStackLimit(cx); if (!stackLimit) return false; @@ -752,8 +748,10 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit); cx->setCurrentRegs(oldRegs); + JS_ASSERT(fp == cx->fp()); - JS_ASSERT(checkFp == cx->fp()); + /* The trampoline wrote the return value but did not set the HAS_RVAL flag. */ + fp->markReturnValue(); #ifdef JS_METHODJIT_SPEW prof.stop(); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 70e405ce2abc..5850c36e46a1 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1338,7 +1338,7 @@ stubs::Debugger(VMFrame &f, jsbytecode *pc) case JSTRAP_RETURN: f.cx->throwing = JS_FALSE; - f.cx->fp()->setAssignedReturnValue(rval); + f.cx->fp()->setReturnValue(rval); #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *, JS_METHODJIT_DATA(f.cx).trampolines.forceReturnFast); @@ -1378,7 +1378,7 @@ stubs::Trap(VMFrame &f, jsbytecode *pc) case JSTRAP_RETURN: f.cx->throwing = JS_FALSE; - f.cx->fp()->setAssignedReturnValue(rval); + f.cx->fp()->setReturnValue(rval); #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) *f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *, JS_METHODJIT_DATA(f.cx).trampolines.forceReturnFast); diff --git a/js/src/methodjit/TrampolineCompiler.cpp b/js/src/methodjit/TrampolineCompiler.cpp index bffa17e9fb58..efca7be919c2 100644 --- a/js/src/methodjit/TrampolineCompiler.cpp +++ b/js/src/methodjit/TrampolineCompiler.cpp @@ -126,7 +126,7 @@ TrampolineCompiler::generateForceReturn(Assembler &masm) /* Store any known return value */ masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); Jump rvalClear = masm.branchTest32(Assembler::Zero, - FrameFlagsAddress(), Imm32(JSFRAME_RVAL_ASSIGNED)); + FrameFlagsAddress(), Imm32(JSFRAME_HAS_RVAL)); Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); masm.loadValueAsComponents(rvalAddress, JSReturnReg_Type, JSReturnReg_Data); rvalClear.linkTo(masm.label(), &masm); From 4872e2463e0f8461311a7425b909274de5c4e9a1 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Sun, 3 Oct 2010 14:59:26 -0700 Subject: [PATCH 025/284] Need to JS_CHECK_RECURSION in GetTrap as well as Trap (aslo rename GetTrap wrappers to start with Get; 589103, r=gal). --- js/src/jsproxy.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 4940037f86a5..4857ba2ee508 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -297,11 +297,13 @@ JSProxyHandler::trace(JSTracer *trc, JSObject *proxy) static bool GetTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp) { + JS_CHECK_RECURSION(cx, return false); + return handler->getProperty(cx, ATOM_TO_JSID(atom), fvalp); } static bool -FundamentalTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp) +GetFundamentalTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp) { if (!GetTrap(cx, handler, atom, fvalp)) return false; @@ -316,7 +318,7 @@ FundamentalTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp) } static bool -DerivedTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp) +GetDerivedTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp) { JS_ASSERT(atom == ATOM(has) || atom == ATOM(hasOwn) || @@ -490,7 +492,7 @@ JSScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, js { JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); - return FundamentalTrap(cx, handler, ATOM(getPropertyDescriptor), tvr.addr()) && + return GetFundamentalTrap(cx, handler, ATOM(getPropertyDescriptor), tvr.addr()) && Trap1(cx, handler, tvr.value(), id, tvr.addr()) && ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), tvr.value()) && ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc); @@ -502,7 +504,7 @@ JSScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, { JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); - return FundamentalTrap(cx, handler, ATOM(getOwnPropertyDescriptor), tvr.addr()) && + return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyDescriptor), tvr.addr()) && Trap1(cx, handler, tvr.value(), id, tvr.addr()) && ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), tvr.value()) && ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc); @@ -515,7 +517,7 @@ JSScriptedProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id, JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); AutoValueRooter fval(cx); - return FundamentalTrap(cx, handler, ATOM(defineProperty), fval.addr()) && + return GetFundamentalTrap(cx, handler, ATOM(defineProperty), fval.addr()) && MakePropertyDescriptorObject(cx, id, desc, tvr.addr()) && Trap2(cx, handler, fval.value(), id, tvr.value(), tvr.addr()); } @@ -525,7 +527,7 @@ JSScriptedProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, Auto { JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); - return FundamentalTrap(cx, handler, ATOM(getOwnPropertyNames), tvr.addr()) && + return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyNames), tvr.addr()) && Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) && ArrayToIdVector(cx, tvr.value(), props); } @@ -535,7 +537,7 @@ JSScriptedProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *b { JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); - return FundamentalTrap(cx, handler, ATOM(delete), tvr.addr()) && + return GetFundamentalTrap(cx, handler, ATOM(delete), tvr.addr()) && Trap1(cx, handler, tvr.value(), id, tvr.addr()) && ValueToBool(cx, tvr.value(), bp); } @@ -545,7 +547,7 @@ JSScriptedProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector & { JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); - return FundamentalTrap(cx, handler, ATOM(enumerate), tvr.addr()) && + return GetFundamentalTrap(cx, handler, ATOM(enumerate), tvr.addr()) && Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) && ArrayToIdVector(cx, tvr.value(), props); } @@ -554,7 +556,7 @@ bool JSScriptedProxyHandler::fix(JSContext *cx, JSObject *proxy, Value *vp) { JSObject *handler = GetProxyHandlerObject(cx, proxy); - return FundamentalTrap(cx, handler, ATOM(fix), vp) && + return GetFundamentalTrap(cx, handler, ATOM(fix), vp) && Trap(cx, handler, *vp, 0, NULL, vp); } @@ -563,7 +565,7 @@ JSScriptedProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); - if (!DerivedTrap(cx, handler, ATOM(has), tvr.addr())) + if (!GetDerivedTrap(cx, handler, ATOM(has), tvr.addr())) return false; if (!js_IsCallable(tvr.value())) return JSProxyHandler::has(cx, proxy, id, bp); @@ -576,7 +578,7 @@ JSScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp { JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); - if (!DerivedTrap(cx, handler, ATOM(hasOwn), tvr.addr())) + if (!GetDerivedTrap(cx, handler, ATOM(hasOwn), tvr.addr())) return false; if (!js_IsCallable(tvr.value())) return JSProxyHandler::hasOwn(cx, proxy, id, bp); @@ -594,7 +596,7 @@ JSScriptedProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, AutoValueRooter tvr(cx, StringValue(str)); Value argv[] = { ObjectOrNullValue(receiver), tvr.value() }; AutoValueRooter fval(cx); - if (!DerivedTrap(cx, handler, ATOM(get), fval.addr())) + if (!GetDerivedTrap(cx, handler, ATOM(get), fval.addr())) return false; if (!js_IsCallable(fval.value())) return JSProxyHandler::get(cx, proxy, receiver, id, vp); @@ -611,7 +613,7 @@ JSScriptedProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, AutoValueRooter tvr(cx, StringValue(str)); Value argv[] = { ObjectOrNullValue(receiver), tvr.value(), *vp }; AutoValueRooter fval(cx); - if (!DerivedTrap(cx, handler, ATOM(set), fval.addr())) + if (!GetDerivedTrap(cx, handler, ATOM(set), fval.addr())) return false; if (!js_IsCallable(fval.value())) return JSProxyHandler::set(cx, proxy, receiver, id, vp); @@ -623,7 +625,7 @@ JSScriptedProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVecto { JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); - if (!DerivedTrap(cx, handler, ATOM(enumerateOwn), tvr.addr())) + if (!GetDerivedTrap(cx, handler, ATOM(enumerateOwn), tvr.addr())) return false; if (!js_IsCallable(tvr.value())) return JSProxyHandler::enumerateOwn(cx, proxy, props); @@ -636,7 +638,7 @@ JSScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, Val { JSObject *handler = GetProxyHandlerObject(cx, proxy); AutoValueRooter tvr(cx); - if (!DerivedTrap(cx, handler, ATOM(iterate), tvr.addr())) + if (!GetDerivedTrap(cx, handler, ATOM(iterate), tvr.addr())) return false; if (!js_IsCallable(tvr.value())) return JSProxyHandler::iterate(cx, proxy, flags, vp); From a62950ac8b0354791accf9ccac0dbd608af5fc1e Mon Sep 17 00:00:00 2001 From: Alan Pierce Date: Sun, 3 Oct 2010 23:20:12 -0400 Subject: [PATCH 026/284] Check for OOM in js_ConcatStrings(). b=596988, r=gal,sstangl; rs=brendan. --- js/src/jsstr.cpp | 62 ++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index fd5b63aa628f..4cec144ee1f4 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -360,54 +360,48 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) /* * There are 4 cases, based on whether on whether the left or right is a - * rope or non-rope string. This can be handled in a general way, but it's - * empirically a little faster and arguably simpler to handle each of the 4 - * cases independently and directly. + * rope or non-rope string. */ + JSRopeBufferInfo *buf = NULL; + if (leftRopeTop) { + /* Left child is a rope. */ + JSRopeBufferInfo *leftBuf = left->topNodeBuffer(); + + /* If both children are ropes, steal the larger buffer. */ if (JS_UNLIKELY(rightRopeTop)) { - JSRopeBufferInfo *leftBuf = left->topNodeBuffer(); JSRopeBufferInfo *rightBuf = right->topNodeBuffer(); - /* Attempt to steal the larger buffer. WLOG, it's the left one. */ + /* Put the larger buffer into 'leftBuf'. */ if (leftBuf->capacity >= rightBuf->capacity) { cx->free(rightBuf); } else { cx->free(leftBuf); leftBuf = rightBuf; } - - JSRopeBufferInfo *buf = ObtainRopeBuffer(cx, true, true, leftBuf, - length, left, right); - return FinishConcat(cx, true, true, left, right, length, buf); - } else { - /* Steal buffer from left if it's big enough. */ - JSRopeBufferInfo *leftBuf = left->topNodeBuffer(); - - JSRopeBufferInfo *buf = ObtainRopeBuffer(cx, true, false, leftBuf, - length, left, right); - return FinishConcat(cx, true, false, left, right, length, buf); } + + buf = ObtainRopeBuffer(cx, true, rightRopeTop, leftBuf, length, left, right); + if (!buf) + return NULL; + } else if (JS_UNLIKELY(rightRopeTop)) { + /* Right child is a rope: steal its buffer if big enough. */ + JSRopeBufferInfo *rightBuf = right->topNodeBuffer(); + + buf = ObtainRopeBuffer(cx, false, true, rightBuf, length, left, right); + if (!buf) + return NULL; } else { - if (JS_UNLIKELY(rightRopeTop)) { - /* Steal buffer from right if it's big enough. */ - JSRopeBufferInfo *rightBuf = right->topNodeBuffer(); - - JSRopeBufferInfo *buf = ObtainRopeBuffer(cx, false, true, rightBuf, - length, left, right); - return FinishConcat(cx, false, true, left, right, length, buf); - } else { - /* Need to make a new buffer. */ - size_t capacity; - size_t allocSize = RopeAllocSize(length, &capacity); - JSRopeBufferInfo *buf = (JSRopeBufferInfo *) cx->malloc(allocSize); - if (!buf) - return NULL; - - buf->capacity = capacity; - return FinishConcat(cx, false, false, left, right, length, buf); - } + /* Neither child is a rope: need to make a new buffer. */ + size_t capacity; + size_t allocSize = RopeAllocSize(length, &capacity); + buf = (JSRopeBufferInfo *) cx->malloc(allocSize); + if (!buf) + return NULL; + buf->capacity = capacity; } + + return FinishConcat(cx, leftRopeTop, rightRopeTop, left, right, length, buf); } JSString * JS_FASTCALL From 7673da965e86a39e2ea6e0dc466c6fd18ea95c3e Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Mon, 4 Oct 2010 13:30:46 -0400 Subject: [PATCH 027/284] Fix pinReg() around copyDataIntoReg() in jsop_bitop(). b=600419, r=dvander. --- js/src/methodjit/FastOps.cpp | 4 ++-- js/src/trace-test/tests/jaeger/bug600419.js | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 js/src/trace-test/tests/jaeger/bug600419.js diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 8e6ee511e226..50d3fb4d1ea3 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -524,14 +524,14 @@ mjit::Compiler::jsop_bitop(JSOp op) RegisterID rr = frame.tempRegForData(rhs); #endif - frame.pinReg(rr); if (lhs->isConstant()) { + frame.pinReg(rr); reg = frame.allocReg(); masm.move(Imm32(lhs->getValue().toInt32()), reg); + frame.unpinReg(rr); } else { reg = frame.copyDataIntoReg(lhs); } - frame.unpinReg(rr); if (op == JSOP_LSH) { masm.lshift32(rr, reg); diff --git a/js/src/trace-test/tests/jaeger/bug600419.js b/js/src/trace-test/tests/jaeger/bug600419.js new file mode 100644 index 000000000000..981a6a17c4c2 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug600419.js @@ -0,0 +1,5 @@ +/* Don't assert. */ +(function() { + var x; + [1].map(function(){}, x << x); +})() From 8ab67f850c66c0880f69ead218f1c9ad200c2133 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Oct 2010 14:13:33 -0700 Subject: [PATCH 028/284] ICs for scripted new (bug 589398, r=luke,dmandelin). --- js/src/jsapi.cpp | 2 +- js/src/jsbuiltins.h | 2 +- js/src/jscompartment.cpp | 4 +- js/src/jsdbgapi.cpp | 11 +- js/src/jsemit.cpp | 9 +- js/src/jsinterp.cpp | 158 +++-- js/src/jsinterp.h | 2 +- js/src/jsiter.cpp | 2 +- js/src/jsobj.cpp | 25 +- js/src/jsobj.h | 12 +- js/src/jsopcode.tbl | 3 + js/src/jsproxy.cpp | 2 +- js/src/jsscript.cpp | 12 +- js/src/jsscript.h | 71 +- js/src/jstracer.cpp | 37 +- js/src/jstracer.h | 2 +- js/src/methodjit/BaseAssembler.h | 5 + js/src/methodjit/Compiler.cpp | 609 ++++++++++-------- js/src/methodjit/Compiler.h | 23 +- js/src/methodjit/FrameState.cpp | 19 +- js/src/methodjit/FrameState.h | 7 +- js/src/methodjit/InvokeHelpers.cpp | 88 ++- js/src/methodjit/MethodJIT.cpp | 56 +- js/src/methodjit/MethodJIT.h | 85 ++- js/src/methodjit/MonoIC.cpp | 157 ++--- js/src/methodjit/MonoIC.h | 12 +- js/src/methodjit/PolyIC.cpp | 142 ++-- js/src/methodjit/PolyIC.h | 15 +- js/src/methodjit/Retcon.cpp | 65 +- js/src/methodjit/Retcon.h | 3 +- js/src/methodjit/StubCalls-inl.h | 1 - js/src/methodjit/StubCalls.cpp | 42 +- js/src/methodjit/StubCalls.h | 10 +- js/src/methodjit/StubCompiler.h | 5 + js/src/shell/js.cpp | 2 +- .../tests/jaeger/bug563000/simple-trap-2.js | 2 +- .../jaeger/bug563000/trap-force-return-1.js | 2 +- .../jaeger/bug563000/trap-own-callsite.js | 2 +- .../jaeger/bug563000/trap-self-as-parent.js | 2 +- .../jaeger/bug563000/trap-self-from-trap.js | 4 +- 40 files changed, 995 insertions(+), 717 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 5b6917660ea0..0527dc85185d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2990,7 +2990,7 @@ JS_NewObjectForConstructor(JSContext *cx, const jsval *vp) CHECK_REQUEST(cx); assertSameCompartment(cx, *vp); - return js_NewInstance(cx, JSVAL_TO_OBJECT(*vp)); + return js_CreateThis(cx, JSVAL_TO_OBJECT(*vp)); } JS_PUBLIC_API(JSBool) diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index 275663e05028..e884098dcd78 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -612,7 +612,7 @@ JS_DECLARE_CALLINFO(js_NumberToString) /* Defined in jsobj.cpp. */ JS_DECLARE_CALLINFO(js_Object_tn) -JS_DECLARE_CALLINFO(js_NewInstanceFromTrace) +JS_DECLARE_CALLINFO(js_CreateThisFromTrace) JS_DECLARE_CALLINFO(js_NonEmptyObject) /* Defined in jsregexp.cpp. */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 96b4ad80dd0e..b6ee679d48c2 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -318,7 +318,7 @@ JSCompartment::sweep(JSContext *cx) #if defined JS_METHODJIT && defined JS_MONOIC for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) { JSScript *script = reinterpret_cast(cursor); - if (script->jit) + if (script->hasJITCode()) mjit::ic::SweepCallICs(script); } #endif @@ -333,7 +333,7 @@ JSCompartment::purge(JSContext *cx) for (JSScript *script = (JSScript *)scripts.next; &script->links != &scripts; script = (JSScript *)script->links.next) { - if (script->jit) { + if (script->hasJITCode()) { # if defined JS_POLYIC mjit::ic::PurgePICs(cx, script); # endif diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 0b92a81b6218..e6fc2e91fec5 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -116,8 +116,7 @@ js_SetDebugMode(JSContext *cx, JSBool debug) &script->links != &cx->compartment->scripts; script = (JSScript *)script->links.next) { if (script->debugMode != debug && - script->ncode && - script->ncode != JS_UNJITTABLE_METHOD && + script->hasJITCode() && !IsScriptLive(cx, script)) { /* * In the event that this fails, debug mode is left partially on, @@ -236,6 +235,10 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, return JS_FALSE; } + // Do not trap BEGIN, it's a special prologue opcode. + if (JSOp(*pc) == JSOP_BEGIN) + pc += JSOP_BEGIN_LENGTH; + JS_ASSERT((JSOp) *pc != JSOP_TRAP); junk = NULL; rt = cx->runtime; @@ -274,7 +277,7 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, cx->free(junk); #ifdef JS_METHODJIT - if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) { + if (script->hasJITCode()) { mjit::Recompiler recompiler(cx, script); if (!recompiler.recompile()) return JS_FALSE; @@ -327,7 +330,7 @@ JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, DBG_UNLOCK(cx->runtime); #ifdef JS_METHODJIT - if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) { + if (script->hasJITCode()) { mjit::Recompiler recompiler(cx, script); recompiler.recompile(); } diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 7d4db58b24f0..d857d1859a1d 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -3678,10 +3678,15 @@ bad: JSBool js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) { + CG_SWITCH_TO_PROLOG(cg); + JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); + if (js_Emit1(cx, cg, JSOP_BEGIN) < 0) + return false; + CG_SWITCH_TO_MAIN(cg); + if (cg->flags & TCF_FUN_IS_GENERATOR) { - /* JSOP_GENERATOR must be the first instruction. */ + /* JSOP_GENERATOR must be the first real instruction. */ CG_SWITCH_TO_PROLOG(cg); - JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) return false; CG_SWITCH_TO_MAIN(cg); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 2879e8094d0c..ea33bbade29e 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -134,12 +134,13 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) #if defined(JS_METHODJIT) && defined(JS_MONOIC) JSScript *script = this->script(); + js::mjit::JITScript *jit = script->getJIT(isConstructing()); size_t low = 0; - size_t high = script->jit->nCallICs; + size_t high = jit->nCallICs; while (high > low + 1) { /* Could overflow here on a script with 2 billion calls. Oh well. */ size_t mid = (high + low) / 2; - void *entry = script->callICs[mid].funGuard.executableAddress(); + void *entry = jit->callICs[mid].funGuard.executableAddress(); /* * Use >= here as the return address of the call is likely to be @@ -151,7 +152,7 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) low = mid; } - js::mjit::ic::CallICInfo &callIC = script->callICs[low]; + js::mjit::ic::CallICInfo &callIC = jit->callICs[low]; JS_ASSERT((uint8*)callIC.funGuard.executableAddress() + callIC.joinPointOffset == next->ncode_); return callIC.pc; @@ -616,7 +617,7 @@ struct AutoInterpPreparer { }; JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain) +RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) { JS_ASSERT(script); @@ -626,8 +627,11 @@ RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain AutoInterpPreparer prepareInterp(cx, script); + JS_ASSERT(fp == cx->fp()); + JS_ASSERT(fp->script() == script); + #ifdef JS_METHODJIT - mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fun, &scopeChain); + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fp); if (status == mjit::Compile_Error) return JS_FALSE; @@ -635,7 +639,7 @@ RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain return mjit::JaegerShot(cx); #endif - return Interpret(cx, cx->fp()); + return Interpret(cx, fp); } /* @@ -686,8 +690,10 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) /* Handle the empty-script special case. */ if (JS_UNLIKELY(script->isEmpty())) { if (flags & JSINVOKE_CONSTRUCT) { - JS_ASSERT(args.thisv().isObject()); - args.rval() = args.thisv(); + JSObject *obj = js_CreateThisForFunction(cx, &callee); + if (!obj) + return false; + args.rval().setObject(*obj); } else { args.rval().setUndefined(); } @@ -718,19 +724,20 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) * and fp->scopeChain is correct because the thisObject hook may call * JS_GetScopeChain. */ - Value &thisv = fp->functionThis(); - JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !thisv.isPrimitive()); - if (thisv.isObject() && !(flags & JSINVOKE_CONSTRUCT)) { - /* - * We must call the thisObject hook in case we are not called from the - * interpreter, where a prior bytecode has computed an appropriate - * |this| already. - */ - JSObject *thisp = thisv.toObject().thisObject(cx); - if (!thisp) - return false; - JS_ASSERT(IsSaneThisObject(*thisp)); - thisv.setObject(*thisp); + if (!(flags & JSINVOKE_CONSTRUCT)) { + Value &thisv = fp->functionThis(); + if (thisv.isObject()) { + /* + * We must call the thisObject hook in case we are not called from the + * interpreter, where a prior bytecode has computed an appropriate + * |this| already. + */ + JSObject *thisp = thisv.toObject().thisObject(cx); + if (!thisp) + return false; + JS_ASSERT(IsSaneThisObject(*thisp)); + thisv.setObject(*thisp); + } } JSInterpreterHook hook = cx->debugHooks->callHook; @@ -743,7 +750,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) { AutoPreserveEnumerators preserve(cx); Probes::enterJSFun(cx, fun); - ok = RunScript(cx, script, fun, fp->scopeChain()); + ok = RunScript(cx, script, fp); Probes::exitJSFun(cx, fun); } @@ -756,6 +763,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) PutActivationObjects(cx, fp); args.rval() = fp->returnValue(); + JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !args.rval().isPrimitive()); + return ok; } @@ -900,7 +909,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script, /* Run script until JSOP_STOP or error. */ AutoPreserveEnumerators preserve(cx); - JSBool ok = RunScript(cx, script, NULL, frame.fp()->scopeChain()); + JSBool ok = RunScript(cx, script, frame.fp()); if (result) *result = frame.fp()->returnValue(); @@ -1153,8 +1162,9 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef) /* Handle the fast-constructors cases before falling into the general case . */ Class *clasp = callee->getClass(); + JSFunction *fun = NULL; if (clasp == &js_FunctionClass) { - JSFunction *fun = callee->getFunctionPrivate(); + fun = callee->getFunctionPrivate(); if (fun->isConstructor()) { args.thisv().setMagicWithObjectOrNullPayload(NULL); return CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base()); @@ -1164,25 +1174,30 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef) return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base()); } - /* Construct 'this'. */ - JSObject *obj = js_NewInstance(cx, callee); - if (!obj) - return false; - args.thisv().setObject(*obj); + /* Scripts create their own |this| in JSOP_BEGIN */ + if (!fun || !fun->isInterpreted()) { + JSObject *obj = js_CreateThis(cx, callee); + if (!obj) + return false; + args.thisv().setObject(*obj); + } if (!Invoke(cx, args, JSINVOKE_CONSTRUCT)) return false; - /* Check the return value and if it's primitive, force it to be obj. */ if (args.rval().isPrimitive()) { - if (callee->getClass() != &js_FunctionClass) { + if (clasp != &js_FunctionClass) { /* native [[Construct]] returning primitive is error */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_NEW_RESULT, js_ValueToPrintableString(cx, args.rval())); return false; } - args.rval().setObject(*obj); + + /* The interpreter fixes rval for us. */ + JS_ASSERT(!fun->isInterpreted()); + + args.rval() = args.thisv(); } JS_RUNTIME_METER(cx->runtime, constructs); @@ -2290,7 +2305,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN do { \ JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \ if (leaveOnSafePoint && !regs.fp->hasImacropc() && \ - script->nmap && script->nmap[regs.pc - script->code]) { \ + script->hasNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \ JS_ASSERT(!TRACE_RECORDER(cx)); \ interpReturnOK = true; \ goto stop_recording; \ @@ -4477,6 +4492,41 @@ BEGIN_CASE(JSOP_ENUMELEM) } END_CASE(JSOP_ENUMELEM) +BEGIN_CASE(JSOP_BEGIN) +{ + if (regs.fp->isConstructing()) { + JSObject *obj2 = js_CreateThisForFunction(cx, ®s.fp->callee()); + if (!obj2) + goto error; + regs.fp->functionThis().setObject(*obj2); + } + + /* Call the debugger hook if present. */ + if (JSInterpreterHook hook = cx->debugHooks->callHook) { + regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0, + cx->debugHooks->callHookData)); + CHECK_INTERRUPT_HANDLER(); + } + + JS_RUNTIME_METER(rt, inlineCalls); + + Probes::enterJSFun(cx, regs.fp->fun()); + +#ifdef JS_METHODJIT + /* Try to ensure methods are method JIT'd. */ + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp); + if (status == mjit::Compile_Error) + goto error; + if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { + if (!mjit::JaegerShot(cx)) + goto error; + interpReturnOK = true; + goto inline_return; + } +#endif +} +END_CASE(JSOP_BEGIN) + { JSFunction *newfun; JSObject *callee; @@ -4498,24 +4548,15 @@ BEGIN_CASE(JSOP_NEW) if (IsFunctionObject(vp[0], &callee)) { newfun = callee->getFunctionPrivate(); if (newfun->isInterpreted()) { - /* Root as we go using vp[1]. */ - if (!callee->getProperty(cx, - ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), - &vp[1])) { - goto error; - } - JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL; - JSObject *obj2 = NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent()); - if (!obj2) - goto error; - if (newfun->u.i.script->isEmpty()) { + JSObject *obj2 = js_CreateThisForFunction(cx, callee); + if (!obj2) + goto error; vp[0].setObject(*obj2); regs.sp = vp + 1; goto end_new; } - vp[1].setObject(*obj2); flags = JSFRAME_CONSTRUCTING; goto inline_call; } @@ -4584,38 +4625,13 @@ BEGIN_CASE(JSOP_APPLY) if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp)) goto error; - /* Call the debugger hook if present. */ - if (JSInterpreterHook hook = cx->debugHooks->callHook) { - regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0, - cx->debugHooks->callHookData)); - CHECK_INTERRUPT_HANDLER(); - } - inlineCallCount++; - JS_RUNTIME_METER(rt, inlineCalls); - - Probes::enterJSFun(cx, newfun); TRACE_0(EnterFrame); -#ifdef JS_METHODJIT - /* Try to ensure methods are method JIT'd. */ - { - JSObject *scope = ®s.fp->scopeChain(); - mjit::CompileStatus status = mjit::CanMethodJIT(cx, newscript, newfun, scope); - if (status == mjit::Compile_Error) - goto error; - if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { - if (!mjit::JaegerShot(cx)) - goto error; - interpReturnOK = true; - goto inline_return; - } - } -#endif - /* Load first op and dispatch it (safe since JSOP_STOP). */ op = (JSOp) *regs.pc; + JS_ASSERT(op == JSOP_BEGIN); DO_OP(); } diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 04708a494bdd..9b09ddaa1f40 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -968,7 +968,7 @@ extern JS_REQUIRES_STACK JS_NEVER_INLINE bool Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, uintN interpFlags = 0); extern JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain); +RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp); #define JSPROP_INITIALIZER 0x100 /* NB: Not a valid property attribute. */ diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 33e569b508b6..a5d18f38abcf 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1283,7 +1283,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, JSObject *enumerators = cx->enumerators; cx->enumerators = gen->enumerators; - ok = RunScript(cx, stackfp->script(), stackfp->fun(), stackfp->scopeChain()); + ok = RunScript(cx, stackfp->script(), stackfp); gen->enumerators = cx->enumerators; cx->enumerators = enumerators; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 32a581e9d271..1ba744e371d1 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2770,7 +2770,7 @@ js_Object(JSContext *cx, uintN argc, Value *vp) } JSObject* -js_NewInstance(JSContext *cx, JSObject *callee) +js_CreateThis(JSContext *cx, JSObject *callee) { Class *clasp = callee->getClass(); @@ -2790,6 +2790,25 @@ js_NewInstance(JSContext *cx, JSObject *callee) return NewObject(cx, newclasp, proto, parent); } +JSObject * +js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto) +{ + return NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent()); +} + +JSObject * +js_CreateThisForFunction(JSContext *cx, JSObject *callee) +{ + Value protov; + if (!callee->getProperty(cx, + ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), + &protov)) { + return NULL; + } + JSObject *proto = protov.isObject() ? &protov.toObject() : NULL; + return js_CreateThisForFunctionWithProto(cx, callee, proto); +} + #ifdef JS_TRACER static JS_ALWAYS_INLINE JSObject* @@ -2840,7 +2859,7 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, ST nanojit::ACCSET_STORE_ANY) JSObject* FASTCALL -js_NewInstanceFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) +js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) { JS_ASSERT(JS_ON_TRACE(cx)); JS_ASSERT(ctor->isFunction()); @@ -2891,7 +2910,7 @@ js_NewInstanceFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) return NewNonFunction(cx, clasp, proto, parent); } -JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstanceFromTrace, CONTEXT, CLASS, OBJECT, 0, +JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0, nanojit::ACCSET_STORE_ANY) #else /* !JS_TRACER */ diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 5b4b66b66cd2..c76af07c3ed3 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1407,8 +1407,18 @@ extern JSObject * js_ConstructObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, uintN argc, js::Value *argv); +// Specialized call for constructing |this| with a known function callee, +// and a known prototype. extern JSObject * -js_NewInstance(JSContext *cx, JSObject *callee); +js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto); + +// Specialized call for constructing |this| with a known function callee. +extern JSObject * +js_CreateThisForFunction(JSContext *cx, JSObject *callee); + +// Generic call for constructing |this|. +extern JSObject * +js_CreateThis(JSContext *cx, JSObject *callee); extern jsid js_CheckForStringIndex(jsid id); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 89c895c84fd5..5707ea478dc8 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -620,3 +620,6 @@ OPDEF(JSOP_FORGLOBAL, 246,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL */ OPDEF(JSOP_BLOCKCHAIN, 247,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT) OPDEF(JSOP_NULLBLOCKCHAIN,248,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE) + +OPDEF(JSOP_BEGIN, 249,"begin", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_TMPSLOT) + diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 4857ba2ee508..937b07515e7b 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1235,7 +1235,7 @@ callable_Call(JSContext *cx, uintN argc, Value *vp) static JSBool callable_Construct(JSContext *cx, uintN argc, Value *vp) { - JSObject *thisobj = js_NewInstance(cx, &JS_CALLEE(cx, vp).toObject()); + JSObject *thisobj = js_CreateThis(cx, &JS_CALLEE(cx, vp).toObject()); if (!thisobj) return false; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index ef2649c5ebc7..db8af79d5ed3 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -118,7 +118,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript, uint32 length, lineno, nslots, magic; uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, nconsts, i; uint32 prologLength, version, encodedClosedCount; - uint16 nClosedArgs, nClosedVars; + uint16 nClosedArgs = 0, nClosedVars = 0; JSPrincipals *principals; uint32 encodeable; JSBool filenameWasSaved; @@ -1641,16 +1641,6 @@ js_GetScriptLineExtent(JSScript *script) return 1 + lineno - script->lineno; } -#ifdef JS_METHODJIT -bool -JSScript::isValidJitCode(void *jcode) -{ - return (char*)jcode >= (char*)jit->invoke && - (char*)jcode < (char*)jit->invoke + - jit->inlineLength + jit->outOfLineLength; -} -#endif - void JSScript::copyClosedSlotsTo(JSScript *other) { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 88321350d88a..b95dc9e5f337 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -169,21 +169,20 @@ struct GlobalSlotArray { namespace JSC { class ExecutablePool; } + +#define JS_UNJITTABLE_SCRIPT (reinterpret_cast(1)) + +enum JITScriptStatus { + JITScript_None, + JITScript_Invalid, + JITScript_Valid +}; + namespace js { namespace mjit { struct JITScript; -namespace ic { -# if defined JS_POLYIC - struct PICInfo; -# endif -# if defined JS_MONOIC - struct MICInfo; - struct CallICInfo; -# endif -} -struct CallSite; } } #endif @@ -285,20 +284,35 @@ struct JSScript { public: #ifdef JS_METHODJIT - // Note: the other pointers in this group may be non-NULL only if - // |execPool| is non-NULL. - void *ncode; /* native code compiled by the method JIT */ - void **nmap; /* maps PCs to native code */ - js::mjit::JITScript *jit; /* Extra JIT info */ -# if defined JS_POLYIC - js::mjit::ic::PICInfo *pics; /* PICs in this script */ -# endif -# if defined JS_MONOIC - js::mjit::ic::MICInfo *mics; /* MICs in this script. */ - js::mjit::ic::CallICInfo *callICs; /* CallICs in this script. */ -# endif + // Fast-cached pointers to make calls faster. These are also used to + // quickly test whether there is JIT code; a NULL value means no + // compilation has been attempted. A JS_UNJITTABLE_SCRIPT value means + // compilation failed. Any value is the arity-check entry point. + void *jitArityCheckNormal; + void *jitArityCheckCtor; - bool isValidJitCode(void *jcode); + js::mjit::JITScript *jitNormal; /* Extra JIT info for normal scripts */ + js::mjit::JITScript *jitCtor; /* Extra JIT info for constructors */ + + bool hasJITCode() { + return jitNormal || jitCtor; + } + + inline void **maybeNativeMap(bool constructing); + inline bool hasNativeCodeForPC(bool constructing, jsbytecode *pc); + + js::mjit::JITScript *getJIT(bool constructing) { + return constructing ? jitCtor : jitNormal; + } + + JITScriptStatus getJITStatus(bool constructing) { + void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal; + if (addr == NULL) + return JITScript_None; + if (addr == JS_UNJITTABLE_SCRIPT) + return JITScript_Invalid; + return JITScript_Valid; + } #endif /* Script notes are allocated right after the code. */ @@ -393,17 +407,6 @@ struct JSScript { return const_cast(&emptyScriptConst); } -#ifdef JS_METHODJIT - /* - * Map the given PC to the corresponding native code address. - */ - void *pcToNative(jsbytecode *pc) { - JS_ASSERT(nmap); - JS_ASSERT(nmap[pc - code]); - return nmap[pc - code]; - } -#endif - uint32 getClosedArg(uint32 index) { JS_ASSERT(index < nClosedArgs); return closedSlots[index]; diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 81cdb7e15213..9cb913a775ab 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -11051,6 +11051,20 @@ TraceRecorder::emitNativePropertyOp(const Shape* shape, LIns* obj_ins, guard(true, lir->insEqI_0(status_ins), STATUS_EXIT); } +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BEGIN() +{ + JSStackFrame* fp = cx->fp(); + if (fp->isConstructing()) { + LIns* callee_ins = get(&cx->fp()->calleeValue()); + LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins }; + LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); + guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); + set(&fp->thisValue(), tv_ins); + } + return ARECORD_CONTINUE; +} + JS_REQUIRES_STACK RecordingStatus TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], bool rooted) { @@ -11360,7 +11374,7 @@ TraceRecorder::callNative(uintN argc, JSOp mode) clasp = &js_ObjectClass; JS_ASSERT(((jsuword) clasp & 3) == 0); - // Abort on |new Function|. js_NewInstance would allocate a regular- + // Abort on |new Function|. js_CreateThis would allocate a regular- // sized JSObject, not a Function-sized one. (The Function ctor would // deep-bail anyway but let's not go there.) if (clasp == &js_FunctionClass) @@ -11379,7 +11393,7 @@ TraceRecorder::callNative(uintN argc, JSOp mode) args[0] = INS_CONSTOBJ(funobj); args[1] = INS_CONSTPTR(clasp); args[2] = cx_ins; - newobj_ins = lir->insCall(&js_NewInstanceFromTrace_ci, args); + newobj_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); guard(false, lir->insEqP_0(newobj_ins), OOM_EXIT); /* @@ -11504,15 +11518,8 @@ TraceRecorder::functionCall(uintN argc, JSOp mode) } #endif - if (FUN_INTERPRETED(fun)) { - if (mode == JSOP_NEW) { - LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; - LIns* tv_ins = lir->insCall(&js_NewInstanceFromTrace_ci, args); - guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); - set(&tval, tv_ins); - } + if (FUN_INTERPRETED(fun)) return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW); - } Native native = fun->maybeNative(); Value* argv = &tval + 1; @@ -13306,7 +13313,15 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, * and does not call any TR::record_*CallComplete hook. */ if (fun->u.i.script->isEmpty()) { - LIns* rval_ins = constructing ? stack(-1 - argc) : INS_UNDEFINED(); + LIns* rval_ins; + if (constructing) { + LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; + LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); + guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); + rval_ins = tv_ins; + } else { + rval_ins = INS_UNDEFINED(); + } stack(-2 - argc, rval_ins); return RECORD_CONTINUE; } diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 25e291e3b76a..cf5280b24d03 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -949,7 +949,7 @@ class TraceRecorder /* Carry the return value from a native call to the record_NativeCallComplete. */ nanojit::LIns* native_rval_ins; - /* Carry the return value of js_NewInstance to record_NativeCallComplete. */ + /* Carry the return value of js_CreateThis to record_NativeCallComplete. */ nanojit::LIns* newobj_ins; /* Carry the JSSpecializedNative used to generate a call to record_NativeCallComplete. */ diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 110c53b57c83..5339d00eee1b 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -342,6 +342,11 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste } }; +/* Return f if the script is strict mode code, f otherwise. */ +#define STRICT_VARIANT(f) \ + (FunctionTemplateConditional(script->strictModeCode, \ + f, f)) + /* Save some typing. */ static const JSC::MacroAssembler::RegisterID JSFrameReg = BaseAssembler::JSFrameReg; static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = BaseAssembler::JSReturnReg_Type; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 83f70832be29..53040f027dfb 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -74,12 +74,16 @@ static const char *OpcodeNames[] = { }; #endif -mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) +mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp) : BaseCompiler(cx), - script(script), - scopeChain(scopeChain), + fp(fp), + script(fp->script()), + scopeChain(&fp->scopeChain()), globalObj(scopeChain->getGlobal()), - fun(fun), + fun(fp->isFunctionFrame() && !fp->isEvalFrame() + ? fp->fun() + : NULL), + isConstructing(fp->isConstructing()), analysis(cx, script), jumpMap(NULL), frame(cx, script, masm), branchPatches(ContextAllocPolicy(cx)), #if defined JS_MONOIC @@ -99,6 +103,34 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObj { } +CompileStatus +mjit::Compiler::compile() +{ + JS_ASSERT(!script->isEmpty()); + JS_ASSERT_IF(isConstructing, !script->jitCtor); + JS_ASSERT_IF(!isConstructing, !script->jitNormal); + + JITScript **jit = isConstructing ? &script->jitCtor : &script->jitNormal; + void **checkAddr = isConstructing + ? &script->jitArityCheckCtor + : &script->jitArityCheckNormal; + + CompileStatus status = performCompilation(jit); + if (status == Compile_Okay) { + // Global scripts don't have an arity check entry. That's okay, we + // just need a pointer so the VM can quickly decide whether this + // method can be JIT'd or not. Global scripts cannot be IC'd, since + // they have no functions, so there is no danger. + *checkAddr = (*jit)->arityCheckEntry + ? (*jit)->arityCheckEntry + : (*jit)->invokeEntry; + } else { + *checkAddr = JS_UNJITTABLE_SCRIPT; + } + + return status; +} + #define CHECK_STATUS(expr) \ JS_BEGIN_MACRO \ CompileStatus status_ = (expr); \ @@ -107,10 +139,8 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObj JS_END_MACRO CompileStatus -mjit::Compiler::Compile() +mjit::Compiler::performCompilation(JITScript **jitp) { - JS_ASSERT(!script->ncode); - JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n", script->filename, script->lineno, script->length); @@ -152,7 +182,7 @@ mjit::Compiler::Compile() CHECK_STATUS(generatePrologue()); CHECK_STATUS(generateMethod()); CHECK_STATUS(generateEpilogue()); - CHECK_STATUS(finishThisUp()); + CHECK_STATUS(finishThisUp(jitp)); #ifdef JS_METHODJIT_SPEW prof.stop(); @@ -160,7 +190,7 @@ mjit::Compiler::Compile() #endif JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%ld\")\n", - (void*)script->ncode, masm.size() + stubcc.size()); + (*jitp)->code.m_code.executableAddress(), (*jitp)->code.m_size); return Compile_Okay; } @@ -173,18 +203,13 @@ mjit::Compiler::~Compiler() } CompileStatus JS_NEVER_INLINE -mjit::TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) +mjit::TryCompile(JSContext *cx, JSStackFrame *fp) { - Compiler cc(cx, script, fun, scopeChain); + JS_ASSERT(cx->fp() == fp); - JS_ASSERT(!script->ncode); - JS_ASSERT(!script->isEmpty()); + Compiler cc(cx, fp); - CompileStatus status = cc.Compile(); - if (status != Compile_Okay) - script->ncode = JS_UNJITTABLE_METHOD; - - return status; + return cc.compile(); } CompileStatus @@ -290,7 +315,7 @@ mjit::Compiler::generateEpilogue() } CompileStatus -mjit::Compiler::finishThisUp() +mjit::Compiler::finishThisUp(JITScript **jitp) { for (size_t i = 0; i < branchPatches.length(); i++) { Label label = labelOf(branchPatches[i].pc); @@ -336,18 +361,16 @@ mjit::Compiler::finishThisUp() return Compile_Error; } - script->jit = (JITScript *)cursor; + JITScript *jit = (JITScript *)cursor; cursor += sizeof(JITScript); - script->jit->execPool = execPool; - script->jit->inlineLength = masm.size(); - script->jit->outOfLineLength = stubcc.size(); - script->jit->nCallSites = callSites.length(); - script->jit->invoke = result; + jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size()); + jit->nCallSites = callSites.length(); + jit->invokeEntry = result; /* Build the pc -> ncode mapping. */ void **nmap = (void **)cursor; - script->nmap = nmap; + jit->nmap = nmap; cursor += sizeof(void *) * script->length; for (size_t i = 0; i < script->length; i++) { @@ -358,107 +381,116 @@ mjit::Compiler::finishThisUp() } } - if (fun) - script->jit->arityCheck = stubCode.locationOf(arityLabel).executableAddress(); - -#if defined JS_MONOIC - script->jit->nMICs = mics.length(); - if (mics.length()) { - script->mics = (ic::MICInfo *)cursor; - cursor += sizeof(ic::MICInfo) * mics.length(); - } else { - script->mics = NULL; + if (fun) { + jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress(); + jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress(); } - for (size_t i = 0; i < mics.length(); i++) { - script->mics[i].kind = mics[i].kind; - script->mics[i].entry = fullCode.locationOf(mics[i].entry); - switch (mics[i].kind) { - case ic::MICInfo::GET: - case ic::MICInfo::SET: - script->mics[i].load = fullCode.locationOf(mics[i].load); - script->mics[i].shape = fullCode.locationOf(mics[i].shape); - script->mics[i].stubCall = stubCode.locationOf(mics[i].call); - script->mics[i].stubEntry = stubCode.locationOf(mics[i].stubEntry); - script->mics[i].u.name.typeConst = mics[i].u.name.typeConst; - script->mics[i].u.name.dataConst = mics[i].u.name.dataConst; +#if defined JS_MONOIC + jit->nMICs = mics.length(); + if (mics.length()) { + jit->mics = (ic::MICInfo *)cursor; + cursor += sizeof(ic::MICInfo) * mics.length(); + } else { + jit->mics = NULL; + } + + if (ic::MICInfo *scriptMICs = jit->mics) { + for (size_t i = 0; i < mics.length(); i++) { + scriptMICs[i].kind = mics[i].kind; + scriptMICs[i].entry = fullCode.locationOf(mics[i].entry); + switch (mics[i].kind) { + case ic::MICInfo::GET: + case ic::MICInfo::SET: + scriptMICs[i].load = fullCode.locationOf(mics[i].load); + scriptMICs[i].shape = fullCode.locationOf(mics[i].shape); + scriptMICs[i].stubCall = stubCode.locationOf(mics[i].call); + scriptMICs[i].stubEntry = stubCode.locationOf(mics[i].stubEntry); + scriptMICs[i].u.name.typeConst = mics[i].u.name.typeConst; + scriptMICs[i].u.name.dataConst = mics[i].u.name.dataConst; #if defined JS_PUNBOX64 - script->mics[i].patchValueOffset = mics[i].patchValueOffset; + scriptMICs[i].patchValueOffset = mics[i].patchValueOffset; #endif - break; - case ic::MICInfo::TRACER: { - uint32 offs = uint32(mics[i].jumpTarget - script->code); - JS_ASSERT(jumpMap[offs].isValid()); - script->mics[i].traceHint = fullCode.locationOf(mics[i].traceHint); - script->mics[i].load = fullCode.locationOf(jumpMap[offs]); - script->mics[i].u.hints.hasSlowTraceHintOne = mics[i].slowTraceHintOne.isSet(); - if (mics[i].slowTraceHintOne.isSet()) - script->mics[i].slowTraceHintOne = stubCode.locationOf(mics[i].slowTraceHintOne.get()); - script->mics[i].u.hints.hasSlowTraceHintTwo = mics[i].slowTraceHintTwo.isSet(); - if (mics[i].slowTraceHintTwo.isSet()) - script->mics[i].slowTraceHintTwo = stubCode.locationOf(mics[i].slowTraceHintTwo.get()); - break; - } - default: - JS_NOT_REACHED("Bad MIC kind"); + break; + case ic::MICInfo::TRACER: { + uint32 offs = uint32(mics[i].jumpTarget - script->code); + JS_ASSERT(jumpMap[offs].isValid()); + scriptMICs[i].traceHint = fullCode.locationOf(mics[i].traceHint); + scriptMICs[i].load = fullCode.locationOf(jumpMap[offs]); + scriptMICs[i].u.hints.hasSlowTraceHintOne = mics[i].slowTraceHintOne.isSet(); + if (mics[i].slowTraceHintOne.isSet()) + scriptMICs[i].slowTraceHintOne = stubCode.locationOf(mics[i].slowTraceHintOne.get()); + scriptMICs[i].u.hints.hasSlowTraceHintTwo = mics[i].slowTraceHintTwo.isSet(); + if (mics[i].slowTraceHintTwo.isSet()) + scriptMICs[i].slowTraceHintTwo = stubCode.locationOf(mics[i].slowTraceHintTwo.get()); + break; + } + default: + JS_NOT_REACHED("Bad MIC kind"); + } + stubCode.patch(mics[i].addrLabel, &scriptMICs[i]); } } - script->jit->nCallICs = callICs.length(); + jit->nCallICs = callICs.length(); if (callICs.length()) { - script->callICs = (ic::CallICInfo *)cursor; + jit->callICs = (ic::CallICInfo *)cursor; cursor += sizeof(ic::CallICInfo) * callICs.length(); } else { - script->callICs = NULL; + jit->callICs = NULL; } - for (size_t i = 0; i < callICs.length(); i++) { - script->callICs[i].reset(); - script->callICs[i].funGuard = fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].funJump = fullCode.locationOf(callICs[i].funJump); - script->callICs[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); + if (ic::CallICInfo *cics = jit->callICs) { + for (size_t i = 0; i < callICs.length(); i++) { + cics[i].reset(); + cics[i].funGuard = fullCode.locationOf(callICs[i].funGuard); + cics[i].funJump = fullCode.locationOf(callICs[i].funJump); + cics[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); - /* Compute the hot call offset. */ - uint32 offset = fullCode.locationOf(callICs[i].hotJump) - - fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].hotJumpOffset = offset; - JS_ASSERT(script->callICs[i].hotJumpOffset == offset); + /* Compute the hot call offset. */ + uint32 offset = fullCode.locationOf(callICs[i].hotJump) - + fullCode.locationOf(callICs[i].funGuard); + cics[i].hotJumpOffset = offset; + JS_ASSERT(cics[i].hotJumpOffset == offset); - /* Compute the join point offset. */ - offset = fullCode.locationOf(callICs[i].joinPoint) - - fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].joinPointOffset = offset; - JS_ASSERT(script->callICs[i].joinPointOffset == offset); - - /* Compute the OOL call offset. */ - offset = stubCode.locationOf(callICs[i].oolCall) - - stubCode.locationOf(callICs[i].slowPathStart); - script->callICs[i].oolCallOffset = offset; - JS_ASSERT(script->callICs[i].oolCallOffset == offset); + /* Compute the join point offset. */ + offset = fullCode.locationOf(callICs[i].joinPoint) - + fullCode.locationOf(callICs[i].funGuard); + cics[i].joinPointOffset = offset; + JS_ASSERT(cics[i].joinPointOffset == offset); + + /* Compute the OOL call offset. */ + offset = stubCode.locationOf(callICs[i].oolCall) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].oolCallOffset = offset; + JS_ASSERT(cics[i].oolCallOffset == offset); - /* Compute the OOL jump offset. */ - offset = stubCode.locationOf(callICs[i].oolJump) - - stubCode.locationOf(callICs[i].slowPathStart); - script->callICs[i].oolJumpOffset = offset; - JS_ASSERT(script->callICs[i].oolJumpOffset == offset); + /* Compute the OOL jump offset. */ + offset = stubCode.locationOf(callICs[i].oolJump) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].oolJumpOffset = offset; + JS_ASSERT(cics[i].oolJumpOffset == offset); - /* Compute the slow join point offset. */ - offset = stubCode.locationOf(callICs[i].slowJoinPoint) - - stubCode.locationOf(callICs[i].slowPathStart); - script->callICs[i].slowJoinOffset = offset; - JS_ASSERT(script->callICs[i].slowJoinOffset == offset); + /* Compute the slow join point offset. */ + offset = stubCode.locationOf(callICs[i].slowJoinPoint) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].slowJoinOffset = offset; + JS_ASSERT(cics[i].slowJoinOffset == offset); - /* Compute the join point offset for continuing on the hot path. */ - offset = stubCode.locationOf(callICs[i].hotPathLabel) - - stubCode.locationOf(callICs[i].funGuard); - script->callICs[i].hotPathOffset = offset; - JS_ASSERT(script->callICs[i].hotPathOffset == offset); + /* Compute the join point offset for continuing on the hot path. */ + offset = stubCode.locationOf(callICs[i].hotPathLabel) - + stubCode.locationOf(callICs[i].funGuard); + cics[i].hotPathOffset = offset; + JS_ASSERT(cics[i].hotPathOffset == offset); - script->callICs[i].pc = callICs[i].pc; - script->callICs[i].argc = callICs[i].argc; - script->callICs[i].funObjReg = callICs[i].funObjReg; - script->callICs[i].funPtrReg = callICs[i].funPtrReg; - script->callICs[i].frameDepth = callICs[i].frameDepth; + cics[i].pc = callICs[i].pc; + cics[i].argc = callICs[i].argc; + cics[i].funObjReg = callICs[i].funObjReg; + cics[i].funPtrReg = callICs[i].funPtrReg; + cics[i].frameDepth = callICs[i].frameDepth; + stubCode.patch(callICs[i].addrLabel1, &cics[i]); + stubCode.patch(callICs[i].addrLabel2, &cics[i]); + } } #endif /* JS_MONOIC */ @@ -471,44 +503,47 @@ mjit::Compiler::finishThisUp() } #if defined JS_POLYIC - script->jit->nPICs = pics.length(); + jit->nPICs = pics.length(); if (pics.length()) { - script->pics = (ic::PICInfo *)cursor; + jit->pics = (ic::PICInfo *)cursor; cursor += sizeof(ic::PICInfo) * pics.length(); } else { - script->pics = NULL; + jit->pics = NULL; } - for (size_t i = 0; i < pics.length(); i++) { - pics[i].copySimpleMembersTo(script->pics[i]); - script->pics[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart); - script->pics[i].storeBack = fullCode.locationOf(pics[i].storeBack); - script->pics[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart); - script->pics[i].callReturn = uint16((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() - - (uint8*)script->pics[i].slowPathStart.executableAddress()); - script->pics[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) - - masm.distanceOf(pics[i].fastPathStart); - JS_ASSERT(script->pics[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) - - masm.distanceOf(pics[i].fastPathStart)); - script->pics[i].shapeRegHasBaseShape = true; + if (ic::PICInfo *scriptPICs = jit->pics) { + for (size_t i = 0; i < pics.length(); i++) { + pics[i].copySimpleMembersTo(scriptPICs[i]); + scriptPICs[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart); + scriptPICs[i].storeBack = fullCode.locationOf(pics[i].storeBack); + scriptPICs[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart); + scriptPICs[i].callReturn = uint16((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() - + (uint8*)scriptPICs[i].slowPathStart.executableAddress()); + scriptPICs[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) - + masm.distanceOf(pics[i].fastPathStart); + JS_ASSERT(scriptPICs[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) - + masm.distanceOf(pics[i].fastPathStart)); + scriptPICs[i].shapeRegHasBaseShape = true; # if defined JS_CPU_X64 - memcpy(&script->pics[i].labels, &pics[i].labels, sizeof(PICLabels)); + memcpy(&scriptPICs[i].labels, &pics[i].labels, sizeof(PICLabels)); # endif - if (pics[i].kind == ic::PICInfo::SET || - pics[i].kind == ic::PICInfo::SETMETHOD) { - script->pics[i].u.vr = pics[i].vr; - } else if (pics[i].kind != ic::PICInfo::NAME) { - if (pics[i].hasTypeCheck) { - int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) - - stubcc.masm.distanceOf(pics[i].slowPathStart); - JS_ASSERT(distance <= 0); - script->pics[i].u.get.typeCheckOffset = distance; + if (pics[i].kind == ic::PICInfo::SET || + pics[i].kind == ic::PICInfo::SETMETHOD) { + scriptPICs[i].u.vr = pics[i].vr; + } else if (pics[i].kind != ic::PICInfo::NAME) { + if (pics[i].hasTypeCheck) { + int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) - + stubcc.masm.distanceOf(pics[i].slowPathStart); + JS_ASSERT(distance <= 0); + scriptPICs[i].u.get.typeCheckOffset = distance; + } } + new (&scriptPICs[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy()); + scriptPICs[i].reset(); + stubCode.patch(pics[i].addrLabel, &scriptPICs[i]); } - new (&script->pics[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy()); - script->pics[i].reset(); } #endif /* JS_POLYIC */ @@ -534,10 +569,8 @@ mjit::Compiler::finishThisUp() JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size()); - script->ncode = (uint8 *)(result + masm.distanceOf(invokeLabel)); - /* Build the table of call sites. */ - script->jit->nCallSites = callSites.length(); + jit->nCallSites = callSites.length(); if (callSites.length()) { CallSite *callSiteList = (CallSite *)cursor; cursor += sizeof(CallSite) * callSites.length(); @@ -550,12 +583,14 @@ mjit::Compiler::finishThisUp() callSiteList[i].pcOffset = callSites[i].pc - script->code; callSiteList[i].id = callSites[i].id; } - script->jit->callSites = callSiteList; + jit->callSites = callSiteList; } else { - script->jit->callSites = NULL; + jit->callSites = NULL; } - JS_ASSERT(size_t(cursor - (uint8*)script->jit) == totalBytes); + JS_ASSERT(size_t(cursor - (uint8*)jit) == totalBytes); + + *jitp = jit; return Compile_Okay; } @@ -1662,6 +1697,11 @@ mjit::Compiler::generateMethod() break; END_CASE(JSOP_GLOBALINC) + BEGIN_CASE(JSOP_BEGIN) + if (isConstructing) + constructThis(); + END_CASE(JSOP_BEGIN) + default: /* Sorry, this opcode isn't implemented yet. */ #ifdef JS_METHODJIT_SPEW @@ -1717,15 +1757,17 @@ mjit::Compiler::findCallSite(const CallSite &callSite) { JS_ASSERT(callSite.pcOffset < script->length); + JITScript *jit = script->getJIT(fp->isConstructing()); + uint8* ilPath = (uint8 *)jit->code.m_code.executableAddress(); + uint8* oolPath = ilPath + masm.size(); + for (uint32 i = 0; i < callSites.length(); i++) { if (callSites[i].pc == script->code + callSite.pcOffset && callSites[i].id == callSite.id) { if (callSites[i].stub) { - return (uint8*)script->jit->invoke + masm.size() + - stubcc.masm.distanceOf(callSites[i].location); + return oolPath + stubcc.masm.distanceOf(callSites[i].location); } - return (uint8*)script->jit->invoke + - stubcc.masm.distanceOf(callSites[i].location); + return ilPath + masm.distanceOf(callSites[i].location); } } @@ -1781,24 +1823,100 @@ mjit::Compiler::emitFinalReturn(Assembler &masm) masm.jump(Registers::ReturnReg); } +// Emits code to load a return value of the frame into the scripted-ABI +// type & data register pair. If the return value is in fp->rval, then |fe| +// is NULL. Otherwise, |fe| contains the return value. +// +// If reading from fp->rval, |undefined| is loaded optimistically, before +// checking if fp->rval is set in the frame flags and loading that instead. +// +// Otherwise, if |masm| is the inline path, it is loaded as efficiently as +// the FrameState can manage. If |masm| is the OOL path, the value is simply +// loaded from its slot in the frame, since the caller has guaranteed it's +// been synced. +// void -mjit::Compiler::loadReturnValue(Assembler &masm) +mjit::Compiler::loadReturnValue(Assembler *masm, FrameEntry *fe) { - /* - * Load a return value from POPV or SETRVAL into the return registers, - * otherwise return undefined. - */ - masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); - if (analysis.usesReturnValue()) { - Jump rvalClear = masm.branchTest32(Assembler::Zero, - FrameFlagsAddress(), Imm32(JSFRAME_HAS_RVAL)); - Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); - masm.loadValueAsComponents(rvalAddress, - JSReturnReg_Type, JSReturnReg_Data); - rvalClear.linkTo(masm.label(), &masm); + RegisterID typeReg = JSReturnReg_Type; + RegisterID dataReg = JSReturnReg_Data; + + if (fe) { + // If using the OOL assembler, the caller signifies that the |fe| is + // synced, but not to rely on its register state. + if (masm != &this->masm) { + if (fe->isConstant()) { + stubcc.masm.loadValueAsComponents(fe->getValue(), typeReg, dataReg); + } else { + Address rval(frame.addressOf(fe)); + if (fe->isTypeKnown()) { + stubcc.masm.loadPayload(rval, dataReg); + stubcc.masm.move(ImmType(fe->getKnownType()), typeReg); + } else { + stubcc.masm.loadValueAsComponents(rval, typeReg, dataReg); + } + } + } else { + frame.loadTo(fe, typeReg, dataReg, Registers::ReturnReg); + } + } else { + // Load a return value from POPV or SETRVAL into the return registers, + // otherwise return undefined. + masm->loadValueAsComponents(UndefinedValue(), typeReg, dataReg); + if (analysis.usesReturnValue()) { + Jump rvalClear = masm->branchTest32(Assembler::Zero, + FrameFlagsAddress(), + Imm32(JSFRAME_HAS_RVAL)); + Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); + masm->loadValueAsComponents(rvalAddress, typeReg, dataReg); + rvalClear.linkTo(masm->label(), masm); + } } } +// This ensures that constructor return values are an object. If a non-object +// is returned, either explicitly or implicitly, the newly created object is +// loaded out of the frame. Otherwise, the explicitly returned object is kept. +// +void +mjit::Compiler::fixPrimitiveReturn(Assembler *masm, FrameEntry *fe) +{ + JS_ASSERT(isConstructing); + + Address thisv(JSFrameReg, JSStackFrame::offsetOfThis(fun)); + + // Easy cases - no return value, or known primitive, so just return thisv. + if (!fe || (fe->isTypeKnown() && fe->getKnownType() != JSVAL_TYPE_OBJECT)) { + masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); + return; + } + + // If the type is known to be an object, just load the return value as normal. + if (fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_OBJECT) { + loadReturnValue(masm, fe); + return; + } + + // There's a return value, and its type is unknown. Test the type and load + // |thisv| if necessary. + loadReturnValue(masm, fe); + Jump j = masm->testObject(Assembler::Equal, JSReturnReg_Type); + masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); + j.linkTo(masm->label(), masm); +} + +// Loads the return value into the scripted ABI register pair, such that JS +// semantics in constructors are preserved. +// +void +mjit::Compiler::emitReturnValue(Assembler *masm, FrameEntry *fe) +{ + if (isConstructing) + fixPrimitiveReturn(masm, fe); + else + loadReturnValue(masm, fe); +} + void mjit::Compiler::emitReturn(FrameEntry *fe) { @@ -1823,8 +1941,7 @@ mjit::Compiler::emitReturn(FrameEntry *fe) stubCall(stubs::PutActivationObjects); if (fe) { - masm.loadValueAsComponents(frame.addressOf(fe), - JSReturnReg_Type, JSReturnReg_Data); + emitReturnValue(&masm, fe); emitFinalReturn(masm); frame.discardFrame(); return; @@ -1839,22 +1956,12 @@ mjit::Compiler::emitReturn(FrameEntry *fe) stubcc.leave(); stubcc.call(stubs::PutActivationObjects); - if (fe) { - stubcc.masm.loadValueAsComponents(frame.addressOf(fe), - JSReturnReg_Type, JSReturnReg_Data); - } else { - loadReturnValue(stubcc.masm); - } - + emitReturnValue(&stubcc.masm, fe); emitFinalReturn(stubcc.masm); } } - if (fe) - frame.storeTo(fe, JSReturnReg_Data, JSReturnReg_Type, Registers::ReturnReg); - else - loadReturnValue(masm); - + emitReturnValue(&masm, fe); emitFinalReturn(masm); frame.discardFrame(); } @@ -1929,18 +2036,6 @@ mjit::Compiler::interruptCheckHelper() #endif } -void -mjit::Compiler::emitPrimitiveTestForNew(uint32 argc) -{ - Jump primitive = masm.testPrimitive(Assembler::Equal, JSReturnReg_Type); - stubcc.linkExitDirect(primitive, stubcc.masm.label()); - FrameEntry *fe = frame.peek(-int(argc + 1)); - Address thisv(frame.addressOf(fe)); - stubcc.masm.loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); - Jump primFix = stubcc.masm.jump(); - stubcc.crossJump(primFix, masm.label()); -} - void mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) { @@ -1973,9 +2068,6 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) callPatch.joinPoint = masm.label(); masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); - if (callingNew) - emitPrimitiveTestForNew(argc); - frame.popn(argc + 2); frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); @@ -1993,6 +2085,10 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) /* Check for interrupts on function call */ interruptCheckHelper(); + // |thisv| does not need to be synced for constructing. + if (callingNew) + frame.discardFe(frame.peek(-int(argc + 1))); + FrameEntry *fe = frame.peek(-int(argc + 2)); /* Currently, we don't support constant functions. */ @@ -2002,12 +2098,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) } #ifdef JS_MONOIC - FrameEntry *thisvFe = frame.peek(-int(argc + 1)); - Address thisvAddr = frame.addressOf(thisvFe); - CallGenInfo callIC(argc); - uint32 callICIndex = callICs.length(); - CallPatchInfo callPatch; /* @@ -2044,16 +2135,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) if (typeReg.isSet()) notObjectJump = masm.testObject(Assembler::NotEqual, typeReg.reg()); - /* - * Ensure that dataReg stays in a register which won't be clobbered - * by the intervening call to NewObject. - */ - if (callingNew && !(Registers::maskReg(dataReg) & Registers::SavedRegs)) { - RegisterID reg = Registers(Registers::SavedRegs).takeAnyReg(); - masm.move(dataReg, reg); - dataReg = reg; - } - tempRegs.takeReg(dataReg); RegisterID t0 = tempRegs.takeAnyReg(); RegisterID t1 = tempRegs.takeAnyReg(); @@ -2083,28 +2164,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) stubcc.masm.and32(Imm32(JSFUN_KINDMASK), t1); Jump isNative = stubcc.masm.branch32(Assembler::Below, t1, Imm32(JSFUN_INTERPRETED)); - /* Create the new object. This requires some fiddling to save the two values. */ - if (callingNew) { - void *pfun = stubcc.masm.getCallTarget(JS_FUNC_TO_DATA_PTR(void *, stubs::NewObject)); - stubcc.masm.storePtr(ImmPtr(PC), - FrameAddress(offsetof(VMFrame, regs) + offsetof(JSFrameRegs, pc))); - stubcc.masm.fixScriptStack(frame.frameDepth()); - stubcc.masm.setupVMFrame(); -#if defined(JS_CPU_X86) - /* Need to stay 16-byte aligned on x86. */ - stubcc.masm.subPtr(Imm32(8), JSC::MacroAssembler::stackPointerRegister); -#endif - stubcc.masm.push(dataReg); - stubcc.masm.push(t0); - stubcc.masm.move(Imm32(argc), Registers::ArgReg1); - stubcc.masm.wrapCall(pfun); - stubcc.masm.pop(t0); - stubcc.masm.pop(dataReg); -#if defined(JS_CPU_X86) - stubcc.masm.addPtr(Imm32(8), JSC::MacroAssembler::stackPointerRegister); -#endif - } - /* * No-op jump that gets re-patched. This is so ArgReg1 won't be * clobbered, with the added bonus that the generated stub doesn't @@ -2115,7 +2174,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) callIC.oolJump = toPatch; /* At this point the function is definitely scripted. Call the link routine. */ - stubcc.masm.move(Imm32(callICIndex), Registers::ArgReg1); + callIC.addrLabel1 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); callIC.oolCall = stubcc.call(callingNew ? ic::New : ic::Call); callIC.funObjReg = dataReg; @@ -2145,7 +2204,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) notFunction.linkTo(stubcc.masm.label(), &stubcc.masm); isNative.linkTo(stubcc.masm.label(), &stubcc.masm); - stubcc.masm.move(Imm32(callICIndex), Registers::ArgReg1); + callIC.addrLabel2 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); stubcc.call(callingNew ? ic::NativeNew : ic::NativeCall); rejoin2 = stubcc.masm.jump(); @@ -2157,13 +2216,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) */ callIC.hotPathLabel = masm.label(); - /* If calling |new|, make sure to allocate a new object. */ - if (callingNew) { - prepareStubCall(Uses(argc + 2)); - masm.move(Imm32(argc), Registers::ArgReg1); - stubCall(stubs::NewObject); - } - uint32 flags = 0; if (callingNew) flags |= JSFRAME_CONSTRUCTING; @@ -2175,13 +2227,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) callIC.joinPoint = callPatch.joinPoint = masm.label(); masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); - /* - * Functions invoked with |new| can return primitive values. - * Just deal with this here. - */ - if (callingNew) - emitPrimitiveTestForNew(argc); - frame.popn(argc + 2); frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); @@ -2375,6 +2420,19 @@ mjit::Compiler::jsop_length() } #if defined JS_POLYIC + +void +mjit::Compiler::passPICAddress(PICGenInfo &pic) +{ + pic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); +} + +void +mjit::Compiler::passMICAddress(MICGenInfo &mic) +{ + mic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); +} + void mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) { @@ -2445,7 +2503,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::GetProp); /* Load dslots. */ @@ -2546,7 +2604,7 @@ mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID obj pic.slowPathStart = stubcc.linkExit(jmpShapeGuard, Uses(2)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::GetElem); /* Load dslots. */ @@ -2672,7 +2730,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom) /* Slow path. */ stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::CallProp); /* Adjust the frame. None of this will generate code. */ @@ -2836,7 +2894,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::CallProp); /* Load dslots. */ @@ -2958,13 +3016,12 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) * the normal SETNAME property cache logic. */ JSOp op = JSOp(*PC); + stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); if (op == JSOP_SETNAME || op == JSOP_SETPROP || op == JSOP_SETGNAME || op == JSOP_SETMETHOD) { - stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); stubcc.call(STRICT_VARIANT(stubs::SetName)); } else { - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); - stubcc.call(ic::SetPropDumb); + stubcc.call(STRICT_VARIANT(stubs::SetPropNoCache)); } typeCheck = stubcc.masm.jump(); @@ -3004,7 +3061,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(j, Uses(2)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::SetProp); } @@ -3084,7 +3141,7 @@ mjit::Compiler::jsop_name(JSAtom *atom) { pic.slowPathStart = stubcc.linkExit(j, Uses(0)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::Name); } @@ -3127,7 +3184,7 @@ mjit::Compiler::jsop_xname(JSAtom *atom) { pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::XName); } @@ -3169,7 +3226,7 @@ mjit::Compiler::jsop_bindname(uint32 index) { pic.slowPathStart = stubcc.linkExit(j, Uses(0)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::BindName); } @@ -3876,7 +3933,7 @@ mjit::Compiler::jsop_getgname(uint32 index) stubcc.linkExit(shapeGuard, Uses(0)); stubcc.leave(); - stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); + passMICAddress(mic); mic.stubEntry = stubcc.masm.label(); mic.call = stubcc.call(ic::GetGlobalName); @@ -3975,7 +4032,7 @@ mjit::Compiler::jsop_setgname(uint32 index) stubcc.linkExit(shapeGuard, Uses(2)); stubcc.leave(); - stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); + passMICAddress(mic); mic.stubEntry = stubcc.masm.label(); mic.call = stubcc.call(ic::SetGlobalName); @@ -4226,7 +4283,7 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne, Jump *sl if (slowTwo) slowTwo->linkTo(traceStart, &stubcc.masm); # if JS_MONOIC - stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); + passMICAddress(mic); # endif /* Save and restore compiler-tracked PC, so cx->regs is right in InvokeTracer. */ @@ -4296,3 +4353,45 @@ mjit::Compiler::leaveBlock() frame.leaveBlock(n); } +// Creates the new object expected for constructors, and places it in |thisv|. +// It is broken down into the following operations: +// CALLEE +// GETPROP "prototype" +// IFPRIMTOP: +// NULL +// call js_CreateThisFromFunctionWithProto(...) +// +void +mjit::Compiler::constructThis() +{ + JS_ASSERT(isConstructing); + + // Load the callee. + Address callee(JSFrameReg, JSStackFrame::offsetOfCallee(fun)); + RegisterID calleeReg = frame.allocReg(); + masm.loadPayload(callee, calleeReg); + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, calleeReg); + + // Get callee.prototype. + jsop_getprop(cx->runtime->atomState.classPrototypeAtom); + + // Reach into the proto Value and grab a register for its data. + FrameEntry *protoFe = frame.peek(-1); + RegisterID protoReg = frame.ownRegForData(protoFe); + + // Now, get the type. If it's not an object, set protoReg to NULL. + Jump isNotObject = frame.testObject(Assembler::NotEqual, protoFe); + stubcc.linkExitDirect(isNotObject, stubcc.masm.label()); + stubcc.masm.move(ImmPtr(NULL), protoReg); + stubcc.crossJump(stubcc.masm.jump(), masm.label()); + + // Done with the protoFe. + frame.pop(); + + prepareStubCall(Uses(0)); + if (protoReg != Registers::ArgReg1) + masm.move(protoReg, Registers::ArgReg1); + stubCall(stubs::CreateThis); + frame.freeReg(protoReg); +} + diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index be20c21df323..4cab7bca831b 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -72,6 +72,7 @@ class Compiler : public BaseCompiler Label entry; Label stubEntry; DataLabel32 shape; + DataLabelPtr addrLabel; #if defined JS_PUNBOX64 uint32 patchValueOffset; #endif @@ -114,6 +115,8 @@ class Compiler : public BaseCompiler Label slowJoinPoint; Label slowPathStart; Label hotPathLabel; + DataLabelPtr addrLabel1; + DataLabelPtr addrLabel2; Jump oolJump; RegisterID funObjReg; RegisterID funPtrReg; @@ -143,6 +146,7 @@ class Compiler : public BaseCompiler Label storeBack; Label typeCheck; Label slowPathStart; + DataLabelPtr addrLabel; RegisterID shapeReg; RegisterID objReg; RegisterID idReg; @@ -196,10 +200,12 @@ class Compiler : public BaseCompiler bool ool; }; + JSStackFrame *fp; JSScript *script; JSObject *scopeChain; JSObject *globalObj; JSFunction *fun; + bool isConstructing; BytecodeAnalyzer analysis; Label *jumpMap; jsbytecode *PC; @@ -211,7 +217,7 @@ class Compiler : public BaseCompiler js::Vector callICs; #endif #if defined JS_POLYIC - js::Vector pics; + js::Vector pics; #endif js::Vector callPatches; js::Vector callSites; @@ -226,10 +232,10 @@ class Compiler : public BaseCompiler // follows interpreter usage in JSOP_LENGTH. enum { LengthAtomIndex = uint32(-2) }; - Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain); + Compiler(JSContext *cx, JSStackFrame *fp); ~Compiler(); - CompileStatus Compile(); + CompileStatus compile(); jsbytecode *getPC() { return PC; } Label getLabel() { return masm.label(); } @@ -238,10 +244,11 @@ class Compiler : public BaseCompiler void *findCallSite(const CallSite &callSite); private: + CompileStatus performCompilation(JITScript **jitp); CompileStatus generatePrologue(); CompileStatus generateMethod(); CompileStatus generateEpilogue(); - CompileStatus finishThisUp(); + CompileStatus finishThisUp(JITScript **jitp); /* Non-emitting helpers. */ uint32 fullAtomIndex(jsbytecode *pc); @@ -257,6 +264,9 @@ class Compiler : public BaseCompiler void iterMore(); void iterEnd(); MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg); + void passPICAddress(PICGenInfo &pic); + void passMICAddress(MICGenInfo &mic); + void constructThis(); /* Opcode handlers. */ void jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne = NULL, Jump *slowTwo = NULL); @@ -268,12 +278,13 @@ class Compiler : public BaseCompiler void jsop_this(); void emitReturn(FrameEntry *fe); void emitFinalReturn(Assembler &masm); - void loadReturnValue(Assembler &masm); + void loadReturnValue(Assembler *masm, FrameEntry *fe); + void emitReturnValue(Assembler *masm, FrameEntry *fe); void dispatchCall(VoidPtrStubUInt32 stub, uint32 argc); void interruptCheckHelper(); void emitUncachedCall(uint32 argc, bool callingNew); - void emitPrimitiveTestForNew(uint32 argc); void inlineCallHelper(uint32 argc, bool callingNew); + void fixPrimitiveReturn(Assembler *masm, FrameEntry *fe); void jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index); void jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index); void jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index); diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 4b26b8929463..838f638fc510 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -327,7 +327,7 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) #endif } -void FrameState::storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg) +void FrameState::loadTo(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg) { JS_ASSERT(dataReg != typeReg && dataReg != tempReg && typeReg != tempReg); @@ -347,6 +347,15 @@ void FrameState::storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, return; } +#ifdef JS_PUNBOX64 + // If the value is synced, and requires at least one load, we can do + // better on x64. + if (fe->type.inMemory() && fe->data.inMemory()) { + masm.loadValueAsComponents(addressOf(fe), typeReg, dataReg); + return; + } +#endif + RegisterID data = tempRegForData(fe); RegisterID type = tempRegForType(fe); if (data == typeReg && type == dataReg) { @@ -893,6 +902,14 @@ FrameState::ownRegForData(FrameEntry *fe) return reg; } +void +FrameState::discardFe(FrameEntry *fe) +{ + forgetEntry(fe); + fe->type.setMemory(); + fe->data.setMemory(); +} + void FrameState::pushCopyOf(uint32 index) { diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index d66415ba6ee5..bca9568d3b7d 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -590,7 +590,7 @@ class FrameState * Fully stores a FrameEntry into two arbitrary registers. tempReg may be * used as a temporary. */ - void storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg); + void loadTo(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg); /* * Stores the top stack slot back to a slot. @@ -654,6 +654,11 @@ class FrameState */ inline void forgetType(FrameEntry *fe); + /* + * Discards a FrameEntry, tricking the FS into thinking it's synced. + */ + void discardFe(FrameEntry *fe); + /* * Helper function. Tests if a slot's type is null. Condition should * be Equal or NotEqual. diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index a7e3eadf9b38..a9af7e6abda1 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -219,29 +219,6 @@ InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame) return ok; } -JSBool JS_FASTCALL -stubs::NewObject(VMFrame &f, uint32 argc) -{ - JSContext *cx = f.cx; - Value *vp = f.regs.sp - (argc + 2); - - JSObject *funobj = &vp[0].toObject(); - JS_ASSERT(funobj->isFunction()); - - jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); - if (!funobj->getProperty(cx, id, &vp[1])) - THROWV(JS_FALSE); - - JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL; - JSObject *obj = NewNonFunction(cx, &js_ObjectClass, proto, funobj->getParent()); - if (!obj) - THROWV(JS_FALSE); - - vp[1].setObject(*obj); - - return JS_TRUE; -} - void JS_FASTCALL stubs::SlowCall(VMFrame &f, uint32 argc) { @@ -361,8 +338,8 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) fp->initCallFrameEarlyPrologue(fun, fp->nativeReturnAddress()); /* Empty script does nothing. */ + bool callingNew = fp->isConstructing(); if (script->isEmpty()) { - bool callingNew = fp->isConstructing(); RemovePartialFrame(cx, fp); Value *vp = f.regs.sp - (nactual + 2); if (callingNew) @@ -389,9 +366,9 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) if (fun->isHeavyweight() && !js_GetCallObject(cx, fp)) THROWV(NULL); - CompileStatus status = CanMethodJIT(cx, script, fun, &fp->scopeChain()); + CompileStatus status = CanMethodJIT(cx, script, fp); if (status == Compile_Okay) - return script->jit->invoke; + return script->getJIT(callingNew)->invokeEntry; /* Function did not compile... interpret it. */ JSBool ok = Interpret(cx, fp); @@ -441,8 +418,8 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) } /* Try to compile if not already compiled. */ - if (!newscript->ncode) { - if (mjit::TryCompile(cx, newscript, newfp->fun(), &newfp->scopeChain()) == Compile_Error) { + if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) { + if (mjit::TryCompile(cx, newfp) == Compile_Error) { /* A runtime exception was thrown, get out. */ InlineReturn(f, JS_FALSE); return false; @@ -450,9 +427,8 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) } /* If newscript was successfully compiled, run it. */ - JS_ASSERT(newscript->ncode); - if (newscript->ncode != JS_UNJITTABLE_METHOD) { - *pret = newscript->jit->invoke; + if (JITScript *jit = newscript->getJIT(newfp->isConstructing())) { + *pret = jit->invokeEntry; return true; } @@ -484,9 +460,6 @@ stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr) if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted() && !ucr->fun->script()->isEmpty()) { - if (!stubs::NewObject(f, argc)) - return; - ucr->callee = &vp->toObject(); if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc)) THROW(); @@ -612,7 +585,10 @@ js_InternalThrow(VMFrame &f) if (!pc) return NULL; - return cx->fp()->script()->pcToNative(pc); + JSStackFrame *fp = cx->fp(); + JSScript *script = fp->script(); + JITScript *jit = script->getJIT(fp->isConstructing()); + return jit->nmap[pc - script->code]; } void JS_FASTCALL @@ -623,6 +599,18 @@ stubs::GetCallObject(VMFrame &f) THROW(); } +void JS_FASTCALL +stubs::CreateThis(VMFrame &f, JSObject *proto) +{ + JSContext *cx = f.cx; + JSStackFrame *fp = f.fp(); + JSObject *callee = &fp->callee(); + JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto); + if (!obj) + THROW(); + fp->formalArgs()[-1].setObject(*obj); +} + static inline void AdvanceReturnPC(JSContext *cx) { @@ -696,11 +684,12 @@ AtSafePoint(JSContext *cx) return false; JSScript *script = fp->script(); - if (!script->nmap) + JITScript *jit = script->getJIT(fp->isConstructing()); + if (!jit->nmap) return false; JS_ASSERT(cx->regs->pc >= script->code && cx->regs->pc < script->code + script->length); - return !!script->nmap[cx->regs->pc - script->code]; + return !!jit->nmap[cx->regs->pc - script->code]; } static inline JSBool @@ -709,8 +698,11 @@ PartialInterpret(VMFrame &f) JSContext *cx = f.cx; JSStackFrame *fp = cx->fp(); - JS_ASSERT(fp->hasImacropc() || !fp->script()->nmap || - !fp->script()->nmap[cx->regs->pc - fp->script()->code]); +#ifdef DEBUG + JITScript *jit = fp->script()->getJIT(fp->isConstructing()); + JS_ASSERT(fp->hasImacropc() || !jit->nmap || + !jit->nmap[cx->regs->pc - fp->script()->code]); +#endif JSBool ok = JS_TRUE; ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT); @@ -740,7 +732,8 @@ FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame) if (AtSafePoint(cx)) { JSScript *script = fp->script(); - if (!JaegerShotAtSafePoint(cx, script->nmap[cx->regs->pc - script->code])) { + JITScript *jit = script->getJIT(fp->isConstructing()); + if (!JaegerShotAtSafePoint(cx, jit->nmap[cx->regs->pc - script->code])) { if (!HandleErrorInExcessFrames(f, entryFrame)) return false; @@ -894,9 +887,10 @@ RunTracer(VMFrame &f) /* Step 2. If entryFrame is at a safe point, just leave. */ if (AtSafePoint(cx)) { + JITScript *jit = entryFrame->script()->getJIT(entryFrame->isConstructing()); uint32 offs = uint32(cx->regs->pc - entryFrame->script()->code); - JS_ASSERT(entryFrame->script()->nmap[offs]); - return entryFrame->script()->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } /* Step 3. If entryFrame is at a RETURN, then leave slightly differently. */ @@ -930,14 +924,10 @@ RunTracer(VMFrame &f) #if defined JS_TRACER # if defined JS_MONOIC void *JS_FASTCALL -stubs::InvokeTracer(VMFrame &f, uint32 index) +stubs::InvokeTracer(VMFrame &f, ic::MICInfo *mic) { - JSScript *script = f.fp()->script(); - ic::MICInfo &mic = script->mics[index]; - - JS_ASSERT(mic.kind == ic::MICInfo::TRACER); - - return RunTracer(f, mic); + JS_ASSERT(mic->kind == ic::MICInfo::TRACER); + return RunTracer(f, *mic); } # else diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 191c9c549604..c457f6f995db 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -764,9 +764,9 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) JSBool mjit::JaegerShot(JSContext *cx) { - JSScript *script = cx->fp()->script(); - - JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD); + JSStackFrame *fp = cx->fp(); + JSScript *script = fp->script(); + JITScript *jit = script->getJIT(fp->isConstructing()); #ifdef JS_TRACER if (TRACE_RECORDER(cx)) @@ -775,7 +775,7 @@ mjit::JaegerShot(JSContext *cx) JS_ASSERT(cx->regs->pc == script->code); - return EnterMethodJIT(cx, cx->fp(), script->jit->invoke); + return EnterMethodJIT(cx, cx->fp(), jit->invokeEntry); } JSBool @@ -795,37 +795,47 @@ static inline void Destroy(T &t) } void -mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) +mjit::JITScript::release() { - if (script->jit) { #if defined DEBUG && (defined JS_CPU_X86 || defined JS_CPU_X64) - memset(script->jit->invoke, 0xcc, script->jit->inlineLength + - script->jit->outOfLineLength); + void *addr = code.m_code.executableAddress(); + memset(addr, 0xcc, code.m_size); #endif - script->jit->execPool->release(); - script->jit->execPool = NULL; - // Releasing the execPool takes care of releasing the code. - script->ncode = NULL; + code.m_executablePool->release(); #if defined JS_POLYIC - for (uint32 i = 0; i < script->jit->nPICs; i++) { - script->pics[i].releasePools(); - Destroy(script->pics[i].execPools); - } + for (uint32 i = 0; i < nPICs; i++) { + pics[i].releasePools(); + Destroy(pics[i].execPools); + } #endif #if defined JS_MONOIC - for (uint32 i = 0; i < script->jit->nCallICs; i++) - script->callICs[i].releasePools(); + for (uint32 i = 0; i < nCallICs; i++) + callICs[i].releasePools(); #endif +} - cx->free(script->jit); +void +mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) +{ + // NB: The recompiler may call ReleaseScriptCode, in which case it + // will get called again when the script is destroyed, so we + // must protect against calling ReleaseScriptCode twice. - // The recompiler may call ReleaseScriptCode, in which case it - // will get called again when the script is destroyed, so we - // must protect against calling ReleaseScriptCode twice. - script->jit = NULL; + if (script->jitNormal) { + script->jitNormal->release(); + script->jitArityCheckNormal = NULL; + cx->free(script->jitNormal); + script->jitNormal = NULL; + } + + if (script->jitCtor) { + script->jitCtor->release(); + script->jitArityCheckCtor = NULL; + cx->free(script->jitCtor); + script->jitCtor = NULL; } } diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 68d6dd0f0c31..850b6a3dfa74 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -139,6 +139,18 @@ struct VMFrame extern "C" void JaegerStubVeneer(void); #endif +namespace mjit { +namespace ic { +# if defined JS_POLYIC + struct PICInfo; +# endif +# if defined JS_MONOIC + struct MICInfo; + struct CallICInfo; +# endif +} +} + typedef void (JS_FASTCALL *VoidStub)(VMFrame &); typedef void (JS_FASTCALL *VoidVpStub)(VMFrame &, Value *); typedef void (JS_FASTCALL *VoidStubUInt32)(VMFrame &, uint32); @@ -158,26 +170,47 @@ typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32); typedef void (JS_FASTCALL *VoidStubJSObj)(VMFrame &, JSObject *); typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *); typedef JSBool (JS_FASTCALL *BoolStubUInt32)(VMFrame &f, uint32); - -#define JS_UNJITTABLE_METHOD (reinterpret_cast(1)) +typedef void (JS_FASTCALL *VoidStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); +typedef void * (JS_FASTCALL *VoidPtrStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); +typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); +typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); +typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); namespace mjit { +struct CallSite; + struct JITScript { - JSC::ExecutablePool *execPool; /* pool that contains |ncode|; script owns the pool */ - uint32 inlineLength; /* length of inline JIT'd code */ - uint32 outOfLineLength; /* length of out of line JIT'd code */ + typedef JSC::MacroAssemblerCodeRef CodeRef; + CodeRef code; /* pool & code addresses */ + js::mjit::CallSite *callSites; uint32 nCallSites; + void **nmap; /* scripted pc to native code map. */ #ifdef JS_MONOIC - uint32 nMICs; /* number of MonoICs */ - uint32 nCallICs; /* number of call ICs */ + ic::MICInfo *mics; /* MICs in this script. */ + uint32 nMICs; /* number of MonoICs */ + ic::CallICInfo *callICs; /* CallICs in this script. */ + uint32 nCallICs; /* number of call ICs */ #endif #ifdef JS_POLYIC - uint32 nPICs; /* number of PolyICs */ + ic::PICInfo *pics; /* PICs in this script */ + uint32 nPICs; /* number of PolyICs */ #endif - void *invoke; /* invoke address */ - void *arityCheck; /* arity check address */ + void *invokeEntry; /* invoke address */ + void *fastEntry; /* cached entry, fastest */ + void *arityCheckEntry; /* arity check address */ + + bool isValidCode(void *ptr) { + char *jitcode = (char *)code.m_code.executableAddress(); + char *jcheck = (char *)ptr; + return jcheck >= jitcode && jcheck < jitcode + code.m_size; + } + + void sweepCallICs(); + void purgeMICs(); + void purgePICs(); + void release(); }; /* Execute a method that has been JIT compiled. */ @@ -197,18 +230,21 @@ void JS_FASTCALL ProfileStubCall(VMFrame &f); CompileStatus JS_NEVER_INLINE -TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain); +TryCompile(JSContext *cx, JSStackFrame *fp); void ReleaseScriptCode(JSContext *cx, JSScript *script); static inline CompileStatus -CanMethodJIT(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) +CanMethodJIT(JSContext *cx, JSScript *script, JSStackFrame *fp) { - if (!cx->methodJitEnabled || script->ncode == JS_UNJITTABLE_METHOD) + if (!cx->methodJitEnabled) return Compile_Abort; - if (script->ncode == NULL) - return TryCompile(cx, script, fun, scopeChain); + JITScriptStatus status = script->getJITStatus(fp->isConstructing()); + if (status == JITScript_Invalid) + return Compile_Abort; + if (status == JITScript_None) + return TryCompile(cx, fp); return Compile_Okay; } @@ -223,6 +259,25 @@ struct CallSite } /* namespace js */ +inline void ** +JSScript::maybeNativeMap(bool constructing) +{ + js::mjit::JITScript *jit = constructing ? jitCtor : jitNormal; + if (!jit) + return NULL; + return jit->nmap; +} + +inline bool +JSScript::hasNativeCodeForPC(bool constructing, jsbytecode *pc) +{ + js::mjit::JITScript *jit = getJIT(constructing); + if (!jit) + return false; + JS_ASSERT(pc >= code && pc < code + length); + return !!jit->nmap[pc - code]; +} + #ifdef _MSC_VER extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame); #else diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index ce12f4a9f3d2..eada7e730948 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -70,22 +70,21 @@ typedef JSC::MacroAssembler::Call Call; #if defined JS_MONOIC static void -PatchGetFallback(VMFrame &f, ic::MICInfo &mic) +PatchGetFallback(VMFrame &f, ic::MICInfo *ic) { - JSC::RepatchBuffer repatch(mic.stubEntry.executableAddress(), 64); + JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64); JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName)); - repatch.relink(mic.stubCall, fptr); + repatch.relink(ic->stubCall, fptr); } void JS_FASTCALL -ic::GetGlobalName(VMFrame &f, uint32 index) +ic::GetGlobalName(VMFrame &f, ic::MICInfo *ic) { JSObject *obj = f.fp()->scopeChain().getGlobal(); - ic::MICInfo &mic = f.fp()->script()->mics[index]; JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc)); jsid id = ATOM_TO_JSID(atom); - JS_ASSERT(mic.kind == ic::MICInfo::GET); + JS_ASSERT(ic->kind == ic::MICInfo::GET); JS_LOCK_OBJ(f.cx, obj); const Shape *shape = obj->nativeLookup(id); @@ -95,33 +94,33 @@ ic::GetGlobalName(VMFrame &f, uint32 index) { JS_UNLOCK_OBJ(f.cx, obj); if (shape) - PatchGetFallback(f, mic); + PatchGetFallback(f, ic); stubs::GetGlobalName(f); return; } uint32 slot = shape->slot; JS_UNLOCK_OBJ(f.cx, obj); - mic.u.name.touched = true; + ic->u.name.touched = true; /* Patch shape guard. */ - JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50); - repatch.repatch(mic.shape, obj->shape()); + JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50); + repatch.repatch(ic->shape, obj->shape()); /* Patch loads. */ JS_ASSERT(slot >= JS_INITIAL_NSLOTS); slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); - JSC::RepatchBuffer loads(mic.load.executableAddress(), 32, false); + JSC::RepatchBuffer loads(ic->load.executableAddress(), 32, false); #if defined JS_CPU_X86 - loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot); - loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4); + loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot); + loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4); #elif defined JS_CPU_ARM - // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch' + // ic->load actually points to the LDR instruction which fetches the offset, but 'repatch' // knows how to dereference it to find the integer value. - loads.repatch(mic.load.dataLabel32AtOffset(0), slot); + loads.repatch(ic->load.dataLabel32AtOffset(0), slot); #elif defined JS_PUNBOX64 - loads.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); + loads.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot); #endif /* Do load anyway... this time. */ @@ -140,11 +139,11 @@ SetGlobalNameSlow(VMFrame &f, uint32 index) } static void -PatchSetFallback(VMFrame &f, ic::MICInfo &mic) +PatchSetFallback(VMFrame &f, ic::MICInfo *ic) { - JSC::RepatchBuffer repatch(mic.stubEntry.executableAddress(), 64); + JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64); JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, SetGlobalNameSlow)); - repatch.relink(mic.stubCall, fptr); + repatch.relink(ic->stubCall, fptr); } static VoidStubAtom @@ -159,14 +158,13 @@ GetStubForSetGlobalName(VMFrame &f) } void JS_FASTCALL -ic::SetGlobalName(VMFrame &f, uint32 index) +ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic) { JSObject *obj = f.fp()->scopeChain().getGlobal(); - ic::MICInfo &mic = f.fp()->script()->mics[index]; JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc)); jsid id = ATOM_TO_JSID(atom); - JS_ASSERT(mic.kind == ic::MICInfo::SET); + JS_ASSERT(ic->kind == ic::MICInfo::SET); JS_LOCK_OBJ(f.cx, obj); const Shape *shape = obj->nativeLookup(id); @@ -177,40 +175,40 @@ ic::SetGlobalName(VMFrame &f, uint32 index) { JS_UNLOCK_OBJ(f.cx, obj); if (shape) - PatchSetFallback(f, mic); + PatchSetFallback(f, ic); GetStubForSetGlobalName(f)(f, atom); return; } uint32 slot = shape->slot; JS_UNLOCK_OBJ(f.cx, obj); - mic.u.name.touched = true; + ic->u.name.touched = true; /* Patch shape guard. */ - JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50); - repatch.repatch(mic.shape, obj->shape()); + JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50); + repatch.repatch(ic->shape, obj->shape()); /* Patch loads. */ JS_ASSERT(slot >= JS_INITIAL_NSLOTS); slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); - JSC::RepatchBuffer stores(mic.load.executableAddress(), 32, false); + JSC::RepatchBuffer stores(ic->load.executableAddress(), 32, false); #if defined JS_CPU_X86 - stores.repatch(mic.load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4); + stores.repatch(ic->load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4); uint32 dataOffset; - if (mic.u.name.typeConst) + if (ic->u.name.typeConst) dataOffset = MICInfo::SET_DATA_CONST_TYPE_OFFSET; else dataOffset = MICInfo::SET_DATA_TYPE_OFFSET; - stores.repatch(mic.load.dataLabel32AtOffset(dataOffset), slot); + stores.repatch(ic->load.dataLabel32AtOffset(dataOffset), slot); #elif defined JS_CPU_ARM - // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch' + // ic->load actually points to the LDR instruction which fetches the offset, but 'repatch' // knows how to dereference it to find the integer value. - stores.repatch(mic.load.dataLabel32AtOffset(0), slot); + stores.repatch(ic->load.dataLabel32AtOffset(0), slot); #elif defined JS_PUNBOX64 - stores.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); + stores.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot); #endif // Actually implement the op the slow way. @@ -218,24 +216,16 @@ ic::SetGlobalName(VMFrame &f, uint32 index) } static void * JS_FASTCALL -SlowCallFromIC(VMFrame &f, uint32 index) +SlowCallFromIC(VMFrame &f, ic::CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic= oldscript->callICs[index]; - - stubs::SlowCall(f, ic.argc); - + stubs::SlowCall(f, ic->argc); return NULL; } static void * JS_FASTCALL -SlowNewFromIC(VMFrame &f, uint32 index) +SlowNewFromIC(VMFrame &f, ic::CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - - stubs::SlowNew(f, ic.argc); - + stubs::SlowNew(f, ic->argc); return NULL; } @@ -325,8 +315,11 @@ class CallCompiler : public BaseCompiler * here since ncode has two failure modes and we need to load out of * nmap anyway. */ - masm.loadPtr(Address(t0, offsetof(JSScript, jit)), t0); - Jump hasCode = masm.branchTestPtr(Assembler::NonZero, t0, t0); + size_t offset = callingNew + ? offsetof(JSScript, jitArityCheckCtor) + : offsetof(JSScript, jitArityCheckNormal); + masm.loadPtr(Address(t0, offset), t0); + Jump hasCode = masm.branchPtr(Assembler::Above, t0, ImmPtr(JS_UNJITTABLE_SCRIPT)); /* Try and compile. On success we get back the nmap pointer. */ masm.storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp))); @@ -345,7 +338,6 @@ class CallCompiler : public BaseCompiler /* Get nmap[ARITY], set argc, call. */ masm.move(Imm32(ic.argc), JSParamReg_Argc); - masm.loadPtr(Address(t0, offsetof(JITScript, arityCheck)), t0); masm.jump(t0); JSC::ExecutablePool *ep = poolForSize(masm.size(), CallICInfo::Pool_ScriptStub); @@ -377,9 +369,11 @@ class CallCompiler : public BaseCompiler ic.fastGuardedObject = obj; + JITScript *jit = script->getJIT(callingNew); + repatch.repatch(ic.funGuard, obj); repatch.relink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset), - JSC::CodeLocationLabel(script->ncode)); + JSC::CodeLocationLabel(jit->fastEntry)); JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n", start, ic.fastGuardedObject); } @@ -649,52 +643,40 @@ class CallCompiler : public BaseCompiler }; void * JS_FASTCALL -ic::Call(VMFrame &f, uint32 index) +ic::Call(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, false); + CallCompiler cc(f, *ic, false); return cc.update(); } void * JS_FASTCALL -ic::New(VMFrame &f, uint32 index) +ic::New(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, true); + CallCompiler cc(f, *ic, true); return cc.update(); } void JS_FASTCALL -ic::NativeCall(VMFrame &f, uint32 index) +ic::NativeCall(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, false); + CallCompiler cc(f, *ic, false); if (!cc.generateNativeStub()) - stubs::SlowCall(f, ic.argc); + stubs::SlowCall(f, ic->argc); } void JS_FASTCALL -ic::NativeNew(VMFrame &f, uint32 index) +ic::NativeNew(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, true); + CallCompiler cc(f, *ic, true); if (!cc.generateNativeStub()) - stubs::SlowNew(f, ic.argc); + stubs::SlowNew(f, ic->argc); } void -ic::PurgeMICs(JSContext *cx, JSScript *script) +JITScript::purgeMICs() { - /* MICs are purged during GC to handle changing shapes. */ - JS_ASSERT(cx->runtime->gcRegenShapes); - - uint32 nmics = script->jit->nMICs; - for (uint32 i = 0; i < nmics; i++) { - ic::MICInfo &mic = script->mics[i]; + for (uint32 i = 0; i < nMICs; i++) { + ic::MICInfo &mic = mics[i]; switch (mic.kind) { case ic::MICInfo::SET: case ic::MICInfo::GET: @@ -720,10 +702,22 @@ ic::PurgeMICs(JSContext *cx, JSScript *script) } void -ic::SweepCallICs(JSScript *script) +ic::PurgeMICs(JSContext *cx, JSScript *script) { - for (uint32 i = 0; i < script->jit->nCallICs; i++) { - ic::CallICInfo &ic = script->callICs[i]; + /* MICs are purged during GC to handle changing shapes. */ + JS_ASSERT(cx->runtime->gcRegenShapes); + + if (script->jitNormal) + script->jitNormal->purgeMICs(); + if (script->jitCtor) + script->jitCtor->purgeMICs(); +} + +void +JITScript::sweepCallICs() +{ + for (uint32 i = 0; i < nCallICs; i++) { + ic::CallICInfo &ic = callICs[i]; /* * If the object is unreachable, we're guaranteed not to be currently @@ -757,5 +751,14 @@ ic::SweepCallICs(JSScript *script) } } +void +ic::SweepCallICs(JSScript *script) +{ + if (script->jitNormal) + script->jitNormal->sweepCallICs(); + if (script->jitCtor) + script->jitCtor->sweepCallICs(); +} + #endif /* JS_MONOIC */ diff --git a/js/src/methodjit/MonoIC.h b/js/src/methodjit/MonoIC.h index 485e21aab9ba..9df110558e92 100644 --- a/js/src/methodjit/MonoIC.h +++ b/js/src/methodjit/MonoIC.h @@ -109,8 +109,8 @@ struct MICInfo { } u; }; -void JS_FASTCALL GetGlobalName(VMFrame &f, uint32 index); -void JS_FASTCALL SetGlobalName(VMFrame &f, uint32 index); +void JS_FASTCALL GetGlobalName(VMFrame &f, ic::MICInfo *ic); +void JS_FASTCALL SetGlobalName(VMFrame &f, ic::MICInfo *ic); /* See MonoIC.cpp, CallCompiler for more information on call ICs. */ struct CallICInfo { @@ -187,10 +187,10 @@ struct CallICInfo { } }; -void * JS_FASTCALL New(VMFrame &f, uint32 index); -void * JS_FASTCALL Call(VMFrame &f, uint32 index); -void JS_FASTCALL NativeNew(VMFrame &f, uint32 index); -void JS_FASTCALL NativeCall(VMFrame &f, uint32 index); +void * JS_FASTCALL New(VMFrame &f, ic::CallICInfo *ic); +void * JS_FASTCALL Call(VMFrame &f, ic::CallICInfo *ic); +void JS_FASTCALL NativeNew(VMFrame &f, ic::CallICInfo *ic); +void JS_FASTCALL NativeCall(VMFrame &f, ic::CallICInfo *ic); void PurgeMICs(JSContext *cx, JSScript *script); void SweepCallICs(JSScript *script); diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 0709f6857138..f869437c20f7 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -111,8 +111,7 @@ class PICStubCompiler : public BaseCompiler return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub)); } - bool disable(const char *reason, VoidStubUInt32 stub) - { + bool disable(const char *reason, VoidStubPIC stub) { return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub)); } @@ -159,7 +158,7 @@ class SetPropCompiler : public PICStubCompiler { JSObject *obj; JSAtom *atom; - VoidStubUInt32 stub; + VoidStubPIC stub; int lastStubSecondShapeGuard; static int32 dslotsLoadOffset(ic::PICInfo &pic) { @@ -224,7 +223,7 @@ class SetPropCompiler : public PICStubCompiler public: SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, - VoidStubUInt32 stub) + VoidStubPIC stub) : PICStubCompiler("setprop", f, script, pic), obj(obj), atom(atom), stub(stub), lastStubSecondShapeGuard(pic.secondShapeGuard) { } @@ -746,7 +745,7 @@ class GetPropCompiler : public PICStubCompiler { } GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, - VoidStubUInt32 stub) + VoidStubPIC stub) : PICStubCompiler("callprop", f, script, pic), obj(obj), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)), lastStubSecondShapeGuard(pic.secondShapeGuard) @@ -770,7 +769,7 @@ class GetPropCompiler : public PICStubCompiler RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH); ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress()); - VoidStubUInt32 stub; + VoidStubPIC stub; switch (pic.kind) { case ic::PICInfo::GET: stub = ic::GetProp; @@ -1573,7 +1572,7 @@ class ScopeNameCompiler : public PICStubCompiler public: ScopeNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubUInt32 stub) + JSAtom *atom, VoidStubPIC stub) : PICStubCompiler("name", f, script, pic), scopeChain(scopeChain), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)), obj(NULL), holder(NULL), prop(NULL) { } @@ -1591,7 +1590,7 @@ class ScopeNameCompiler : public PICStubCompiler RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH); ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress()); - VoidStubUInt32 stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName; + VoidStubPIC stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName; MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, stub)); repatcher.relinkCallerToTrampoline(retPtr, target); } @@ -1912,7 +1911,7 @@ class BindNameCompiler : public PICStubCompiler public: BindNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubUInt32 stub) + JSAtom *atom, VoidStubPIC stub) : PICStubCompiler("bind", f, script, pic), scopeChain(scopeChain), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)) { } @@ -2020,15 +2019,14 @@ class BindNameCompiler : public PICStubCompiler }; void JS_FASTCALL -ic::GetProp(VMFrame &f, uint32 index) +ic::GetProp(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; + JSAtom *atom = pic->atom; if (atom == f.cx->runtime->atomState.lengthAtom) { if (f.regs.sp[-1].isString()) { - GetPropCompiler cc(f, script, NULL, pic, NULL, stubs::Length); + GetPropCompiler cc(f, script, NULL, *pic, NULL, stubs::Length); if (!cc.generateStringLengthStub()) { cc.disable("error"); THROW(); @@ -2039,7 +2037,7 @@ ic::GetProp(VMFrame &f, uint32 index) } else if (!f.regs.sp[-1].isPrimitive()) { JSObject *obj = &f.regs.sp[-1].toObject(); if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden())) { - GetPropCompiler cc(f, script, obj, pic, NULL, stubs::Length); + GetPropCompiler cc(f, script, obj, *pic, NULL, stubs::Length); if (obj->isArray()) { if (!cc.generateArrayLengthStub()) { cc.disable("error"); @@ -2063,8 +2061,8 @@ ic::GetProp(VMFrame &f, uint32 index) if (!obj) THROW(); - if (pic.shouldGenerate()) { - GetPropCompiler cc(f, script, obj, pic, atom, stubs::GetProp); + if (pic->shouldGenerate()) { + GetPropCompiler cc(f, script, obj, *pic, atom, stubs::GetProp); if (!cc.update()) { cc.disable("error"); THROW(); @@ -2078,10 +2076,9 @@ ic::GetProp(VMFrame &f, uint32 index) } void JS_FASTCALL -ic::GetElem(VMFrame &f, uint32 picIndex) +ic::GetElem(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - PICInfo &pic = script->pics[picIndex]; JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); if (!obj) @@ -2090,8 +2087,8 @@ ic::GetElem(VMFrame &f, uint32 picIndex) Value idval = f.regs.sp[-1]; JS_ASSERT(idval.isString()); JSString *id = idval.toString(); - if (pic.shouldGenerate()) { - GetElemCompiler cc(f, script, obj, pic, id, stubs::GetElem); + if (pic->shouldGenerate()) { + GetElemCompiler cc(f, script, obj, *pic, id, stubs::GetElem); if (!cc.update()) { cc.disable("error"); THROW(); @@ -2107,46 +2104,29 @@ ic::GetElem(VMFrame &f, uint32 picIndex) f.regs.sp[-2] = v; } -void JS_FASTCALL -ic::SetPropDumb(VMFrame &f, uint32 index) -{ - JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JS_ASSERT(pic.isSet()); - JSAtom *atom = pic.atom; - - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - Value rval = f.regs.sp[-1]; - if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], - script->strictModeCode)) - THROW(); - f.regs.sp[-2] = rval; -} - +template static void JS_FASTCALL -SetPropSlow(VMFrame &f, uint32 index) +SetPropDumb(VMFrame &f, ic::PICInfo *pic) { - JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JS_ASSERT(pic.isSet()); + stubs::SetPropNoCache(f, pic->atom); +} - JSAtom *atom = pic.atom; - STRICT_VARIANT(stubs::SetName)(f, atom); +template +static void JS_FASTCALL +SetPropSlow(VMFrame &f, ic::PICInfo *pic) +{ + stubs::SetName(f, pic->atom); } void JS_FASTCALL -ic::SetProp(VMFrame &f, uint32 index) +ic::SetProp(VMFrame &f, ic::PICInfo *pic) { JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); if (!obj) THROW(); JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; - JS_ASSERT(pic.isSet()); + JS_ASSERT(pic->isSet()); // // Important: We update the PIC before looking up the property so that the @@ -2157,7 +2137,7 @@ ic::SetProp(VMFrame &f, uint32 index) // cache can't handle a GET and SET from the same scripted PC. // - VoidStubUInt32 stub; + VoidStubPIC stub; switch (JSOp(*f.regs.pc)) { case JSOP_PROPINC: case JSOP_PROPDEC: @@ -2167,40 +2147,36 @@ ic::SetProp(VMFrame &f, uint32 index) case JSOP_NAMEDEC: case JSOP_INCNAME: case JSOP_DECNAME: - stub = SetPropDumb; + stub = STRICT_VARIANT(SetPropDumb); break; default: - stub = SetPropSlow; + stub = STRICT_VARIANT(SetPropSlow); break; } - SetPropCompiler cc(f, script, obj, pic, atom, stub); + SetPropCompiler cc(f, script, obj, *pic, pic->atom, stub); if (!cc.update()) { cc.disable("error"); THROW(); } Value rval = f.regs.sp[-1]; - stub(f, index); + stub(f, pic); } static void JS_FASTCALL -CallPropSlow(VMFrame &f, uint32 index) +CallPropSlow(VMFrame &f, ic::PICInfo *pic) { - JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - stubs::CallProp(f, pic.atom); + stubs::CallProp(f, pic->atom); } void JS_FASTCALL -ic::CallProp(VMFrame &f, uint32 index) +ic::CallProp(VMFrame &f, ic::PICInfo *pic) { JSContext *cx = f.cx; JSFrameRegs ®s = f.regs; JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *origAtom = pic.atom; Value lval; lval = regs.sp[-1]; @@ -2260,7 +2236,7 @@ ic::CallProp(VMFrame &f, uint32 index) * PropertyCache::test. */ jsid id; - id = ATOM_TO_JSID(origAtom); + id = ATOM_TO_JSID(pic->atom); regs.sp++; regs.sp[-1].setNull(); @@ -2298,7 +2274,7 @@ ic::CallProp(VMFrame &f, uint32 index) } } - GetPropCompiler cc(f, script, &objv.toObject(), pic, origAtom, CallPropSlow); + GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, CallPropSlow); if (usePIC) { if (lval.isObject()) { if (!cc.update()) { @@ -2319,7 +2295,7 @@ ic::CallProp(VMFrame &f, uint32 index) #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(rval.isUndefined())) { - regs.sp[-2].setString(ATOM_TO_STRING(origAtom)); + regs.sp[-2].setString(ATOM_TO_STRING(pic->atom)); if (!js_OnUnknownMethod(cx, regs.sp - 2)) THROW(); } @@ -2327,28 +2303,26 @@ ic::CallProp(VMFrame &f, uint32 index) } static void JS_FASTCALL -SlowName(VMFrame &f, uint32 index) +SlowName(VMFrame &f, ic::PICInfo *pic) { stubs::Name(f); } static void JS_FASTCALL -SlowXName(VMFrame &f, uint32 index) +SlowXName(VMFrame &f, ic::PICInfo *pic) { stubs::GetProp(f); } void JS_FASTCALL -ic::XName(VMFrame &f, uint32 index) +ic::XName(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; /* GETXPROP is guaranteed to have an object. */ JSObject *obj = &f.regs.sp[-1].toObject(); - ScopeNameCompiler cc(f, script, obj, pic, atom, SlowXName); + ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, SlowXName); if (!cc.updateForXName()) { cc.disable("error"); @@ -2362,13 +2336,11 @@ ic::XName(VMFrame &f, uint32 index) } void JS_FASTCALL -ic::Name(VMFrame &f, uint32 index) +ic::Name(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; - ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), pic, atom, SlowName); + ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowName); if (!cc.updateForName()) { cc.disable("error"); @@ -2382,19 +2354,17 @@ ic::Name(VMFrame &f, uint32 index) } static void JS_FASTCALL -SlowBindName(VMFrame &f, uint32 index) +SlowBindName(VMFrame &f, ic::PICInfo *pic) { stubs::BindName(f); } void JS_FASTCALL -ic::BindName(VMFrame &f, uint32 index) +ic::BindName(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; - BindNameCompiler cc(f, script, &f.fp()->scopeChain(), pic, atom, SlowBindName); + BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowBindName); JSObject *obj = cc.update(); if (!obj) { @@ -2406,11 +2376,10 @@ ic::BindName(VMFrame &f, uint32 index) } void -ic::PurgePICs(JSContext *cx, JSScript *script) +JITScript::purgePICs() { - uint32 npics = script->jit->nPICs; - for (uint32 i = 0; i < npics; i++) { - ic::PICInfo &pic = script->pics[i]; + for (uint32 i = 0; i < nPICs; i++) { + ic::PICInfo &pic = pics[i]; switch (pic.kind) { case ic::PICInfo::SET: case ic::PICInfo::SETMETHOD: @@ -2438,5 +2407,14 @@ ic::PurgePICs(JSContext *cx, JSScript *script) } } +void +ic::PurgePICs(JSContext *cx, JSScript *script) +{ + if (script->jitNormal) + script->jitNormal->purgePICs(); + if (script->jitCtor) + script->jitCtor->purgePICs(); +} + #endif /* JS_POLYIC */ diff --git a/js/src/methodjit/PolyIC.h b/js/src/methodjit/PolyIC.h index 0edbe26321c8..9e3d63e03bef 100644 --- a/js/src/methodjit/PolyIC.h +++ b/js/src/methodjit/PolyIC.h @@ -352,14 +352,13 @@ struct PICInfo { }; void PurgePICs(JSContext *cx, JSScript *script); -void JS_FASTCALL GetProp(VMFrame &f, uint32 index); -void JS_FASTCALL GetElem(VMFrame &f, uint32 index); -void JS_FASTCALL SetProp(VMFrame &f, uint32 index); -void JS_FASTCALL CallProp(VMFrame &f, uint32 index); -void JS_FASTCALL Name(VMFrame &f, uint32 index); -void JS_FASTCALL XName(VMFrame &f, uint32 index); -void JS_FASTCALL BindName(VMFrame &f, uint32 index); -void JS_FASTCALL SetPropDumb(VMFrame &f, uint32 index); +void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL GetElem(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *); } /* namespace ic */ } /* namespace mjit */ diff --git a/js/src/methodjit/Retcon.cpp b/js/src/methodjit/Retcon.cpp index 99526fb01f49..f5069e1f9cb1 100644 --- a/js/src/methodjit/Retcon.cpp +++ b/js/src/methodjit/Retcon.cpp @@ -73,13 +73,14 @@ AutoScriptRetrapper::untrap(jsbytecode *pc) } Recompiler::PatchableAddress -Recompiler::findPatch(void **location) +Recompiler::findPatch(JITScript *jit, void **location) { - for (uint32 i = 0; i < script->jit->nCallSites; i++) { - if (script->jit->callSites[i].codeOffset + (uint8*)script->jit->invoke == *location) { + uint8* codeStart = (uint8 *)jit->code.m_code.executableAddress(); + for (uint32 i = 0; i < jit->nCallSites; i++) { + if (jit->callSites[i].codeOffset + codeStart == *location) { PatchableAddress result; result.location = location; - result.callSite = script->jit->callSites[i]; + result.callSite = jit->callSites[i]; return result; } } @@ -116,17 +117,27 @@ Recompiler::Recompiler(JSContext *cx, JSScript *script) bool Recompiler::recompile() { - JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD); + JS_ASSERT(script->hasJITCode()); - Vector toPatch(cx); + Vector normalPatches(cx); + Vector ctorPatches(cx); /* Scan the stack, saving the ncode elements of the frames. */ - JSStackFrame *firstFrame = NULL; + JSStackFrame *firstCtorFrame = NULL; + JSStackFrame *firstNormalFrame = NULL; for (AllFramesIter i(cx); !i.done(); ++i) { - if (!firstFrame && i.fp()->maybeScript() == script) - firstFrame = i.fp(); - if (script->isValidJitCode(i.fp()->nativeReturnAddress())) { - if (!toPatch.append(findPatch(i.fp()->addressOfNativeReturnAddress()))) + if (!firstCtorFrame && i.fp()->maybeScript() == script && i.fp()->isConstructing()) + firstCtorFrame = i.fp(); + else if (!firstNormalFrame && i.fp()->maybeScript() == script && !i.fp()->isConstructing()) + firstNormalFrame = i.fp(); + void **addr = i.fp()->addressOfNativeReturnAddress(); + if (!*addr) + continue; + if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { + if (!ctorPatches.append(findPatch(script->jitCtor, addr))) + return false; + } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { + if (!normalPatches.append(findPatch(script->jitNormal, addr))) return false; } } @@ -136,29 +147,41 @@ Recompiler::recompile() f != NULL; f = f->previous) { - void **machineReturn = f->returnAddressLocation(); - if (script->isValidJitCode(*machineReturn)) { - if (!toPatch.append(findPatch(machineReturn))) + void **addr = f->returnAddressLocation(); + if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { + if (!ctorPatches.append(findPatch(script->jitCtor, addr))) + return false; + } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { + if (!normalPatches.append(findPatch(script->jitNormal, addr))) return false; } } ReleaseScriptCode(cx, script); - /* No need to actually compile or fixup if no frames on the stack */ - if (!firstFrame) - return true; + if (normalPatches.length() && !recompile(firstNormalFrame, normalPatches)) + return false; + if (ctorPatches.length() && !recompile(firstCtorFrame, ctorPatches)) + return false; + + return true; +} + +bool +Recompiler::recompile(JSStackFrame *fp, Vector &patches) +{ /* If we get this far, the script is live, and we better be safe to re-jit. */ JS_ASSERT(cx->compartment->debugMode); + JS_ASSERT(fp); - Compiler c(cx, script, firstFrame->maybeFun(), &firstFrame->scopeChain()); - if (c.Compile() != Compile_Okay) + Compiler c(cx, fp); + if (c.compile() != Compile_Okay) return false; /* Perform the earlier scanned patches */ - for (uint32 i = 0; i < toPatch.length(); i++) - applyPatch(c, toPatch[i]); + for (uint32 i = 0; i < patches.length(); i++) + applyPatch(c, patches[i]); return true; } diff --git a/js/src/methodjit/Retcon.h b/js/src/methodjit/Retcon.h index 8280329a8fe0..99050c0e5eed 100644 --- a/js/src/methodjit/Retcon.h +++ b/js/src/methodjit/Retcon.h @@ -97,8 +97,9 @@ private: JSContext *cx; JSScript *script; - PatchableAddress findPatch(void **location); + PatchableAddress findPatch(JITScript *jit, void **location); void applyPatch(Compiler& c, PatchableAddress& toPatch); + bool recompile(JSStackFrame *fp, Vector &patches); }; } /* namespace mjit */ diff --git a/js/src/methodjit/StubCalls-inl.h b/js/src/methodjit/StubCalls-inl.h index 55b3513ca9ce..2b2844a2ea9f 100644 --- a/js/src/methodjit/StubCalls-inl.h +++ b/js/src/methodjit/StubCalls-inl.h @@ -99,7 +99,6 @@ ReportAtomNotDefined(JSContext *cx, JSAtom *atom) } \ JS_END_MACRO - }} #endif /* jslogic_h__ */ diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 5850c36e46a1..a0231f1ed997 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -281,6 +281,22 @@ stubs::SetName(VMFrame &f, JSAtom *origAtom) template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); +template +void JS_FASTCALL +stubs::SetPropNoCache(VMFrame &f, JSAtom *atom) +{ + JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); + if (!obj) + THROW(); + Value rval = f.regs.sp[-1]; + if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], strict)) + THROW(); + f.regs.sp[-2] = rval; +} + +template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); +template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); + template void JS_FASTCALL stubs::SetGlobalNameDumb(VMFrame &f, JSAtom *atom) @@ -2517,14 +2533,15 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) { jsbytecode *jpc = pc; JSScript *script = f.fp()->script(); + JITScript *jit = script->getJIT(f.fp()->isConstructing()); /* This is correct because the compiler adjusts the stack beforehand. */ Value lval = f.regs.sp[-1]; if (!lval.isPrimitive()) { ptrdiff_t offs = (pc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH); @@ -2544,8 +2561,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) JSString *rhs = rval.toString(); if (rhs == str || js_EqualStrings(str, rhs)) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } } pc += JUMP_OFFSET_LEN; @@ -2557,8 +2574,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (rval.isNumber() && d == rval.toNumber()) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } pc += JUMP_OFFSET_LEN; } @@ -2568,16 +2585,16 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (lval == rval) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } pc += JUMP_OFFSET_LEN; } } ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(jpc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } void * JS_FASTCALL @@ -2586,6 +2603,7 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) jsbytecode * const originalPC = origPc; jsbytecode *pc = originalPC; JSScript *script = f.fp()->script(); + JITScript *jit = script->getJIT(f.fp()->isConstructing()); uint32 jumpOffset = GET_JUMP_OFFSET(pc); pc += JUMP_OFFSET_LEN; @@ -2625,8 +2643,8 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) finally: /* Provide the native address. */ ptrdiff_t offset = (originalPC + jumpOffset) - script->code; - JS_ASSERT(script->nmap[offset]); - return script->nmap[offset]; + JS_ASSERT(jit->nmap[offset]); + return jit->nmap[offset]; } void JS_FASTCALL diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index fe49e1ba1106..da104367cbcb 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -98,14 +98,14 @@ struct UncachedCallResult { void UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); -JSBool JS_FASTCALL NewObject(VMFrame &f, uint32 argc); +void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto); void JS_FASTCALL Throw(VMFrame &f); void JS_FASTCALL PutCallObject(VMFrame &f); void JS_FASTCALL PutActivationObjects(VMFrame &f); void JS_FASTCALL GetCallObject(VMFrame &f); void JS_FASTCALL WrapPrimitiveThis(VMFrame &f); #if JS_MONOIC -void * JS_FASTCALL InvokeTracer(VMFrame &f, uint32 index); +void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::MICInfo *mic); #else void * JS_FASTCALL InvokeTracer(VMFrame &f); #endif @@ -116,6 +116,7 @@ void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc); void JS_FASTCALL BindName(VMFrame &f); JSObject * JS_FASTCALL BindGlobalName(VMFrame &f); template void JS_FASTCALL SetName(VMFrame &f, JSAtom *atom); +template void JS_FASTCALL SetPropNoCache(VMFrame &f, JSAtom *atom); template void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom); template void JS_FASTCALL SetGlobalNameDumb(VMFrame &f, JSAtom *atom); void JS_FASTCALL Name(VMFrame &f); @@ -226,11 +227,6 @@ inline FuncPtr FunctionTemplateConditional(bool cond, FuncPtr a, FuncPtr b) { return cond ? a : b; } -/* Return f if the script is strict mode code, f otherwise. */ -#define STRICT_VARIANT(f) \ - (FunctionTemplateConditional(script->strictModeCode, \ - f, f)) - }} /* namespace stubs,mjit,js */ extern "C" void * diff --git a/js/src/methodjit/StubCompiler.h b/js/src/methodjit/StubCompiler.h index bdfaff63bd70..6d945312c984 100644 --- a/js/src/methodjit/StubCompiler.h +++ b/js/src/methodjit/StubCompiler.h @@ -125,6 +125,11 @@ class StubCompiler STUB_CALL_TYPE(BoolStub); STUB_CALL_TYPE(VoidStubAtom); STUB_CALL_TYPE(VoidStubPC); + STUB_CALL_TYPE(VoidStubMIC); + STUB_CALL_TYPE(VoidPtrStubMIC); + STUB_CALL_TYPE(VoidStubPIC); + STUB_CALL_TYPE(VoidStubCallIC); + STUB_CALL_TYPE(VoidPtrStubCallIC); #undef STUB_CALL_TYPE diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index dea6d8c0c79d..005675639ba8 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1196,7 +1196,7 @@ AssertJit(JSContext *cx, uintN argc, jsval *vp) { #ifdef JS_METHODJIT if (JS_GetOptions(cx) & JSOPTION_METHODJIT) { - if (cx->fp()->script()->nmap == NULL) { + if (!cx->fp()->script()->getJIT(cx->fp()->isConstructing())) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_JIT_FAILED); return JS_FALSE; } diff --git a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js index a2b8d32d76de..db7e4b82b2a0 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js +++ b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js @@ -4,7 +4,7 @@ function main() { x = "failure"; } function success() { x = "success"; } /* The JSOP_STOP in a. */ -trap(main, 6, "success()"); +trap(main, 7, "success()"); main(); assertEq(x, "success"); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js index 1d19aeebeb11..fcb2eab3ef1b 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js @@ -3,5 +3,5 @@ function main() { return "failure"; } /* JSOP_RETURN in main. */ -trap(main, 3, "'success'"); +trap(main, 4, "'success'"); assertEq(main(), "success"); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js index 18561be47267..16a5a11c445e 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js @@ -3,7 +3,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* myparent call in myparent. */ - trap(myparent, 37, "failure()"); + trap(myparent, 38, "failure()"); } else { x = "success"; myparent(true); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js index eefc0bb86005..6c302163e197 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js @@ -4,7 +4,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* noop call in myparent */ - trap(myparent, 48, "success()"); + trap(myparent, 49, "success()"); } else { myparent(true); x = "failure"; diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js index 5cecf58cd5ad..b01d5ac3b12c 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js @@ -6,14 +6,14 @@ function doNothing() { } function myparent(nested) { if (nested) { /* JSOP_CALL to doNothing in myparent with nested = true. */ - trap(myparent, 24, "success()"); + trap(myparent, 25, "success()"); doNothing(); } else { doNothing(); } } /* JSOP_CALL to doNothing in myparent with nested = false. */ -trap(myparent, 34, "myparent(true)"); +trap(myparent, 35, "myparent(true)"); function success() { x = "success"; From 0fdc0de6a9edc4cd53a7c95584366e672e7220d7 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Oct 2010 15:04:19 -0700 Subject: [PATCH 029/284] Fix build on ARM, bump XDR bytecode (bug 589398 fallout). --- js/src/jsxdrapi.h | 2 +- js/src/methodjit/Compiler.cpp | 17 +++++++++-------- js/src/methodjit/Compiler.h | 4 ++++ js/src/methodjit/MethodJIT.h | 10 +++++++--- js/src/methodjit/StubCompiler.h | 6 +++++- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index 70e10b0f9c62..e53bc9392211 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -205,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); * before deserialization of bytecode. If the saved version does not match * the current version, abort deserialization and invalidate the file. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 71) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 72) /* * Library-private functions. diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 53040f027dfb..06a7a8d49108 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2419,19 +2419,20 @@ mjit::Compiler::jsop_length() #endif } -#if defined JS_POLYIC - -void -mjit::Compiler::passPICAddress(PICGenInfo &pic) -{ - pic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); -} - +#ifdef JS_MONOIC void mjit::Compiler::passMICAddress(MICGenInfo &mic) { mic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); } +#endif + +#if defined JS_POLYIC +void +mjit::Compiler::passPICAddress(PICGenInfo &pic) +{ + pic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); +} void mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 4cab7bca831b..5b8d5ecd3bff 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -264,8 +264,12 @@ class Compiler : public BaseCompiler void iterMore(); void iterEnd(); MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg); +#ifdef JS_POLYIC void passPICAddress(PICGenInfo &pic); +#endif +#ifdef JS_MONOIC void passMICAddress(MICGenInfo &mic); +#endif void constructThis(); /* Opcode handlers. */ diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 850b6a3dfa74..7e672c6f3c10 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -170,11 +170,15 @@ typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32); typedef void (JS_FASTCALL *VoidStubJSObj)(VMFrame &, JSObject *); typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *); typedef JSBool (JS_FASTCALL *BoolStubUInt32)(VMFrame &f, uint32); -typedef void (JS_FASTCALL *VoidStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); -typedef void * (JS_FASTCALL *VoidPtrStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); -typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); +#ifdef JS_MONOIC typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); +typedef void (JS_FASTCALL *VoidStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); +typedef void * (JS_FASTCALL *VoidPtrStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); +#endif +#ifdef JS_POLYIC +typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); +#endif namespace mjit { diff --git a/js/src/methodjit/StubCompiler.h b/js/src/methodjit/StubCompiler.h index 6d945312c984..d70c7c4b0c61 100644 --- a/js/src/methodjit/StubCompiler.h +++ b/js/src/methodjit/StubCompiler.h @@ -125,11 +125,15 @@ class StubCompiler STUB_CALL_TYPE(BoolStub); STUB_CALL_TYPE(VoidStubAtom); STUB_CALL_TYPE(VoidStubPC); +#ifdef JS_POLYIC + STUB_CALL_TYPE(VoidStubPIC); +#endif +#ifdef JS_MONOIC STUB_CALL_TYPE(VoidStubMIC); STUB_CALL_TYPE(VoidPtrStubMIC); - STUB_CALL_TYPE(VoidStubPIC); STUB_CALL_TYPE(VoidStubCallIC); STUB_CALL_TYPE(VoidPtrStubCallIC); +#endif #undef STUB_CALL_TYPE From 126f3cc49b1668540106bf4e423fb21470c176d6 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Oct 2010 15:11:35 -0700 Subject: [PATCH 030/284] Backed out changeset 144254e0344c --- js/src/jsxdrapi.h | 2 +- js/src/methodjit/Compiler.cpp | 15 +++++++-------- js/src/methodjit/Compiler.h | 4 ---- js/src/methodjit/MethodJIT.h | 8 ++------ js/src/methodjit/StubCompiler.h | 6 +----- 5 files changed, 11 insertions(+), 24 deletions(-) diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index e53bc9392211..70e10b0f9c62 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -205,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); * before deserialization of bytecode. If the saved version does not match * the current version, abort deserialization and invalidate the file. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 72) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 71) /* * Library-private functions. diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 06a7a8d49108..53040f027dfb 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2419,21 +2419,20 @@ mjit::Compiler::jsop_length() #endif } -#ifdef JS_MONOIC -void -mjit::Compiler::passMICAddress(MICGenInfo &mic) -{ - mic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); -} -#endif - #if defined JS_POLYIC + void mjit::Compiler::passPICAddress(PICGenInfo &pic) { pic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); } +void +mjit::Compiler::passMICAddress(MICGenInfo &mic) +{ + mic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); +} + void mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) { diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 5b8d5ecd3bff..4cab7bca831b 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -264,12 +264,8 @@ class Compiler : public BaseCompiler void iterMore(); void iterEnd(); MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg); -#ifdef JS_POLYIC void passPICAddress(PICGenInfo &pic); -#endif -#ifdef JS_MONOIC void passMICAddress(MICGenInfo &mic); -#endif void constructThis(); /* Opcode handlers. */ diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 7e672c6f3c10..850b6a3dfa74 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -170,15 +170,11 @@ typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32); typedef void (JS_FASTCALL *VoidStubJSObj)(VMFrame &, JSObject *); typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *); typedef JSBool (JS_FASTCALL *BoolStubUInt32)(VMFrame &f, uint32); -#ifdef JS_MONOIC -typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); -typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); typedef void (JS_FASTCALL *VoidStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); typedef void * (JS_FASTCALL *VoidPtrStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); -#endif -#ifdef JS_POLYIC typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); -#endif +typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); +typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); namespace mjit { diff --git a/js/src/methodjit/StubCompiler.h b/js/src/methodjit/StubCompiler.h index d70c7c4b0c61..6d945312c984 100644 --- a/js/src/methodjit/StubCompiler.h +++ b/js/src/methodjit/StubCompiler.h @@ -125,15 +125,11 @@ class StubCompiler STUB_CALL_TYPE(BoolStub); STUB_CALL_TYPE(VoidStubAtom); STUB_CALL_TYPE(VoidStubPC); -#ifdef JS_POLYIC - STUB_CALL_TYPE(VoidStubPIC); -#endif -#ifdef JS_MONOIC STUB_CALL_TYPE(VoidStubMIC); STUB_CALL_TYPE(VoidPtrStubMIC); + STUB_CALL_TYPE(VoidStubPIC); STUB_CALL_TYPE(VoidStubCallIC); STUB_CALL_TYPE(VoidPtrStubCallIC); -#endif #undef STUB_CALL_TYPE From 1f9978e7e967be3ec4a5fd004e8e191777acd181 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Oct 2010 15:12:00 -0700 Subject: [PATCH 031/284] Backed out changeset de5d1b528b9a --- js/src/jsapi.cpp | 2 +- js/src/jsbuiltins.h | 2 +- js/src/jscompartment.cpp | 4 +- js/src/jsdbgapi.cpp | 11 +- js/src/jsemit.cpp | 9 +- js/src/jsinterp.cpp | 158 +++-- js/src/jsinterp.h | 2 +- js/src/jsiter.cpp | 2 +- js/src/jsobj.cpp | 25 +- js/src/jsobj.h | 12 +- js/src/jsopcode.tbl | 3 - js/src/jsproxy.cpp | 2 +- js/src/jsscript.cpp | 12 +- js/src/jsscript.h | 71 +-- js/src/jstracer.cpp | 37 +- js/src/jstracer.h | 2 +- js/src/methodjit/BaseAssembler.h | 5 - js/src/methodjit/Compiler.cpp | 599 ++++++++---------- js/src/methodjit/Compiler.h | 23 +- js/src/methodjit/FrameState.cpp | 19 +- js/src/methodjit/FrameState.h | 7 +- js/src/methodjit/InvokeHelpers.cpp | 88 +-- js/src/methodjit/MethodJIT.cpp | 56 +- js/src/methodjit/MethodJIT.h | 85 +-- js/src/methodjit/MonoIC.cpp | 157 +++-- js/src/methodjit/MonoIC.h | 12 +- js/src/methodjit/PolyIC.cpp | 138 ++-- js/src/methodjit/PolyIC.h | 15 +- js/src/methodjit/Retcon.cpp | 65 +- js/src/methodjit/Retcon.h | 3 +- js/src/methodjit/StubCalls-inl.h | 1 + js/src/methodjit/StubCalls.cpp | 42 +- js/src/methodjit/StubCalls.h | 10 +- js/src/methodjit/StubCompiler.h | 5 - js/src/shell/js.cpp | 2 +- .../tests/jaeger/bug563000/simple-trap-2.js | 2 +- .../jaeger/bug563000/trap-force-return-1.js | 2 +- .../jaeger/bug563000/trap-own-callsite.js | 2 +- .../jaeger/bug563000/trap-self-as-parent.js | 2 +- .../jaeger/bug563000/trap-self-from-trap.js | 4 +- 40 files changed, 710 insertions(+), 988 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 0527dc85185d..5b6917660ea0 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2990,7 +2990,7 @@ JS_NewObjectForConstructor(JSContext *cx, const jsval *vp) CHECK_REQUEST(cx); assertSameCompartment(cx, *vp); - return js_CreateThis(cx, JSVAL_TO_OBJECT(*vp)); + return js_NewInstance(cx, JSVAL_TO_OBJECT(*vp)); } JS_PUBLIC_API(JSBool) diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index e884098dcd78..275663e05028 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -612,7 +612,7 @@ JS_DECLARE_CALLINFO(js_NumberToString) /* Defined in jsobj.cpp. */ JS_DECLARE_CALLINFO(js_Object_tn) -JS_DECLARE_CALLINFO(js_CreateThisFromTrace) +JS_DECLARE_CALLINFO(js_NewInstanceFromTrace) JS_DECLARE_CALLINFO(js_NonEmptyObject) /* Defined in jsregexp.cpp. */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index b6ee679d48c2..96b4ad80dd0e 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -318,7 +318,7 @@ JSCompartment::sweep(JSContext *cx) #if defined JS_METHODJIT && defined JS_MONOIC for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) { JSScript *script = reinterpret_cast(cursor); - if (script->hasJITCode()) + if (script->jit) mjit::ic::SweepCallICs(script); } #endif @@ -333,7 +333,7 @@ JSCompartment::purge(JSContext *cx) for (JSScript *script = (JSScript *)scripts.next; &script->links != &scripts; script = (JSScript *)script->links.next) { - if (script->hasJITCode()) { + if (script->jit) { # if defined JS_POLYIC mjit::ic::PurgePICs(cx, script); # endif diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index e6fc2e91fec5..0b92a81b6218 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -116,7 +116,8 @@ js_SetDebugMode(JSContext *cx, JSBool debug) &script->links != &cx->compartment->scripts; script = (JSScript *)script->links.next) { if (script->debugMode != debug && - script->hasJITCode() && + script->ncode && + script->ncode != JS_UNJITTABLE_METHOD && !IsScriptLive(cx, script)) { /* * In the event that this fails, debug mode is left partially on, @@ -235,10 +236,6 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, return JS_FALSE; } - // Do not trap BEGIN, it's a special prologue opcode. - if (JSOp(*pc) == JSOP_BEGIN) - pc += JSOP_BEGIN_LENGTH; - JS_ASSERT((JSOp) *pc != JSOP_TRAP); junk = NULL; rt = cx->runtime; @@ -277,7 +274,7 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, cx->free(junk); #ifdef JS_METHODJIT - if (script->hasJITCode()) { + if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) { mjit::Recompiler recompiler(cx, script); if (!recompiler.recompile()) return JS_FALSE; @@ -330,7 +327,7 @@ JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, DBG_UNLOCK(cx->runtime); #ifdef JS_METHODJIT - if (script->hasJITCode()) { + if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) { mjit::Recompiler recompiler(cx, script); recompiler.recompile(); } diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index d857d1859a1d..7d4db58b24f0 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -3678,15 +3678,10 @@ bad: JSBool js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) { - CG_SWITCH_TO_PROLOG(cg); - JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); - if (js_Emit1(cx, cg, JSOP_BEGIN) < 0) - return false; - CG_SWITCH_TO_MAIN(cg); - if (cg->flags & TCF_FUN_IS_GENERATOR) { - /* JSOP_GENERATOR must be the first real instruction. */ + /* JSOP_GENERATOR must be the first instruction. */ CG_SWITCH_TO_PROLOG(cg); + JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) return false; CG_SWITCH_TO_MAIN(cg); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index ea33bbade29e..2879e8094d0c 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -134,13 +134,12 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) #if defined(JS_METHODJIT) && defined(JS_MONOIC) JSScript *script = this->script(); - js::mjit::JITScript *jit = script->getJIT(isConstructing()); size_t low = 0; - size_t high = jit->nCallICs; + size_t high = script->jit->nCallICs; while (high > low + 1) { /* Could overflow here on a script with 2 billion calls. Oh well. */ size_t mid = (high + low) / 2; - void *entry = jit->callICs[mid].funGuard.executableAddress(); + void *entry = script->callICs[mid].funGuard.executableAddress(); /* * Use >= here as the return address of the call is likely to be @@ -152,7 +151,7 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) low = mid; } - js::mjit::ic::CallICInfo &callIC = jit->callICs[low]; + js::mjit::ic::CallICInfo &callIC = script->callICs[low]; JS_ASSERT((uint8*)callIC.funGuard.executableAddress() + callIC.joinPointOffset == next->ncode_); return callIC.pc; @@ -617,7 +616,7 @@ struct AutoInterpPreparer { }; JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) +RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain) { JS_ASSERT(script); @@ -627,11 +626,8 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) AutoInterpPreparer prepareInterp(cx, script); - JS_ASSERT(fp == cx->fp()); - JS_ASSERT(fp->script() == script); - #ifdef JS_METHODJIT - mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fp); + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fun, &scopeChain); if (status == mjit::Compile_Error) return JS_FALSE; @@ -639,7 +635,7 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) return mjit::JaegerShot(cx); #endif - return Interpret(cx, fp); + return Interpret(cx, cx->fp()); } /* @@ -690,10 +686,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) /* Handle the empty-script special case. */ if (JS_UNLIKELY(script->isEmpty())) { if (flags & JSINVOKE_CONSTRUCT) { - JSObject *obj = js_CreateThisForFunction(cx, &callee); - if (!obj) - return false; - args.rval().setObject(*obj); + JS_ASSERT(args.thisv().isObject()); + args.rval() = args.thisv(); } else { args.rval().setUndefined(); } @@ -724,20 +718,19 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) * and fp->scopeChain is correct because the thisObject hook may call * JS_GetScopeChain. */ - if (!(flags & JSINVOKE_CONSTRUCT)) { - Value &thisv = fp->functionThis(); - if (thisv.isObject()) { - /* - * We must call the thisObject hook in case we are not called from the - * interpreter, where a prior bytecode has computed an appropriate - * |this| already. - */ - JSObject *thisp = thisv.toObject().thisObject(cx); - if (!thisp) - return false; - JS_ASSERT(IsSaneThisObject(*thisp)); - thisv.setObject(*thisp); - } + Value &thisv = fp->functionThis(); + JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !thisv.isPrimitive()); + if (thisv.isObject() && !(flags & JSINVOKE_CONSTRUCT)) { + /* + * We must call the thisObject hook in case we are not called from the + * interpreter, where a prior bytecode has computed an appropriate + * |this| already. + */ + JSObject *thisp = thisv.toObject().thisObject(cx); + if (!thisp) + return false; + JS_ASSERT(IsSaneThisObject(*thisp)); + thisv.setObject(*thisp); } JSInterpreterHook hook = cx->debugHooks->callHook; @@ -750,7 +743,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) { AutoPreserveEnumerators preserve(cx); Probes::enterJSFun(cx, fun); - ok = RunScript(cx, script, fp); + ok = RunScript(cx, script, fun, fp->scopeChain()); Probes::exitJSFun(cx, fun); } @@ -763,8 +756,6 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) PutActivationObjects(cx, fp); args.rval() = fp->returnValue(); - JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !args.rval().isPrimitive()); - return ok; } @@ -909,7 +900,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script, /* Run script until JSOP_STOP or error. */ AutoPreserveEnumerators preserve(cx); - JSBool ok = RunScript(cx, script, frame.fp()); + JSBool ok = RunScript(cx, script, NULL, frame.fp()->scopeChain()); if (result) *result = frame.fp()->returnValue(); @@ -1162,9 +1153,8 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef) /* Handle the fast-constructors cases before falling into the general case . */ Class *clasp = callee->getClass(); - JSFunction *fun = NULL; if (clasp == &js_FunctionClass) { - fun = callee->getFunctionPrivate(); + JSFunction *fun = callee->getFunctionPrivate(); if (fun->isConstructor()) { args.thisv().setMagicWithObjectOrNullPayload(NULL); return CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base()); @@ -1174,30 +1164,25 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef) return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base()); } - /* Scripts create their own |this| in JSOP_BEGIN */ - if (!fun || !fun->isInterpreted()) { - JSObject *obj = js_CreateThis(cx, callee); - if (!obj) - return false; - args.thisv().setObject(*obj); - } + /* Construct 'this'. */ + JSObject *obj = js_NewInstance(cx, callee); + if (!obj) + return false; + args.thisv().setObject(*obj); if (!Invoke(cx, args, JSINVOKE_CONSTRUCT)) return false; + /* Check the return value and if it's primitive, force it to be obj. */ if (args.rval().isPrimitive()) { - if (clasp != &js_FunctionClass) { + if (callee->getClass() != &js_FunctionClass) { /* native [[Construct]] returning primitive is error */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_NEW_RESULT, js_ValueToPrintableString(cx, args.rval())); return false; } - - /* The interpreter fixes rval for us. */ - JS_ASSERT(!fun->isInterpreted()); - - args.rval() = args.thisv(); + args.rval().setObject(*obj); } JS_RUNTIME_METER(cx->runtime, constructs); @@ -2305,7 +2290,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN do { \ JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \ if (leaveOnSafePoint && !regs.fp->hasImacropc() && \ - script->hasNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \ + script->nmap && script->nmap[regs.pc - script->code]) { \ JS_ASSERT(!TRACE_RECORDER(cx)); \ interpReturnOK = true; \ goto stop_recording; \ @@ -4492,41 +4477,6 @@ BEGIN_CASE(JSOP_ENUMELEM) } END_CASE(JSOP_ENUMELEM) -BEGIN_CASE(JSOP_BEGIN) -{ - if (regs.fp->isConstructing()) { - JSObject *obj2 = js_CreateThisForFunction(cx, ®s.fp->callee()); - if (!obj2) - goto error; - regs.fp->functionThis().setObject(*obj2); - } - - /* Call the debugger hook if present. */ - if (JSInterpreterHook hook = cx->debugHooks->callHook) { - regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0, - cx->debugHooks->callHookData)); - CHECK_INTERRUPT_HANDLER(); - } - - JS_RUNTIME_METER(rt, inlineCalls); - - Probes::enterJSFun(cx, regs.fp->fun()); - -#ifdef JS_METHODJIT - /* Try to ensure methods are method JIT'd. */ - mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp); - if (status == mjit::Compile_Error) - goto error; - if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { - if (!mjit::JaegerShot(cx)) - goto error; - interpReturnOK = true; - goto inline_return; - } -#endif -} -END_CASE(JSOP_BEGIN) - { JSFunction *newfun; JSObject *callee; @@ -4548,15 +4498,24 @@ BEGIN_CASE(JSOP_NEW) if (IsFunctionObject(vp[0], &callee)) { newfun = callee->getFunctionPrivate(); if (newfun->isInterpreted()) { + /* Root as we go using vp[1]. */ + if (!callee->getProperty(cx, + ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), + &vp[1])) { + goto error; + } + JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL; + JSObject *obj2 = NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent()); + if (!obj2) + goto error; + if (newfun->u.i.script->isEmpty()) { - JSObject *obj2 = js_CreateThisForFunction(cx, callee); - if (!obj2) - goto error; vp[0].setObject(*obj2); regs.sp = vp + 1; goto end_new; } + vp[1].setObject(*obj2); flags = JSFRAME_CONSTRUCTING; goto inline_call; } @@ -4625,13 +4584,38 @@ BEGIN_CASE(JSOP_APPLY) if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp)) goto error; + /* Call the debugger hook if present. */ + if (JSInterpreterHook hook = cx->debugHooks->callHook) { + regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0, + cx->debugHooks->callHookData)); + CHECK_INTERRUPT_HANDLER(); + } + inlineCallCount++; + JS_RUNTIME_METER(rt, inlineCalls); + + Probes::enterJSFun(cx, newfun); TRACE_0(EnterFrame); +#ifdef JS_METHODJIT + /* Try to ensure methods are method JIT'd. */ + { + JSObject *scope = ®s.fp->scopeChain(); + mjit::CompileStatus status = mjit::CanMethodJIT(cx, newscript, newfun, scope); + if (status == mjit::Compile_Error) + goto error; + if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { + if (!mjit::JaegerShot(cx)) + goto error; + interpReturnOK = true; + goto inline_return; + } + } +#endif + /* Load first op and dispatch it (safe since JSOP_STOP). */ op = (JSOp) *regs.pc; - JS_ASSERT(op == JSOP_BEGIN); DO_OP(); } diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 9b09ddaa1f40..04708a494bdd 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -968,7 +968,7 @@ extern JS_REQUIRES_STACK JS_NEVER_INLINE bool Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, uintN interpFlags = 0); extern JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp); +RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain); #define JSPROP_INITIALIZER 0x100 /* NB: Not a valid property attribute. */ diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index a5d18f38abcf..33e569b508b6 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1283,7 +1283,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, JSObject *enumerators = cx->enumerators; cx->enumerators = gen->enumerators; - ok = RunScript(cx, stackfp->script(), stackfp); + ok = RunScript(cx, stackfp->script(), stackfp->fun(), stackfp->scopeChain()); gen->enumerators = cx->enumerators; cx->enumerators = enumerators; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 1ba744e371d1..32a581e9d271 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2770,7 +2770,7 @@ js_Object(JSContext *cx, uintN argc, Value *vp) } JSObject* -js_CreateThis(JSContext *cx, JSObject *callee) +js_NewInstance(JSContext *cx, JSObject *callee) { Class *clasp = callee->getClass(); @@ -2790,25 +2790,6 @@ js_CreateThis(JSContext *cx, JSObject *callee) return NewObject(cx, newclasp, proto, parent); } -JSObject * -js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto) -{ - return NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent()); -} - -JSObject * -js_CreateThisForFunction(JSContext *cx, JSObject *callee) -{ - Value protov; - if (!callee->getProperty(cx, - ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), - &protov)) { - return NULL; - } - JSObject *proto = protov.isObject() ? &protov.toObject() : NULL; - return js_CreateThisForFunctionWithProto(cx, callee, proto); -} - #ifdef JS_TRACER static JS_ALWAYS_INLINE JSObject* @@ -2859,7 +2840,7 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, ST nanojit::ACCSET_STORE_ANY) JSObject* FASTCALL -js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) +js_NewInstanceFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) { JS_ASSERT(JS_ON_TRACE(cx)); JS_ASSERT(ctor->isFunction()); @@ -2910,7 +2891,7 @@ js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) return NewNonFunction(cx, clasp, proto, parent); } -JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0, +JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstanceFromTrace, CONTEXT, CLASS, OBJECT, 0, nanojit::ACCSET_STORE_ANY) #else /* !JS_TRACER */ diff --git a/js/src/jsobj.h b/js/src/jsobj.h index c76af07c3ed3..5b4b66b66cd2 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1407,18 +1407,8 @@ extern JSObject * js_ConstructObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, uintN argc, js::Value *argv); -// Specialized call for constructing |this| with a known function callee, -// and a known prototype. extern JSObject * -js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto); - -// Specialized call for constructing |this| with a known function callee. -extern JSObject * -js_CreateThisForFunction(JSContext *cx, JSObject *callee); - -// Generic call for constructing |this|. -extern JSObject * -js_CreateThis(JSContext *cx, JSObject *callee); +js_NewInstance(JSContext *cx, JSObject *callee); extern jsid js_CheckForStringIndex(jsid id); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 5707ea478dc8..89c895c84fd5 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -620,6 +620,3 @@ OPDEF(JSOP_FORGLOBAL, 246,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL */ OPDEF(JSOP_BLOCKCHAIN, 247,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT) OPDEF(JSOP_NULLBLOCKCHAIN,248,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE) - -OPDEF(JSOP_BEGIN, 249,"begin", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_TMPSLOT) - diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 937b07515e7b..4857ba2ee508 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1235,7 +1235,7 @@ callable_Call(JSContext *cx, uintN argc, Value *vp) static JSBool callable_Construct(JSContext *cx, uintN argc, Value *vp) { - JSObject *thisobj = js_CreateThis(cx, &JS_CALLEE(cx, vp).toObject()); + JSObject *thisobj = js_NewInstance(cx, &JS_CALLEE(cx, vp).toObject()); if (!thisobj) return false; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index db8af79d5ed3..ef2649c5ebc7 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -118,7 +118,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript, uint32 length, lineno, nslots, magic; uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, nconsts, i; uint32 prologLength, version, encodedClosedCount; - uint16 nClosedArgs = 0, nClosedVars = 0; + uint16 nClosedArgs, nClosedVars; JSPrincipals *principals; uint32 encodeable; JSBool filenameWasSaved; @@ -1641,6 +1641,16 @@ js_GetScriptLineExtent(JSScript *script) return 1 + lineno - script->lineno; } +#ifdef JS_METHODJIT +bool +JSScript::isValidJitCode(void *jcode) +{ + return (char*)jcode >= (char*)jit->invoke && + (char*)jcode < (char*)jit->invoke + + jit->inlineLength + jit->outOfLineLength; +} +#endif + void JSScript::copyClosedSlotsTo(JSScript *other) { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index b95dc9e5f337..88321350d88a 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -169,20 +169,21 @@ struct GlobalSlotArray { namespace JSC { class ExecutablePool; } - -#define JS_UNJITTABLE_SCRIPT (reinterpret_cast(1)) - -enum JITScriptStatus { - JITScript_None, - JITScript_Invalid, - JITScript_Valid -}; - namespace js { namespace mjit { struct JITScript; +namespace ic { +# if defined JS_POLYIC + struct PICInfo; +# endif +# if defined JS_MONOIC + struct MICInfo; + struct CallICInfo; +# endif +} +struct CallSite; } } #endif @@ -284,35 +285,20 @@ struct JSScript { public: #ifdef JS_METHODJIT - // Fast-cached pointers to make calls faster. These are also used to - // quickly test whether there is JIT code; a NULL value means no - // compilation has been attempted. A JS_UNJITTABLE_SCRIPT value means - // compilation failed. Any value is the arity-check entry point. - void *jitArityCheckNormal; - void *jitArityCheckCtor; + // Note: the other pointers in this group may be non-NULL only if + // |execPool| is non-NULL. + void *ncode; /* native code compiled by the method JIT */ + void **nmap; /* maps PCs to native code */ + js::mjit::JITScript *jit; /* Extra JIT info */ +# if defined JS_POLYIC + js::mjit::ic::PICInfo *pics; /* PICs in this script */ +# endif +# if defined JS_MONOIC + js::mjit::ic::MICInfo *mics; /* MICs in this script. */ + js::mjit::ic::CallICInfo *callICs; /* CallICs in this script. */ +# endif - js::mjit::JITScript *jitNormal; /* Extra JIT info for normal scripts */ - js::mjit::JITScript *jitCtor; /* Extra JIT info for constructors */ - - bool hasJITCode() { - return jitNormal || jitCtor; - } - - inline void **maybeNativeMap(bool constructing); - inline bool hasNativeCodeForPC(bool constructing, jsbytecode *pc); - - js::mjit::JITScript *getJIT(bool constructing) { - return constructing ? jitCtor : jitNormal; - } - - JITScriptStatus getJITStatus(bool constructing) { - void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal; - if (addr == NULL) - return JITScript_None; - if (addr == JS_UNJITTABLE_SCRIPT) - return JITScript_Invalid; - return JITScript_Valid; - } + bool isValidJitCode(void *jcode); #endif /* Script notes are allocated right after the code. */ @@ -407,6 +393,17 @@ struct JSScript { return const_cast(&emptyScriptConst); } +#ifdef JS_METHODJIT + /* + * Map the given PC to the corresponding native code address. + */ + void *pcToNative(jsbytecode *pc) { + JS_ASSERT(nmap); + JS_ASSERT(nmap[pc - code]); + return nmap[pc - code]; + } +#endif + uint32 getClosedArg(uint32 index) { JS_ASSERT(index < nClosedArgs); return closedSlots[index]; diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 9cb913a775ab..81cdb7e15213 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -11051,20 +11051,6 @@ TraceRecorder::emitNativePropertyOp(const Shape* shape, LIns* obj_ins, guard(true, lir->insEqI_0(status_ins), STATUS_EXIT); } -JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::record_JSOP_BEGIN() -{ - JSStackFrame* fp = cx->fp(); - if (fp->isConstructing()) { - LIns* callee_ins = get(&cx->fp()->calleeValue()); - LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins }; - LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); - guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); - set(&fp->thisValue(), tv_ins); - } - return ARECORD_CONTINUE; -} - JS_REQUIRES_STACK RecordingStatus TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], bool rooted) { @@ -11374,7 +11360,7 @@ TraceRecorder::callNative(uintN argc, JSOp mode) clasp = &js_ObjectClass; JS_ASSERT(((jsuword) clasp & 3) == 0); - // Abort on |new Function|. js_CreateThis would allocate a regular- + // Abort on |new Function|. js_NewInstance would allocate a regular- // sized JSObject, not a Function-sized one. (The Function ctor would // deep-bail anyway but let's not go there.) if (clasp == &js_FunctionClass) @@ -11393,7 +11379,7 @@ TraceRecorder::callNative(uintN argc, JSOp mode) args[0] = INS_CONSTOBJ(funobj); args[1] = INS_CONSTPTR(clasp); args[2] = cx_ins; - newobj_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); + newobj_ins = lir->insCall(&js_NewInstanceFromTrace_ci, args); guard(false, lir->insEqP_0(newobj_ins), OOM_EXIT); /* @@ -11518,8 +11504,15 @@ TraceRecorder::functionCall(uintN argc, JSOp mode) } #endif - if (FUN_INTERPRETED(fun)) + if (FUN_INTERPRETED(fun)) { + if (mode == JSOP_NEW) { + LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; + LIns* tv_ins = lir->insCall(&js_NewInstanceFromTrace_ci, args); + guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); + set(&tval, tv_ins); + } return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW); + } Native native = fun->maybeNative(); Value* argv = &tval + 1; @@ -13313,15 +13306,7 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, * and does not call any TR::record_*CallComplete hook. */ if (fun->u.i.script->isEmpty()) { - LIns* rval_ins; - if (constructing) { - LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; - LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); - guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); - rval_ins = tv_ins; - } else { - rval_ins = INS_UNDEFINED(); - } + LIns* rval_ins = constructing ? stack(-1 - argc) : INS_UNDEFINED(); stack(-2 - argc, rval_ins); return RECORD_CONTINUE; } diff --git a/js/src/jstracer.h b/js/src/jstracer.h index cf5280b24d03..25e291e3b76a 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -949,7 +949,7 @@ class TraceRecorder /* Carry the return value from a native call to the record_NativeCallComplete. */ nanojit::LIns* native_rval_ins; - /* Carry the return value of js_CreateThis to record_NativeCallComplete. */ + /* Carry the return value of js_NewInstance to record_NativeCallComplete. */ nanojit::LIns* newobj_ins; /* Carry the JSSpecializedNative used to generate a call to record_NativeCallComplete. */ diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 5339d00eee1b..110c53b57c83 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -342,11 +342,6 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste } }; -/* Return f if the script is strict mode code, f otherwise. */ -#define STRICT_VARIANT(f) \ - (FunctionTemplateConditional(script->strictModeCode, \ - f, f)) - /* Save some typing. */ static const JSC::MacroAssembler::RegisterID JSFrameReg = BaseAssembler::JSFrameReg; static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = BaseAssembler::JSReturnReg_Type; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 53040f027dfb..83f70832be29 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -74,16 +74,12 @@ static const char *OpcodeNames[] = { }; #endif -mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp) +mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) : BaseCompiler(cx), - fp(fp), - script(fp->script()), - scopeChain(&fp->scopeChain()), + script(script), + scopeChain(scopeChain), globalObj(scopeChain->getGlobal()), - fun(fp->isFunctionFrame() && !fp->isEvalFrame() - ? fp->fun() - : NULL), - isConstructing(fp->isConstructing()), + fun(fun), analysis(cx, script), jumpMap(NULL), frame(cx, script, masm), branchPatches(ContextAllocPolicy(cx)), #if defined JS_MONOIC @@ -103,34 +99,6 @@ mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp) { } -CompileStatus -mjit::Compiler::compile() -{ - JS_ASSERT(!script->isEmpty()); - JS_ASSERT_IF(isConstructing, !script->jitCtor); - JS_ASSERT_IF(!isConstructing, !script->jitNormal); - - JITScript **jit = isConstructing ? &script->jitCtor : &script->jitNormal; - void **checkAddr = isConstructing - ? &script->jitArityCheckCtor - : &script->jitArityCheckNormal; - - CompileStatus status = performCompilation(jit); - if (status == Compile_Okay) { - // Global scripts don't have an arity check entry. That's okay, we - // just need a pointer so the VM can quickly decide whether this - // method can be JIT'd or not. Global scripts cannot be IC'd, since - // they have no functions, so there is no danger. - *checkAddr = (*jit)->arityCheckEntry - ? (*jit)->arityCheckEntry - : (*jit)->invokeEntry; - } else { - *checkAddr = JS_UNJITTABLE_SCRIPT; - } - - return status; -} - #define CHECK_STATUS(expr) \ JS_BEGIN_MACRO \ CompileStatus status_ = (expr); \ @@ -139,8 +107,10 @@ mjit::Compiler::compile() JS_END_MACRO CompileStatus -mjit::Compiler::performCompilation(JITScript **jitp) +mjit::Compiler::Compile() { + JS_ASSERT(!script->ncode); + JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n", script->filename, script->lineno, script->length); @@ -182,7 +152,7 @@ mjit::Compiler::performCompilation(JITScript **jitp) CHECK_STATUS(generatePrologue()); CHECK_STATUS(generateMethod()); CHECK_STATUS(generateEpilogue()); - CHECK_STATUS(finishThisUp(jitp)); + CHECK_STATUS(finishThisUp()); #ifdef JS_METHODJIT_SPEW prof.stop(); @@ -190,7 +160,7 @@ mjit::Compiler::performCompilation(JITScript **jitp) #endif JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%ld\")\n", - (*jitp)->code.m_code.executableAddress(), (*jitp)->code.m_size); + (void*)script->ncode, masm.size() + stubcc.size()); return Compile_Okay; } @@ -203,13 +173,18 @@ mjit::Compiler::~Compiler() } CompileStatus JS_NEVER_INLINE -mjit::TryCompile(JSContext *cx, JSStackFrame *fp) +mjit::TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) { - JS_ASSERT(cx->fp() == fp); + Compiler cc(cx, script, fun, scopeChain); - Compiler cc(cx, fp); + JS_ASSERT(!script->ncode); + JS_ASSERT(!script->isEmpty()); - return cc.compile(); + CompileStatus status = cc.Compile(); + if (status != Compile_Okay) + script->ncode = JS_UNJITTABLE_METHOD; + + return status; } CompileStatus @@ -315,7 +290,7 @@ mjit::Compiler::generateEpilogue() } CompileStatus -mjit::Compiler::finishThisUp(JITScript **jitp) +mjit::Compiler::finishThisUp() { for (size_t i = 0; i < branchPatches.length(); i++) { Label label = labelOf(branchPatches[i].pc); @@ -361,16 +336,18 @@ mjit::Compiler::finishThisUp(JITScript **jitp) return Compile_Error; } - JITScript *jit = (JITScript *)cursor; + script->jit = (JITScript *)cursor; cursor += sizeof(JITScript); - jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size()); - jit->nCallSites = callSites.length(); - jit->invokeEntry = result; + script->jit->execPool = execPool; + script->jit->inlineLength = masm.size(); + script->jit->outOfLineLength = stubcc.size(); + script->jit->nCallSites = callSites.length(); + script->jit->invoke = result; /* Build the pc -> ncode mapping. */ void **nmap = (void **)cursor; - jit->nmap = nmap; + script->nmap = nmap; cursor += sizeof(void *) * script->length; for (size_t i = 0; i < script->length; i++) { @@ -381,116 +358,107 @@ mjit::Compiler::finishThisUp(JITScript **jitp) } } - if (fun) { - jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress(); - jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress(); - } + if (fun) + script->jit->arityCheck = stubCode.locationOf(arityLabel).executableAddress(); #if defined JS_MONOIC - jit->nMICs = mics.length(); + script->jit->nMICs = mics.length(); if (mics.length()) { - jit->mics = (ic::MICInfo *)cursor; + script->mics = (ic::MICInfo *)cursor; cursor += sizeof(ic::MICInfo) * mics.length(); } else { - jit->mics = NULL; + script->mics = NULL; } - if (ic::MICInfo *scriptMICs = jit->mics) { - for (size_t i = 0; i < mics.length(); i++) { - scriptMICs[i].kind = mics[i].kind; - scriptMICs[i].entry = fullCode.locationOf(mics[i].entry); - switch (mics[i].kind) { - case ic::MICInfo::GET: - case ic::MICInfo::SET: - scriptMICs[i].load = fullCode.locationOf(mics[i].load); - scriptMICs[i].shape = fullCode.locationOf(mics[i].shape); - scriptMICs[i].stubCall = stubCode.locationOf(mics[i].call); - scriptMICs[i].stubEntry = stubCode.locationOf(mics[i].stubEntry); - scriptMICs[i].u.name.typeConst = mics[i].u.name.typeConst; - scriptMICs[i].u.name.dataConst = mics[i].u.name.dataConst; + for (size_t i = 0; i < mics.length(); i++) { + script->mics[i].kind = mics[i].kind; + script->mics[i].entry = fullCode.locationOf(mics[i].entry); + switch (mics[i].kind) { + case ic::MICInfo::GET: + case ic::MICInfo::SET: + script->mics[i].load = fullCode.locationOf(mics[i].load); + script->mics[i].shape = fullCode.locationOf(mics[i].shape); + script->mics[i].stubCall = stubCode.locationOf(mics[i].call); + script->mics[i].stubEntry = stubCode.locationOf(mics[i].stubEntry); + script->mics[i].u.name.typeConst = mics[i].u.name.typeConst; + script->mics[i].u.name.dataConst = mics[i].u.name.dataConst; #if defined JS_PUNBOX64 - scriptMICs[i].patchValueOffset = mics[i].patchValueOffset; + script->mics[i].patchValueOffset = mics[i].patchValueOffset; #endif - break; - case ic::MICInfo::TRACER: { - uint32 offs = uint32(mics[i].jumpTarget - script->code); - JS_ASSERT(jumpMap[offs].isValid()); - scriptMICs[i].traceHint = fullCode.locationOf(mics[i].traceHint); - scriptMICs[i].load = fullCode.locationOf(jumpMap[offs]); - scriptMICs[i].u.hints.hasSlowTraceHintOne = mics[i].slowTraceHintOne.isSet(); - if (mics[i].slowTraceHintOne.isSet()) - scriptMICs[i].slowTraceHintOne = stubCode.locationOf(mics[i].slowTraceHintOne.get()); - scriptMICs[i].u.hints.hasSlowTraceHintTwo = mics[i].slowTraceHintTwo.isSet(); - if (mics[i].slowTraceHintTwo.isSet()) - scriptMICs[i].slowTraceHintTwo = stubCode.locationOf(mics[i].slowTraceHintTwo.get()); - break; - } - default: - JS_NOT_REACHED("Bad MIC kind"); - } - stubCode.patch(mics[i].addrLabel, &scriptMICs[i]); + break; + case ic::MICInfo::TRACER: { + uint32 offs = uint32(mics[i].jumpTarget - script->code); + JS_ASSERT(jumpMap[offs].isValid()); + script->mics[i].traceHint = fullCode.locationOf(mics[i].traceHint); + script->mics[i].load = fullCode.locationOf(jumpMap[offs]); + script->mics[i].u.hints.hasSlowTraceHintOne = mics[i].slowTraceHintOne.isSet(); + if (mics[i].slowTraceHintOne.isSet()) + script->mics[i].slowTraceHintOne = stubCode.locationOf(mics[i].slowTraceHintOne.get()); + script->mics[i].u.hints.hasSlowTraceHintTwo = mics[i].slowTraceHintTwo.isSet(); + if (mics[i].slowTraceHintTwo.isSet()) + script->mics[i].slowTraceHintTwo = stubCode.locationOf(mics[i].slowTraceHintTwo.get()); + break; + } + default: + JS_NOT_REACHED("Bad MIC kind"); } } - jit->nCallICs = callICs.length(); + script->jit->nCallICs = callICs.length(); if (callICs.length()) { - jit->callICs = (ic::CallICInfo *)cursor; + script->callICs = (ic::CallICInfo *)cursor; cursor += sizeof(ic::CallICInfo) * callICs.length(); } else { - jit->callICs = NULL; + script->callICs = NULL; } - if (ic::CallICInfo *cics = jit->callICs) { - for (size_t i = 0; i < callICs.length(); i++) { - cics[i].reset(); - cics[i].funGuard = fullCode.locationOf(callICs[i].funGuard); - cics[i].funJump = fullCode.locationOf(callICs[i].funJump); - cics[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); + for (size_t i = 0; i < callICs.length(); i++) { + script->callICs[i].reset(); + script->callICs[i].funGuard = fullCode.locationOf(callICs[i].funGuard); + script->callICs[i].funJump = fullCode.locationOf(callICs[i].funJump); + script->callICs[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); - /* Compute the hot call offset. */ - uint32 offset = fullCode.locationOf(callICs[i].hotJump) - - fullCode.locationOf(callICs[i].funGuard); - cics[i].hotJumpOffset = offset; - JS_ASSERT(cics[i].hotJumpOffset == offset); + /* Compute the hot call offset. */ + uint32 offset = fullCode.locationOf(callICs[i].hotJump) - + fullCode.locationOf(callICs[i].funGuard); + script->callICs[i].hotJumpOffset = offset; + JS_ASSERT(script->callICs[i].hotJumpOffset == offset); - /* Compute the join point offset. */ - offset = fullCode.locationOf(callICs[i].joinPoint) - - fullCode.locationOf(callICs[i].funGuard); - cics[i].joinPointOffset = offset; - JS_ASSERT(cics[i].joinPointOffset == offset); - - /* Compute the OOL call offset. */ - offset = stubCode.locationOf(callICs[i].oolCall) - - stubCode.locationOf(callICs[i].slowPathStart); - cics[i].oolCallOffset = offset; - JS_ASSERT(cics[i].oolCallOffset == offset); + /* Compute the join point offset. */ + offset = fullCode.locationOf(callICs[i].joinPoint) - + fullCode.locationOf(callICs[i].funGuard); + script->callICs[i].joinPointOffset = offset; + JS_ASSERT(script->callICs[i].joinPointOffset == offset); + + /* Compute the OOL call offset. */ + offset = stubCode.locationOf(callICs[i].oolCall) - + stubCode.locationOf(callICs[i].slowPathStart); + script->callICs[i].oolCallOffset = offset; + JS_ASSERT(script->callICs[i].oolCallOffset == offset); - /* Compute the OOL jump offset. */ - offset = stubCode.locationOf(callICs[i].oolJump) - - stubCode.locationOf(callICs[i].slowPathStart); - cics[i].oolJumpOffset = offset; - JS_ASSERT(cics[i].oolJumpOffset == offset); + /* Compute the OOL jump offset. */ + offset = stubCode.locationOf(callICs[i].oolJump) - + stubCode.locationOf(callICs[i].slowPathStart); + script->callICs[i].oolJumpOffset = offset; + JS_ASSERT(script->callICs[i].oolJumpOffset == offset); - /* Compute the slow join point offset. */ - offset = stubCode.locationOf(callICs[i].slowJoinPoint) - - stubCode.locationOf(callICs[i].slowPathStart); - cics[i].slowJoinOffset = offset; - JS_ASSERT(cics[i].slowJoinOffset == offset); + /* Compute the slow join point offset. */ + offset = stubCode.locationOf(callICs[i].slowJoinPoint) - + stubCode.locationOf(callICs[i].slowPathStart); + script->callICs[i].slowJoinOffset = offset; + JS_ASSERT(script->callICs[i].slowJoinOffset == offset); - /* Compute the join point offset for continuing on the hot path. */ - offset = stubCode.locationOf(callICs[i].hotPathLabel) - - stubCode.locationOf(callICs[i].funGuard); - cics[i].hotPathOffset = offset; - JS_ASSERT(cics[i].hotPathOffset == offset); + /* Compute the join point offset for continuing on the hot path. */ + offset = stubCode.locationOf(callICs[i].hotPathLabel) - + stubCode.locationOf(callICs[i].funGuard); + script->callICs[i].hotPathOffset = offset; + JS_ASSERT(script->callICs[i].hotPathOffset == offset); - cics[i].pc = callICs[i].pc; - cics[i].argc = callICs[i].argc; - cics[i].funObjReg = callICs[i].funObjReg; - cics[i].funPtrReg = callICs[i].funPtrReg; - cics[i].frameDepth = callICs[i].frameDepth; - stubCode.patch(callICs[i].addrLabel1, &cics[i]); - stubCode.patch(callICs[i].addrLabel2, &cics[i]); - } + script->callICs[i].pc = callICs[i].pc; + script->callICs[i].argc = callICs[i].argc; + script->callICs[i].funObjReg = callICs[i].funObjReg; + script->callICs[i].funPtrReg = callICs[i].funPtrReg; + script->callICs[i].frameDepth = callICs[i].frameDepth; } #endif /* JS_MONOIC */ @@ -503,47 +471,44 @@ mjit::Compiler::finishThisUp(JITScript **jitp) } #if defined JS_POLYIC - jit->nPICs = pics.length(); + script->jit->nPICs = pics.length(); if (pics.length()) { - jit->pics = (ic::PICInfo *)cursor; + script->pics = (ic::PICInfo *)cursor; cursor += sizeof(ic::PICInfo) * pics.length(); } else { - jit->pics = NULL; + script->pics = NULL; } - if (ic::PICInfo *scriptPICs = jit->pics) { - for (size_t i = 0; i < pics.length(); i++) { - pics[i].copySimpleMembersTo(scriptPICs[i]); - scriptPICs[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart); - scriptPICs[i].storeBack = fullCode.locationOf(pics[i].storeBack); - scriptPICs[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart); - scriptPICs[i].callReturn = uint16((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() - - (uint8*)scriptPICs[i].slowPathStart.executableAddress()); - scriptPICs[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) - - masm.distanceOf(pics[i].fastPathStart); - JS_ASSERT(scriptPICs[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) - - masm.distanceOf(pics[i].fastPathStart)); - scriptPICs[i].shapeRegHasBaseShape = true; + for (size_t i = 0; i < pics.length(); i++) { + pics[i].copySimpleMembersTo(script->pics[i]); + script->pics[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart); + script->pics[i].storeBack = fullCode.locationOf(pics[i].storeBack); + script->pics[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart); + script->pics[i].callReturn = uint16((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() - + (uint8*)script->pics[i].slowPathStart.executableAddress()); + script->pics[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) - + masm.distanceOf(pics[i].fastPathStart); + JS_ASSERT(script->pics[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) - + masm.distanceOf(pics[i].fastPathStart)); + script->pics[i].shapeRegHasBaseShape = true; # if defined JS_CPU_X64 - memcpy(&scriptPICs[i].labels, &pics[i].labels, sizeof(PICLabels)); + memcpy(&script->pics[i].labels, &pics[i].labels, sizeof(PICLabels)); # endif - if (pics[i].kind == ic::PICInfo::SET || - pics[i].kind == ic::PICInfo::SETMETHOD) { - scriptPICs[i].u.vr = pics[i].vr; - } else if (pics[i].kind != ic::PICInfo::NAME) { - if (pics[i].hasTypeCheck) { - int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) - - stubcc.masm.distanceOf(pics[i].slowPathStart); - JS_ASSERT(distance <= 0); - scriptPICs[i].u.get.typeCheckOffset = distance; - } + if (pics[i].kind == ic::PICInfo::SET || + pics[i].kind == ic::PICInfo::SETMETHOD) { + script->pics[i].u.vr = pics[i].vr; + } else if (pics[i].kind != ic::PICInfo::NAME) { + if (pics[i].hasTypeCheck) { + int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) - + stubcc.masm.distanceOf(pics[i].slowPathStart); + JS_ASSERT(distance <= 0); + script->pics[i].u.get.typeCheckOffset = distance; } - new (&scriptPICs[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy()); - scriptPICs[i].reset(); - stubCode.patch(pics[i].addrLabel, &scriptPICs[i]); } + new (&script->pics[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy()); + script->pics[i].reset(); } #endif /* JS_POLYIC */ @@ -569,8 +534,10 @@ mjit::Compiler::finishThisUp(JITScript **jitp) JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size()); + script->ncode = (uint8 *)(result + masm.distanceOf(invokeLabel)); + /* Build the table of call sites. */ - jit->nCallSites = callSites.length(); + script->jit->nCallSites = callSites.length(); if (callSites.length()) { CallSite *callSiteList = (CallSite *)cursor; cursor += sizeof(CallSite) * callSites.length(); @@ -583,14 +550,12 @@ mjit::Compiler::finishThisUp(JITScript **jitp) callSiteList[i].pcOffset = callSites[i].pc - script->code; callSiteList[i].id = callSites[i].id; } - jit->callSites = callSiteList; + script->jit->callSites = callSiteList; } else { - jit->callSites = NULL; + script->jit->callSites = NULL; } - JS_ASSERT(size_t(cursor - (uint8*)jit) == totalBytes); - - *jitp = jit; + JS_ASSERT(size_t(cursor - (uint8*)script->jit) == totalBytes); return Compile_Okay; } @@ -1697,11 +1662,6 @@ mjit::Compiler::generateMethod() break; END_CASE(JSOP_GLOBALINC) - BEGIN_CASE(JSOP_BEGIN) - if (isConstructing) - constructThis(); - END_CASE(JSOP_BEGIN) - default: /* Sorry, this opcode isn't implemented yet. */ #ifdef JS_METHODJIT_SPEW @@ -1757,17 +1717,15 @@ mjit::Compiler::findCallSite(const CallSite &callSite) { JS_ASSERT(callSite.pcOffset < script->length); - JITScript *jit = script->getJIT(fp->isConstructing()); - uint8* ilPath = (uint8 *)jit->code.m_code.executableAddress(); - uint8* oolPath = ilPath + masm.size(); - for (uint32 i = 0; i < callSites.length(); i++) { if (callSites[i].pc == script->code + callSite.pcOffset && callSites[i].id == callSite.id) { if (callSites[i].stub) { - return oolPath + stubcc.masm.distanceOf(callSites[i].location); + return (uint8*)script->jit->invoke + masm.size() + + stubcc.masm.distanceOf(callSites[i].location); } - return ilPath + masm.distanceOf(callSites[i].location); + return (uint8*)script->jit->invoke + + stubcc.masm.distanceOf(callSites[i].location); } } @@ -1823,100 +1781,24 @@ mjit::Compiler::emitFinalReturn(Assembler &masm) masm.jump(Registers::ReturnReg); } -// Emits code to load a return value of the frame into the scripted-ABI -// type & data register pair. If the return value is in fp->rval, then |fe| -// is NULL. Otherwise, |fe| contains the return value. -// -// If reading from fp->rval, |undefined| is loaded optimistically, before -// checking if fp->rval is set in the frame flags and loading that instead. -// -// Otherwise, if |masm| is the inline path, it is loaded as efficiently as -// the FrameState can manage. If |masm| is the OOL path, the value is simply -// loaded from its slot in the frame, since the caller has guaranteed it's -// been synced. -// void -mjit::Compiler::loadReturnValue(Assembler *masm, FrameEntry *fe) +mjit::Compiler::loadReturnValue(Assembler &masm) { - RegisterID typeReg = JSReturnReg_Type; - RegisterID dataReg = JSReturnReg_Data; - - if (fe) { - // If using the OOL assembler, the caller signifies that the |fe| is - // synced, but not to rely on its register state. - if (masm != &this->masm) { - if (fe->isConstant()) { - stubcc.masm.loadValueAsComponents(fe->getValue(), typeReg, dataReg); - } else { - Address rval(frame.addressOf(fe)); - if (fe->isTypeKnown()) { - stubcc.masm.loadPayload(rval, dataReg); - stubcc.masm.move(ImmType(fe->getKnownType()), typeReg); - } else { - stubcc.masm.loadValueAsComponents(rval, typeReg, dataReg); - } - } - } else { - frame.loadTo(fe, typeReg, dataReg, Registers::ReturnReg); - } - } else { - // Load a return value from POPV or SETRVAL into the return registers, - // otherwise return undefined. - masm->loadValueAsComponents(UndefinedValue(), typeReg, dataReg); - if (analysis.usesReturnValue()) { - Jump rvalClear = masm->branchTest32(Assembler::Zero, - FrameFlagsAddress(), - Imm32(JSFRAME_HAS_RVAL)); - Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); - masm->loadValueAsComponents(rvalAddress, typeReg, dataReg); - rvalClear.linkTo(masm->label(), masm); - } + /* + * Load a return value from POPV or SETRVAL into the return registers, + * otherwise return undefined. + */ + masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); + if (analysis.usesReturnValue()) { + Jump rvalClear = masm.branchTest32(Assembler::Zero, + FrameFlagsAddress(), Imm32(JSFRAME_HAS_RVAL)); + Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); + masm.loadValueAsComponents(rvalAddress, + JSReturnReg_Type, JSReturnReg_Data); + rvalClear.linkTo(masm.label(), &masm); } } -// This ensures that constructor return values are an object. If a non-object -// is returned, either explicitly or implicitly, the newly created object is -// loaded out of the frame. Otherwise, the explicitly returned object is kept. -// -void -mjit::Compiler::fixPrimitiveReturn(Assembler *masm, FrameEntry *fe) -{ - JS_ASSERT(isConstructing); - - Address thisv(JSFrameReg, JSStackFrame::offsetOfThis(fun)); - - // Easy cases - no return value, or known primitive, so just return thisv. - if (!fe || (fe->isTypeKnown() && fe->getKnownType() != JSVAL_TYPE_OBJECT)) { - masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); - return; - } - - // If the type is known to be an object, just load the return value as normal. - if (fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_OBJECT) { - loadReturnValue(masm, fe); - return; - } - - // There's a return value, and its type is unknown. Test the type and load - // |thisv| if necessary. - loadReturnValue(masm, fe); - Jump j = masm->testObject(Assembler::Equal, JSReturnReg_Type); - masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); - j.linkTo(masm->label(), masm); -} - -// Loads the return value into the scripted ABI register pair, such that JS -// semantics in constructors are preserved. -// -void -mjit::Compiler::emitReturnValue(Assembler *masm, FrameEntry *fe) -{ - if (isConstructing) - fixPrimitiveReturn(masm, fe); - else - loadReturnValue(masm, fe); -} - void mjit::Compiler::emitReturn(FrameEntry *fe) { @@ -1941,7 +1823,8 @@ mjit::Compiler::emitReturn(FrameEntry *fe) stubCall(stubs::PutActivationObjects); if (fe) { - emitReturnValue(&masm, fe); + masm.loadValueAsComponents(frame.addressOf(fe), + JSReturnReg_Type, JSReturnReg_Data); emitFinalReturn(masm); frame.discardFrame(); return; @@ -1956,12 +1839,22 @@ mjit::Compiler::emitReturn(FrameEntry *fe) stubcc.leave(); stubcc.call(stubs::PutActivationObjects); - emitReturnValue(&stubcc.masm, fe); + if (fe) { + stubcc.masm.loadValueAsComponents(frame.addressOf(fe), + JSReturnReg_Type, JSReturnReg_Data); + } else { + loadReturnValue(stubcc.masm); + } + emitFinalReturn(stubcc.masm); } } - emitReturnValue(&masm, fe); + if (fe) + frame.storeTo(fe, JSReturnReg_Data, JSReturnReg_Type, Registers::ReturnReg); + else + loadReturnValue(masm); + emitFinalReturn(masm); frame.discardFrame(); } @@ -2036,6 +1929,18 @@ mjit::Compiler::interruptCheckHelper() #endif } +void +mjit::Compiler::emitPrimitiveTestForNew(uint32 argc) +{ + Jump primitive = masm.testPrimitive(Assembler::Equal, JSReturnReg_Type); + stubcc.linkExitDirect(primitive, stubcc.masm.label()); + FrameEntry *fe = frame.peek(-int(argc + 1)); + Address thisv(frame.addressOf(fe)); + stubcc.masm.loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); + Jump primFix = stubcc.masm.jump(); + stubcc.crossJump(primFix, masm.label()); +} + void mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) { @@ -2068,6 +1973,9 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) callPatch.joinPoint = masm.label(); masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); + if (callingNew) + emitPrimitiveTestForNew(argc); + frame.popn(argc + 2); frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); @@ -2085,10 +1993,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) /* Check for interrupts on function call */ interruptCheckHelper(); - // |thisv| does not need to be synced for constructing. - if (callingNew) - frame.discardFe(frame.peek(-int(argc + 1))); - FrameEntry *fe = frame.peek(-int(argc + 2)); /* Currently, we don't support constant functions. */ @@ -2098,7 +2002,12 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) } #ifdef JS_MONOIC + FrameEntry *thisvFe = frame.peek(-int(argc + 1)); + Address thisvAddr = frame.addressOf(thisvFe); + CallGenInfo callIC(argc); + uint32 callICIndex = callICs.length(); + CallPatchInfo callPatch; /* @@ -2135,6 +2044,16 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) if (typeReg.isSet()) notObjectJump = masm.testObject(Assembler::NotEqual, typeReg.reg()); + /* + * Ensure that dataReg stays in a register which won't be clobbered + * by the intervening call to NewObject. + */ + if (callingNew && !(Registers::maskReg(dataReg) & Registers::SavedRegs)) { + RegisterID reg = Registers(Registers::SavedRegs).takeAnyReg(); + masm.move(dataReg, reg); + dataReg = reg; + } + tempRegs.takeReg(dataReg); RegisterID t0 = tempRegs.takeAnyReg(); RegisterID t1 = tempRegs.takeAnyReg(); @@ -2164,6 +2083,28 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) stubcc.masm.and32(Imm32(JSFUN_KINDMASK), t1); Jump isNative = stubcc.masm.branch32(Assembler::Below, t1, Imm32(JSFUN_INTERPRETED)); + /* Create the new object. This requires some fiddling to save the two values. */ + if (callingNew) { + void *pfun = stubcc.masm.getCallTarget(JS_FUNC_TO_DATA_PTR(void *, stubs::NewObject)); + stubcc.masm.storePtr(ImmPtr(PC), + FrameAddress(offsetof(VMFrame, regs) + offsetof(JSFrameRegs, pc))); + stubcc.masm.fixScriptStack(frame.frameDepth()); + stubcc.masm.setupVMFrame(); +#if defined(JS_CPU_X86) + /* Need to stay 16-byte aligned on x86. */ + stubcc.masm.subPtr(Imm32(8), JSC::MacroAssembler::stackPointerRegister); +#endif + stubcc.masm.push(dataReg); + stubcc.masm.push(t0); + stubcc.masm.move(Imm32(argc), Registers::ArgReg1); + stubcc.masm.wrapCall(pfun); + stubcc.masm.pop(t0); + stubcc.masm.pop(dataReg); +#if defined(JS_CPU_X86) + stubcc.masm.addPtr(Imm32(8), JSC::MacroAssembler::stackPointerRegister); +#endif + } + /* * No-op jump that gets re-patched. This is so ArgReg1 won't be * clobbered, with the added bonus that the generated stub doesn't @@ -2174,7 +2115,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) callIC.oolJump = toPatch; /* At this point the function is definitely scripted. Call the link routine. */ - callIC.addrLabel1 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); + stubcc.masm.move(Imm32(callICIndex), Registers::ArgReg1); callIC.oolCall = stubcc.call(callingNew ? ic::New : ic::Call); callIC.funObjReg = dataReg; @@ -2204,7 +2145,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) notFunction.linkTo(stubcc.masm.label(), &stubcc.masm); isNative.linkTo(stubcc.masm.label(), &stubcc.masm); - callIC.addrLabel2 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); + stubcc.masm.move(Imm32(callICIndex), Registers::ArgReg1); stubcc.call(callingNew ? ic::NativeNew : ic::NativeCall); rejoin2 = stubcc.masm.jump(); @@ -2216,6 +2157,13 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) */ callIC.hotPathLabel = masm.label(); + /* If calling |new|, make sure to allocate a new object. */ + if (callingNew) { + prepareStubCall(Uses(argc + 2)); + masm.move(Imm32(argc), Registers::ArgReg1); + stubCall(stubs::NewObject); + } + uint32 flags = 0; if (callingNew) flags |= JSFRAME_CONSTRUCTING; @@ -2227,6 +2175,13 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) callIC.joinPoint = callPatch.joinPoint = masm.label(); masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); + /* + * Functions invoked with |new| can return primitive values. + * Just deal with this here. + */ + if (callingNew) + emitPrimitiveTestForNew(argc); + frame.popn(argc + 2); frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); @@ -2420,19 +2375,6 @@ mjit::Compiler::jsop_length() } #if defined JS_POLYIC - -void -mjit::Compiler::passPICAddress(PICGenInfo &pic) -{ - pic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); -} - -void -mjit::Compiler::passMICAddress(MICGenInfo &mic) -{ - mic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); -} - void mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) { @@ -2503,7 +2445,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - passPICAddress(pic); + stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); pic.callReturn = stubcc.call(ic::GetProp); /* Load dslots. */ @@ -2604,7 +2546,7 @@ mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID obj pic.slowPathStart = stubcc.linkExit(jmpShapeGuard, Uses(2)); stubcc.leave(); - passPICAddress(pic); + stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); pic.callReturn = stubcc.call(ic::GetElem); /* Load dslots. */ @@ -2730,7 +2672,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom) /* Slow path. */ stubcc.leave(); - passPICAddress(pic); + stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); pic.callReturn = stubcc.call(ic::CallProp); /* Adjust the frame. None of this will generate code. */ @@ -2894,7 +2836,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - passPICAddress(pic); + stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); pic.callReturn = stubcc.call(ic::CallProp); /* Load dslots. */ @@ -3016,12 +2958,13 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) * the normal SETNAME property cache logic. */ JSOp op = JSOp(*PC); - stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); if (op == JSOP_SETNAME || op == JSOP_SETPROP || op == JSOP_SETGNAME || op == JSOP_SETMETHOD) { + stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); stubcc.call(STRICT_VARIANT(stubs::SetName)); } else { - stubcc.call(STRICT_VARIANT(stubs::SetPropNoCache)); + stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + stubcc.call(ic::SetPropDumb); } typeCheck = stubcc.masm.jump(); @@ -3061,7 +3004,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(j, Uses(2)); stubcc.leave(); - passPICAddress(pic); + stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); pic.callReturn = stubcc.call(ic::SetProp); } @@ -3141,7 +3084,7 @@ mjit::Compiler::jsop_name(JSAtom *atom) { pic.slowPathStart = stubcc.linkExit(j, Uses(0)); stubcc.leave(); - passPICAddress(pic); + stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); pic.callReturn = stubcc.call(ic::Name); } @@ -3184,7 +3127,7 @@ mjit::Compiler::jsop_xname(JSAtom *atom) { pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - passPICAddress(pic); + stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); pic.callReturn = stubcc.call(ic::XName); } @@ -3226,7 +3169,7 @@ mjit::Compiler::jsop_bindname(uint32 index) { pic.slowPathStart = stubcc.linkExit(j, Uses(0)); stubcc.leave(); - passPICAddress(pic); + stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); pic.callReturn = stubcc.call(ic::BindName); } @@ -3933,7 +3876,7 @@ mjit::Compiler::jsop_getgname(uint32 index) stubcc.linkExit(shapeGuard, Uses(0)); stubcc.leave(); - passMICAddress(mic); + stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); mic.stubEntry = stubcc.masm.label(); mic.call = stubcc.call(ic::GetGlobalName); @@ -4032,7 +3975,7 @@ mjit::Compiler::jsop_setgname(uint32 index) stubcc.linkExit(shapeGuard, Uses(2)); stubcc.leave(); - passMICAddress(mic); + stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); mic.stubEntry = stubcc.masm.label(); mic.call = stubcc.call(ic::SetGlobalName); @@ -4283,7 +4226,7 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne, Jump *sl if (slowTwo) slowTwo->linkTo(traceStart, &stubcc.masm); # if JS_MONOIC - passMICAddress(mic); + stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); # endif /* Save and restore compiler-tracked PC, so cx->regs is right in InvokeTracer. */ @@ -4353,45 +4296,3 @@ mjit::Compiler::leaveBlock() frame.leaveBlock(n); } -// Creates the new object expected for constructors, and places it in |thisv|. -// It is broken down into the following operations: -// CALLEE -// GETPROP "prototype" -// IFPRIMTOP: -// NULL -// call js_CreateThisFromFunctionWithProto(...) -// -void -mjit::Compiler::constructThis() -{ - JS_ASSERT(isConstructing); - - // Load the callee. - Address callee(JSFrameReg, JSStackFrame::offsetOfCallee(fun)); - RegisterID calleeReg = frame.allocReg(); - masm.loadPayload(callee, calleeReg); - frame.pushTypedPayload(JSVAL_TYPE_OBJECT, calleeReg); - - // Get callee.prototype. - jsop_getprop(cx->runtime->atomState.classPrototypeAtom); - - // Reach into the proto Value and grab a register for its data. - FrameEntry *protoFe = frame.peek(-1); - RegisterID protoReg = frame.ownRegForData(protoFe); - - // Now, get the type. If it's not an object, set protoReg to NULL. - Jump isNotObject = frame.testObject(Assembler::NotEqual, protoFe); - stubcc.linkExitDirect(isNotObject, stubcc.masm.label()); - stubcc.masm.move(ImmPtr(NULL), protoReg); - stubcc.crossJump(stubcc.masm.jump(), masm.label()); - - // Done with the protoFe. - frame.pop(); - - prepareStubCall(Uses(0)); - if (protoReg != Registers::ArgReg1) - masm.move(protoReg, Registers::ArgReg1); - stubCall(stubs::CreateThis); - frame.freeReg(protoReg); -} - diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 4cab7bca831b..be20c21df323 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -72,7 +72,6 @@ class Compiler : public BaseCompiler Label entry; Label stubEntry; DataLabel32 shape; - DataLabelPtr addrLabel; #if defined JS_PUNBOX64 uint32 patchValueOffset; #endif @@ -115,8 +114,6 @@ class Compiler : public BaseCompiler Label slowJoinPoint; Label slowPathStart; Label hotPathLabel; - DataLabelPtr addrLabel1; - DataLabelPtr addrLabel2; Jump oolJump; RegisterID funObjReg; RegisterID funPtrReg; @@ -146,7 +143,6 @@ class Compiler : public BaseCompiler Label storeBack; Label typeCheck; Label slowPathStart; - DataLabelPtr addrLabel; RegisterID shapeReg; RegisterID objReg; RegisterID idReg; @@ -200,12 +196,10 @@ class Compiler : public BaseCompiler bool ool; }; - JSStackFrame *fp; JSScript *script; JSObject *scopeChain; JSObject *globalObj; JSFunction *fun; - bool isConstructing; BytecodeAnalyzer analysis; Label *jumpMap; jsbytecode *PC; @@ -217,7 +211,7 @@ class Compiler : public BaseCompiler js::Vector callICs; #endif #if defined JS_POLYIC - js::Vector pics; + js::Vector pics; #endif js::Vector callPatches; js::Vector callSites; @@ -232,10 +226,10 @@ class Compiler : public BaseCompiler // follows interpreter usage in JSOP_LENGTH. enum { LengthAtomIndex = uint32(-2) }; - Compiler(JSContext *cx, JSStackFrame *fp); + Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain); ~Compiler(); - CompileStatus compile(); + CompileStatus Compile(); jsbytecode *getPC() { return PC; } Label getLabel() { return masm.label(); } @@ -244,11 +238,10 @@ class Compiler : public BaseCompiler void *findCallSite(const CallSite &callSite); private: - CompileStatus performCompilation(JITScript **jitp); CompileStatus generatePrologue(); CompileStatus generateMethod(); CompileStatus generateEpilogue(); - CompileStatus finishThisUp(JITScript **jitp); + CompileStatus finishThisUp(); /* Non-emitting helpers. */ uint32 fullAtomIndex(jsbytecode *pc); @@ -264,9 +257,6 @@ class Compiler : public BaseCompiler void iterMore(); void iterEnd(); MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg); - void passPICAddress(PICGenInfo &pic); - void passMICAddress(MICGenInfo &mic); - void constructThis(); /* Opcode handlers. */ void jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne = NULL, Jump *slowTwo = NULL); @@ -278,13 +268,12 @@ class Compiler : public BaseCompiler void jsop_this(); void emitReturn(FrameEntry *fe); void emitFinalReturn(Assembler &masm); - void loadReturnValue(Assembler *masm, FrameEntry *fe); - void emitReturnValue(Assembler *masm, FrameEntry *fe); + void loadReturnValue(Assembler &masm); void dispatchCall(VoidPtrStubUInt32 stub, uint32 argc); void interruptCheckHelper(); void emitUncachedCall(uint32 argc, bool callingNew); + void emitPrimitiveTestForNew(uint32 argc); void inlineCallHelper(uint32 argc, bool callingNew); - void fixPrimitiveReturn(Assembler *masm, FrameEntry *fe); void jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index); void jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index); void jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index); diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 838f638fc510..4b26b8929463 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -327,7 +327,7 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) #endif } -void FrameState::loadTo(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg) +void FrameState::storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg) { JS_ASSERT(dataReg != typeReg && dataReg != tempReg && typeReg != tempReg); @@ -347,15 +347,6 @@ void FrameState::loadTo(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, return; } -#ifdef JS_PUNBOX64 - // If the value is synced, and requires at least one load, we can do - // better on x64. - if (fe->type.inMemory() && fe->data.inMemory()) { - masm.loadValueAsComponents(addressOf(fe), typeReg, dataReg); - return; - } -#endif - RegisterID data = tempRegForData(fe); RegisterID type = tempRegForType(fe); if (data == typeReg && type == dataReg) { @@ -902,14 +893,6 @@ FrameState::ownRegForData(FrameEntry *fe) return reg; } -void -FrameState::discardFe(FrameEntry *fe) -{ - forgetEntry(fe); - fe->type.setMemory(); - fe->data.setMemory(); -} - void FrameState::pushCopyOf(uint32 index) { diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index bca9568d3b7d..d66415ba6ee5 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -590,7 +590,7 @@ class FrameState * Fully stores a FrameEntry into two arbitrary registers. tempReg may be * used as a temporary. */ - void loadTo(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg); + void storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg); /* * Stores the top stack slot back to a slot. @@ -654,11 +654,6 @@ class FrameState */ inline void forgetType(FrameEntry *fe); - /* - * Discards a FrameEntry, tricking the FS into thinking it's synced. - */ - void discardFe(FrameEntry *fe); - /* * Helper function. Tests if a slot's type is null. Condition should * be Equal or NotEqual. diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index a9af7e6abda1..a7e3eadf9b38 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -219,6 +219,29 @@ InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame) return ok; } +JSBool JS_FASTCALL +stubs::NewObject(VMFrame &f, uint32 argc) +{ + JSContext *cx = f.cx; + Value *vp = f.regs.sp - (argc + 2); + + JSObject *funobj = &vp[0].toObject(); + JS_ASSERT(funobj->isFunction()); + + jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); + if (!funobj->getProperty(cx, id, &vp[1])) + THROWV(JS_FALSE); + + JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL; + JSObject *obj = NewNonFunction(cx, &js_ObjectClass, proto, funobj->getParent()); + if (!obj) + THROWV(JS_FALSE); + + vp[1].setObject(*obj); + + return JS_TRUE; +} + void JS_FASTCALL stubs::SlowCall(VMFrame &f, uint32 argc) { @@ -338,8 +361,8 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) fp->initCallFrameEarlyPrologue(fun, fp->nativeReturnAddress()); /* Empty script does nothing. */ - bool callingNew = fp->isConstructing(); if (script->isEmpty()) { + bool callingNew = fp->isConstructing(); RemovePartialFrame(cx, fp); Value *vp = f.regs.sp - (nactual + 2); if (callingNew) @@ -366,9 +389,9 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) if (fun->isHeavyweight() && !js_GetCallObject(cx, fp)) THROWV(NULL); - CompileStatus status = CanMethodJIT(cx, script, fp); + CompileStatus status = CanMethodJIT(cx, script, fun, &fp->scopeChain()); if (status == Compile_Okay) - return script->getJIT(callingNew)->invokeEntry; + return script->jit->invoke; /* Function did not compile... interpret it. */ JSBool ok = Interpret(cx, fp); @@ -418,8 +441,8 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) } /* Try to compile if not already compiled. */ - if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) { - if (mjit::TryCompile(cx, newfp) == Compile_Error) { + if (!newscript->ncode) { + if (mjit::TryCompile(cx, newscript, newfp->fun(), &newfp->scopeChain()) == Compile_Error) { /* A runtime exception was thrown, get out. */ InlineReturn(f, JS_FALSE); return false; @@ -427,8 +450,9 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) } /* If newscript was successfully compiled, run it. */ - if (JITScript *jit = newscript->getJIT(newfp->isConstructing())) { - *pret = jit->invokeEntry; + JS_ASSERT(newscript->ncode); + if (newscript->ncode != JS_UNJITTABLE_METHOD) { + *pret = newscript->jit->invoke; return true; } @@ -460,6 +484,9 @@ stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr) if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted() && !ucr->fun->script()->isEmpty()) { + if (!stubs::NewObject(f, argc)) + return; + ucr->callee = &vp->toObject(); if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc)) THROW(); @@ -585,10 +612,7 @@ js_InternalThrow(VMFrame &f) if (!pc) return NULL; - JSStackFrame *fp = cx->fp(); - JSScript *script = fp->script(); - JITScript *jit = script->getJIT(fp->isConstructing()); - return jit->nmap[pc - script->code]; + return cx->fp()->script()->pcToNative(pc); } void JS_FASTCALL @@ -599,18 +623,6 @@ stubs::GetCallObject(VMFrame &f) THROW(); } -void JS_FASTCALL -stubs::CreateThis(VMFrame &f, JSObject *proto) -{ - JSContext *cx = f.cx; - JSStackFrame *fp = f.fp(); - JSObject *callee = &fp->callee(); - JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto); - if (!obj) - THROW(); - fp->formalArgs()[-1].setObject(*obj); -} - static inline void AdvanceReturnPC(JSContext *cx) { @@ -684,12 +696,11 @@ AtSafePoint(JSContext *cx) return false; JSScript *script = fp->script(); - JITScript *jit = script->getJIT(fp->isConstructing()); - if (!jit->nmap) + if (!script->nmap) return false; JS_ASSERT(cx->regs->pc >= script->code && cx->regs->pc < script->code + script->length); - return !!jit->nmap[cx->regs->pc - script->code]; + return !!script->nmap[cx->regs->pc - script->code]; } static inline JSBool @@ -698,11 +709,8 @@ PartialInterpret(VMFrame &f) JSContext *cx = f.cx; JSStackFrame *fp = cx->fp(); -#ifdef DEBUG - JITScript *jit = fp->script()->getJIT(fp->isConstructing()); - JS_ASSERT(fp->hasImacropc() || !jit->nmap || - !jit->nmap[cx->regs->pc - fp->script()->code]); -#endif + JS_ASSERT(fp->hasImacropc() || !fp->script()->nmap || + !fp->script()->nmap[cx->regs->pc - fp->script()->code]); JSBool ok = JS_TRUE; ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT); @@ -732,8 +740,7 @@ FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame) if (AtSafePoint(cx)) { JSScript *script = fp->script(); - JITScript *jit = script->getJIT(fp->isConstructing()); - if (!JaegerShotAtSafePoint(cx, jit->nmap[cx->regs->pc - script->code])) { + if (!JaegerShotAtSafePoint(cx, script->nmap[cx->regs->pc - script->code])) { if (!HandleErrorInExcessFrames(f, entryFrame)) return false; @@ -887,10 +894,9 @@ RunTracer(VMFrame &f) /* Step 2. If entryFrame is at a safe point, just leave. */ if (AtSafePoint(cx)) { - JITScript *jit = entryFrame->script()->getJIT(entryFrame->isConstructing()); uint32 offs = uint32(cx->regs->pc - entryFrame->script()->code); - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(entryFrame->script()->nmap[offs]); + return entryFrame->script()->nmap[offs]; } /* Step 3. If entryFrame is at a RETURN, then leave slightly differently. */ @@ -924,10 +930,14 @@ RunTracer(VMFrame &f) #if defined JS_TRACER # if defined JS_MONOIC void *JS_FASTCALL -stubs::InvokeTracer(VMFrame &f, ic::MICInfo *mic) +stubs::InvokeTracer(VMFrame &f, uint32 index) { - JS_ASSERT(mic->kind == ic::MICInfo::TRACER); - return RunTracer(f, *mic); + JSScript *script = f.fp()->script(); + ic::MICInfo &mic = script->mics[index]; + + JS_ASSERT(mic.kind == ic::MICInfo::TRACER); + + return RunTracer(f, mic); } # else diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index c457f6f995db..191c9c549604 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -764,9 +764,9 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) JSBool mjit::JaegerShot(JSContext *cx) { - JSStackFrame *fp = cx->fp(); - JSScript *script = fp->script(); - JITScript *jit = script->getJIT(fp->isConstructing()); + JSScript *script = cx->fp()->script(); + + JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD); #ifdef JS_TRACER if (TRACE_RECORDER(cx)) @@ -775,7 +775,7 @@ mjit::JaegerShot(JSContext *cx) JS_ASSERT(cx->regs->pc == script->code); - return EnterMethodJIT(cx, cx->fp(), jit->invokeEntry); + return EnterMethodJIT(cx, cx->fp(), script->jit->invoke); } JSBool @@ -795,47 +795,37 @@ static inline void Destroy(T &t) } void -mjit::JITScript::release() +mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) { + if (script->jit) { #if defined DEBUG && (defined JS_CPU_X86 || defined JS_CPU_X64) - void *addr = code.m_code.executableAddress(); - memset(addr, 0xcc, code.m_size); + memset(script->jit->invoke, 0xcc, script->jit->inlineLength + + script->jit->outOfLineLength); #endif + script->jit->execPool->release(); + script->jit->execPool = NULL; - code.m_executablePool->release(); + // Releasing the execPool takes care of releasing the code. + script->ncode = NULL; #if defined JS_POLYIC - for (uint32 i = 0; i < nPICs; i++) { - pics[i].releasePools(); - Destroy(pics[i].execPools); - } + for (uint32 i = 0; i < script->jit->nPICs; i++) { + script->pics[i].releasePools(); + Destroy(script->pics[i].execPools); + } #endif #if defined JS_MONOIC - for (uint32 i = 0; i < nCallICs; i++) - callICs[i].releasePools(); + for (uint32 i = 0; i < script->jit->nCallICs; i++) + script->callICs[i].releasePools(); #endif -} -void -mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) -{ - // NB: The recompiler may call ReleaseScriptCode, in which case it - // will get called again when the script is destroyed, so we - // must protect against calling ReleaseScriptCode twice. + cx->free(script->jit); - if (script->jitNormal) { - script->jitNormal->release(); - script->jitArityCheckNormal = NULL; - cx->free(script->jitNormal); - script->jitNormal = NULL; - } - - if (script->jitCtor) { - script->jitCtor->release(); - script->jitArityCheckCtor = NULL; - cx->free(script->jitCtor); - script->jitCtor = NULL; + // The recompiler may call ReleaseScriptCode, in which case it + // will get called again when the script is destroyed, so we + // must protect against calling ReleaseScriptCode twice. + script->jit = NULL; } } diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 850b6a3dfa74..68d6dd0f0c31 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -139,18 +139,6 @@ struct VMFrame extern "C" void JaegerStubVeneer(void); #endif -namespace mjit { -namespace ic { -# if defined JS_POLYIC - struct PICInfo; -# endif -# if defined JS_MONOIC - struct MICInfo; - struct CallICInfo; -# endif -} -} - typedef void (JS_FASTCALL *VoidStub)(VMFrame &); typedef void (JS_FASTCALL *VoidVpStub)(VMFrame &, Value *); typedef void (JS_FASTCALL *VoidStubUInt32)(VMFrame &, uint32); @@ -170,47 +158,26 @@ typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32); typedef void (JS_FASTCALL *VoidStubJSObj)(VMFrame &, JSObject *); typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *); typedef JSBool (JS_FASTCALL *BoolStubUInt32)(VMFrame &f, uint32); -typedef void (JS_FASTCALL *VoidStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); -typedef void * (JS_FASTCALL *VoidPtrStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); -typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); -typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); -typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); + +#define JS_UNJITTABLE_METHOD (reinterpret_cast(1)) namespace mjit { -struct CallSite; - struct JITScript { - typedef JSC::MacroAssemblerCodeRef CodeRef; - CodeRef code; /* pool & code addresses */ - + JSC::ExecutablePool *execPool; /* pool that contains |ncode|; script owns the pool */ + uint32 inlineLength; /* length of inline JIT'd code */ + uint32 outOfLineLength; /* length of out of line JIT'd code */ js::mjit::CallSite *callSites; uint32 nCallSites; - void **nmap; /* scripted pc to native code map. */ #ifdef JS_MONOIC - ic::MICInfo *mics; /* MICs in this script. */ - uint32 nMICs; /* number of MonoICs */ - ic::CallICInfo *callICs; /* CallICs in this script. */ - uint32 nCallICs; /* number of call ICs */ + uint32 nMICs; /* number of MonoICs */ + uint32 nCallICs; /* number of call ICs */ #endif #ifdef JS_POLYIC - ic::PICInfo *pics; /* PICs in this script */ - uint32 nPICs; /* number of PolyICs */ + uint32 nPICs; /* number of PolyICs */ #endif - void *invokeEntry; /* invoke address */ - void *fastEntry; /* cached entry, fastest */ - void *arityCheckEntry; /* arity check address */ - - bool isValidCode(void *ptr) { - char *jitcode = (char *)code.m_code.executableAddress(); - char *jcheck = (char *)ptr; - return jcheck >= jitcode && jcheck < jitcode + code.m_size; - } - - void sweepCallICs(); - void purgeMICs(); - void purgePICs(); - void release(); + void *invoke; /* invoke address */ + void *arityCheck; /* arity check address */ }; /* Execute a method that has been JIT compiled. */ @@ -230,21 +197,18 @@ void JS_FASTCALL ProfileStubCall(VMFrame &f); CompileStatus JS_NEVER_INLINE -TryCompile(JSContext *cx, JSStackFrame *fp); +TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain); void ReleaseScriptCode(JSContext *cx, JSScript *script); static inline CompileStatus -CanMethodJIT(JSContext *cx, JSScript *script, JSStackFrame *fp) +CanMethodJIT(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) { - if (!cx->methodJitEnabled) + if (!cx->methodJitEnabled || script->ncode == JS_UNJITTABLE_METHOD) return Compile_Abort; - JITScriptStatus status = script->getJITStatus(fp->isConstructing()); - if (status == JITScript_Invalid) - return Compile_Abort; - if (status == JITScript_None) - return TryCompile(cx, fp); + if (script->ncode == NULL) + return TryCompile(cx, script, fun, scopeChain); return Compile_Okay; } @@ -259,25 +223,6 @@ struct CallSite } /* namespace js */ -inline void ** -JSScript::maybeNativeMap(bool constructing) -{ - js::mjit::JITScript *jit = constructing ? jitCtor : jitNormal; - if (!jit) - return NULL; - return jit->nmap; -} - -inline bool -JSScript::hasNativeCodeForPC(bool constructing, jsbytecode *pc) -{ - js::mjit::JITScript *jit = getJIT(constructing); - if (!jit) - return false; - JS_ASSERT(pc >= code && pc < code + length); - return !!jit->nmap[pc - code]; -} - #ifdef _MSC_VER extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame); #else diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index eada7e730948..ce12f4a9f3d2 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -70,21 +70,22 @@ typedef JSC::MacroAssembler::Call Call; #if defined JS_MONOIC static void -PatchGetFallback(VMFrame &f, ic::MICInfo *ic) +PatchGetFallback(VMFrame &f, ic::MICInfo &mic) { - JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64); + JSC::RepatchBuffer repatch(mic.stubEntry.executableAddress(), 64); JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName)); - repatch.relink(ic->stubCall, fptr); + repatch.relink(mic.stubCall, fptr); } void JS_FASTCALL -ic::GetGlobalName(VMFrame &f, ic::MICInfo *ic) +ic::GetGlobalName(VMFrame &f, uint32 index) { JSObject *obj = f.fp()->scopeChain().getGlobal(); + ic::MICInfo &mic = f.fp()->script()->mics[index]; JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc)); jsid id = ATOM_TO_JSID(atom); - JS_ASSERT(ic->kind == ic::MICInfo::GET); + JS_ASSERT(mic.kind == ic::MICInfo::GET); JS_LOCK_OBJ(f.cx, obj); const Shape *shape = obj->nativeLookup(id); @@ -94,33 +95,33 @@ ic::GetGlobalName(VMFrame &f, ic::MICInfo *ic) { JS_UNLOCK_OBJ(f.cx, obj); if (shape) - PatchGetFallback(f, ic); + PatchGetFallback(f, mic); stubs::GetGlobalName(f); return; } uint32 slot = shape->slot; JS_UNLOCK_OBJ(f.cx, obj); - ic->u.name.touched = true; + mic.u.name.touched = true; /* Patch shape guard. */ - JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50); - repatch.repatch(ic->shape, obj->shape()); + JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50); + repatch.repatch(mic.shape, obj->shape()); /* Patch loads. */ JS_ASSERT(slot >= JS_INITIAL_NSLOTS); slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); - JSC::RepatchBuffer loads(ic->load.executableAddress(), 32, false); + JSC::RepatchBuffer loads(mic.load.executableAddress(), 32, false); #if defined JS_CPU_X86 - loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot); - loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4); + loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot); + loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4); #elif defined JS_CPU_ARM - // ic->load actually points to the LDR instruction which fetches the offset, but 'repatch' + // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch' // knows how to dereference it to find the integer value. - loads.repatch(ic->load.dataLabel32AtOffset(0), slot); + loads.repatch(mic.load.dataLabel32AtOffset(0), slot); #elif defined JS_PUNBOX64 - loads.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot); + loads.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); #endif /* Do load anyway... this time. */ @@ -139,11 +140,11 @@ SetGlobalNameSlow(VMFrame &f, uint32 index) } static void -PatchSetFallback(VMFrame &f, ic::MICInfo *ic) +PatchSetFallback(VMFrame &f, ic::MICInfo &mic) { - JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64); + JSC::RepatchBuffer repatch(mic.stubEntry.executableAddress(), 64); JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, SetGlobalNameSlow)); - repatch.relink(ic->stubCall, fptr); + repatch.relink(mic.stubCall, fptr); } static VoidStubAtom @@ -158,13 +159,14 @@ GetStubForSetGlobalName(VMFrame &f) } void JS_FASTCALL -ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic) +ic::SetGlobalName(VMFrame &f, uint32 index) { JSObject *obj = f.fp()->scopeChain().getGlobal(); + ic::MICInfo &mic = f.fp()->script()->mics[index]; JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc)); jsid id = ATOM_TO_JSID(atom); - JS_ASSERT(ic->kind == ic::MICInfo::SET); + JS_ASSERT(mic.kind == ic::MICInfo::SET); JS_LOCK_OBJ(f.cx, obj); const Shape *shape = obj->nativeLookup(id); @@ -175,40 +177,40 @@ ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic) { JS_UNLOCK_OBJ(f.cx, obj); if (shape) - PatchSetFallback(f, ic); + PatchSetFallback(f, mic); GetStubForSetGlobalName(f)(f, atom); return; } uint32 slot = shape->slot; JS_UNLOCK_OBJ(f.cx, obj); - ic->u.name.touched = true; + mic.u.name.touched = true; /* Patch shape guard. */ - JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50); - repatch.repatch(ic->shape, obj->shape()); + JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50); + repatch.repatch(mic.shape, obj->shape()); /* Patch loads. */ JS_ASSERT(slot >= JS_INITIAL_NSLOTS); slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); - JSC::RepatchBuffer stores(ic->load.executableAddress(), 32, false); + JSC::RepatchBuffer stores(mic.load.executableAddress(), 32, false); #if defined JS_CPU_X86 - stores.repatch(ic->load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4); + stores.repatch(mic.load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4); uint32 dataOffset; - if (ic->u.name.typeConst) + if (mic.u.name.typeConst) dataOffset = MICInfo::SET_DATA_CONST_TYPE_OFFSET; else dataOffset = MICInfo::SET_DATA_TYPE_OFFSET; - stores.repatch(ic->load.dataLabel32AtOffset(dataOffset), slot); + stores.repatch(mic.load.dataLabel32AtOffset(dataOffset), slot); #elif defined JS_CPU_ARM - // ic->load actually points to the LDR instruction which fetches the offset, but 'repatch' + // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch' // knows how to dereference it to find the integer value. - stores.repatch(ic->load.dataLabel32AtOffset(0), slot); + stores.repatch(mic.load.dataLabel32AtOffset(0), slot); #elif defined JS_PUNBOX64 - stores.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot); + stores.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); #endif // Actually implement the op the slow way. @@ -216,16 +218,24 @@ ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic) } static void * JS_FASTCALL -SlowCallFromIC(VMFrame &f, ic::CallICInfo *ic) +SlowCallFromIC(VMFrame &f, uint32 index) { - stubs::SlowCall(f, ic->argc); + JSScript *oldscript = f.fp()->script(); + CallICInfo &ic= oldscript->callICs[index]; + + stubs::SlowCall(f, ic.argc); + return NULL; } static void * JS_FASTCALL -SlowNewFromIC(VMFrame &f, ic::CallICInfo *ic) +SlowNewFromIC(VMFrame &f, uint32 index) { - stubs::SlowNew(f, ic->argc); + JSScript *oldscript = f.fp()->script(); + CallICInfo &ic = oldscript->callICs[index]; + + stubs::SlowNew(f, ic.argc); + return NULL; } @@ -315,11 +325,8 @@ class CallCompiler : public BaseCompiler * here since ncode has two failure modes and we need to load out of * nmap anyway. */ - size_t offset = callingNew - ? offsetof(JSScript, jitArityCheckCtor) - : offsetof(JSScript, jitArityCheckNormal); - masm.loadPtr(Address(t0, offset), t0); - Jump hasCode = masm.branchPtr(Assembler::Above, t0, ImmPtr(JS_UNJITTABLE_SCRIPT)); + masm.loadPtr(Address(t0, offsetof(JSScript, jit)), t0); + Jump hasCode = masm.branchTestPtr(Assembler::NonZero, t0, t0); /* Try and compile. On success we get back the nmap pointer. */ masm.storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp))); @@ -338,6 +345,7 @@ class CallCompiler : public BaseCompiler /* Get nmap[ARITY], set argc, call. */ masm.move(Imm32(ic.argc), JSParamReg_Argc); + masm.loadPtr(Address(t0, offsetof(JITScript, arityCheck)), t0); masm.jump(t0); JSC::ExecutablePool *ep = poolForSize(masm.size(), CallICInfo::Pool_ScriptStub); @@ -369,11 +377,9 @@ class CallCompiler : public BaseCompiler ic.fastGuardedObject = obj; - JITScript *jit = script->getJIT(callingNew); - repatch.repatch(ic.funGuard, obj); repatch.relink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset), - JSC::CodeLocationLabel(jit->fastEntry)); + JSC::CodeLocationLabel(script->ncode)); JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n", start, ic.fastGuardedObject); } @@ -643,40 +649,52 @@ class CallCompiler : public BaseCompiler }; void * JS_FASTCALL -ic::Call(VMFrame &f, CallICInfo *ic) +ic::Call(VMFrame &f, uint32 index) { - CallCompiler cc(f, *ic, false); + JSScript *oldscript = f.fp()->script(); + CallICInfo &ic = oldscript->callICs[index]; + CallCompiler cc(f, ic, false); return cc.update(); } void * JS_FASTCALL -ic::New(VMFrame &f, CallICInfo *ic) +ic::New(VMFrame &f, uint32 index) { - CallCompiler cc(f, *ic, true); + JSScript *oldscript = f.fp()->script(); + CallICInfo &ic = oldscript->callICs[index]; + CallCompiler cc(f, ic, true); return cc.update(); } void JS_FASTCALL -ic::NativeCall(VMFrame &f, CallICInfo *ic) +ic::NativeCall(VMFrame &f, uint32 index) { - CallCompiler cc(f, *ic, false); + JSScript *oldscript = f.fp()->script(); + CallICInfo &ic = oldscript->callICs[index]; + CallCompiler cc(f, ic, false); if (!cc.generateNativeStub()) - stubs::SlowCall(f, ic->argc); + stubs::SlowCall(f, ic.argc); } void JS_FASTCALL -ic::NativeNew(VMFrame &f, CallICInfo *ic) +ic::NativeNew(VMFrame &f, uint32 index) { - CallCompiler cc(f, *ic, true); + JSScript *oldscript = f.fp()->script(); + CallICInfo &ic = oldscript->callICs[index]; + CallCompiler cc(f, ic, true); if (!cc.generateNativeStub()) - stubs::SlowNew(f, ic->argc); + stubs::SlowNew(f, ic.argc); } void -JITScript::purgeMICs() +ic::PurgeMICs(JSContext *cx, JSScript *script) { - for (uint32 i = 0; i < nMICs; i++) { - ic::MICInfo &mic = mics[i]; + /* MICs are purged during GC to handle changing shapes. */ + JS_ASSERT(cx->runtime->gcRegenShapes); + + uint32 nmics = script->jit->nMICs; + for (uint32 i = 0; i < nmics; i++) { + ic::MICInfo &mic = script->mics[i]; switch (mic.kind) { case ic::MICInfo::SET: case ic::MICInfo::GET: @@ -702,22 +720,10 @@ JITScript::purgeMICs() } void -ic::PurgeMICs(JSContext *cx, JSScript *script) +ic::SweepCallICs(JSScript *script) { - /* MICs are purged during GC to handle changing shapes. */ - JS_ASSERT(cx->runtime->gcRegenShapes); - - if (script->jitNormal) - script->jitNormal->purgeMICs(); - if (script->jitCtor) - script->jitCtor->purgeMICs(); -} - -void -JITScript::sweepCallICs() -{ - for (uint32 i = 0; i < nCallICs; i++) { - ic::CallICInfo &ic = callICs[i]; + for (uint32 i = 0; i < script->jit->nCallICs; i++) { + ic::CallICInfo &ic = script->callICs[i]; /* * If the object is unreachable, we're guaranteed not to be currently @@ -751,14 +757,5 @@ JITScript::sweepCallICs() } } -void -ic::SweepCallICs(JSScript *script) -{ - if (script->jitNormal) - script->jitNormal->sweepCallICs(); - if (script->jitCtor) - script->jitCtor->sweepCallICs(); -} - #endif /* JS_MONOIC */ diff --git a/js/src/methodjit/MonoIC.h b/js/src/methodjit/MonoIC.h index 9df110558e92..485e21aab9ba 100644 --- a/js/src/methodjit/MonoIC.h +++ b/js/src/methodjit/MonoIC.h @@ -109,8 +109,8 @@ struct MICInfo { } u; }; -void JS_FASTCALL GetGlobalName(VMFrame &f, ic::MICInfo *ic); -void JS_FASTCALL SetGlobalName(VMFrame &f, ic::MICInfo *ic); +void JS_FASTCALL GetGlobalName(VMFrame &f, uint32 index); +void JS_FASTCALL SetGlobalName(VMFrame &f, uint32 index); /* See MonoIC.cpp, CallCompiler for more information on call ICs. */ struct CallICInfo { @@ -187,10 +187,10 @@ struct CallICInfo { } }; -void * JS_FASTCALL New(VMFrame &f, ic::CallICInfo *ic); -void * JS_FASTCALL Call(VMFrame &f, ic::CallICInfo *ic); -void JS_FASTCALL NativeNew(VMFrame &f, ic::CallICInfo *ic); -void JS_FASTCALL NativeCall(VMFrame &f, ic::CallICInfo *ic); +void * JS_FASTCALL New(VMFrame &f, uint32 index); +void * JS_FASTCALL Call(VMFrame &f, uint32 index); +void JS_FASTCALL NativeNew(VMFrame &f, uint32 index); +void JS_FASTCALL NativeCall(VMFrame &f, uint32 index); void PurgeMICs(JSContext *cx, JSScript *script); void SweepCallICs(JSScript *script); diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index f869437c20f7..0709f6857138 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -111,7 +111,8 @@ class PICStubCompiler : public BaseCompiler return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub)); } - bool disable(const char *reason, VoidStubPIC stub) { + bool disable(const char *reason, VoidStubUInt32 stub) + { return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub)); } @@ -158,7 +159,7 @@ class SetPropCompiler : public PICStubCompiler { JSObject *obj; JSAtom *atom; - VoidStubPIC stub; + VoidStubUInt32 stub; int lastStubSecondShapeGuard; static int32 dslotsLoadOffset(ic::PICInfo &pic) { @@ -223,7 +224,7 @@ class SetPropCompiler : public PICStubCompiler public: SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, - VoidStubPIC stub) + VoidStubUInt32 stub) : PICStubCompiler("setprop", f, script, pic), obj(obj), atom(atom), stub(stub), lastStubSecondShapeGuard(pic.secondShapeGuard) { } @@ -745,7 +746,7 @@ class GetPropCompiler : public PICStubCompiler { } GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, - VoidStubPIC stub) + VoidStubUInt32 stub) : PICStubCompiler("callprop", f, script, pic), obj(obj), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)), lastStubSecondShapeGuard(pic.secondShapeGuard) @@ -769,7 +770,7 @@ class GetPropCompiler : public PICStubCompiler RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH); ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress()); - VoidStubPIC stub; + VoidStubUInt32 stub; switch (pic.kind) { case ic::PICInfo::GET: stub = ic::GetProp; @@ -1572,7 +1573,7 @@ class ScopeNameCompiler : public PICStubCompiler public: ScopeNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubPIC stub) + JSAtom *atom, VoidStubUInt32 stub) : PICStubCompiler("name", f, script, pic), scopeChain(scopeChain), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)), obj(NULL), holder(NULL), prop(NULL) { } @@ -1590,7 +1591,7 @@ class ScopeNameCompiler : public PICStubCompiler RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH); ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress()); - VoidStubPIC stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName; + VoidStubUInt32 stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName; MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, stub)); repatcher.relinkCallerToTrampoline(retPtr, target); } @@ -1911,7 +1912,7 @@ class BindNameCompiler : public PICStubCompiler public: BindNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubPIC stub) + JSAtom *atom, VoidStubUInt32 stub) : PICStubCompiler("bind", f, script, pic), scopeChain(scopeChain), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)) { } @@ -2019,14 +2020,15 @@ class BindNameCompiler : public PICStubCompiler }; void JS_FASTCALL -ic::GetProp(VMFrame &f, ic::PICInfo *pic) +ic::GetProp(VMFrame &f, uint32 index) { JSScript *script = f.fp()->script(); + PICInfo &pic = script->pics[index]; - JSAtom *atom = pic->atom; + JSAtom *atom = pic.atom; if (atom == f.cx->runtime->atomState.lengthAtom) { if (f.regs.sp[-1].isString()) { - GetPropCompiler cc(f, script, NULL, *pic, NULL, stubs::Length); + GetPropCompiler cc(f, script, NULL, pic, NULL, stubs::Length); if (!cc.generateStringLengthStub()) { cc.disable("error"); THROW(); @@ -2037,7 +2039,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic) } else if (!f.regs.sp[-1].isPrimitive()) { JSObject *obj = &f.regs.sp[-1].toObject(); if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden())) { - GetPropCompiler cc(f, script, obj, *pic, NULL, stubs::Length); + GetPropCompiler cc(f, script, obj, pic, NULL, stubs::Length); if (obj->isArray()) { if (!cc.generateArrayLengthStub()) { cc.disable("error"); @@ -2061,8 +2063,8 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic) if (!obj) THROW(); - if (pic->shouldGenerate()) { - GetPropCompiler cc(f, script, obj, *pic, atom, stubs::GetProp); + if (pic.shouldGenerate()) { + GetPropCompiler cc(f, script, obj, pic, atom, stubs::GetProp); if (!cc.update()) { cc.disable("error"); THROW(); @@ -2076,9 +2078,10 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic) } void JS_FASTCALL -ic::GetElem(VMFrame &f, ic::PICInfo *pic) +ic::GetElem(VMFrame &f, uint32 picIndex) { JSScript *script = f.fp()->script(); + PICInfo &pic = script->pics[picIndex]; JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); if (!obj) @@ -2087,8 +2090,8 @@ ic::GetElem(VMFrame &f, ic::PICInfo *pic) Value idval = f.regs.sp[-1]; JS_ASSERT(idval.isString()); JSString *id = idval.toString(); - if (pic->shouldGenerate()) { - GetElemCompiler cc(f, script, obj, *pic, id, stubs::GetElem); + if (pic.shouldGenerate()) { + GetElemCompiler cc(f, script, obj, pic, id, stubs::GetElem); if (!cc.update()) { cc.disable("error"); THROW(); @@ -2104,29 +2107,46 @@ ic::GetElem(VMFrame &f, ic::PICInfo *pic) f.regs.sp[-2] = v; } -template -static void JS_FASTCALL -SetPropDumb(VMFrame &f, ic::PICInfo *pic) +void JS_FASTCALL +ic::SetPropDumb(VMFrame &f, uint32 index) { - stubs::SetPropNoCache(f, pic->atom); + JSScript *script = f.fp()->script(); + ic::PICInfo &pic = script->pics[index]; + JS_ASSERT(pic.isSet()); + JSAtom *atom = pic.atom; + + JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); + if (!obj) + THROW(); + Value rval = f.regs.sp[-1]; + if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], + script->strictModeCode)) + THROW(); + f.regs.sp[-2] = rval; } -template static void JS_FASTCALL -SetPropSlow(VMFrame &f, ic::PICInfo *pic) +SetPropSlow(VMFrame &f, uint32 index) { - stubs::SetName(f, pic->atom); + JSScript *script = f.fp()->script(); + ic::PICInfo &pic = script->pics[index]; + JS_ASSERT(pic.isSet()); + + JSAtom *atom = pic.atom; + STRICT_VARIANT(stubs::SetName)(f, atom); } void JS_FASTCALL -ic::SetProp(VMFrame &f, ic::PICInfo *pic) +ic::SetProp(VMFrame &f, uint32 index) { JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); if (!obj) THROW(); JSScript *script = f.fp()->script(); - JS_ASSERT(pic->isSet()); + ic::PICInfo &pic = script->pics[index]; + JSAtom *atom = pic.atom; + JS_ASSERT(pic.isSet()); // // Important: We update the PIC before looking up the property so that the @@ -2137,7 +2157,7 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic) // cache can't handle a GET and SET from the same scripted PC. // - VoidStubPIC stub; + VoidStubUInt32 stub; switch (JSOp(*f.regs.pc)) { case JSOP_PROPINC: case JSOP_PROPDEC: @@ -2147,36 +2167,40 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic) case JSOP_NAMEDEC: case JSOP_INCNAME: case JSOP_DECNAME: - stub = STRICT_VARIANT(SetPropDumb); + stub = SetPropDumb; break; default: - stub = STRICT_VARIANT(SetPropSlow); + stub = SetPropSlow; break; } - SetPropCompiler cc(f, script, obj, *pic, pic->atom, stub); + SetPropCompiler cc(f, script, obj, pic, atom, stub); if (!cc.update()) { cc.disable("error"); THROW(); } Value rval = f.regs.sp[-1]; - stub(f, pic); + stub(f, index); } static void JS_FASTCALL -CallPropSlow(VMFrame &f, ic::PICInfo *pic) +CallPropSlow(VMFrame &f, uint32 index) { - stubs::CallProp(f, pic->atom); + JSScript *script = f.fp()->script(); + ic::PICInfo &pic = script->pics[index]; + stubs::CallProp(f, pic.atom); } void JS_FASTCALL -ic::CallProp(VMFrame &f, ic::PICInfo *pic) +ic::CallProp(VMFrame &f, uint32 index) { JSContext *cx = f.cx; JSFrameRegs ®s = f.regs; JSScript *script = f.fp()->script(); + ic::PICInfo &pic = script->pics[index]; + JSAtom *origAtom = pic.atom; Value lval; lval = regs.sp[-1]; @@ -2236,7 +2260,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic) * PropertyCache::test. */ jsid id; - id = ATOM_TO_JSID(pic->atom); + id = ATOM_TO_JSID(origAtom); regs.sp++; regs.sp[-1].setNull(); @@ -2274,7 +2298,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic) } } - GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, CallPropSlow); + GetPropCompiler cc(f, script, &objv.toObject(), pic, origAtom, CallPropSlow); if (usePIC) { if (lval.isObject()) { if (!cc.update()) { @@ -2295,7 +2319,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic) #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(rval.isUndefined())) { - regs.sp[-2].setString(ATOM_TO_STRING(pic->atom)); + regs.sp[-2].setString(ATOM_TO_STRING(origAtom)); if (!js_OnUnknownMethod(cx, regs.sp - 2)) THROW(); } @@ -2303,26 +2327,28 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic) } static void JS_FASTCALL -SlowName(VMFrame &f, ic::PICInfo *pic) +SlowName(VMFrame &f, uint32 index) { stubs::Name(f); } static void JS_FASTCALL -SlowXName(VMFrame &f, ic::PICInfo *pic) +SlowXName(VMFrame &f, uint32 index) { stubs::GetProp(f); } void JS_FASTCALL -ic::XName(VMFrame &f, ic::PICInfo *pic) +ic::XName(VMFrame &f, uint32 index) { JSScript *script = f.fp()->script(); + ic::PICInfo &pic = script->pics[index]; + JSAtom *atom = pic.atom; /* GETXPROP is guaranteed to have an object. */ JSObject *obj = &f.regs.sp[-1].toObject(); - ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, SlowXName); + ScopeNameCompiler cc(f, script, obj, pic, atom, SlowXName); if (!cc.updateForXName()) { cc.disable("error"); @@ -2336,11 +2362,13 @@ ic::XName(VMFrame &f, ic::PICInfo *pic) } void JS_FASTCALL -ic::Name(VMFrame &f, ic::PICInfo *pic) +ic::Name(VMFrame &f, uint32 index) { JSScript *script = f.fp()->script(); + ic::PICInfo &pic = script->pics[index]; + JSAtom *atom = pic.atom; - ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowName); + ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), pic, atom, SlowName); if (!cc.updateForName()) { cc.disable("error"); @@ -2354,17 +2382,19 @@ ic::Name(VMFrame &f, ic::PICInfo *pic) } static void JS_FASTCALL -SlowBindName(VMFrame &f, ic::PICInfo *pic) +SlowBindName(VMFrame &f, uint32 index) { stubs::BindName(f); } void JS_FASTCALL -ic::BindName(VMFrame &f, ic::PICInfo *pic) +ic::BindName(VMFrame &f, uint32 index) { JSScript *script = f.fp()->script(); + ic::PICInfo &pic = script->pics[index]; + JSAtom *atom = pic.atom; - BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowBindName); + BindNameCompiler cc(f, script, &f.fp()->scopeChain(), pic, atom, SlowBindName); JSObject *obj = cc.update(); if (!obj) { @@ -2376,10 +2406,11 @@ ic::BindName(VMFrame &f, ic::PICInfo *pic) } void -JITScript::purgePICs() +ic::PurgePICs(JSContext *cx, JSScript *script) { - for (uint32 i = 0; i < nPICs; i++) { - ic::PICInfo &pic = pics[i]; + uint32 npics = script->jit->nPICs; + for (uint32 i = 0; i < npics; i++) { + ic::PICInfo &pic = script->pics[i]; switch (pic.kind) { case ic::PICInfo::SET: case ic::PICInfo::SETMETHOD: @@ -2407,14 +2438,5 @@ JITScript::purgePICs() } } -void -ic::PurgePICs(JSContext *cx, JSScript *script) -{ - if (script->jitNormal) - script->jitNormal->purgePICs(); - if (script->jitCtor) - script->jitCtor->purgePICs(); -} - #endif /* JS_POLYIC */ diff --git a/js/src/methodjit/PolyIC.h b/js/src/methodjit/PolyIC.h index 9e3d63e03bef..0edbe26321c8 100644 --- a/js/src/methodjit/PolyIC.h +++ b/js/src/methodjit/PolyIC.h @@ -352,13 +352,14 @@ struct PICInfo { }; void PurgePICs(JSContext *cx, JSScript *script); -void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *); -void JS_FASTCALL GetElem(VMFrame &f, ic::PICInfo *); -void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *); -void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *); -void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *); -void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *); -void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL GetProp(VMFrame &f, uint32 index); +void JS_FASTCALL GetElem(VMFrame &f, uint32 index); +void JS_FASTCALL SetProp(VMFrame &f, uint32 index); +void JS_FASTCALL CallProp(VMFrame &f, uint32 index); +void JS_FASTCALL Name(VMFrame &f, uint32 index); +void JS_FASTCALL XName(VMFrame &f, uint32 index); +void JS_FASTCALL BindName(VMFrame &f, uint32 index); +void JS_FASTCALL SetPropDumb(VMFrame &f, uint32 index); } /* namespace ic */ } /* namespace mjit */ diff --git a/js/src/methodjit/Retcon.cpp b/js/src/methodjit/Retcon.cpp index f5069e1f9cb1..99526fb01f49 100644 --- a/js/src/methodjit/Retcon.cpp +++ b/js/src/methodjit/Retcon.cpp @@ -73,14 +73,13 @@ AutoScriptRetrapper::untrap(jsbytecode *pc) } Recompiler::PatchableAddress -Recompiler::findPatch(JITScript *jit, void **location) +Recompiler::findPatch(void **location) { - uint8* codeStart = (uint8 *)jit->code.m_code.executableAddress(); - for (uint32 i = 0; i < jit->nCallSites; i++) { - if (jit->callSites[i].codeOffset + codeStart == *location) { + for (uint32 i = 0; i < script->jit->nCallSites; i++) { + if (script->jit->callSites[i].codeOffset + (uint8*)script->jit->invoke == *location) { PatchableAddress result; result.location = location; - result.callSite = jit->callSites[i]; + result.callSite = script->jit->callSites[i]; return result; } } @@ -117,27 +116,17 @@ Recompiler::Recompiler(JSContext *cx, JSScript *script) bool Recompiler::recompile() { - JS_ASSERT(script->hasJITCode()); + JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD); - Vector normalPatches(cx); - Vector ctorPatches(cx); + Vector toPatch(cx); /* Scan the stack, saving the ncode elements of the frames. */ - JSStackFrame *firstCtorFrame = NULL; - JSStackFrame *firstNormalFrame = NULL; + JSStackFrame *firstFrame = NULL; for (AllFramesIter i(cx); !i.done(); ++i) { - if (!firstCtorFrame && i.fp()->maybeScript() == script && i.fp()->isConstructing()) - firstCtorFrame = i.fp(); - else if (!firstNormalFrame && i.fp()->maybeScript() == script && !i.fp()->isConstructing()) - firstNormalFrame = i.fp(); - void **addr = i.fp()->addressOfNativeReturnAddress(); - if (!*addr) - continue; - if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { - if (!ctorPatches.append(findPatch(script->jitCtor, addr))) - return false; - } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { - if (!normalPatches.append(findPatch(script->jitNormal, addr))) + if (!firstFrame && i.fp()->maybeScript() == script) + firstFrame = i.fp(); + if (script->isValidJitCode(i.fp()->nativeReturnAddress())) { + if (!toPatch.append(findPatch(i.fp()->addressOfNativeReturnAddress()))) return false; } } @@ -147,41 +136,29 @@ Recompiler::recompile() f != NULL; f = f->previous) { - void **addr = f->returnAddressLocation(); - if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { - if (!ctorPatches.append(findPatch(script->jitCtor, addr))) - return false; - } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { - if (!normalPatches.append(findPatch(script->jitNormal, addr))) + void **machineReturn = f->returnAddressLocation(); + if (script->isValidJitCode(*machineReturn)) { + if (!toPatch.append(findPatch(machineReturn))) return false; } } ReleaseScriptCode(cx, script); - if (normalPatches.length() && !recompile(firstNormalFrame, normalPatches)) - return false; + /* No need to actually compile or fixup if no frames on the stack */ + if (!firstFrame) + return true; - if (ctorPatches.length() && !recompile(firstCtorFrame, ctorPatches)) - return false; - - return true; -} - -bool -Recompiler::recompile(JSStackFrame *fp, Vector &patches) -{ /* If we get this far, the script is live, and we better be safe to re-jit. */ JS_ASSERT(cx->compartment->debugMode); - JS_ASSERT(fp); - Compiler c(cx, fp); - if (c.compile() != Compile_Okay) + Compiler c(cx, script, firstFrame->maybeFun(), &firstFrame->scopeChain()); + if (c.Compile() != Compile_Okay) return false; /* Perform the earlier scanned patches */ - for (uint32 i = 0; i < patches.length(); i++) - applyPatch(c, patches[i]); + for (uint32 i = 0; i < toPatch.length(); i++) + applyPatch(c, toPatch[i]); return true; } diff --git a/js/src/methodjit/Retcon.h b/js/src/methodjit/Retcon.h index 99050c0e5eed..8280329a8fe0 100644 --- a/js/src/methodjit/Retcon.h +++ b/js/src/methodjit/Retcon.h @@ -97,9 +97,8 @@ private: JSContext *cx; JSScript *script; - PatchableAddress findPatch(JITScript *jit, void **location); + PatchableAddress findPatch(void **location); void applyPatch(Compiler& c, PatchableAddress& toPatch); - bool recompile(JSStackFrame *fp, Vector &patches); }; } /* namespace mjit */ diff --git a/js/src/methodjit/StubCalls-inl.h b/js/src/methodjit/StubCalls-inl.h index 2b2844a2ea9f..55b3513ca9ce 100644 --- a/js/src/methodjit/StubCalls-inl.h +++ b/js/src/methodjit/StubCalls-inl.h @@ -99,6 +99,7 @@ ReportAtomNotDefined(JSContext *cx, JSAtom *atom) } \ JS_END_MACRO + }} #endif /* jslogic_h__ */ diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index a0231f1ed997..5850c36e46a1 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -281,22 +281,6 @@ stubs::SetName(VMFrame &f, JSAtom *origAtom) template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); -template -void JS_FASTCALL -stubs::SetPropNoCache(VMFrame &f, JSAtom *atom) -{ - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - Value rval = f.regs.sp[-1]; - if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], strict)) - THROW(); - f.regs.sp[-2] = rval; -} - -template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); -template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); - template void JS_FASTCALL stubs::SetGlobalNameDumb(VMFrame &f, JSAtom *atom) @@ -2533,15 +2517,14 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) { jsbytecode *jpc = pc; JSScript *script = f.fp()->script(); - JITScript *jit = script->getJIT(f.fp()->isConstructing()); /* This is correct because the compiler adjusts the stack beforehand. */ Value lval = f.regs.sp[-1]; if (!lval.isPrimitive()) { ptrdiff_t offs = (pc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(script->nmap[offs]); + return script->nmap[offs]; } JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH); @@ -2561,8 +2544,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) JSString *rhs = rval.toString(); if (rhs == str || js_EqualStrings(str, rhs)) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(script->nmap[offs]); + return script->nmap[offs]; } } pc += JUMP_OFFSET_LEN; @@ -2574,8 +2557,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (rval.isNumber() && d == rval.toNumber()) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(script->nmap[offs]); + return script->nmap[offs]; } pc += JUMP_OFFSET_LEN; } @@ -2585,16 +2568,16 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (lval == rval) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(script->nmap[offs]); + return script->nmap[offs]; } pc += JUMP_OFFSET_LEN; } } ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(jpc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(script->nmap[offs]); + return script->nmap[offs]; } void * JS_FASTCALL @@ -2603,7 +2586,6 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) jsbytecode * const originalPC = origPc; jsbytecode *pc = originalPC; JSScript *script = f.fp()->script(); - JITScript *jit = script->getJIT(f.fp()->isConstructing()); uint32 jumpOffset = GET_JUMP_OFFSET(pc); pc += JUMP_OFFSET_LEN; @@ -2643,8 +2625,8 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) finally: /* Provide the native address. */ ptrdiff_t offset = (originalPC + jumpOffset) - script->code; - JS_ASSERT(jit->nmap[offset]); - return jit->nmap[offset]; + JS_ASSERT(script->nmap[offset]); + return script->nmap[offset]; } void JS_FASTCALL diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index da104367cbcb..fe49e1ba1106 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -98,14 +98,14 @@ struct UncachedCallResult { void UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); -void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto); +JSBool JS_FASTCALL NewObject(VMFrame &f, uint32 argc); void JS_FASTCALL Throw(VMFrame &f); void JS_FASTCALL PutCallObject(VMFrame &f); void JS_FASTCALL PutActivationObjects(VMFrame &f); void JS_FASTCALL GetCallObject(VMFrame &f); void JS_FASTCALL WrapPrimitiveThis(VMFrame &f); #if JS_MONOIC -void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::MICInfo *mic); +void * JS_FASTCALL InvokeTracer(VMFrame &f, uint32 index); #else void * JS_FASTCALL InvokeTracer(VMFrame &f); #endif @@ -116,7 +116,6 @@ void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc); void JS_FASTCALL BindName(VMFrame &f); JSObject * JS_FASTCALL BindGlobalName(VMFrame &f); template void JS_FASTCALL SetName(VMFrame &f, JSAtom *atom); -template void JS_FASTCALL SetPropNoCache(VMFrame &f, JSAtom *atom); template void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom); template void JS_FASTCALL SetGlobalNameDumb(VMFrame &f, JSAtom *atom); void JS_FASTCALL Name(VMFrame &f); @@ -227,6 +226,11 @@ inline FuncPtr FunctionTemplateConditional(bool cond, FuncPtr a, FuncPtr b) { return cond ? a : b; } +/* Return f if the script is strict mode code, f otherwise. */ +#define STRICT_VARIANT(f) \ + (FunctionTemplateConditional(script->strictModeCode, \ + f, f)) + }} /* namespace stubs,mjit,js */ extern "C" void * diff --git a/js/src/methodjit/StubCompiler.h b/js/src/methodjit/StubCompiler.h index 6d945312c984..bdfaff63bd70 100644 --- a/js/src/methodjit/StubCompiler.h +++ b/js/src/methodjit/StubCompiler.h @@ -125,11 +125,6 @@ class StubCompiler STUB_CALL_TYPE(BoolStub); STUB_CALL_TYPE(VoidStubAtom); STUB_CALL_TYPE(VoidStubPC); - STUB_CALL_TYPE(VoidStubMIC); - STUB_CALL_TYPE(VoidPtrStubMIC); - STUB_CALL_TYPE(VoidStubPIC); - STUB_CALL_TYPE(VoidStubCallIC); - STUB_CALL_TYPE(VoidPtrStubCallIC); #undef STUB_CALL_TYPE diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 005675639ba8..dea6d8c0c79d 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1196,7 +1196,7 @@ AssertJit(JSContext *cx, uintN argc, jsval *vp) { #ifdef JS_METHODJIT if (JS_GetOptions(cx) & JSOPTION_METHODJIT) { - if (!cx->fp()->script()->getJIT(cx->fp()->isConstructing())) { + if (cx->fp()->script()->nmap == NULL) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_JIT_FAILED); return JS_FALSE; } diff --git a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js index db7e4b82b2a0..a2b8d32d76de 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js +++ b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js @@ -4,7 +4,7 @@ function main() { x = "failure"; } function success() { x = "success"; } /* The JSOP_STOP in a. */ -trap(main, 7, "success()"); +trap(main, 6, "success()"); main(); assertEq(x, "success"); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js index fcb2eab3ef1b..1d19aeebeb11 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js @@ -3,5 +3,5 @@ function main() { return "failure"; } /* JSOP_RETURN in main. */ -trap(main, 4, "'success'"); +trap(main, 3, "'success'"); assertEq(main(), "success"); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js index 16a5a11c445e..18561be47267 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js @@ -3,7 +3,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* myparent call in myparent. */ - trap(myparent, 38, "failure()"); + trap(myparent, 37, "failure()"); } else { x = "success"; myparent(true); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js index 6c302163e197..eefc0bb86005 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js @@ -4,7 +4,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* noop call in myparent */ - trap(myparent, 49, "success()"); + trap(myparent, 48, "success()"); } else { myparent(true); x = "failure"; diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js index b01d5ac3b12c..5cecf58cd5ad 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js @@ -6,14 +6,14 @@ function doNothing() { } function myparent(nested) { if (nested) { /* JSOP_CALL to doNothing in myparent with nested = true. */ - trap(myparent, 25, "success()"); + trap(myparent, 24, "success()"); doNothing(); } else { doNothing(); } } /* JSOP_CALL to doNothing in myparent with nested = false. */ -trap(myparent, 35, "myparent(true)"); +trap(myparent, 34, "myparent(true)"); function success() { x = "success"; From 3b38fa406693093c5f152d9f841b6b5e7dced77a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 4 Oct 2010 15:40:32 -0700 Subject: [PATCH 032/284] Bug 601539 - nanojit: fix bogus generation of suffix names in LIR dumps. r=rreitmai. --HG-- extra : convert_revision : 2c66185d4d358f589fc1fe44f48e4c7eafe1a0c1 --- js/src/nanojit/Containers.h | 1 + js/src/nanojit/LIR.cpp | 49 ++++++++++++++++++++----------------- js/src/nanojit/LIR.h | 34 ++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/js/src/nanojit/Containers.h b/js/src/nanojit/Containers.h index a583bbaf3856..cd4fe6656801 100644 --- a/js/src/nanojit/Containers.h +++ b/js/src/nanojit/Containers.h @@ -253,6 +253,7 @@ namespace nanojit /** Bucket hashtable with a fixed # of buckets (never rehash) * Intended for use when a reasonable # of buckets can be estimated ahead of time. + * Note that operator== is used to compare keys. */ template > class HashMap { Allocator& allocator; diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index e30978ff5f24..c002dddb8087 100644 --- a/js/src/nanojit/LIR.cpp +++ b/js/src/nanojit/LIR.cpp @@ -1543,6 +1543,24 @@ namespace nanojit void LirNameMap::addNameWithSuffix(LIns* ins, const char *name, int suffix, bool ignoreOneSuffix) { + NanoAssert(!names.containsKey(ins)); + const int N = 100; + char name2[N]; + if (suffix == 1 && ignoreOneSuffix) { + VMPI_snprintf(name2, N, "%s", name); // don't add '1' suffix + } else if (VMPI_isdigit(name[VMPI_strlen(name)-1])) { + VMPI_snprintf(name2, N, "%s_%d", name, suffix); // use '_' to avoid confusion + } else { + VMPI_snprintf(name2, N, "%s%d", name, suffix); // normal case + } + + char *copy = new (alloc) char[VMPI_strlen(name2)+1]; + VMPI_strcpy(copy, name2); + Entry *e = new (alloc) Entry(copy); + names.put(ins, e); + } + + void LirNameMap::addName(LIns* ins, const char* name) { // The lookup may succeed, ie. we may already have a name for this // instruction. This can happen because of CSE. Eg. if we have this: // @@ -1557,27 +1575,12 @@ namespace nanojit // name "foo2". // if (!names.containsKey(ins)) { - const int N = 100; - char name2[N]; - if (suffix == 1 && ignoreOneSuffix) { - VMPI_snprintf(name2, N, "%s", name); // don't add '1' suffix - } else if (VMPI_isdigit(name[VMPI_strlen(name)-1])) { - VMPI_snprintf(name2, N, "%s_%d", name, suffix); // use '_' to avoid confusion - } else { - VMPI_snprintf(name2, N, "%s%d", name, suffix); // normal case - } - - char *copy = new (alloc) char[VMPI_strlen(name2)+1]; - VMPI_strcpy(copy, name2); - Entry *e = new (alloc) Entry(copy); - names.put(ins, e); + Str* str = new (alloc) Str(alloc, name); + int suffix = namecounts.add(*str); + addNameWithSuffix(ins, name, suffix, /*ignoreOneSuffix*/true); } } - void LirNameMap::addName(LIns* ins, const char* name) { - addNameWithSuffix(ins, name, namecounts.add(name), /*ignoreOneSuffix*/true); - } - const char* LirNameMap::createName(LIns* ins) { if (ins->isCall()) { #if NJ_SOFTFLOAT_SUPPORTED @@ -1586,12 +1589,14 @@ namespace nanojit } else #endif { - addNameWithSuffix(ins, ins->callInfo()->_name, funccounts.add(ins->callInfo()), - /*ignoreOneSuffix*/false); + if (!names.containsKey(ins)) + addNameWithSuffix(ins, ins->callInfo()->_name, funccounts.add(ins->callInfo()), + /*ignoreOneSuffix*/false); } } else { - addNameWithSuffix(ins, lirNames[ins->opcode()], lircounts.add(ins->opcode()), - /*ignoreOneSuffix*/false); + if (!names.containsKey(ins)) + addNameWithSuffix(ins, lirNames[ins->opcode()], lircounts.add(ins->opcode()), + /*ignoreOneSuffix*/false); } return names.get(ins)->name; diff --git a/js/src/nanojit/LIR.h b/js/src/nanojit/LIR.h index fa5924a11ac2..b9aa6fe6ab89 100644 --- a/js/src/nanojit/LIR.h +++ b/js/src/nanojit/LIR.h @@ -1676,10 +1676,36 @@ namespace nanojit private: Allocator& alloc; - template - class CountMap: public HashMap { + // A small string-wrapper class, required because we need '==' to + // compare string contents, not string pointers, when strings are used + // as keys in CountMap. + struct Str { + Allocator& alloc; + char* s; + + Str(Allocator& alloc_, const char* s_) : alloc(alloc_) { + s = new (alloc) char[1+strlen(s_)]; + strcpy(s, s_); + } + + bool operator==(const Str& str) const { + return (0 == strcmp(this->s, str.s)); + } + }; + + // Similar to 'struct Str' -- we need to hash the string's contents, + // not its pointer. + template struct StrHash { + static size_t hash(const Str &k) { + // (const void*) cast is required by ARM RVCT 2.2 + return murmurhash((const void*)k.s, strlen(k.s)); + } + }; + + template > + class CountMap: public HashMap { public: - CountMap(Allocator& alloc) : HashMap(alloc) {} + CountMap(Allocator& alloc) : HashMap(alloc, 128) {} int add(Key k) { int c = 1; if (this->containsKey(k)) { @@ -1692,7 +1718,7 @@ namespace nanojit CountMap lircounts; CountMap funccounts; - CountMap namecounts; + CountMap > namecounts; void addNameWithSuffix(LIns* i, const char *s, int suffix, bool ignoreOneSuffix); From ca0f8e1f84b2ff9165f0c4d00b60f62858e343bf Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 5 Oct 2010 09:52:40 +1100 Subject: [PATCH 033/284] Fix non-Linux compile bustage for bug 601539. r=me. --HG-- extra : convert_revision : 12776aa248b916be646dd7b9c760be1b3fa7ba8a --- js/src/nanojit/LIR.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/nanojit/LIR.h b/js/src/nanojit/LIR.h index b9aa6fe6ab89..dd72a21c3874 100644 --- a/js/src/nanojit/LIR.h +++ b/js/src/nanojit/LIR.h @@ -1718,7 +1718,7 @@ namespace nanojit CountMap lircounts; CountMap funccounts; - CountMap > namecounts; + CountMap > namecounts; void addNameWithSuffix(LIns* i, const char *s, int suffix, bool ignoreOneSuffix); From a62029765767899ad9a3e819826d0bcf434b3744 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 4 Oct 2010 16:16:10 -0700 Subject: [PATCH 034/284] Update nanojit-import-rev stamp. --- js/src/nanojit-import-rev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/nanojit-import-rev b/js/src/nanojit-import-rev index 9ce6937c4859..a7ab32eb0ed5 100644 --- a/js/src/nanojit-import-rev +++ b/js/src/nanojit-import-rev @@ -1 +1 @@ -ce381ce50f62501ae385870d1328df73e68d7a27 +12776aa248b916be646dd7b9c760be1b3fa7ba8a From bc6cad425c8b14f798150e6d3f471b61843a2692 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Sun, 3 Oct 2010 22:46:39 -0700 Subject: [PATCH 035/284] Per ECMA-262, no .prototype for built-in functions and Function.prototype (445319, r=Waldo). --- js/src/jsfun.cpp | 91 +++++++++---------- js/src/jsfun.h | 5 + .../ecma_5/Function/builtin-no-prototype.js | 40 ++++++++ js/src/tests/ecma_5/Function/jstests.list | 1 + 4 files changed, 90 insertions(+), 47 deletions(-) create mode 100644 js/src/tests/ecma_5/Function/builtin-no-prototype.js diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 4963789c88b6..c5b077c68955 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1086,7 +1086,7 @@ CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, /* Copy however many args fit into fslots. */ uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1; JS_ASSERT(first <= JS_INITIAL_NSLOTS); - + Value *vp = &callobj.fslots[first]; uintN len = Min(nargs, uintN(JS_INITIAL_NSLOTS) - first); @@ -1720,38 +1720,38 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) { if (!JSID_IS_ATOM(id)) - return JS_TRUE; + return true; JSFunction *fun = obj->getFunctionPrivate(); - /* - * No need to reflect fun.prototype in 'fun.prototype = ... '. Assert that - * fun is not a compiler-created function object, which must never leak to - * script or embedding code and then be mutated. - */ - if ((flags & JSRESOLVE_ASSIGNING) && JSID_IS_ATOM(id, cx->runtime->atomState.classPrototypeAtom)) { - JS_ASSERT(!IsInternalFunctionObject(obj)); - return JS_TRUE; - } - - /* - * Ok, check whether id is 'prototype' and bootstrap the function object's - * prototype property. - */ - JSAtom *atom = cx->runtime->atomState.classPrototypeAtom; - if (id == ATOM_TO_JSID(atom)) { - JS_ASSERT(!IsInternalFunctionObject(obj)); + if (JSID_IS_ATOM(id, cx->runtime->atomState.classPrototypeAtom)) { + /* + * Native or "built-in" functions do not have a .prototype property per + * ECMA-262 (all editions). Built-in constructor functions, e.g. Object + * and Function to name two conspicuous examples, do have a .prototype + * property, but it is created eagerly by js_InitClass (jsobj.cpp). + * + * ES5 15.3.4: the non-native function object named Function.prototype + * must not have a .prototype property. + * + * ES5 15.3.4.5: bound functions don't have a prototype property. The + * isNative() test covers this case because bound functions are native + * functions by definition/construction. + */ + if (fun->isNative() || fun->isFunctionPrototype()) + return true; /* - * Beware of the wacky case of a user function named Object -- trying - * to find a prototype for that will recur back here _ad perniciem_. + * Assert that fun is not a compiler-created function object, which + * must never leak to script or embedding code and then be mutated. + * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above. */ - if (fun->atom == CLASS_ATOM(cx, Object)) - return JS_TRUE; + JS_ASSERT(!IsInternalFunctionObject(obj)); + JS_ASSERT(!obj->isBoundFunction()); - /* ES5 15.3.4.5: bound functions don't have a prototype property. */ - if (obj->isBoundFunction()) - return JS_TRUE; + /* No need to reflect fun.prototype in 'fun.prototype = ... '. */ + if (flags & JSRESOLVE_ASSIGNING) + return true; /* * Make the prototype object an instance of Object with the same parent @@ -1760,10 +1760,10 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject *parent = obj->getParent(); JSObject *proto; if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) - return JS_FALSE; + return false; proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent); if (!proto) - return JS_FALSE; + return false; /* * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for @@ -1773,48 +1773,44 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, * in js_InitClass, with the right attributes. */ if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT)) - return JS_FALSE; + return false; *objp = obj; - return JS_TRUE; + return true; } - atom = cx->runtime->atomState.lengthAtom; - if (id == ATOM_TO_JSID(atom)) { + if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { JS_ASSERT(!IsInternalFunctionObject(obj)); - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), Int32Value(fun->nargs), + if (!js_DefineNativeProperty(cx, obj, id, Int32Value(fun->nargs), PropertyStub, PropertyStub, JSPROP_PERMANENT | JSPROP_READONLY, 0, 0, NULL)) { - return JS_FALSE; + return false; } *objp = obj; - return JS_TRUE; + return true; } for (uintN i = 0; i < JS_ARRAY_LENGTH(lazyFunctionDataProps); i++) { const LazyFunctionDataProp *lfp = &lazyFunctionDataProps[i]; - atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset); - if (id == ATOM_TO_JSID(atom)) { + if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset))) { JS_ASSERT(!IsInternalFunctionObject(obj)); - if (!js_DefineNativeProperty(cx, obj, - ATOM_TO_JSID(atom), UndefinedValue(), + if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(), fun_getProperty, PropertyStub, lfp->attrs, Shape::HAS_SHORTID, lfp->tinyid, NULL)) { - return JS_FALSE; + return false; } *objp = obj; - return JS_TRUE; + return true; } } for (uintN i = 0; i < JS_ARRAY_LENGTH(poisonPillProps); i++) { const PoisonPillProp &p = poisonPillProps[i]; - atom = OFFSET_TO_ATOM(cx->runtime, p.atomOffset); - if (id == ATOM_TO_JSID(atom)) { + if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, p.atomOffset))) { JS_ASSERT(!IsInternalFunctionObject(obj)); PropertyOp getter, setter; @@ -1830,18 +1826,18 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, setter = PropertyStub; } - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), UndefinedValue(), + if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(), getter, setter, attrs, Shape::HAS_SHORTID, p.tinyid, NULL)) { - return JS_FALSE; + return false; } *objp = obj; - return JS_TRUE; + return true; } } - return JS_TRUE; + return true; } #if JS_HAS_XDR @@ -2858,6 +2854,7 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj) JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL); if (!fun) return NULL; + fun->flags |= JSFUN_PROTOTYPE; fun->u.i.script = JSScript::emptyScript(); if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) { diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 6166d59b74a6..9bf82edb5341 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -86,6 +86,9 @@ appear to call itself via its own name or arguments.callee */ +#define JSFUN_PROTOTYPE 0x0800 /* function is Function.prototype for some + global object */ + #define JSFUN_EXPR_CLOSURE 0x1000 /* expression closure: function(x) x*x */ #define JSFUN_TRCINFO 0x2000 /* when set, u.n.trcinfo is non-null, JSFunctionSpec::call points to a @@ -170,6 +173,8 @@ struct JSFunction : public JSObject bool isConstructor() const { return flags & JSFUN_CONSTRUCTOR; } bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); } + bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; } + inline bool inStrictMode() const; uintN countVars() const { diff --git a/js/src/tests/ecma_5/Function/builtin-no-prototype.js b/js/src/tests/ecma_5/Function/builtin-no-prototype.js new file mode 100644 index 000000000000..3ad17e8e8e50 --- /dev/null +++ b/js/src/tests/ecma_5/Function/builtin-no-prototype.js @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +assertEq(undefined, void 0); + +assertEq(Function.prototype.hasOwnProperty('prototype'), false); +assertEq(Function.prototype.prototype, undefined); + +var builtin_ctors = [ + Object, Function, Array, String, Boolean, Number, Date, RegExp, Error, + EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError +]; + +for (var i = 0; i < builtin_ctors.length; i++) { + var c = builtin_ctors[i]; + assertEq(typeof c.prototype, (c === Function) ? "function" : "object"); + assertEq(c.prototype.constructor, c); +} + +var builtin_funcs = [ + eval, isFinite, isNaN, parseFloat, parseInt, + decodeURI, decodeURIComponent, encodeURI, encodeURIComponent +]; + +for (var i = 0; i < builtin_funcs.length; i++) { + assertEq(builtin_funcs[i].hasOwnProperty('prototype'), false); + assertEq(builtin_funcs[i].prototype, undefined); +} + +var names = Object.getOwnPropertyNames(Math); +for (var i = 0; i < names.length; i++) { + var m = Math[names[i]]; + if (typeof m === "function") + assertEq(m.prototype, undefined); +} + +reportCompare(0, 0, "don't crash"); diff --git a/js/src/tests/ecma_5/Function/jstests.list b/js/src/tests/ecma_5/Function/jstests.list index d13af73580f2..8b1e06824776 100644 --- a/js/src/tests/ecma_5/Function/jstests.list +++ b/js/src/tests/ecma_5/Function/jstests.list @@ -6,3 +6,4 @@ script strict-arguments.js script arguments-property-attributes.js script function-bind.js script redefine-arguments-length.js +script builtin-no-prototype.js From c5ac73b2c62efef6a90e77996c4599b5e6d170a9 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Mon, 4 Oct 2010 16:54:22 -0700 Subject: [PATCH 036/284] Test for bug 600137 (fixed by patch for 445319). --- js/src/tests/js1_8_5/regress/jstests.list | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/tests/js1_8_5/regress/jstests.list b/js/src/tests/js1_8_5/regress/jstests.list index 218e82ce1ed4..6b812d62e8ff 100644 --- a/js/src/tests/js1_8_5/regress/jstests.list +++ b/js/src/tests/js1_8_5/regress/jstests.list @@ -46,3 +46,4 @@ fails-if(!xulRuntime.shell) script regress-597945-1.js script regress-597945-2.js script regress-598176.js script regress-600067.js +script regress-600137.js From c44759f91b382b955dcaafb299b641791d495ec4 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Mon, 4 Oct 2010 17:53:28 -0700 Subject: [PATCH 037/284] Adjust js1_5/extensions/regress-369696-02.js in wake of 445319 fix. --- js/src/tests/js1_5/extensions/regress-369696-02.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/tests/js1_5/extensions/regress-369696-02.js b/js/src/tests/js1_5/extensions/regress-369696-02.js index 8e929ea54821..87b8c87ab286 100755 --- a/js/src/tests/js1_5/extensions/regress-369696-02.js +++ b/js/src/tests/js1_5/extensions/regress-369696-02.js @@ -52,8 +52,8 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - native = encodeURIComponent; - n = native.prototype; + function fun() {} + n = fun.prototype; n.__defineGetter__("prototype", n.toSource); p = n.__lookupGetter__("prototype"); n = p; From 2eb9db76daddc8a5d8f6ba9137c767b27437f084 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Mon, 4 Oct 2010 18:30:04 -0700 Subject: [PATCH 038/284] Forgot the test for 600137. --- js/src/tests/js1_8_5/regress/regress-600137.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 js/src/tests/js1_8_5/regress/regress-600137.js diff --git a/js/src/tests/js1_8_5/regress/regress-600137.js b/js/src/tests/js1_8_5/regress/regress-600137.js new file mode 100644 index 000000000000..d3c27562f6f6 --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-600137.js @@ -0,0 +1,18 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +try { + for (var [e] = /x/ in d) { + function () {} + } +} catch (e) {} +try { + let(x = Object.freeze(this, /x/)) + e = #0= * .toString + function y() {} +} catch (e) {} + +reportCompare(0, 0, "don't crash"); From 3a6fcbd2d9f4e9222ff99cf901fc7c44b7267350 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Mon, 4 Oct 2010 18:41:32 -0700 Subject: [PATCH 039/284] More old test hacking in wake of 445319. --- js/src/tests/js1_5/Regress/regress-328012.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/js1_5/Regress/regress-328012.js b/js/src/tests/js1_5/Regress/regress-328012.js index 582e9460f091..bd9fc9f6c150 100644 --- a/js/src/tests/js1_5/Regress/regress-328012.js +++ b/js/src/tests/js1_5/Regress/regress-328012.js @@ -45,7 +45,7 @@ var expect = 'No Error'; printBugNumber(BUGNUMBER); printStatus (summary); -if (typeof focus != 'undefined') +if (typeof focus != 'undefined' && focus.prototype) { try { From 9d89e2e6b3ab6546925480d91822be74e33c2d02 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Oct 2010 14:13:33 -0700 Subject: [PATCH 040/284] ICs for scripted new (bug 589398, r=luke,dmandelin). --HG-- extra : rebase_source : d7311ce916e8d6c876d1851090806bc7d073bd63 --- js/src/jsapi.cpp | 2 +- js/src/jsbuiltins.h | 2 +- js/src/jscompartment.cpp | 4 +- js/src/jsdbgapi.cpp | 11 +- js/src/jsemit.cpp | 9 +- js/src/jsgc.cpp | 1 - js/src/jsinterp.cpp | 160 ++--- js/src/jsinterp.h | 2 +- js/src/jsiter.cpp | 2 +- js/src/jsobj.cpp | 25 +- js/src/jsobj.h | 12 +- js/src/jsopcode.tbl | 3 + js/src/jsproxy.cpp | 2 +- js/src/jsscript.cpp | 12 +- js/src/jsscript.h | 71 +- js/src/jstracer.cpp | 37 +- js/src/jstracer.h | 2 +- js/src/jsxdrapi.h | 2 +- js/src/methodjit/BaseAssembler.h | 5 + js/src/methodjit/Compiler.cpp | 610 ++++++++++-------- js/src/methodjit/Compiler.h | 27 +- js/src/methodjit/FrameState.cpp | 19 +- js/src/methodjit/FrameState.h | 7 +- js/src/methodjit/InvokeHelpers.cpp | 91 ++- js/src/methodjit/MethodJIT.cpp | 56 +- js/src/methodjit/MethodJIT.h | 89 ++- js/src/methodjit/MonoIC.cpp | 157 ++--- js/src/methodjit/MonoIC.h | 12 +- js/src/methodjit/PolyIC.cpp | 142 ++-- js/src/methodjit/PolyIC.h | 15 +- js/src/methodjit/Retcon.cpp | 65 +- js/src/methodjit/Retcon.h | 3 +- js/src/methodjit/StubCalls-inl.h | 1 - js/src/methodjit/StubCalls.cpp | 42 +- js/src/methodjit/StubCalls.h | 10 +- js/src/methodjit/StubCompiler.h | 9 + js/src/shell/js.cpp | 2 +- .../tests/jaeger/bug563000/simple-trap-2.js | 2 +- .../jaeger/bug563000/trap-force-return-1.js | 2 +- .../jaeger/bug563000/trap-own-callsite.js | 2 +- .../jaeger/bug563000/trap-self-as-parent.js | 2 +- .../jaeger/bug563000/trap-self-from-trap.js | 4 +- 42 files changed, 1011 insertions(+), 722 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 5b6917660ea0..0527dc85185d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2990,7 +2990,7 @@ JS_NewObjectForConstructor(JSContext *cx, const jsval *vp) CHECK_REQUEST(cx); assertSameCompartment(cx, *vp); - return js_NewInstance(cx, JSVAL_TO_OBJECT(*vp)); + return js_CreateThis(cx, JSVAL_TO_OBJECT(*vp)); } JS_PUBLIC_API(JSBool) diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index 275663e05028..e884098dcd78 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -612,7 +612,7 @@ JS_DECLARE_CALLINFO(js_NumberToString) /* Defined in jsobj.cpp. */ JS_DECLARE_CALLINFO(js_Object_tn) -JS_DECLARE_CALLINFO(js_NewInstanceFromTrace) +JS_DECLARE_CALLINFO(js_CreateThisFromTrace) JS_DECLARE_CALLINFO(js_NonEmptyObject) /* Defined in jsregexp.cpp. */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 96b4ad80dd0e..b6ee679d48c2 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -318,7 +318,7 @@ JSCompartment::sweep(JSContext *cx) #if defined JS_METHODJIT && defined JS_MONOIC for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) { JSScript *script = reinterpret_cast(cursor); - if (script->jit) + if (script->hasJITCode()) mjit::ic::SweepCallICs(script); } #endif @@ -333,7 +333,7 @@ JSCompartment::purge(JSContext *cx) for (JSScript *script = (JSScript *)scripts.next; &script->links != &scripts; script = (JSScript *)script->links.next) { - if (script->jit) { + if (script->hasJITCode()) { # if defined JS_POLYIC mjit::ic::PurgePICs(cx, script); # endif diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 0b92a81b6218..e6fc2e91fec5 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -116,8 +116,7 @@ js_SetDebugMode(JSContext *cx, JSBool debug) &script->links != &cx->compartment->scripts; script = (JSScript *)script->links.next) { if (script->debugMode != debug && - script->ncode && - script->ncode != JS_UNJITTABLE_METHOD && + script->hasJITCode() && !IsScriptLive(cx, script)) { /* * In the event that this fails, debug mode is left partially on, @@ -236,6 +235,10 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, return JS_FALSE; } + // Do not trap BEGIN, it's a special prologue opcode. + if (JSOp(*pc) == JSOP_BEGIN) + pc += JSOP_BEGIN_LENGTH; + JS_ASSERT((JSOp) *pc != JSOP_TRAP); junk = NULL; rt = cx->runtime; @@ -274,7 +277,7 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, cx->free(junk); #ifdef JS_METHODJIT - if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) { + if (script->hasJITCode()) { mjit::Recompiler recompiler(cx, script); if (!recompiler.recompile()) return JS_FALSE; @@ -327,7 +330,7 @@ JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, DBG_UNLOCK(cx->runtime); #ifdef JS_METHODJIT - if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) { + if (script->hasJITCode()) { mjit::Recompiler recompiler(cx, script); recompiler.recompile(); } diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 7d4db58b24f0..d857d1859a1d 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -3678,10 +3678,15 @@ bad: JSBool js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) { + CG_SWITCH_TO_PROLOG(cg); + JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); + if (js_Emit1(cx, cg, JSOP_BEGIN) < 0) + return false; + CG_SWITCH_TO_MAIN(cg); + if (cg->flags & TCF_FUN_IS_GENERATOR) { - /* JSOP_GENERATOR must be the first instruction. */ + /* JSOP_GENERATOR must be the first real instruction. */ CG_SWITCH_TO_PROLOG(cg); - JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) return false; CG_SWITCH_TO_MAIN(cg); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index fd122508ec9f..e1d10452d66e 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1569,7 +1569,6 @@ js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp) if (fp->isScriptFrame()) js_TraceScript(trc, fp->script()); - MarkValue(trc, fp->thisValue(), "this"); MarkValue(trc, fp->returnValue(), "rval"); } diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 2879e8094d0c..9a155087dbf9 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -134,12 +134,13 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) #if defined(JS_METHODJIT) && defined(JS_MONOIC) JSScript *script = this->script(); + js::mjit::JITScript *jit = script->getJIT(isConstructing()); size_t low = 0; - size_t high = script->jit->nCallICs; + size_t high = jit->nCallICs; while (high > low + 1) { /* Could overflow here on a script with 2 billion calls. Oh well. */ size_t mid = (high + low) / 2; - void *entry = script->callICs[mid].funGuard.executableAddress(); + void *entry = jit->callICs[mid].funGuard.executableAddress(); /* * Use >= here as the return address of the call is likely to be @@ -151,7 +152,7 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) low = mid; } - js::mjit::ic::CallICInfo &callIC = script->callICs[low]; + js::mjit::ic::CallICInfo &callIC = jit->callICs[low]; JS_ASSERT((uint8*)callIC.funGuard.executableAddress() + callIC.joinPointOffset == next->ncode_); return callIC.pc; @@ -616,7 +617,7 @@ struct AutoInterpPreparer { }; JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain) +RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) { JS_ASSERT(script); @@ -626,8 +627,11 @@ RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain AutoInterpPreparer prepareInterp(cx, script); + JS_ASSERT(fp == cx->fp()); + JS_ASSERT(fp->script() == script); + #ifdef JS_METHODJIT - mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fun, &scopeChain); + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fp); if (status == mjit::Compile_Error) return JS_FALSE; @@ -635,7 +639,7 @@ RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain return mjit::JaegerShot(cx); #endif - return Interpret(cx, cx->fp()); + return Interpret(cx, fp); } /* @@ -686,16 +690,16 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) /* Handle the empty-script special case. */ if (JS_UNLIKELY(script->isEmpty())) { if (flags & JSINVOKE_CONSTRUCT) { - JS_ASSERT(args.thisv().isObject()); - args.rval() = args.thisv(); + JSObject *obj = js_CreateThisForFunction(cx, &callee); + if (!obj) + return false; + args.rval().setObject(*obj); } else { args.rval().setUndefined(); } return true; } - JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, args.thisv().isObject()); - /* Get pointer to new frame/slots, prepare arguments. */ InvokeFrameGuard frame; if (JS_UNLIKELY(!cx->stack().getInvokeFrame(cx, args, fun, script, &flags, &frame))) @@ -718,19 +722,20 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) * and fp->scopeChain is correct because the thisObject hook may call * JS_GetScopeChain. */ - Value &thisv = fp->functionThis(); - JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !thisv.isPrimitive()); - if (thisv.isObject() && !(flags & JSINVOKE_CONSTRUCT)) { - /* - * We must call the thisObject hook in case we are not called from the - * interpreter, where a prior bytecode has computed an appropriate - * |this| already. - */ - JSObject *thisp = thisv.toObject().thisObject(cx); - if (!thisp) - return false; - JS_ASSERT(IsSaneThisObject(*thisp)); - thisv.setObject(*thisp); + if (!(flags & JSINVOKE_CONSTRUCT)) { + Value &thisv = fp->functionThis(); + if (thisv.isObject()) { + /* + * We must call the thisObject hook in case we are not called from the + * interpreter, where a prior bytecode has computed an appropriate + * |this| already. + */ + JSObject *thisp = thisv.toObject().thisObject(cx); + if (!thisp) + return false; + JS_ASSERT(IsSaneThisObject(*thisp)); + thisv.setObject(*thisp); + } } JSInterpreterHook hook = cx->debugHooks->callHook; @@ -743,7 +748,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) { AutoPreserveEnumerators preserve(cx); Probes::enterJSFun(cx, fun); - ok = RunScript(cx, script, fun, fp->scopeChain()); + ok = RunScript(cx, script, fp); Probes::exitJSFun(cx, fun); } @@ -756,6 +761,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) PutActivationObjects(cx, fp); args.rval() = fp->returnValue(); + JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !args.rval().isPrimitive()); + return ok; } @@ -900,7 +907,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script, /* Run script until JSOP_STOP or error. */ AutoPreserveEnumerators preserve(cx); - JSBool ok = RunScript(cx, script, NULL, frame.fp()->scopeChain()); + JSBool ok = RunScript(cx, script, frame.fp()); if (result) *result = frame.fp()->returnValue(); @@ -1153,8 +1160,9 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef) /* Handle the fast-constructors cases before falling into the general case . */ Class *clasp = callee->getClass(); + JSFunction *fun = NULL; if (clasp == &js_FunctionClass) { - JSFunction *fun = callee->getFunctionPrivate(); + fun = callee->getFunctionPrivate(); if (fun->isConstructor()) { args.thisv().setMagicWithObjectOrNullPayload(NULL); return CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base()); @@ -1164,25 +1172,30 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef) return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base()); } - /* Construct 'this'. */ - JSObject *obj = js_NewInstance(cx, callee); - if (!obj) - return false; - args.thisv().setObject(*obj); + /* Scripts create their own |this| in JSOP_BEGIN */ + if (!fun || !fun->isInterpreted()) { + JSObject *obj = js_CreateThis(cx, callee); + if (!obj) + return false; + args.thisv().setObject(*obj); + } if (!Invoke(cx, args, JSINVOKE_CONSTRUCT)) return false; - /* Check the return value and if it's primitive, force it to be obj. */ if (args.rval().isPrimitive()) { - if (callee->getClass() != &js_FunctionClass) { + if (clasp != &js_FunctionClass) { /* native [[Construct]] returning primitive is error */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_NEW_RESULT, js_ValueToPrintableString(cx, args.rval())); return false; } - args.rval().setObject(*obj); + + /* The interpreter fixes rval for us. */ + JS_ASSERT(!fun->isInterpreted()); + + args.rval() = args.thisv(); } JS_RUNTIME_METER(cx->runtime, constructs); @@ -2290,7 +2303,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN do { \ JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \ if (leaveOnSafePoint && !regs.fp->hasImacropc() && \ - script->nmap && script->nmap[regs.pc - script->code]) { \ + script->hasNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \ JS_ASSERT(!TRACE_RECORDER(cx)); \ interpReturnOK = true; \ goto stop_recording; \ @@ -4477,6 +4490,41 @@ BEGIN_CASE(JSOP_ENUMELEM) } END_CASE(JSOP_ENUMELEM) +BEGIN_CASE(JSOP_BEGIN) +{ + if (regs.fp->isConstructing()) { + JSObject *obj2 = js_CreateThisForFunction(cx, ®s.fp->callee()); + if (!obj2) + goto error; + regs.fp->functionThis().setObject(*obj2); + } + + /* Call the debugger hook if present. */ + if (JSInterpreterHook hook = cx->debugHooks->callHook) { + regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0, + cx->debugHooks->callHookData)); + CHECK_INTERRUPT_HANDLER(); + } + + JS_RUNTIME_METER(rt, inlineCalls); + + Probes::enterJSFun(cx, regs.fp->fun()); + +#ifdef JS_METHODJIT + /* Try to ensure methods are method JIT'd. */ + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp); + if (status == mjit::Compile_Error) + goto error; + if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { + if (!mjit::JaegerShot(cx)) + goto error; + interpReturnOK = true; + goto inline_return; + } +#endif +} +END_CASE(JSOP_BEGIN) + { JSFunction *newfun; JSObject *callee; @@ -4498,24 +4546,15 @@ BEGIN_CASE(JSOP_NEW) if (IsFunctionObject(vp[0], &callee)) { newfun = callee->getFunctionPrivate(); if (newfun->isInterpreted()) { - /* Root as we go using vp[1]. */ - if (!callee->getProperty(cx, - ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), - &vp[1])) { - goto error; - } - JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL; - JSObject *obj2 = NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent()); - if (!obj2) - goto error; - if (newfun->u.i.script->isEmpty()) { + JSObject *obj2 = js_CreateThisForFunction(cx, callee); + if (!obj2) + goto error; vp[0].setObject(*obj2); regs.sp = vp + 1; goto end_new; } - vp[1].setObject(*obj2); flags = JSFRAME_CONSTRUCTING; goto inline_call; } @@ -4584,38 +4623,13 @@ BEGIN_CASE(JSOP_APPLY) if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp)) goto error; - /* Call the debugger hook if present. */ - if (JSInterpreterHook hook = cx->debugHooks->callHook) { - regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0, - cx->debugHooks->callHookData)); - CHECK_INTERRUPT_HANDLER(); - } - inlineCallCount++; - JS_RUNTIME_METER(rt, inlineCalls); - - Probes::enterJSFun(cx, newfun); TRACE_0(EnterFrame); -#ifdef JS_METHODJIT - /* Try to ensure methods are method JIT'd. */ - { - JSObject *scope = ®s.fp->scopeChain(); - mjit::CompileStatus status = mjit::CanMethodJIT(cx, newscript, newfun, scope); - if (status == mjit::Compile_Error) - goto error; - if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) { - if (!mjit::JaegerShot(cx)) - goto error; - interpReturnOK = true; - goto inline_return; - } - } -#endif - /* Load first op and dispatch it (safe since JSOP_STOP). */ op = (JSOp) *regs.pc; + JS_ASSERT(op == JSOP_BEGIN); DO_OP(); } diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 04708a494bdd..9b09ddaa1f40 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -968,7 +968,7 @@ extern JS_REQUIRES_STACK JS_NEVER_INLINE bool Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, uintN interpFlags = 0); extern JS_REQUIRES_STACK bool -RunScript(JSContext *cx, JSScript *script, JSFunction *fun, JSObject &scopeChain); +RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp); #define JSPROP_INITIALIZER 0x100 /* NB: Not a valid property attribute. */ diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 33e569b508b6..a5d18f38abcf 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1283,7 +1283,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, JSObject *enumerators = cx->enumerators; cx->enumerators = gen->enumerators; - ok = RunScript(cx, stackfp->script(), stackfp->fun(), stackfp->scopeChain()); + ok = RunScript(cx, stackfp->script(), stackfp); gen->enumerators = cx->enumerators; cx->enumerators = enumerators; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 1f04385b6726..cf4ae4d336ba 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2790,7 +2790,7 @@ js_Object(JSContext *cx, uintN argc, Value *vp) } JSObject* -js_NewInstance(JSContext *cx, JSObject *callee) +js_CreateThis(JSContext *cx, JSObject *callee) { Class *clasp = callee->getClass(); @@ -2810,6 +2810,25 @@ js_NewInstance(JSContext *cx, JSObject *callee) return NewObject(cx, newclasp, proto, parent); } +JSObject * +js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto) +{ + return NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent()); +} + +JSObject * +js_CreateThisForFunction(JSContext *cx, JSObject *callee) +{ + Value protov; + if (!callee->getProperty(cx, + ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), + &protov)) { + return NULL; + } + JSObject *proto = protov.isObject() ? &protov.toObject() : NULL; + return js_CreateThisForFunctionWithProto(cx, callee, proto); +} + #ifdef JS_TRACER static JS_ALWAYS_INLINE JSObject* @@ -2860,7 +2879,7 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, ST nanojit::ACCSET_STORE_ANY) JSObject* FASTCALL -js_NewInstanceFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) +js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) { JS_ASSERT(JS_ON_TRACE(cx)); JS_ASSERT(ctor->isFunction()); @@ -2911,7 +2930,7 @@ js_NewInstanceFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) return NewNonFunction(cx, clasp, proto, parent); } -JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstanceFromTrace, CONTEXT, CLASS, OBJECT, 0, +JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0, nanojit::ACCSET_STORE_ANY) #else /* !JS_TRACER */ diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 5b4b66b66cd2..c76af07c3ed3 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1407,8 +1407,18 @@ extern JSObject * js_ConstructObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, uintN argc, js::Value *argv); +// Specialized call for constructing |this| with a known function callee, +// and a known prototype. extern JSObject * -js_NewInstance(JSContext *cx, JSObject *callee); +js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto); + +// Specialized call for constructing |this| with a known function callee. +extern JSObject * +js_CreateThisForFunction(JSContext *cx, JSObject *callee); + +// Generic call for constructing |this|. +extern JSObject * +js_CreateThis(JSContext *cx, JSObject *callee); extern jsid js_CheckForStringIndex(jsid id); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 89c895c84fd5..5707ea478dc8 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -620,3 +620,6 @@ OPDEF(JSOP_FORGLOBAL, 246,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL */ OPDEF(JSOP_BLOCKCHAIN, 247,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT) OPDEF(JSOP_NULLBLOCKCHAIN,248,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE) + +OPDEF(JSOP_BEGIN, 249,"begin", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_TMPSLOT) + diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 4857ba2ee508..937b07515e7b 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1235,7 +1235,7 @@ callable_Call(JSContext *cx, uintN argc, Value *vp) static JSBool callable_Construct(JSContext *cx, uintN argc, Value *vp) { - JSObject *thisobj = js_NewInstance(cx, &JS_CALLEE(cx, vp).toObject()); + JSObject *thisobj = js_CreateThis(cx, &JS_CALLEE(cx, vp).toObject()); if (!thisobj) return false; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index ef2649c5ebc7..db8af79d5ed3 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -118,7 +118,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript, uint32 length, lineno, nslots, magic; uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, nconsts, i; uint32 prologLength, version, encodedClosedCount; - uint16 nClosedArgs, nClosedVars; + uint16 nClosedArgs = 0, nClosedVars = 0; JSPrincipals *principals; uint32 encodeable; JSBool filenameWasSaved; @@ -1641,16 +1641,6 @@ js_GetScriptLineExtent(JSScript *script) return 1 + lineno - script->lineno; } -#ifdef JS_METHODJIT -bool -JSScript::isValidJitCode(void *jcode) -{ - return (char*)jcode >= (char*)jit->invoke && - (char*)jcode < (char*)jit->invoke + - jit->inlineLength + jit->outOfLineLength; -} -#endif - void JSScript::copyClosedSlotsTo(JSScript *other) { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 88321350d88a..b95dc9e5f337 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -169,21 +169,20 @@ struct GlobalSlotArray { namespace JSC { class ExecutablePool; } + +#define JS_UNJITTABLE_SCRIPT (reinterpret_cast(1)) + +enum JITScriptStatus { + JITScript_None, + JITScript_Invalid, + JITScript_Valid +}; + namespace js { namespace mjit { struct JITScript; -namespace ic { -# if defined JS_POLYIC - struct PICInfo; -# endif -# if defined JS_MONOIC - struct MICInfo; - struct CallICInfo; -# endif -} -struct CallSite; } } #endif @@ -285,20 +284,35 @@ struct JSScript { public: #ifdef JS_METHODJIT - // Note: the other pointers in this group may be non-NULL only if - // |execPool| is non-NULL. - void *ncode; /* native code compiled by the method JIT */ - void **nmap; /* maps PCs to native code */ - js::mjit::JITScript *jit; /* Extra JIT info */ -# if defined JS_POLYIC - js::mjit::ic::PICInfo *pics; /* PICs in this script */ -# endif -# if defined JS_MONOIC - js::mjit::ic::MICInfo *mics; /* MICs in this script. */ - js::mjit::ic::CallICInfo *callICs; /* CallICs in this script. */ -# endif + // Fast-cached pointers to make calls faster. These are also used to + // quickly test whether there is JIT code; a NULL value means no + // compilation has been attempted. A JS_UNJITTABLE_SCRIPT value means + // compilation failed. Any value is the arity-check entry point. + void *jitArityCheckNormal; + void *jitArityCheckCtor; - bool isValidJitCode(void *jcode); + js::mjit::JITScript *jitNormal; /* Extra JIT info for normal scripts */ + js::mjit::JITScript *jitCtor; /* Extra JIT info for constructors */ + + bool hasJITCode() { + return jitNormal || jitCtor; + } + + inline void **maybeNativeMap(bool constructing); + inline bool hasNativeCodeForPC(bool constructing, jsbytecode *pc); + + js::mjit::JITScript *getJIT(bool constructing) { + return constructing ? jitCtor : jitNormal; + } + + JITScriptStatus getJITStatus(bool constructing) { + void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal; + if (addr == NULL) + return JITScript_None; + if (addr == JS_UNJITTABLE_SCRIPT) + return JITScript_Invalid; + return JITScript_Valid; + } #endif /* Script notes are allocated right after the code. */ @@ -393,17 +407,6 @@ struct JSScript { return const_cast(&emptyScriptConst); } -#ifdef JS_METHODJIT - /* - * Map the given PC to the corresponding native code address. - */ - void *pcToNative(jsbytecode *pc) { - JS_ASSERT(nmap); - JS_ASSERT(nmap[pc - code]); - return nmap[pc - code]; - } -#endif - uint32 getClosedArg(uint32 index) { JS_ASSERT(index < nClosedArgs); return closedSlots[index]; diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 81cdb7e15213..9cb913a775ab 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -11051,6 +11051,20 @@ TraceRecorder::emitNativePropertyOp(const Shape* shape, LIns* obj_ins, guard(true, lir->insEqI_0(status_ins), STATUS_EXIT); } +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BEGIN() +{ + JSStackFrame* fp = cx->fp(); + if (fp->isConstructing()) { + LIns* callee_ins = get(&cx->fp()->calleeValue()); + LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins }; + LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); + guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); + set(&fp->thisValue(), tv_ins); + } + return ARECORD_CONTINUE; +} + JS_REQUIRES_STACK RecordingStatus TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], bool rooted) { @@ -11360,7 +11374,7 @@ TraceRecorder::callNative(uintN argc, JSOp mode) clasp = &js_ObjectClass; JS_ASSERT(((jsuword) clasp & 3) == 0); - // Abort on |new Function|. js_NewInstance would allocate a regular- + // Abort on |new Function|. js_CreateThis would allocate a regular- // sized JSObject, not a Function-sized one. (The Function ctor would // deep-bail anyway but let's not go there.) if (clasp == &js_FunctionClass) @@ -11379,7 +11393,7 @@ TraceRecorder::callNative(uintN argc, JSOp mode) args[0] = INS_CONSTOBJ(funobj); args[1] = INS_CONSTPTR(clasp); args[2] = cx_ins; - newobj_ins = lir->insCall(&js_NewInstanceFromTrace_ci, args); + newobj_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); guard(false, lir->insEqP_0(newobj_ins), OOM_EXIT); /* @@ -11504,15 +11518,8 @@ TraceRecorder::functionCall(uintN argc, JSOp mode) } #endif - if (FUN_INTERPRETED(fun)) { - if (mode == JSOP_NEW) { - LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; - LIns* tv_ins = lir->insCall(&js_NewInstanceFromTrace_ci, args); - guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); - set(&tval, tv_ins); - } + if (FUN_INTERPRETED(fun)) return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW); - } Native native = fun->maybeNative(); Value* argv = &tval + 1; @@ -13306,7 +13313,15 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, * and does not call any TR::record_*CallComplete hook. */ if (fun->u.i.script->isEmpty()) { - LIns* rval_ins = constructing ? stack(-1 - argc) : INS_UNDEFINED(); + LIns* rval_ins; + if (constructing) { + LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; + LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args); + guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); + rval_ins = tv_ins; + } else { + rval_ins = INS_UNDEFINED(); + } stack(-2 - argc, rval_ins); return RECORD_CONTINUE; } diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 25e291e3b76a..cf5280b24d03 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -949,7 +949,7 @@ class TraceRecorder /* Carry the return value from a native call to the record_NativeCallComplete. */ nanojit::LIns* native_rval_ins; - /* Carry the return value of js_NewInstance to record_NativeCallComplete. */ + /* Carry the return value of js_CreateThis to record_NativeCallComplete. */ nanojit::LIns* newobj_ins; /* Carry the JSSpecializedNative used to generate a call to record_NativeCallComplete. */ diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index 70e10b0f9c62..e53bc9392211 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -205,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); * before deserialization of bytecode. If the saved version does not match * the current version, abort deserialization and invalidate the file. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 71) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 72) /* * Library-private functions. diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 110c53b57c83..5339d00eee1b 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -342,6 +342,11 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste } }; +/* Return f if the script is strict mode code, f otherwise. */ +#define STRICT_VARIANT(f) \ + (FunctionTemplateConditional(script->strictModeCode, \ + f, f)) + /* Save some typing. */ static const JSC::MacroAssembler::RegisterID JSFrameReg = BaseAssembler::JSFrameReg; static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = BaseAssembler::JSReturnReg_Type; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 83f70832be29..06a7a8d49108 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -74,12 +74,16 @@ static const char *OpcodeNames[] = { }; #endif -mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) +mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp) : BaseCompiler(cx), - script(script), - scopeChain(scopeChain), + fp(fp), + script(fp->script()), + scopeChain(&fp->scopeChain()), globalObj(scopeChain->getGlobal()), - fun(fun), + fun(fp->isFunctionFrame() && !fp->isEvalFrame() + ? fp->fun() + : NULL), + isConstructing(fp->isConstructing()), analysis(cx, script), jumpMap(NULL), frame(cx, script, masm), branchPatches(ContextAllocPolicy(cx)), #if defined JS_MONOIC @@ -99,6 +103,34 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObj { } +CompileStatus +mjit::Compiler::compile() +{ + JS_ASSERT(!script->isEmpty()); + JS_ASSERT_IF(isConstructing, !script->jitCtor); + JS_ASSERT_IF(!isConstructing, !script->jitNormal); + + JITScript **jit = isConstructing ? &script->jitCtor : &script->jitNormal; + void **checkAddr = isConstructing + ? &script->jitArityCheckCtor + : &script->jitArityCheckNormal; + + CompileStatus status = performCompilation(jit); + if (status == Compile_Okay) { + // Global scripts don't have an arity check entry. That's okay, we + // just need a pointer so the VM can quickly decide whether this + // method can be JIT'd or not. Global scripts cannot be IC'd, since + // they have no functions, so there is no danger. + *checkAddr = (*jit)->arityCheckEntry + ? (*jit)->arityCheckEntry + : (*jit)->invokeEntry; + } else { + *checkAddr = JS_UNJITTABLE_SCRIPT; + } + + return status; +} + #define CHECK_STATUS(expr) \ JS_BEGIN_MACRO \ CompileStatus status_ = (expr); \ @@ -107,10 +139,8 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObj JS_END_MACRO CompileStatus -mjit::Compiler::Compile() +mjit::Compiler::performCompilation(JITScript **jitp) { - JS_ASSERT(!script->ncode); - JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n", script->filename, script->lineno, script->length); @@ -152,7 +182,7 @@ mjit::Compiler::Compile() CHECK_STATUS(generatePrologue()); CHECK_STATUS(generateMethod()); CHECK_STATUS(generateEpilogue()); - CHECK_STATUS(finishThisUp()); + CHECK_STATUS(finishThisUp(jitp)); #ifdef JS_METHODJIT_SPEW prof.stop(); @@ -160,7 +190,7 @@ mjit::Compiler::Compile() #endif JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%ld\")\n", - (void*)script->ncode, masm.size() + stubcc.size()); + (*jitp)->code.m_code.executableAddress(), (*jitp)->code.m_size); return Compile_Okay; } @@ -173,18 +203,13 @@ mjit::Compiler::~Compiler() } CompileStatus JS_NEVER_INLINE -mjit::TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) +mjit::TryCompile(JSContext *cx, JSStackFrame *fp) { - Compiler cc(cx, script, fun, scopeChain); + JS_ASSERT(cx->fp() == fp); - JS_ASSERT(!script->ncode); - JS_ASSERT(!script->isEmpty()); + Compiler cc(cx, fp); - CompileStatus status = cc.Compile(); - if (status != Compile_Okay) - script->ncode = JS_UNJITTABLE_METHOD; - - return status; + return cc.compile(); } CompileStatus @@ -290,7 +315,7 @@ mjit::Compiler::generateEpilogue() } CompileStatus -mjit::Compiler::finishThisUp() +mjit::Compiler::finishThisUp(JITScript **jitp) { for (size_t i = 0; i < branchPatches.length(); i++) { Label label = labelOf(branchPatches[i].pc); @@ -336,18 +361,16 @@ mjit::Compiler::finishThisUp() return Compile_Error; } - script->jit = (JITScript *)cursor; + JITScript *jit = (JITScript *)cursor; cursor += sizeof(JITScript); - script->jit->execPool = execPool; - script->jit->inlineLength = masm.size(); - script->jit->outOfLineLength = stubcc.size(); - script->jit->nCallSites = callSites.length(); - script->jit->invoke = result; + jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size()); + jit->nCallSites = callSites.length(); + jit->invokeEntry = result; /* Build the pc -> ncode mapping. */ void **nmap = (void **)cursor; - script->nmap = nmap; + jit->nmap = nmap; cursor += sizeof(void *) * script->length; for (size_t i = 0; i < script->length; i++) { @@ -358,107 +381,116 @@ mjit::Compiler::finishThisUp() } } - if (fun) - script->jit->arityCheck = stubCode.locationOf(arityLabel).executableAddress(); - -#if defined JS_MONOIC - script->jit->nMICs = mics.length(); - if (mics.length()) { - script->mics = (ic::MICInfo *)cursor; - cursor += sizeof(ic::MICInfo) * mics.length(); - } else { - script->mics = NULL; + if (fun) { + jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress(); + jit->fastEntry = fullCode.locationOf(invokeLabel).executableAddress(); } - for (size_t i = 0; i < mics.length(); i++) { - script->mics[i].kind = mics[i].kind; - script->mics[i].entry = fullCode.locationOf(mics[i].entry); - switch (mics[i].kind) { - case ic::MICInfo::GET: - case ic::MICInfo::SET: - script->mics[i].load = fullCode.locationOf(mics[i].load); - script->mics[i].shape = fullCode.locationOf(mics[i].shape); - script->mics[i].stubCall = stubCode.locationOf(mics[i].call); - script->mics[i].stubEntry = stubCode.locationOf(mics[i].stubEntry); - script->mics[i].u.name.typeConst = mics[i].u.name.typeConst; - script->mics[i].u.name.dataConst = mics[i].u.name.dataConst; +#if defined JS_MONOIC + jit->nMICs = mics.length(); + if (mics.length()) { + jit->mics = (ic::MICInfo *)cursor; + cursor += sizeof(ic::MICInfo) * mics.length(); + } else { + jit->mics = NULL; + } + + if (ic::MICInfo *scriptMICs = jit->mics) { + for (size_t i = 0; i < mics.length(); i++) { + scriptMICs[i].kind = mics[i].kind; + scriptMICs[i].entry = fullCode.locationOf(mics[i].entry); + switch (mics[i].kind) { + case ic::MICInfo::GET: + case ic::MICInfo::SET: + scriptMICs[i].load = fullCode.locationOf(mics[i].load); + scriptMICs[i].shape = fullCode.locationOf(mics[i].shape); + scriptMICs[i].stubCall = stubCode.locationOf(mics[i].call); + scriptMICs[i].stubEntry = stubCode.locationOf(mics[i].stubEntry); + scriptMICs[i].u.name.typeConst = mics[i].u.name.typeConst; + scriptMICs[i].u.name.dataConst = mics[i].u.name.dataConst; #if defined JS_PUNBOX64 - script->mics[i].patchValueOffset = mics[i].patchValueOffset; + scriptMICs[i].patchValueOffset = mics[i].patchValueOffset; #endif - break; - case ic::MICInfo::TRACER: { - uint32 offs = uint32(mics[i].jumpTarget - script->code); - JS_ASSERT(jumpMap[offs].isValid()); - script->mics[i].traceHint = fullCode.locationOf(mics[i].traceHint); - script->mics[i].load = fullCode.locationOf(jumpMap[offs]); - script->mics[i].u.hints.hasSlowTraceHintOne = mics[i].slowTraceHintOne.isSet(); - if (mics[i].slowTraceHintOne.isSet()) - script->mics[i].slowTraceHintOne = stubCode.locationOf(mics[i].slowTraceHintOne.get()); - script->mics[i].u.hints.hasSlowTraceHintTwo = mics[i].slowTraceHintTwo.isSet(); - if (mics[i].slowTraceHintTwo.isSet()) - script->mics[i].slowTraceHintTwo = stubCode.locationOf(mics[i].slowTraceHintTwo.get()); - break; - } - default: - JS_NOT_REACHED("Bad MIC kind"); + break; + case ic::MICInfo::TRACER: { + uint32 offs = uint32(mics[i].jumpTarget - script->code); + JS_ASSERT(jumpMap[offs].isValid()); + scriptMICs[i].traceHint = fullCode.locationOf(mics[i].traceHint); + scriptMICs[i].load = fullCode.locationOf(jumpMap[offs]); + scriptMICs[i].u.hints.hasSlowTraceHintOne = mics[i].slowTraceHintOne.isSet(); + if (mics[i].slowTraceHintOne.isSet()) + scriptMICs[i].slowTraceHintOne = stubCode.locationOf(mics[i].slowTraceHintOne.get()); + scriptMICs[i].u.hints.hasSlowTraceHintTwo = mics[i].slowTraceHintTwo.isSet(); + if (mics[i].slowTraceHintTwo.isSet()) + scriptMICs[i].slowTraceHintTwo = stubCode.locationOf(mics[i].slowTraceHintTwo.get()); + break; + } + default: + JS_NOT_REACHED("Bad MIC kind"); + } + stubCode.patch(mics[i].addrLabel, &scriptMICs[i]); } } - script->jit->nCallICs = callICs.length(); + jit->nCallICs = callICs.length(); if (callICs.length()) { - script->callICs = (ic::CallICInfo *)cursor; + jit->callICs = (ic::CallICInfo *)cursor; cursor += sizeof(ic::CallICInfo) * callICs.length(); } else { - script->callICs = NULL; + jit->callICs = NULL; } - for (size_t i = 0; i < callICs.length(); i++) { - script->callICs[i].reset(); - script->callICs[i].funGuard = fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].funJump = fullCode.locationOf(callICs[i].funJump); - script->callICs[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); + if (ic::CallICInfo *cics = jit->callICs) { + for (size_t i = 0; i < callICs.length(); i++) { + cics[i].reset(); + cics[i].funGuard = fullCode.locationOf(callICs[i].funGuard); + cics[i].funJump = fullCode.locationOf(callICs[i].funJump); + cics[i].slowPathStart = stubCode.locationOf(callICs[i].slowPathStart); - /* Compute the hot call offset. */ - uint32 offset = fullCode.locationOf(callICs[i].hotJump) - - fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].hotJumpOffset = offset; - JS_ASSERT(script->callICs[i].hotJumpOffset == offset); + /* Compute the hot call offset. */ + uint32 offset = fullCode.locationOf(callICs[i].hotJump) - + fullCode.locationOf(callICs[i].funGuard); + cics[i].hotJumpOffset = offset; + JS_ASSERT(cics[i].hotJumpOffset == offset); - /* Compute the join point offset. */ - offset = fullCode.locationOf(callICs[i].joinPoint) - - fullCode.locationOf(callICs[i].funGuard); - script->callICs[i].joinPointOffset = offset; - JS_ASSERT(script->callICs[i].joinPointOffset == offset); - - /* Compute the OOL call offset. */ - offset = stubCode.locationOf(callICs[i].oolCall) - - stubCode.locationOf(callICs[i].slowPathStart); - script->callICs[i].oolCallOffset = offset; - JS_ASSERT(script->callICs[i].oolCallOffset == offset); + /* Compute the join point offset. */ + offset = fullCode.locationOf(callICs[i].joinPoint) - + fullCode.locationOf(callICs[i].funGuard); + cics[i].joinPointOffset = offset; + JS_ASSERT(cics[i].joinPointOffset == offset); + + /* Compute the OOL call offset. */ + offset = stubCode.locationOf(callICs[i].oolCall) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].oolCallOffset = offset; + JS_ASSERT(cics[i].oolCallOffset == offset); - /* Compute the OOL jump offset. */ - offset = stubCode.locationOf(callICs[i].oolJump) - - stubCode.locationOf(callICs[i].slowPathStart); - script->callICs[i].oolJumpOffset = offset; - JS_ASSERT(script->callICs[i].oolJumpOffset == offset); + /* Compute the OOL jump offset. */ + offset = stubCode.locationOf(callICs[i].oolJump) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].oolJumpOffset = offset; + JS_ASSERT(cics[i].oolJumpOffset == offset); - /* Compute the slow join point offset. */ - offset = stubCode.locationOf(callICs[i].slowJoinPoint) - - stubCode.locationOf(callICs[i].slowPathStart); - script->callICs[i].slowJoinOffset = offset; - JS_ASSERT(script->callICs[i].slowJoinOffset == offset); + /* Compute the slow join point offset. */ + offset = stubCode.locationOf(callICs[i].slowJoinPoint) - + stubCode.locationOf(callICs[i].slowPathStart); + cics[i].slowJoinOffset = offset; + JS_ASSERT(cics[i].slowJoinOffset == offset); - /* Compute the join point offset for continuing on the hot path. */ - offset = stubCode.locationOf(callICs[i].hotPathLabel) - - stubCode.locationOf(callICs[i].funGuard); - script->callICs[i].hotPathOffset = offset; - JS_ASSERT(script->callICs[i].hotPathOffset == offset); + /* Compute the join point offset for continuing on the hot path. */ + offset = stubCode.locationOf(callICs[i].hotPathLabel) - + stubCode.locationOf(callICs[i].funGuard); + cics[i].hotPathOffset = offset; + JS_ASSERT(cics[i].hotPathOffset == offset); - script->callICs[i].pc = callICs[i].pc; - script->callICs[i].argc = callICs[i].argc; - script->callICs[i].funObjReg = callICs[i].funObjReg; - script->callICs[i].funPtrReg = callICs[i].funPtrReg; - script->callICs[i].frameDepth = callICs[i].frameDepth; + cics[i].pc = callICs[i].pc; + cics[i].argc = callICs[i].argc; + cics[i].funObjReg = callICs[i].funObjReg; + cics[i].funPtrReg = callICs[i].funPtrReg; + cics[i].frameDepth = callICs[i].frameDepth; + stubCode.patch(callICs[i].addrLabel1, &cics[i]); + stubCode.patch(callICs[i].addrLabel2, &cics[i]); + } } #endif /* JS_MONOIC */ @@ -471,44 +503,47 @@ mjit::Compiler::finishThisUp() } #if defined JS_POLYIC - script->jit->nPICs = pics.length(); + jit->nPICs = pics.length(); if (pics.length()) { - script->pics = (ic::PICInfo *)cursor; + jit->pics = (ic::PICInfo *)cursor; cursor += sizeof(ic::PICInfo) * pics.length(); } else { - script->pics = NULL; + jit->pics = NULL; } - for (size_t i = 0; i < pics.length(); i++) { - pics[i].copySimpleMembersTo(script->pics[i]); - script->pics[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart); - script->pics[i].storeBack = fullCode.locationOf(pics[i].storeBack); - script->pics[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart); - script->pics[i].callReturn = uint16((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() - - (uint8*)script->pics[i].slowPathStart.executableAddress()); - script->pics[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) - - masm.distanceOf(pics[i].fastPathStart); - JS_ASSERT(script->pics[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) - - masm.distanceOf(pics[i].fastPathStart)); - script->pics[i].shapeRegHasBaseShape = true; + if (ic::PICInfo *scriptPICs = jit->pics) { + for (size_t i = 0; i < pics.length(); i++) { + pics[i].copySimpleMembersTo(scriptPICs[i]); + scriptPICs[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart); + scriptPICs[i].storeBack = fullCode.locationOf(pics[i].storeBack); + scriptPICs[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart); + scriptPICs[i].callReturn = uint16((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() - + (uint8*)scriptPICs[i].slowPathStart.executableAddress()); + scriptPICs[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) - + masm.distanceOf(pics[i].fastPathStart); + JS_ASSERT(scriptPICs[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) - + masm.distanceOf(pics[i].fastPathStart)); + scriptPICs[i].shapeRegHasBaseShape = true; # if defined JS_CPU_X64 - memcpy(&script->pics[i].labels, &pics[i].labels, sizeof(PICLabels)); + memcpy(&scriptPICs[i].labels, &pics[i].labels, sizeof(PICLabels)); # endif - if (pics[i].kind == ic::PICInfo::SET || - pics[i].kind == ic::PICInfo::SETMETHOD) { - script->pics[i].u.vr = pics[i].vr; - } else if (pics[i].kind != ic::PICInfo::NAME) { - if (pics[i].hasTypeCheck) { - int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) - - stubcc.masm.distanceOf(pics[i].slowPathStart); - JS_ASSERT(distance <= 0); - script->pics[i].u.get.typeCheckOffset = distance; + if (pics[i].kind == ic::PICInfo::SET || + pics[i].kind == ic::PICInfo::SETMETHOD) { + scriptPICs[i].u.vr = pics[i].vr; + } else if (pics[i].kind != ic::PICInfo::NAME) { + if (pics[i].hasTypeCheck) { + int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) - + stubcc.masm.distanceOf(pics[i].slowPathStart); + JS_ASSERT(distance <= 0); + scriptPICs[i].u.get.typeCheckOffset = distance; + } } + new (&scriptPICs[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy()); + scriptPICs[i].reset(); + stubCode.patch(pics[i].addrLabel, &scriptPICs[i]); } - new (&script->pics[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy()); - script->pics[i].reset(); } #endif /* JS_POLYIC */ @@ -534,10 +569,8 @@ mjit::Compiler::finishThisUp() JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size()); - script->ncode = (uint8 *)(result + masm.distanceOf(invokeLabel)); - /* Build the table of call sites. */ - script->jit->nCallSites = callSites.length(); + jit->nCallSites = callSites.length(); if (callSites.length()) { CallSite *callSiteList = (CallSite *)cursor; cursor += sizeof(CallSite) * callSites.length(); @@ -550,12 +583,14 @@ mjit::Compiler::finishThisUp() callSiteList[i].pcOffset = callSites[i].pc - script->code; callSiteList[i].id = callSites[i].id; } - script->jit->callSites = callSiteList; + jit->callSites = callSiteList; } else { - script->jit->callSites = NULL; + jit->callSites = NULL; } - JS_ASSERT(size_t(cursor - (uint8*)script->jit) == totalBytes); + JS_ASSERT(size_t(cursor - (uint8*)jit) == totalBytes); + + *jitp = jit; return Compile_Okay; } @@ -1662,6 +1697,11 @@ mjit::Compiler::generateMethod() break; END_CASE(JSOP_GLOBALINC) + BEGIN_CASE(JSOP_BEGIN) + if (isConstructing) + constructThis(); + END_CASE(JSOP_BEGIN) + default: /* Sorry, this opcode isn't implemented yet. */ #ifdef JS_METHODJIT_SPEW @@ -1717,15 +1757,17 @@ mjit::Compiler::findCallSite(const CallSite &callSite) { JS_ASSERT(callSite.pcOffset < script->length); + JITScript *jit = script->getJIT(fp->isConstructing()); + uint8* ilPath = (uint8 *)jit->code.m_code.executableAddress(); + uint8* oolPath = ilPath + masm.size(); + for (uint32 i = 0; i < callSites.length(); i++) { if (callSites[i].pc == script->code + callSite.pcOffset && callSites[i].id == callSite.id) { if (callSites[i].stub) { - return (uint8*)script->jit->invoke + masm.size() + - stubcc.masm.distanceOf(callSites[i].location); + return oolPath + stubcc.masm.distanceOf(callSites[i].location); } - return (uint8*)script->jit->invoke + - stubcc.masm.distanceOf(callSites[i].location); + return ilPath + masm.distanceOf(callSites[i].location); } } @@ -1781,24 +1823,100 @@ mjit::Compiler::emitFinalReturn(Assembler &masm) masm.jump(Registers::ReturnReg); } +// Emits code to load a return value of the frame into the scripted-ABI +// type & data register pair. If the return value is in fp->rval, then |fe| +// is NULL. Otherwise, |fe| contains the return value. +// +// If reading from fp->rval, |undefined| is loaded optimistically, before +// checking if fp->rval is set in the frame flags and loading that instead. +// +// Otherwise, if |masm| is the inline path, it is loaded as efficiently as +// the FrameState can manage. If |masm| is the OOL path, the value is simply +// loaded from its slot in the frame, since the caller has guaranteed it's +// been synced. +// void -mjit::Compiler::loadReturnValue(Assembler &masm) +mjit::Compiler::loadReturnValue(Assembler *masm, FrameEntry *fe) { - /* - * Load a return value from POPV or SETRVAL into the return registers, - * otherwise return undefined. - */ - masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data); - if (analysis.usesReturnValue()) { - Jump rvalClear = masm.branchTest32(Assembler::Zero, - FrameFlagsAddress(), Imm32(JSFRAME_HAS_RVAL)); - Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); - masm.loadValueAsComponents(rvalAddress, - JSReturnReg_Type, JSReturnReg_Data); - rvalClear.linkTo(masm.label(), &masm); + RegisterID typeReg = JSReturnReg_Type; + RegisterID dataReg = JSReturnReg_Data; + + if (fe) { + // If using the OOL assembler, the caller signifies that the |fe| is + // synced, but not to rely on its register state. + if (masm != &this->masm) { + if (fe->isConstant()) { + stubcc.masm.loadValueAsComponents(fe->getValue(), typeReg, dataReg); + } else { + Address rval(frame.addressOf(fe)); + if (fe->isTypeKnown()) { + stubcc.masm.loadPayload(rval, dataReg); + stubcc.masm.move(ImmType(fe->getKnownType()), typeReg); + } else { + stubcc.masm.loadValueAsComponents(rval, typeReg, dataReg); + } + } + } else { + frame.loadTo(fe, typeReg, dataReg, Registers::ReturnReg); + } + } else { + // Load a return value from POPV or SETRVAL into the return registers, + // otherwise return undefined. + masm->loadValueAsComponents(UndefinedValue(), typeReg, dataReg); + if (analysis.usesReturnValue()) { + Jump rvalClear = masm->branchTest32(Assembler::Zero, + FrameFlagsAddress(), + Imm32(JSFRAME_HAS_RVAL)); + Address rvalAddress(JSFrameReg, JSStackFrame::offsetOfReturnValue()); + masm->loadValueAsComponents(rvalAddress, typeReg, dataReg); + rvalClear.linkTo(masm->label(), masm); + } } } +// This ensures that constructor return values are an object. If a non-object +// is returned, either explicitly or implicitly, the newly created object is +// loaded out of the frame. Otherwise, the explicitly returned object is kept. +// +void +mjit::Compiler::fixPrimitiveReturn(Assembler *masm, FrameEntry *fe) +{ + JS_ASSERT(isConstructing); + + Address thisv(JSFrameReg, JSStackFrame::offsetOfThis(fun)); + + // Easy cases - no return value, or known primitive, so just return thisv. + if (!fe || (fe->isTypeKnown() && fe->getKnownType() != JSVAL_TYPE_OBJECT)) { + masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); + return; + } + + // If the type is known to be an object, just load the return value as normal. + if (fe->isTypeKnown() && fe->getKnownType() == JSVAL_TYPE_OBJECT) { + loadReturnValue(masm, fe); + return; + } + + // There's a return value, and its type is unknown. Test the type and load + // |thisv| if necessary. + loadReturnValue(masm, fe); + Jump j = masm->testObject(Assembler::Equal, JSReturnReg_Type); + masm->loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); + j.linkTo(masm->label(), masm); +} + +// Loads the return value into the scripted ABI register pair, such that JS +// semantics in constructors are preserved. +// +void +mjit::Compiler::emitReturnValue(Assembler *masm, FrameEntry *fe) +{ + if (isConstructing) + fixPrimitiveReturn(masm, fe); + else + loadReturnValue(masm, fe); +} + void mjit::Compiler::emitReturn(FrameEntry *fe) { @@ -1823,8 +1941,7 @@ mjit::Compiler::emitReturn(FrameEntry *fe) stubCall(stubs::PutActivationObjects); if (fe) { - masm.loadValueAsComponents(frame.addressOf(fe), - JSReturnReg_Type, JSReturnReg_Data); + emitReturnValue(&masm, fe); emitFinalReturn(masm); frame.discardFrame(); return; @@ -1839,22 +1956,12 @@ mjit::Compiler::emitReturn(FrameEntry *fe) stubcc.leave(); stubcc.call(stubs::PutActivationObjects); - if (fe) { - stubcc.masm.loadValueAsComponents(frame.addressOf(fe), - JSReturnReg_Type, JSReturnReg_Data); - } else { - loadReturnValue(stubcc.masm); - } - + emitReturnValue(&stubcc.masm, fe); emitFinalReturn(stubcc.masm); } } - if (fe) - frame.storeTo(fe, JSReturnReg_Data, JSReturnReg_Type, Registers::ReturnReg); - else - loadReturnValue(masm); - + emitReturnValue(&masm, fe); emitFinalReturn(masm); frame.discardFrame(); } @@ -1929,18 +2036,6 @@ mjit::Compiler::interruptCheckHelper() #endif } -void -mjit::Compiler::emitPrimitiveTestForNew(uint32 argc) -{ - Jump primitive = masm.testPrimitive(Assembler::Equal, JSReturnReg_Type); - stubcc.linkExitDirect(primitive, stubcc.masm.label()); - FrameEntry *fe = frame.peek(-int(argc + 1)); - Address thisv(frame.addressOf(fe)); - stubcc.masm.loadValueAsComponents(thisv, JSReturnReg_Type, JSReturnReg_Data); - Jump primFix = stubcc.masm.jump(); - stubcc.crossJump(primFix, masm.label()); -} - void mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) { @@ -1973,9 +2068,6 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) callPatch.joinPoint = masm.label(); masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); - if (callingNew) - emitPrimitiveTestForNew(argc); - frame.popn(argc + 2); frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); @@ -1993,6 +2085,10 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) /* Check for interrupts on function call */ interruptCheckHelper(); + // |thisv| does not need to be synced for constructing. + if (callingNew) + frame.discardFe(frame.peek(-int(argc + 1))); + FrameEntry *fe = frame.peek(-int(argc + 2)); /* Currently, we don't support constant functions. */ @@ -2002,12 +2098,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) } #ifdef JS_MONOIC - FrameEntry *thisvFe = frame.peek(-int(argc + 1)); - Address thisvAddr = frame.addressOf(thisvFe); - CallGenInfo callIC(argc); - uint32 callICIndex = callICs.length(); - CallPatchInfo callPatch; /* @@ -2044,16 +2135,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) if (typeReg.isSet()) notObjectJump = masm.testObject(Assembler::NotEqual, typeReg.reg()); - /* - * Ensure that dataReg stays in a register which won't be clobbered - * by the intervening call to NewObject. - */ - if (callingNew && !(Registers::maskReg(dataReg) & Registers::SavedRegs)) { - RegisterID reg = Registers(Registers::SavedRegs).takeAnyReg(); - masm.move(dataReg, reg); - dataReg = reg; - } - tempRegs.takeReg(dataReg); RegisterID t0 = tempRegs.takeAnyReg(); RegisterID t1 = tempRegs.takeAnyReg(); @@ -2083,28 +2164,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) stubcc.masm.and32(Imm32(JSFUN_KINDMASK), t1); Jump isNative = stubcc.masm.branch32(Assembler::Below, t1, Imm32(JSFUN_INTERPRETED)); - /* Create the new object. This requires some fiddling to save the two values. */ - if (callingNew) { - void *pfun = stubcc.masm.getCallTarget(JS_FUNC_TO_DATA_PTR(void *, stubs::NewObject)); - stubcc.masm.storePtr(ImmPtr(PC), - FrameAddress(offsetof(VMFrame, regs) + offsetof(JSFrameRegs, pc))); - stubcc.masm.fixScriptStack(frame.frameDepth()); - stubcc.masm.setupVMFrame(); -#if defined(JS_CPU_X86) - /* Need to stay 16-byte aligned on x86. */ - stubcc.masm.subPtr(Imm32(8), JSC::MacroAssembler::stackPointerRegister); -#endif - stubcc.masm.push(dataReg); - stubcc.masm.push(t0); - stubcc.masm.move(Imm32(argc), Registers::ArgReg1); - stubcc.masm.wrapCall(pfun); - stubcc.masm.pop(t0); - stubcc.masm.pop(dataReg); -#if defined(JS_CPU_X86) - stubcc.masm.addPtr(Imm32(8), JSC::MacroAssembler::stackPointerRegister); -#endif - } - /* * No-op jump that gets re-patched. This is so ArgReg1 won't be * clobbered, with the added bonus that the generated stub doesn't @@ -2115,7 +2174,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) callIC.oolJump = toPatch; /* At this point the function is definitely scripted. Call the link routine. */ - stubcc.masm.move(Imm32(callICIndex), Registers::ArgReg1); + callIC.addrLabel1 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); callIC.oolCall = stubcc.call(callingNew ? ic::New : ic::Call); callIC.funObjReg = dataReg; @@ -2145,7 +2204,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) notFunction.linkTo(stubcc.masm.label(), &stubcc.masm); isNative.linkTo(stubcc.masm.label(), &stubcc.masm); - stubcc.masm.move(Imm32(callICIndex), Registers::ArgReg1); + callIC.addrLabel2 = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); stubcc.call(callingNew ? ic::NativeNew : ic::NativeCall); rejoin2 = stubcc.masm.jump(); @@ -2157,13 +2216,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) */ callIC.hotPathLabel = masm.label(); - /* If calling |new|, make sure to allocate a new object. */ - if (callingNew) { - prepareStubCall(Uses(argc + 2)); - masm.move(Imm32(argc), Registers::ArgReg1); - stubCall(stubs::NewObject); - } - uint32 flags = 0; if (callingNew) flags |= JSFRAME_CONSTRUCTING; @@ -2175,13 +2227,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) callIC.joinPoint = callPatch.joinPoint = masm.label(); masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); - /* - * Functions invoked with |new| can return primitive values. - * Just deal with this here. - */ - if (callingNew) - emitPrimitiveTestForNew(argc); - frame.popn(argc + 2); frame.takeReg(JSReturnReg_Type); frame.takeReg(JSReturnReg_Data); @@ -2374,7 +2419,21 @@ mjit::Compiler::jsop_length() #endif } +#ifdef JS_MONOIC +void +mjit::Compiler::passMICAddress(MICGenInfo &mic) +{ + mic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); +} +#endif + #if defined JS_POLYIC +void +mjit::Compiler::passPICAddress(PICGenInfo &pic) +{ + pic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1); +} + void mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) { @@ -2445,7 +2504,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::GetProp); /* Load dslots. */ @@ -2546,7 +2605,7 @@ mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID obj pic.slowPathStart = stubcc.linkExit(jmpShapeGuard, Uses(2)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::GetElem); /* Load dslots. */ @@ -2672,7 +2731,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom) /* Slow path. */ stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::CallProp); /* Adjust the frame. None of this will generate code. */ @@ -2836,7 +2895,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::CallProp); /* Load dslots. */ @@ -2958,13 +3017,12 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) * the normal SETNAME property cache logic. */ JSOp op = JSOp(*PC); + stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); if (op == JSOP_SETNAME || op == JSOP_SETPROP || op == JSOP_SETGNAME || op == JSOP_SETMETHOD) { - stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1); stubcc.call(STRICT_VARIANT(stubs::SetName)); } else { - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); - stubcc.call(ic::SetPropDumb); + stubcc.call(STRICT_VARIANT(stubs::SetPropNoCache)); } typeCheck = stubcc.masm.jump(); @@ -3004,7 +3062,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) pic.slowPathStart = stubcc.linkExit(j, Uses(2)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::SetProp); } @@ -3084,7 +3142,7 @@ mjit::Compiler::jsop_name(JSAtom *atom) { pic.slowPathStart = stubcc.linkExit(j, Uses(0)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::Name); } @@ -3127,7 +3185,7 @@ mjit::Compiler::jsop_xname(JSAtom *atom) { pic.slowPathStart = stubcc.linkExit(j, Uses(1)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::XName); } @@ -3169,7 +3227,7 @@ mjit::Compiler::jsop_bindname(uint32 index) { pic.slowPathStart = stubcc.linkExit(j, Uses(0)); stubcc.leave(); - stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1); + passPICAddress(pic); pic.callReturn = stubcc.call(ic::BindName); } @@ -3876,7 +3934,7 @@ mjit::Compiler::jsop_getgname(uint32 index) stubcc.linkExit(shapeGuard, Uses(0)); stubcc.leave(); - stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); + passMICAddress(mic); mic.stubEntry = stubcc.masm.label(); mic.call = stubcc.call(ic::GetGlobalName); @@ -3975,7 +4033,7 @@ mjit::Compiler::jsop_setgname(uint32 index) stubcc.linkExit(shapeGuard, Uses(2)); stubcc.leave(); - stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); + passMICAddress(mic); mic.stubEntry = stubcc.masm.label(); mic.call = stubcc.call(ic::SetGlobalName); @@ -4226,7 +4284,7 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne, Jump *sl if (slowTwo) slowTwo->linkTo(traceStart, &stubcc.masm); # if JS_MONOIC - stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1); + passMICAddress(mic); # endif /* Save and restore compiler-tracked PC, so cx->regs is right in InvokeTracer. */ @@ -4296,3 +4354,45 @@ mjit::Compiler::leaveBlock() frame.leaveBlock(n); } +// Creates the new object expected for constructors, and places it in |thisv|. +// It is broken down into the following operations: +// CALLEE +// GETPROP "prototype" +// IFPRIMTOP: +// NULL +// call js_CreateThisFromFunctionWithProto(...) +// +void +mjit::Compiler::constructThis() +{ + JS_ASSERT(isConstructing); + + // Load the callee. + Address callee(JSFrameReg, JSStackFrame::offsetOfCallee(fun)); + RegisterID calleeReg = frame.allocReg(); + masm.loadPayload(callee, calleeReg); + frame.pushTypedPayload(JSVAL_TYPE_OBJECT, calleeReg); + + // Get callee.prototype. + jsop_getprop(cx->runtime->atomState.classPrototypeAtom); + + // Reach into the proto Value and grab a register for its data. + FrameEntry *protoFe = frame.peek(-1); + RegisterID protoReg = frame.ownRegForData(protoFe); + + // Now, get the type. If it's not an object, set protoReg to NULL. + Jump isNotObject = frame.testObject(Assembler::NotEqual, protoFe); + stubcc.linkExitDirect(isNotObject, stubcc.masm.label()); + stubcc.masm.move(ImmPtr(NULL), protoReg); + stubcc.crossJump(stubcc.masm.jump(), masm.label()); + + // Done with the protoFe. + frame.pop(); + + prepareStubCall(Uses(0)); + if (protoReg != Registers::ArgReg1) + masm.move(protoReg, Registers::ArgReg1); + stubCall(stubs::CreateThis); + frame.freeReg(protoReg); +} + diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index be20c21df323..5b8d5ecd3bff 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -72,6 +72,7 @@ class Compiler : public BaseCompiler Label entry; Label stubEntry; DataLabel32 shape; + DataLabelPtr addrLabel; #if defined JS_PUNBOX64 uint32 patchValueOffset; #endif @@ -114,6 +115,8 @@ class Compiler : public BaseCompiler Label slowJoinPoint; Label slowPathStart; Label hotPathLabel; + DataLabelPtr addrLabel1; + DataLabelPtr addrLabel2; Jump oolJump; RegisterID funObjReg; RegisterID funPtrReg; @@ -143,6 +146,7 @@ class Compiler : public BaseCompiler Label storeBack; Label typeCheck; Label slowPathStart; + DataLabelPtr addrLabel; RegisterID shapeReg; RegisterID objReg; RegisterID idReg; @@ -196,10 +200,12 @@ class Compiler : public BaseCompiler bool ool; }; + JSStackFrame *fp; JSScript *script; JSObject *scopeChain; JSObject *globalObj; JSFunction *fun; + bool isConstructing; BytecodeAnalyzer analysis; Label *jumpMap; jsbytecode *PC; @@ -211,7 +217,7 @@ class Compiler : public BaseCompiler js::Vector callICs; #endif #if defined JS_POLYIC - js::Vector pics; + js::Vector pics; #endif js::Vector callPatches; js::Vector callSites; @@ -226,10 +232,10 @@ class Compiler : public BaseCompiler // follows interpreter usage in JSOP_LENGTH. enum { LengthAtomIndex = uint32(-2) }; - Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain); + Compiler(JSContext *cx, JSStackFrame *fp); ~Compiler(); - CompileStatus Compile(); + CompileStatus compile(); jsbytecode *getPC() { return PC; } Label getLabel() { return masm.label(); } @@ -238,10 +244,11 @@ class Compiler : public BaseCompiler void *findCallSite(const CallSite &callSite); private: + CompileStatus performCompilation(JITScript **jitp); CompileStatus generatePrologue(); CompileStatus generateMethod(); CompileStatus generateEpilogue(); - CompileStatus finishThisUp(); + CompileStatus finishThisUp(JITScript **jitp); /* Non-emitting helpers. */ uint32 fullAtomIndex(jsbytecode *pc); @@ -257,6 +264,13 @@ class Compiler : public BaseCompiler void iterMore(); void iterEnd(); MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg); +#ifdef JS_POLYIC + void passPICAddress(PICGenInfo &pic); +#endif +#ifdef JS_MONOIC + void passMICAddress(MICGenInfo &mic); +#endif + void constructThis(); /* Opcode handlers. */ void jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne = NULL, Jump *slowTwo = NULL); @@ -268,12 +282,13 @@ class Compiler : public BaseCompiler void jsop_this(); void emitReturn(FrameEntry *fe); void emitFinalReturn(Assembler &masm); - void loadReturnValue(Assembler &masm); + void loadReturnValue(Assembler *masm, FrameEntry *fe); + void emitReturnValue(Assembler *masm, FrameEntry *fe); void dispatchCall(VoidPtrStubUInt32 stub, uint32 argc); void interruptCheckHelper(); void emitUncachedCall(uint32 argc, bool callingNew); - void emitPrimitiveTestForNew(uint32 argc); void inlineCallHelper(uint32 argc, bool callingNew); + void fixPrimitiveReturn(Assembler *masm, FrameEntry *fe); void jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index); void jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index); void jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index); diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 4b26b8929463..838f638fc510 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -327,7 +327,7 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped) #endif } -void FrameState::storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg) +void FrameState::loadTo(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg) { JS_ASSERT(dataReg != typeReg && dataReg != tempReg && typeReg != tempReg); @@ -347,6 +347,15 @@ void FrameState::storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, return; } +#ifdef JS_PUNBOX64 + // If the value is synced, and requires at least one load, we can do + // better on x64. + if (fe->type.inMemory() && fe->data.inMemory()) { + masm.loadValueAsComponents(addressOf(fe), typeReg, dataReg); + return; + } +#endif + RegisterID data = tempRegForData(fe); RegisterID type = tempRegForType(fe); if (data == typeReg && type == dataReg) { @@ -893,6 +902,14 @@ FrameState::ownRegForData(FrameEntry *fe) return reg; } +void +FrameState::discardFe(FrameEntry *fe) +{ + forgetEntry(fe); + fe->type.setMemory(); + fe->data.setMemory(); +} + void FrameState::pushCopyOf(uint32 index) { diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index d66415ba6ee5..bca9568d3b7d 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -590,7 +590,7 @@ class FrameState * Fully stores a FrameEntry into two arbitrary registers. tempReg may be * used as a temporary. */ - void storeTo(FrameEntry *fe, RegisterID dataReg, RegisterID typeReg, RegisterID tempReg); + void loadTo(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg); /* * Stores the top stack slot back to a slot. @@ -654,6 +654,11 @@ class FrameState */ inline void forgetType(FrameEntry *fe); + /* + * Discards a FrameEntry, tricking the FS into thinking it's synced. + */ + void discardFe(FrameEntry *fe); + /* * Helper function. Tests if a slot's type is null. Condition should * be Equal or NotEqual. diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index a7e3eadf9b38..801128ca202f 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -219,29 +219,6 @@ InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame) return ok; } -JSBool JS_FASTCALL -stubs::NewObject(VMFrame &f, uint32 argc) -{ - JSContext *cx = f.cx; - Value *vp = f.regs.sp - (argc + 2); - - JSObject *funobj = &vp[0].toObject(); - JS_ASSERT(funobj->isFunction()); - - jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); - if (!funobj->getProperty(cx, id, &vp[1])) - THROWV(JS_FALSE); - - JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL; - JSObject *obj = NewNonFunction(cx, &js_ObjectClass, proto, funobj->getParent()); - if (!obj) - THROWV(JS_FALSE); - - vp[1].setObject(*obj); - - return JS_TRUE; -} - void JS_FASTCALL stubs::SlowCall(VMFrame &f, uint32 argc) { @@ -361,8 +338,8 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) fp->initCallFrameEarlyPrologue(fun, fp->nativeReturnAddress()); /* Empty script does nothing. */ + bool callingNew = fp->isConstructing(); if (script->isEmpty()) { - bool callingNew = fp->isConstructing(); RemovePartialFrame(cx, fp); Value *vp = f.regs.sp - (nactual + 2); if (callingNew) @@ -389,9 +366,9 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual) if (fun->isHeavyweight() && !js_GetCallObject(cx, fp)) THROWV(NULL); - CompileStatus status = CanMethodJIT(cx, script, fun, &fp->scopeChain()); + CompileStatus status = CanMethodJIT(cx, script, fp); if (status == Compile_Okay) - return script->jit->invoke; + return script->getJIT(callingNew)->invokeEntry; /* Function did not compile... interpret it. */ JSBool ok = Interpret(cx, fp); @@ -420,7 +397,8 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) f.entryFp, &f.stackLimit); if (JS_UNLIKELY(!newfp)) return false; - JS_ASSERT_IF(!vp[1].isPrimitive(), IsSaneThisObject(vp[1].toObject())); + JS_ASSERT_IF(!vp[1].isPrimitive() && !(flags & JSFRAME_CONSTRUCTING), + IsSaneThisObject(vp[1].toObject())); /* Initialize frame, locals. */ newfp->initCallFrame(cx, callee, newfun, argc, flags); @@ -441,8 +419,8 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) } /* Try to compile if not already compiled. */ - if (!newscript->ncode) { - if (mjit::TryCompile(cx, newscript, newfp->fun(), &newfp->scopeChain()) == Compile_Error) { + if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) { + if (mjit::TryCompile(cx, newfp) == Compile_Error) { /* A runtime exception was thrown, get out. */ InlineReturn(f, JS_FALSE); return false; @@ -450,9 +428,8 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) } /* If newscript was successfully compiled, run it. */ - JS_ASSERT(newscript->ncode); - if (newscript->ncode != JS_UNJITTABLE_METHOD) { - *pret = newscript->jit->invoke; + if (JITScript *jit = newscript->getJIT(newfp->isConstructing())) { + *pret = jit->invokeEntry; return true; } @@ -484,9 +461,6 @@ stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr) if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted() && !ucr->fun->script()->isEmpty()) { - if (!stubs::NewObject(f, argc)) - return; - ucr->callee = &vp->toObject(); if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc)) THROW(); @@ -612,7 +586,10 @@ js_InternalThrow(VMFrame &f) if (!pc) return NULL; - return cx->fp()->script()->pcToNative(pc); + JSStackFrame *fp = cx->fp(); + JSScript *script = fp->script(); + JITScript *jit = script->getJIT(fp->isConstructing()); + return jit->nmap[pc - script->code]; } void JS_FASTCALL @@ -623,6 +600,18 @@ stubs::GetCallObject(VMFrame &f) THROW(); } +void JS_FASTCALL +stubs::CreateThis(VMFrame &f, JSObject *proto) +{ + JSContext *cx = f.cx; + JSStackFrame *fp = f.fp(); + JSObject *callee = &fp->callee(); + JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto); + if (!obj) + THROW(); + fp->formalArgs()[-1].setObject(*obj); +} + static inline void AdvanceReturnPC(JSContext *cx) { @@ -696,11 +685,12 @@ AtSafePoint(JSContext *cx) return false; JSScript *script = fp->script(); - if (!script->nmap) + JITScript *jit = script->getJIT(fp->isConstructing()); + if (!jit->nmap) return false; JS_ASSERT(cx->regs->pc >= script->code && cx->regs->pc < script->code + script->length); - return !!script->nmap[cx->regs->pc - script->code]; + return !!jit->nmap[cx->regs->pc - script->code]; } static inline JSBool @@ -709,8 +699,11 @@ PartialInterpret(VMFrame &f) JSContext *cx = f.cx; JSStackFrame *fp = cx->fp(); - JS_ASSERT(fp->hasImacropc() || !fp->script()->nmap || - !fp->script()->nmap[cx->regs->pc - fp->script()->code]); +#ifdef DEBUG + JITScript *jit = fp->script()->getJIT(fp->isConstructing()); + JS_ASSERT(fp->hasImacropc() || !jit->nmap || + !jit->nmap[cx->regs->pc - fp->script()->code]); +#endif JSBool ok = JS_TRUE; ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT); @@ -740,7 +733,8 @@ FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame) if (AtSafePoint(cx)) { JSScript *script = fp->script(); - if (!JaegerShotAtSafePoint(cx, script->nmap[cx->regs->pc - script->code])) { + JITScript *jit = script->getJIT(fp->isConstructing()); + if (!JaegerShotAtSafePoint(cx, jit->nmap[cx->regs->pc - script->code])) { if (!HandleErrorInExcessFrames(f, entryFrame)) return false; @@ -894,9 +888,10 @@ RunTracer(VMFrame &f) /* Step 2. If entryFrame is at a safe point, just leave. */ if (AtSafePoint(cx)) { + JITScript *jit = entryFrame->script()->getJIT(entryFrame->isConstructing()); uint32 offs = uint32(cx->regs->pc - entryFrame->script()->code); - JS_ASSERT(entryFrame->script()->nmap[offs]); - return entryFrame->script()->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } /* Step 3. If entryFrame is at a RETURN, then leave slightly differently. */ @@ -930,14 +925,10 @@ RunTracer(VMFrame &f) #if defined JS_TRACER # if defined JS_MONOIC void *JS_FASTCALL -stubs::InvokeTracer(VMFrame &f, uint32 index) +stubs::InvokeTracer(VMFrame &f, ic::MICInfo *mic) { - JSScript *script = f.fp()->script(); - ic::MICInfo &mic = script->mics[index]; - - JS_ASSERT(mic.kind == ic::MICInfo::TRACER); - - return RunTracer(f, mic); + JS_ASSERT(mic->kind == ic::MICInfo::TRACER); + return RunTracer(f, *mic); } # else diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 191c9c549604..c457f6f995db 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -764,9 +764,9 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) JSBool mjit::JaegerShot(JSContext *cx) { - JSScript *script = cx->fp()->script(); - - JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD); + JSStackFrame *fp = cx->fp(); + JSScript *script = fp->script(); + JITScript *jit = script->getJIT(fp->isConstructing()); #ifdef JS_TRACER if (TRACE_RECORDER(cx)) @@ -775,7 +775,7 @@ mjit::JaegerShot(JSContext *cx) JS_ASSERT(cx->regs->pc == script->code); - return EnterMethodJIT(cx, cx->fp(), script->jit->invoke); + return EnterMethodJIT(cx, cx->fp(), jit->invokeEntry); } JSBool @@ -795,37 +795,47 @@ static inline void Destroy(T &t) } void -mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) +mjit::JITScript::release() { - if (script->jit) { #if defined DEBUG && (defined JS_CPU_X86 || defined JS_CPU_X64) - memset(script->jit->invoke, 0xcc, script->jit->inlineLength + - script->jit->outOfLineLength); + void *addr = code.m_code.executableAddress(); + memset(addr, 0xcc, code.m_size); #endif - script->jit->execPool->release(); - script->jit->execPool = NULL; - // Releasing the execPool takes care of releasing the code. - script->ncode = NULL; + code.m_executablePool->release(); #if defined JS_POLYIC - for (uint32 i = 0; i < script->jit->nPICs; i++) { - script->pics[i].releasePools(); - Destroy(script->pics[i].execPools); - } + for (uint32 i = 0; i < nPICs; i++) { + pics[i].releasePools(); + Destroy(pics[i].execPools); + } #endif #if defined JS_MONOIC - for (uint32 i = 0; i < script->jit->nCallICs; i++) - script->callICs[i].releasePools(); + for (uint32 i = 0; i < nCallICs; i++) + callICs[i].releasePools(); #endif +} - cx->free(script->jit); +void +mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) +{ + // NB: The recompiler may call ReleaseScriptCode, in which case it + // will get called again when the script is destroyed, so we + // must protect against calling ReleaseScriptCode twice. - // The recompiler may call ReleaseScriptCode, in which case it - // will get called again when the script is destroyed, so we - // must protect against calling ReleaseScriptCode twice. - script->jit = NULL; + if (script->jitNormal) { + script->jitNormal->release(); + script->jitArityCheckNormal = NULL; + cx->free(script->jitNormal); + script->jitNormal = NULL; + } + + if (script->jitCtor) { + script->jitCtor->release(); + script->jitArityCheckCtor = NULL; + cx->free(script->jitCtor); + script->jitCtor = NULL; } } diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 68d6dd0f0c31..7e672c6f3c10 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -139,6 +139,18 @@ struct VMFrame extern "C" void JaegerStubVeneer(void); #endif +namespace mjit { +namespace ic { +# if defined JS_POLYIC + struct PICInfo; +# endif +# if defined JS_MONOIC + struct MICInfo; + struct CallICInfo; +# endif +} +} + typedef void (JS_FASTCALL *VoidStub)(VMFrame &); typedef void (JS_FASTCALL *VoidVpStub)(VMFrame &, Value *); typedef void (JS_FASTCALL *VoidStubUInt32)(VMFrame &, uint32); @@ -158,26 +170,51 @@ typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32); typedef void (JS_FASTCALL *VoidStubJSObj)(VMFrame &, JSObject *); typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *); typedef JSBool (JS_FASTCALL *BoolStubUInt32)(VMFrame &f, uint32); - -#define JS_UNJITTABLE_METHOD (reinterpret_cast(1)) +#ifdef JS_MONOIC +typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); +typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); +typedef void (JS_FASTCALL *VoidStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); +typedef void * (JS_FASTCALL *VoidPtrStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); +#endif +#ifdef JS_POLYIC +typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); +#endif namespace mjit { +struct CallSite; + struct JITScript { - JSC::ExecutablePool *execPool; /* pool that contains |ncode|; script owns the pool */ - uint32 inlineLength; /* length of inline JIT'd code */ - uint32 outOfLineLength; /* length of out of line JIT'd code */ + typedef JSC::MacroAssemblerCodeRef CodeRef; + CodeRef code; /* pool & code addresses */ + js::mjit::CallSite *callSites; uint32 nCallSites; + void **nmap; /* scripted pc to native code map. */ #ifdef JS_MONOIC - uint32 nMICs; /* number of MonoICs */ - uint32 nCallICs; /* number of call ICs */ + ic::MICInfo *mics; /* MICs in this script. */ + uint32 nMICs; /* number of MonoICs */ + ic::CallICInfo *callICs; /* CallICs in this script. */ + uint32 nCallICs; /* number of call ICs */ #endif #ifdef JS_POLYIC - uint32 nPICs; /* number of PolyICs */ + ic::PICInfo *pics; /* PICs in this script */ + uint32 nPICs; /* number of PolyICs */ #endif - void *invoke; /* invoke address */ - void *arityCheck; /* arity check address */ + void *invokeEntry; /* invoke address */ + void *fastEntry; /* cached entry, fastest */ + void *arityCheckEntry; /* arity check address */ + + bool isValidCode(void *ptr) { + char *jitcode = (char *)code.m_code.executableAddress(); + char *jcheck = (char *)ptr; + return jcheck >= jitcode && jcheck < jitcode + code.m_size; + } + + void sweepCallICs(); + void purgeMICs(); + void purgePICs(); + void release(); }; /* Execute a method that has been JIT compiled. */ @@ -197,18 +234,21 @@ void JS_FASTCALL ProfileStubCall(VMFrame &f); CompileStatus JS_NEVER_INLINE -TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain); +TryCompile(JSContext *cx, JSStackFrame *fp); void ReleaseScriptCode(JSContext *cx, JSScript *script); static inline CompileStatus -CanMethodJIT(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) +CanMethodJIT(JSContext *cx, JSScript *script, JSStackFrame *fp) { - if (!cx->methodJitEnabled || script->ncode == JS_UNJITTABLE_METHOD) + if (!cx->methodJitEnabled) return Compile_Abort; - if (script->ncode == NULL) - return TryCompile(cx, script, fun, scopeChain); + JITScriptStatus status = script->getJITStatus(fp->isConstructing()); + if (status == JITScript_Invalid) + return Compile_Abort; + if (status == JITScript_None) + return TryCompile(cx, fp); return Compile_Okay; } @@ -223,6 +263,25 @@ struct CallSite } /* namespace js */ +inline void ** +JSScript::maybeNativeMap(bool constructing) +{ + js::mjit::JITScript *jit = constructing ? jitCtor : jitNormal; + if (!jit) + return NULL; + return jit->nmap; +} + +inline bool +JSScript::hasNativeCodeForPC(bool constructing, jsbytecode *pc) +{ + js::mjit::JITScript *jit = getJIT(constructing); + if (!jit) + return false; + JS_ASSERT(pc >= code && pc < code + length); + return !!jit->nmap[pc - code]; +} + #ifdef _MSC_VER extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame); #else diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index ce12f4a9f3d2..eada7e730948 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -70,22 +70,21 @@ typedef JSC::MacroAssembler::Call Call; #if defined JS_MONOIC static void -PatchGetFallback(VMFrame &f, ic::MICInfo &mic) +PatchGetFallback(VMFrame &f, ic::MICInfo *ic) { - JSC::RepatchBuffer repatch(mic.stubEntry.executableAddress(), 64); + JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64); JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName)); - repatch.relink(mic.stubCall, fptr); + repatch.relink(ic->stubCall, fptr); } void JS_FASTCALL -ic::GetGlobalName(VMFrame &f, uint32 index) +ic::GetGlobalName(VMFrame &f, ic::MICInfo *ic) { JSObject *obj = f.fp()->scopeChain().getGlobal(); - ic::MICInfo &mic = f.fp()->script()->mics[index]; JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc)); jsid id = ATOM_TO_JSID(atom); - JS_ASSERT(mic.kind == ic::MICInfo::GET); + JS_ASSERT(ic->kind == ic::MICInfo::GET); JS_LOCK_OBJ(f.cx, obj); const Shape *shape = obj->nativeLookup(id); @@ -95,33 +94,33 @@ ic::GetGlobalName(VMFrame &f, uint32 index) { JS_UNLOCK_OBJ(f.cx, obj); if (shape) - PatchGetFallback(f, mic); + PatchGetFallback(f, ic); stubs::GetGlobalName(f); return; } uint32 slot = shape->slot; JS_UNLOCK_OBJ(f.cx, obj); - mic.u.name.touched = true; + ic->u.name.touched = true; /* Patch shape guard. */ - JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50); - repatch.repatch(mic.shape, obj->shape()); + JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50); + repatch.repatch(ic->shape, obj->shape()); /* Patch loads. */ JS_ASSERT(slot >= JS_INITIAL_NSLOTS); slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); - JSC::RepatchBuffer loads(mic.load.executableAddress(), 32, false); + JSC::RepatchBuffer loads(ic->load.executableAddress(), 32, false); #if defined JS_CPU_X86 - loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot); - loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4); + loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot); + loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4); #elif defined JS_CPU_ARM - // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch' + // ic->load actually points to the LDR instruction which fetches the offset, but 'repatch' // knows how to dereference it to find the integer value. - loads.repatch(mic.load.dataLabel32AtOffset(0), slot); + loads.repatch(ic->load.dataLabel32AtOffset(0), slot); #elif defined JS_PUNBOX64 - loads.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); + loads.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot); #endif /* Do load anyway... this time. */ @@ -140,11 +139,11 @@ SetGlobalNameSlow(VMFrame &f, uint32 index) } static void -PatchSetFallback(VMFrame &f, ic::MICInfo &mic) +PatchSetFallback(VMFrame &f, ic::MICInfo *ic) { - JSC::RepatchBuffer repatch(mic.stubEntry.executableAddress(), 64); + JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64); JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, SetGlobalNameSlow)); - repatch.relink(mic.stubCall, fptr); + repatch.relink(ic->stubCall, fptr); } static VoidStubAtom @@ -159,14 +158,13 @@ GetStubForSetGlobalName(VMFrame &f) } void JS_FASTCALL -ic::SetGlobalName(VMFrame &f, uint32 index) +ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic) { JSObject *obj = f.fp()->scopeChain().getGlobal(); - ic::MICInfo &mic = f.fp()->script()->mics[index]; JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc)); jsid id = ATOM_TO_JSID(atom); - JS_ASSERT(mic.kind == ic::MICInfo::SET); + JS_ASSERT(ic->kind == ic::MICInfo::SET); JS_LOCK_OBJ(f.cx, obj); const Shape *shape = obj->nativeLookup(id); @@ -177,40 +175,40 @@ ic::SetGlobalName(VMFrame &f, uint32 index) { JS_UNLOCK_OBJ(f.cx, obj); if (shape) - PatchSetFallback(f, mic); + PatchSetFallback(f, ic); GetStubForSetGlobalName(f)(f, atom); return; } uint32 slot = shape->slot; JS_UNLOCK_OBJ(f.cx, obj); - mic.u.name.touched = true; + ic->u.name.touched = true; /* Patch shape guard. */ - JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50); - repatch.repatch(mic.shape, obj->shape()); + JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50); + repatch.repatch(ic->shape, obj->shape()); /* Patch loads. */ JS_ASSERT(slot >= JS_INITIAL_NSLOTS); slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); - JSC::RepatchBuffer stores(mic.load.executableAddress(), 32, false); + JSC::RepatchBuffer stores(ic->load.executableAddress(), 32, false); #if defined JS_CPU_X86 - stores.repatch(mic.load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4); + stores.repatch(ic->load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4); uint32 dataOffset; - if (mic.u.name.typeConst) + if (ic->u.name.typeConst) dataOffset = MICInfo::SET_DATA_CONST_TYPE_OFFSET; else dataOffset = MICInfo::SET_DATA_TYPE_OFFSET; - stores.repatch(mic.load.dataLabel32AtOffset(dataOffset), slot); + stores.repatch(ic->load.dataLabel32AtOffset(dataOffset), slot); #elif defined JS_CPU_ARM - // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch' + // ic->load actually points to the LDR instruction which fetches the offset, but 'repatch' // knows how to dereference it to find the integer value. - stores.repatch(mic.load.dataLabel32AtOffset(0), slot); + stores.repatch(ic->load.dataLabel32AtOffset(0), slot); #elif defined JS_PUNBOX64 - stores.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot); + stores.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot); #endif // Actually implement the op the slow way. @@ -218,24 +216,16 @@ ic::SetGlobalName(VMFrame &f, uint32 index) } static void * JS_FASTCALL -SlowCallFromIC(VMFrame &f, uint32 index) +SlowCallFromIC(VMFrame &f, ic::CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic= oldscript->callICs[index]; - - stubs::SlowCall(f, ic.argc); - + stubs::SlowCall(f, ic->argc); return NULL; } static void * JS_FASTCALL -SlowNewFromIC(VMFrame &f, uint32 index) +SlowNewFromIC(VMFrame &f, ic::CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - - stubs::SlowNew(f, ic.argc); - + stubs::SlowNew(f, ic->argc); return NULL; } @@ -325,8 +315,11 @@ class CallCompiler : public BaseCompiler * here since ncode has two failure modes and we need to load out of * nmap anyway. */ - masm.loadPtr(Address(t0, offsetof(JSScript, jit)), t0); - Jump hasCode = masm.branchTestPtr(Assembler::NonZero, t0, t0); + size_t offset = callingNew + ? offsetof(JSScript, jitArityCheckCtor) + : offsetof(JSScript, jitArityCheckNormal); + masm.loadPtr(Address(t0, offset), t0); + Jump hasCode = masm.branchPtr(Assembler::Above, t0, ImmPtr(JS_UNJITTABLE_SCRIPT)); /* Try and compile. On success we get back the nmap pointer. */ masm.storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp))); @@ -345,7 +338,6 @@ class CallCompiler : public BaseCompiler /* Get nmap[ARITY], set argc, call. */ masm.move(Imm32(ic.argc), JSParamReg_Argc); - masm.loadPtr(Address(t0, offsetof(JITScript, arityCheck)), t0); masm.jump(t0); JSC::ExecutablePool *ep = poolForSize(masm.size(), CallICInfo::Pool_ScriptStub); @@ -377,9 +369,11 @@ class CallCompiler : public BaseCompiler ic.fastGuardedObject = obj; + JITScript *jit = script->getJIT(callingNew); + repatch.repatch(ic.funGuard, obj); repatch.relink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset), - JSC::CodeLocationLabel(script->ncode)); + JSC::CodeLocationLabel(jit->fastEntry)); JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n", start, ic.fastGuardedObject); } @@ -649,52 +643,40 @@ class CallCompiler : public BaseCompiler }; void * JS_FASTCALL -ic::Call(VMFrame &f, uint32 index) +ic::Call(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, false); + CallCompiler cc(f, *ic, false); return cc.update(); } void * JS_FASTCALL -ic::New(VMFrame &f, uint32 index) +ic::New(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, true); + CallCompiler cc(f, *ic, true); return cc.update(); } void JS_FASTCALL -ic::NativeCall(VMFrame &f, uint32 index) +ic::NativeCall(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, false); + CallCompiler cc(f, *ic, false); if (!cc.generateNativeStub()) - stubs::SlowCall(f, ic.argc); + stubs::SlowCall(f, ic->argc); } void JS_FASTCALL -ic::NativeNew(VMFrame &f, uint32 index) +ic::NativeNew(VMFrame &f, CallICInfo *ic) { - JSScript *oldscript = f.fp()->script(); - CallICInfo &ic = oldscript->callICs[index]; - CallCompiler cc(f, ic, true); + CallCompiler cc(f, *ic, true); if (!cc.generateNativeStub()) - stubs::SlowNew(f, ic.argc); + stubs::SlowNew(f, ic->argc); } void -ic::PurgeMICs(JSContext *cx, JSScript *script) +JITScript::purgeMICs() { - /* MICs are purged during GC to handle changing shapes. */ - JS_ASSERT(cx->runtime->gcRegenShapes); - - uint32 nmics = script->jit->nMICs; - for (uint32 i = 0; i < nmics; i++) { - ic::MICInfo &mic = script->mics[i]; + for (uint32 i = 0; i < nMICs; i++) { + ic::MICInfo &mic = mics[i]; switch (mic.kind) { case ic::MICInfo::SET: case ic::MICInfo::GET: @@ -720,10 +702,22 @@ ic::PurgeMICs(JSContext *cx, JSScript *script) } void -ic::SweepCallICs(JSScript *script) +ic::PurgeMICs(JSContext *cx, JSScript *script) { - for (uint32 i = 0; i < script->jit->nCallICs; i++) { - ic::CallICInfo &ic = script->callICs[i]; + /* MICs are purged during GC to handle changing shapes. */ + JS_ASSERT(cx->runtime->gcRegenShapes); + + if (script->jitNormal) + script->jitNormal->purgeMICs(); + if (script->jitCtor) + script->jitCtor->purgeMICs(); +} + +void +JITScript::sweepCallICs() +{ + for (uint32 i = 0; i < nCallICs; i++) { + ic::CallICInfo &ic = callICs[i]; /* * If the object is unreachable, we're guaranteed not to be currently @@ -757,5 +751,14 @@ ic::SweepCallICs(JSScript *script) } } +void +ic::SweepCallICs(JSScript *script) +{ + if (script->jitNormal) + script->jitNormal->sweepCallICs(); + if (script->jitCtor) + script->jitCtor->sweepCallICs(); +} + #endif /* JS_MONOIC */ diff --git a/js/src/methodjit/MonoIC.h b/js/src/methodjit/MonoIC.h index 485e21aab9ba..9df110558e92 100644 --- a/js/src/methodjit/MonoIC.h +++ b/js/src/methodjit/MonoIC.h @@ -109,8 +109,8 @@ struct MICInfo { } u; }; -void JS_FASTCALL GetGlobalName(VMFrame &f, uint32 index); -void JS_FASTCALL SetGlobalName(VMFrame &f, uint32 index); +void JS_FASTCALL GetGlobalName(VMFrame &f, ic::MICInfo *ic); +void JS_FASTCALL SetGlobalName(VMFrame &f, ic::MICInfo *ic); /* See MonoIC.cpp, CallCompiler for more information on call ICs. */ struct CallICInfo { @@ -187,10 +187,10 @@ struct CallICInfo { } }; -void * JS_FASTCALL New(VMFrame &f, uint32 index); -void * JS_FASTCALL Call(VMFrame &f, uint32 index); -void JS_FASTCALL NativeNew(VMFrame &f, uint32 index); -void JS_FASTCALL NativeCall(VMFrame &f, uint32 index); +void * JS_FASTCALL New(VMFrame &f, ic::CallICInfo *ic); +void * JS_FASTCALL Call(VMFrame &f, ic::CallICInfo *ic); +void JS_FASTCALL NativeNew(VMFrame &f, ic::CallICInfo *ic); +void JS_FASTCALL NativeCall(VMFrame &f, ic::CallICInfo *ic); void PurgeMICs(JSContext *cx, JSScript *script); void SweepCallICs(JSScript *script); diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 0709f6857138..f869437c20f7 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -111,8 +111,7 @@ class PICStubCompiler : public BaseCompiler return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub)); } - bool disable(const char *reason, VoidStubUInt32 stub) - { + bool disable(const char *reason, VoidStubPIC stub) { return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub)); } @@ -159,7 +158,7 @@ class SetPropCompiler : public PICStubCompiler { JSObject *obj; JSAtom *atom; - VoidStubUInt32 stub; + VoidStubPIC stub; int lastStubSecondShapeGuard; static int32 dslotsLoadOffset(ic::PICInfo &pic) { @@ -224,7 +223,7 @@ class SetPropCompiler : public PICStubCompiler public: SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, - VoidStubUInt32 stub) + VoidStubPIC stub) : PICStubCompiler("setprop", f, script, pic), obj(obj), atom(atom), stub(stub), lastStubSecondShapeGuard(pic.secondShapeGuard) { } @@ -746,7 +745,7 @@ class GetPropCompiler : public PICStubCompiler { } GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom, - VoidStubUInt32 stub) + VoidStubPIC stub) : PICStubCompiler("callprop", f, script, pic), obj(obj), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)), lastStubSecondShapeGuard(pic.secondShapeGuard) @@ -770,7 +769,7 @@ class GetPropCompiler : public PICStubCompiler RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH); ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress()); - VoidStubUInt32 stub; + VoidStubPIC stub; switch (pic.kind) { case ic::PICInfo::GET: stub = ic::GetProp; @@ -1573,7 +1572,7 @@ class ScopeNameCompiler : public PICStubCompiler public: ScopeNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubUInt32 stub) + JSAtom *atom, VoidStubPIC stub) : PICStubCompiler("name", f, script, pic), scopeChain(scopeChain), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)), obj(NULL), holder(NULL), prop(NULL) { } @@ -1591,7 +1590,7 @@ class ScopeNameCompiler : public PICStubCompiler RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH); ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress()); - VoidStubUInt32 stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName; + VoidStubPIC stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName; MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, stub)); repatcher.relinkCallerToTrampoline(retPtr, target); } @@ -1912,7 +1911,7 @@ class BindNameCompiler : public PICStubCompiler public: BindNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic, - JSAtom *atom, VoidStubUInt32 stub) + JSAtom *atom, VoidStubPIC stub) : PICStubCompiler("bind", f, script, pic), scopeChain(scopeChain), atom(atom), stub(JS_FUNC_TO_DATA_PTR(void *, stub)) { } @@ -2020,15 +2019,14 @@ class BindNameCompiler : public PICStubCompiler }; void JS_FASTCALL -ic::GetProp(VMFrame &f, uint32 index) +ic::GetProp(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; + JSAtom *atom = pic->atom; if (atom == f.cx->runtime->atomState.lengthAtom) { if (f.regs.sp[-1].isString()) { - GetPropCompiler cc(f, script, NULL, pic, NULL, stubs::Length); + GetPropCompiler cc(f, script, NULL, *pic, NULL, stubs::Length); if (!cc.generateStringLengthStub()) { cc.disable("error"); THROW(); @@ -2039,7 +2037,7 @@ ic::GetProp(VMFrame &f, uint32 index) } else if (!f.regs.sp[-1].isPrimitive()) { JSObject *obj = &f.regs.sp[-1].toObject(); if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden())) { - GetPropCompiler cc(f, script, obj, pic, NULL, stubs::Length); + GetPropCompiler cc(f, script, obj, *pic, NULL, stubs::Length); if (obj->isArray()) { if (!cc.generateArrayLengthStub()) { cc.disable("error"); @@ -2063,8 +2061,8 @@ ic::GetProp(VMFrame &f, uint32 index) if (!obj) THROW(); - if (pic.shouldGenerate()) { - GetPropCompiler cc(f, script, obj, pic, atom, stubs::GetProp); + if (pic->shouldGenerate()) { + GetPropCompiler cc(f, script, obj, *pic, atom, stubs::GetProp); if (!cc.update()) { cc.disable("error"); THROW(); @@ -2078,10 +2076,9 @@ ic::GetProp(VMFrame &f, uint32 index) } void JS_FASTCALL -ic::GetElem(VMFrame &f, uint32 picIndex) +ic::GetElem(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - PICInfo &pic = script->pics[picIndex]; JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); if (!obj) @@ -2090,8 +2087,8 @@ ic::GetElem(VMFrame &f, uint32 picIndex) Value idval = f.regs.sp[-1]; JS_ASSERT(idval.isString()); JSString *id = idval.toString(); - if (pic.shouldGenerate()) { - GetElemCompiler cc(f, script, obj, pic, id, stubs::GetElem); + if (pic->shouldGenerate()) { + GetElemCompiler cc(f, script, obj, *pic, id, stubs::GetElem); if (!cc.update()) { cc.disable("error"); THROW(); @@ -2107,46 +2104,29 @@ ic::GetElem(VMFrame &f, uint32 picIndex) f.regs.sp[-2] = v; } -void JS_FASTCALL -ic::SetPropDumb(VMFrame &f, uint32 index) -{ - JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JS_ASSERT(pic.isSet()); - JSAtom *atom = pic.atom; - - JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); - if (!obj) - THROW(); - Value rval = f.regs.sp[-1]; - if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], - script->strictModeCode)) - THROW(); - f.regs.sp[-2] = rval; -} - +template static void JS_FASTCALL -SetPropSlow(VMFrame &f, uint32 index) +SetPropDumb(VMFrame &f, ic::PICInfo *pic) { - JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JS_ASSERT(pic.isSet()); + stubs::SetPropNoCache(f, pic->atom); +} - JSAtom *atom = pic.atom; - STRICT_VARIANT(stubs::SetName)(f, atom); +template +static void JS_FASTCALL +SetPropSlow(VMFrame &f, ic::PICInfo *pic) +{ + stubs::SetName(f, pic->atom); } void JS_FASTCALL -ic::SetProp(VMFrame &f, uint32 index) +ic::SetProp(VMFrame &f, ic::PICInfo *pic) { JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); if (!obj) THROW(); JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; - JS_ASSERT(pic.isSet()); + JS_ASSERT(pic->isSet()); // // Important: We update the PIC before looking up the property so that the @@ -2157,7 +2137,7 @@ ic::SetProp(VMFrame &f, uint32 index) // cache can't handle a GET and SET from the same scripted PC. // - VoidStubUInt32 stub; + VoidStubPIC stub; switch (JSOp(*f.regs.pc)) { case JSOP_PROPINC: case JSOP_PROPDEC: @@ -2167,40 +2147,36 @@ ic::SetProp(VMFrame &f, uint32 index) case JSOP_NAMEDEC: case JSOP_INCNAME: case JSOP_DECNAME: - stub = SetPropDumb; + stub = STRICT_VARIANT(SetPropDumb); break; default: - stub = SetPropSlow; + stub = STRICT_VARIANT(SetPropSlow); break; } - SetPropCompiler cc(f, script, obj, pic, atom, stub); + SetPropCompiler cc(f, script, obj, *pic, pic->atom, stub); if (!cc.update()) { cc.disable("error"); THROW(); } Value rval = f.regs.sp[-1]; - stub(f, index); + stub(f, pic); } static void JS_FASTCALL -CallPropSlow(VMFrame &f, uint32 index) +CallPropSlow(VMFrame &f, ic::PICInfo *pic) { - JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - stubs::CallProp(f, pic.atom); + stubs::CallProp(f, pic->atom); } void JS_FASTCALL -ic::CallProp(VMFrame &f, uint32 index) +ic::CallProp(VMFrame &f, ic::PICInfo *pic) { JSContext *cx = f.cx; JSFrameRegs ®s = f.regs; JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *origAtom = pic.atom; Value lval; lval = regs.sp[-1]; @@ -2260,7 +2236,7 @@ ic::CallProp(VMFrame &f, uint32 index) * PropertyCache::test. */ jsid id; - id = ATOM_TO_JSID(origAtom); + id = ATOM_TO_JSID(pic->atom); regs.sp++; regs.sp[-1].setNull(); @@ -2298,7 +2274,7 @@ ic::CallProp(VMFrame &f, uint32 index) } } - GetPropCompiler cc(f, script, &objv.toObject(), pic, origAtom, CallPropSlow); + GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, CallPropSlow); if (usePIC) { if (lval.isObject()) { if (!cc.update()) { @@ -2319,7 +2295,7 @@ ic::CallProp(VMFrame &f, uint32 index) #if JS_HAS_NO_SUCH_METHOD if (JS_UNLIKELY(rval.isUndefined())) { - regs.sp[-2].setString(ATOM_TO_STRING(origAtom)); + regs.sp[-2].setString(ATOM_TO_STRING(pic->atom)); if (!js_OnUnknownMethod(cx, regs.sp - 2)) THROW(); } @@ -2327,28 +2303,26 @@ ic::CallProp(VMFrame &f, uint32 index) } static void JS_FASTCALL -SlowName(VMFrame &f, uint32 index) +SlowName(VMFrame &f, ic::PICInfo *pic) { stubs::Name(f); } static void JS_FASTCALL -SlowXName(VMFrame &f, uint32 index) +SlowXName(VMFrame &f, ic::PICInfo *pic) { stubs::GetProp(f); } void JS_FASTCALL -ic::XName(VMFrame &f, uint32 index) +ic::XName(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; /* GETXPROP is guaranteed to have an object. */ JSObject *obj = &f.regs.sp[-1].toObject(); - ScopeNameCompiler cc(f, script, obj, pic, atom, SlowXName); + ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, SlowXName); if (!cc.updateForXName()) { cc.disable("error"); @@ -2362,13 +2336,11 @@ ic::XName(VMFrame &f, uint32 index) } void JS_FASTCALL -ic::Name(VMFrame &f, uint32 index) +ic::Name(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; - ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), pic, atom, SlowName); + ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowName); if (!cc.updateForName()) { cc.disable("error"); @@ -2382,19 +2354,17 @@ ic::Name(VMFrame &f, uint32 index) } static void JS_FASTCALL -SlowBindName(VMFrame &f, uint32 index) +SlowBindName(VMFrame &f, ic::PICInfo *pic) { stubs::BindName(f); } void JS_FASTCALL -ic::BindName(VMFrame &f, uint32 index) +ic::BindName(VMFrame &f, ic::PICInfo *pic) { JSScript *script = f.fp()->script(); - ic::PICInfo &pic = script->pics[index]; - JSAtom *atom = pic.atom; - BindNameCompiler cc(f, script, &f.fp()->scopeChain(), pic, atom, SlowBindName); + BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowBindName); JSObject *obj = cc.update(); if (!obj) { @@ -2406,11 +2376,10 @@ ic::BindName(VMFrame &f, uint32 index) } void -ic::PurgePICs(JSContext *cx, JSScript *script) +JITScript::purgePICs() { - uint32 npics = script->jit->nPICs; - for (uint32 i = 0; i < npics; i++) { - ic::PICInfo &pic = script->pics[i]; + for (uint32 i = 0; i < nPICs; i++) { + ic::PICInfo &pic = pics[i]; switch (pic.kind) { case ic::PICInfo::SET: case ic::PICInfo::SETMETHOD: @@ -2438,5 +2407,14 @@ ic::PurgePICs(JSContext *cx, JSScript *script) } } +void +ic::PurgePICs(JSContext *cx, JSScript *script) +{ + if (script->jitNormal) + script->jitNormal->purgePICs(); + if (script->jitCtor) + script->jitCtor->purgePICs(); +} + #endif /* JS_POLYIC */ diff --git a/js/src/methodjit/PolyIC.h b/js/src/methodjit/PolyIC.h index 0edbe26321c8..9e3d63e03bef 100644 --- a/js/src/methodjit/PolyIC.h +++ b/js/src/methodjit/PolyIC.h @@ -352,14 +352,13 @@ struct PICInfo { }; void PurgePICs(JSContext *cx, JSScript *script); -void JS_FASTCALL GetProp(VMFrame &f, uint32 index); -void JS_FASTCALL GetElem(VMFrame &f, uint32 index); -void JS_FASTCALL SetProp(VMFrame &f, uint32 index); -void JS_FASTCALL CallProp(VMFrame &f, uint32 index); -void JS_FASTCALL Name(VMFrame &f, uint32 index); -void JS_FASTCALL XName(VMFrame &f, uint32 index); -void JS_FASTCALL BindName(VMFrame &f, uint32 index); -void JS_FASTCALL SetPropDumb(VMFrame &f, uint32 index); +void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL GetElem(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *); +void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *); } /* namespace ic */ } /* namespace mjit */ diff --git a/js/src/methodjit/Retcon.cpp b/js/src/methodjit/Retcon.cpp index 99526fb01f49..f5069e1f9cb1 100644 --- a/js/src/methodjit/Retcon.cpp +++ b/js/src/methodjit/Retcon.cpp @@ -73,13 +73,14 @@ AutoScriptRetrapper::untrap(jsbytecode *pc) } Recompiler::PatchableAddress -Recompiler::findPatch(void **location) +Recompiler::findPatch(JITScript *jit, void **location) { - for (uint32 i = 0; i < script->jit->nCallSites; i++) { - if (script->jit->callSites[i].codeOffset + (uint8*)script->jit->invoke == *location) { + uint8* codeStart = (uint8 *)jit->code.m_code.executableAddress(); + for (uint32 i = 0; i < jit->nCallSites; i++) { + if (jit->callSites[i].codeOffset + codeStart == *location) { PatchableAddress result; result.location = location; - result.callSite = script->jit->callSites[i]; + result.callSite = jit->callSites[i]; return result; } } @@ -116,17 +117,27 @@ Recompiler::Recompiler(JSContext *cx, JSScript *script) bool Recompiler::recompile() { - JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD); + JS_ASSERT(script->hasJITCode()); - Vector toPatch(cx); + Vector normalPatches(cx); + Vector ctorPatches(cx); /* Scan the stack, saving the ncode elements of the frames. */ - JSStackFrame *firstFrame = NULL; + JSStackFrame *firstCtorFrame = NULL; + JSStackFrame *firstNormalFrame = NULL; for (AllFramesIter i(cx); !i.done(); ++i) { - if (!firstFrame && i.fp()->maybeScript() == script) - firstFrame = i.fp(); - if (script->isValidJitCode(i.fp()->nativeReturnAddress())) { - if (!toPatch.append(findPatch(i.fp()->addressOfNativeReturnAddress()))) + if (!firstCtorFrame && i.fp()->maybeScript() == script && i.fp()->isConstructing()) + firstCtorFrame = i.fp(); + else if (!firstNormalFrame && i.fp()->maybeScript() == script && !i.fp()->isConstructing()) + firstNormalFrame = i.fp(); + void **addr = i.fp()->addressOfNativeReturnAddress(); + if (!*addr) + continue; + if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { + if (!ctorPatches.append(findPatch(script->jitCtor, addr))) + return false; + } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { + if (!normalPatches.append(findPatch(script->jitNormal, addr))) return false; } } @@ -136,29 +147,41 @@ Recompiler::recompile() f != NULL; f = f->previous) { - void **machineReturn = f->returnAddressLocation(); - if (script->isValidJitCode(*machineReturn)) { - if (!toPatch.append(findPatch(machineReturn))) + void **addr = f->returnAddressLocation(); + if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { + if (!ctorPatches.append(findPatch(script->jitCtor, addr))) + return false; + } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { + if (!normalPatches.append(findPatch(script->jitNormal, addr))) return false; } } ReleaseScriptCode(cx, script); - /* No need to actually compile or fixup if no frames on the stack */ - if (!firstFrame) - return true; + if (normalPatches.length() && !recompile(firstNormalFrame, normalPatches)) + return false; + if (ctorPatches.length() && !recompile(firstCtorFrame, ctorPatches)) + return false; + + return true; +} + +bool +Recompiler::recompile(JSStackFrame *fp, Vector &patches) +{ /* If we get this far, the script is live, and we better be safe to re-jit. */ JS_ASSERT(cx->compartment->debugMode); + JS_ASSERT(fp); - Compiler c(cx, script, firstFrame->maybeFun(), &firstFrame->scopeChain()); - if (c.Compile() != Compile_Okay) + Compiler c(cx, fp); + if (c.compile() != Compile_Okay) return false; /* Perform the earlier scanned patches */ - for (uint32 i = 0; i < toPatch.length(); i++) - applyPatch(c, toPatch[i]); + for (uint32 i = 0; i < patches.length(); i++) + applyPatch(c, patches[i]); return true; } diff --git a/js/src/methodjit/Retcon.h b/js/src/methodjit/Retcon.h index 8280329a8fe0..99050c0e5eed 100644 --- a/js/src/methodjit/Retcon.h +++ b/js/src/methodjit/Retcon.h @@ -97,8 +97,9 @@ private: JSContext *cx; JSScript *script; - PatchableAddress findPatch(void **location); + PatchableAddress findPatch(JITScript *jit, void **location); void applyPatch(Compiler& c, PatchableAddress& toPatch); + bool recompile(JSStackFrame *fp, Vector &patches); }; } /* namespace mjit */ diff --git a/js/src/methodjit/StubCalls-inl.h b/js/src/methodjit/StubCalls-inl.h index 55b3513ca9ce..2b2844a2ea9f 100644 --- a/js/src/methodjit/StubCalls-inl.h +++ b/js/src/methodjit/StubCalls-inl.h @@ -99,7 +99,6 @@ ReportAtomNotDefined(JSContext *cx, JSAtom *atom) } \ JS_END_MACRO - }} #endif /* jslogic_h__ */ diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 5850c36e46a1..a0231f1ed997 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -281,6 +281,22 @@ stubs::SetName(VMFrame &f, JSAtom *origAtom) template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); template void JS_FASTCALL stubs::SetName(VMFrame &f, JSAtom *origAtom); +template +void JS_FASTCALL +stubs::SetPropNoCache(VMFrame &f, JSAtom *atom) +{ + JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]); + if (!obj) + THROW(); + Value rval = f.regs.sp[-1]; + if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1], strict)) + THROW(); + f.regs.sp[-2] = rval; +} + +template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); +template void JS_FASTCALL stubs::SetPropNoCache(VMFrame &f, JSAtom *origAtom); + template void JS_FASTCALL stubs::SetGlobalNameDumb(VMFrame &f, JSAtom *atom) @@ -2517,14 +2533,15 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) { jsbytecode *jpc = pc; JSScript *script = f.fp()->script(); + JITScript *jit = script->getJIT(f.fp()->isConstructing()); /* This is correct because the compiler adjusts the stack beforehand. */ Value lval = f.regs.sp[-1]; if (!lval.isPrimitive()) { ptrdiff_t offs = (pc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH); @@ -2544,8 +2561,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) JSString *rhs = rval.toString(); if (rhs == str || js_EqualStrings(str, rhs)) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } } pc += JUMP_OFFSET_LEN; @@ -2557,8 +2574,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (rval.isNumber() && d == rval.toNumber()) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } pc += JUMP_OFFSET_LEN; } @@ -2568,16 +2585,16 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (lval == rval) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } pc += JUMP_OFFSET_LEN; } } ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(jpc)) - script->code; - JS_ASSERT(script->nmap[offs]); - return script->nmap[offs]; + JS_ASSERT(jit->nmap[offs]); + return jit->nmap[offs]; } void * JS_FASTCALL @@ -2586,6 +2603,7 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) jsbytecode * const originalPC = origPc; jsbytecode *pc = originalPC; JSScript *script = f.fp()->script(); + JITScript *jit = script->getJIT(f.fp()->isConstructing()); uint32 jumpOffset = GET_JUMP_OFFSET(pc); pc += JUMP_OFFSET_LEN; @@ -2625,8 +2643,8 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) finally: /* Provide the native address. */ ptrdiff_t offset = (originalPC + jumpOffset) - script->code; - JS_ASSERT(script->nmap[offset]); - return script->nmap[offset]; + JS_ASSERT(jit->nmap[offset]); + return jit->nmap[offset]; } void JS_FASTCALL diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index fe49e1ba1106..da104367cbcb 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -98,14 +98,14 @@ struct UncachedCallResult { void UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr); -JSBool JS_FASTCALL NewObject(VMFrame &f, uint32 argc); +void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto); void JS_FASTCALL Throw(VMFrame &f); void JS_FASTCALL PutCallObject(VMFrame &f); void JS_FASTCALL PutActivationObjects(VMFrame &f); void JS_FASTCALL GetCallObject(VMFrame &f); void JS_FASTCALL WrapPrimitiveThis(VMFrame &f); #if JS_MONOIC -void * JS_FASTCALL InvokeTracer(VMFrame &f, uint32 index); +void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::MICInfo *mic); #else void * JS_FASTCALL InvokeTracer(VMFrame &f); #endif @@ -116,6 +116,7 @@ void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc); void JS_FASTCALL BindName(VMFrame &f); JSObject * JS_FASTCALL BindGlobalName(VMFrame &f); template void JS_FASTCALL SetName(VMFrame &f, JSAtom *atom); +template void JS_FASTCALL SetPropNoCache(VMFrame &f, JSAtom *atom); template void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom); template void JS_FASTCALL SetGlobalNameDumb(VMFrame &f, JSAtom *atom); void JS_FASTCALL Name(VMFrame &f); @@ -226,11 +227,6 @@ inline FuncPtr FunctionTemplateConditional(bool cond, FuncPtr a, FuncPtr b) { return cond ? a : b; } -/* Return f if the script is strict mode code, f otherwise. */ -#define STRICT_VARIANT(f) \ - (FunctionTemplateConditional(script->strictModeCode, \ - f, f)) - }} /* namespace stubs,mjit,js */ extern "C" void * diff --git a/js/src/methodjit/StubCompiler.h b/js/src/methodjit/StubCompiler.h index bdfaff63bd70..d70c7c4b0c61 100644 --- a/js/src/methodjit/StubCompiler.h +++ b/js/src/methodjit/StubCompiler.h @@ -125,6 +125,15 @@ class StubCompiler STUB_CALL_TYPE(BoolStub); STUB_CALL_TYPE(VoidStubAtom); STUB_CALL_TYPE(VoidStubPC); +#ifdef JS_POLYIC + STUB_CALL_TYPE(VoidStubPIC); +#endif +#ifdef JS_MONOIC + STUB_CALL_TYPE(VoidStubMIC); + STUB_CALL_TYPE(VoidPtrStubMIC); + STUB_CALL_TYPE(VoidStubCallIC); + STUB_CALL_TYPE(VoidPtrStubCallIC); +#endif #undef STUB_CALL_TYPE diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index dea6d8c0c79d..005675639ba8 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1196,7 +1196,7 @@ AssertJit(JSContext *cx, uintN argc, jsval *vp) { #ifdef JS_METHODJIT if (JS_GetOptions(cx) & JSOPTION_METHODJIT) { - if (cx->fp()->script()->nmap == NULL) { + if (!cx->fp()->script()->getJIT(cx->fp()->isConstructing())) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_JIT_FAILED); return JS_FALSE; } diff --git a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js index a2b8d32d76de..db7e4b82b2a0 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js +++ b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js @@ -4,7 +4,7 @@ function main() { x = "failure"; } function success() { x = "success"; } /* The JSOP_STOP in a. */ -trap(main, 6, "success()"); +trap(main, 7, "success()"); main(); assertEq(x, "success"); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js index 1d19aeebeb11..fcb2eab3ef1b 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js @@ -3,5 +3,5 @@ function main() { return "failure"; } /* JSOP_RETURN in main. */ -trap(main, 3, "'success'"); +trap(main, 4, "'success'"); assertEq(main(), "success"); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js index 18561be47267..16a5a11c445e 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js @@ -3,7 +3,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* myparent call in myparent. */ - trap(myparent, 37, "failure()"); + trap(myparent, 38, "failure()"); } else { x = "success"; myparent(true); diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js index eefc0bb86005..6c302163e197 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js @@ -4,7 +4,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* noop call in myparent */ - trap(myparent, 48, "success()"); + trap(myparent, 49, "success()"); } else { myparent(true); x = "failure"; diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js index 5cecf58cd5ad..b01d5ac3b12c 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js @@ -6,14 +6,14 @@ function doNothing() { } function myparent(nested) { if (nested) { /* JSOP_CALL to doNothing in myparent with nested = true. */ - trap(myparent, 24, "success()"); + trap(myparent, 25, "success()"); doNothing(); } else { doNothing(); } } /* JSOP_CALL to doNothing in myparent with nested = false. */ -trap(myparent, 34, "myparent(true)"); +trap(myparent, 35, "myparent(true)"); function success() { x = "success"; From c50ce4789fce5fce1774fd14635d7f8c790c2102 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Mon, 4 Oct 2010 19:23:39 -0700 Subject: [PATCH 041/284] Burned by JSOPTION_ANONFUNFIX again. --- js/src/tests/js1_8_5/regress/regress-600137.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/js1_8_5/regress/regress-600137.js b/js/src/tests/js1_8_5/regress/regress-600137.js index d3c27562f6f6..62c9bd1c5b10 100644 --- a/js/src/tests/js1_8_5/regress/regress-600137.js +++ b/js/src/tests/js1_8_5/regress/regress-600137.js @@ -6,7 +6,7 @@ try { for (var [e] = /x/ in d) { - function () {} + (function () {}); } } catch (e) {} try { From 22af90493dfbe3aae2504375906a912f020a2076 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 4 Oct 2010 19:57:15 -0700 Subject: [PATCH 042/284] Bug 600781 - TM: untangle TraceRecorder::getCharAt. r=dvander. --- js/src/jstracer.cpp | 83 +++++++++++++++++++++++---------------------- js/src/jstracer.h | 1 + 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 9cb913a775ab..061bad6b7148 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -12484,59 +12484,60 @@ TraceRecorder::getCharCodeAt(JSString *str, LIns* str_ins, LIns* idx_ins) JS_STATIC_ASSERT(sizeof(JSString) == 16 || sizeof(JSString) == 32); + +JS_REQUIRES_STACK LIns* +TraceRecorder::getUnitString(LIns* str_ins, LIns* idx_ins) +{ + LIns *chars_ins = getStringChars(str_ins); + LIns *ch_ins = lir->insLoad(LIR_ldus2ui, + lir->ins2(LIR_addp, + chars_ins, + lir->ins2ImmI(LIR_lshp, idx_ins, 1)), + 0, ACCSET_OTHER, LOAD_CONST); + guard(true, lir->ins2ImmI(LIR_ltui, ch_ins, UNIT_STRING_LIMIT), snapshot(MISMATCH_EXIT)); + return lir->ins2(LIR_addp, + INS_CONSTPTR(JSString::unitStringTable), + lir->ins2ImmI(LIR_lshp, + lir->insUI2P(ch_ins), + (sizeof(JSString) == 16) ? 4 : 5)); +} + JS_REQUIRES_STACK LIns* TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode) { idx_ins = lir->insUI2P(makeNumberInt32(idx_ins)); LIns *length_ins = lir->insLoad(LIR_ldp, str_ins, offsetof(JSString, mLengthAndFlags), ACCSET_OTHER); - LIns *br = lir->insBranch(LIR_jt, - lir->insEqP_0(lir->ins2(LIR_andp, - length_ins, - INS_CONSTWORD(JSString::ROPE_BIT))), - NULL); - lir->insCall(&js_Flatten_ci, &str_ins); - label(br); - LIns *phi_ins = NULL; + LIns *br1 = lir->insBranch(LIR_jt, + lir->insEqP_0(lir->ins2(LIR_andp, + length_ins, + INS_CONSTWORD(JSString::ROPE_BIT))), + NULL); + lir->insCall(&js_Flatten_ci, &str_ins); + label(br1); + + LIns* inRange = lir->ins2(LIR_ltup, + idx_ins, + lir->ins2ImmI(LIR_rshup, length_ins, JSString::FLAGS_LENGTH_SHIFT)); + + LIns* ret; if (mode == JSOP_GETELEM) { - guard(true, - lir->ins2(LIR_ltup, - idx_ins, - lir->ins2ImmI(LIR_rshup, - length_ins, - JSString::FLAGS_LENGTH_SHIFT)), - MISMATCH_EXIT); + guard(true, inRange, MISMATCH_EXIT); + + ret = getUnitString(str_ins, idx_ins); } else { - phi_ins = lir->insAlloc(sizeof(JSString *)); + LIns *phi_ins = lir->insAlloc(sizeof(JSString *)); lir->insStore(LIR_stp, INS_CONSTSTR(cx->runtime->emptyString), phi_ins, 0, ACCSET_OTHER); - br = lir->insBranch(LIR_jf, - lir->ins2(LIR_ltup, - idx_ins, - lir->ins2ImmI(LIR_rshup, - length_ins, - JSString::FLAGS_LENGTH_SHIFT)), - NULL); + LIns* br2 = lir->insBranch(LIR_jf, inRange, NULL); + LIns *unitstr_ins = getUnitString(str_ins, idx_ins); + lir->insStore(LIR_stp, unitstr_ins, phi_ins, 0, ACCSET_OTHER); + label(br2); + + ret = lir->insLoad(LIR_ldp, phi_ins, 0, ACCSET_OTHER); } - - LIns *chars_ins = getStringChars(str_ins); - LIns *ch_ins = lir->insLoad(LIR_ldus2ui, - lir->ins2(LIR_addp, chars_ins, lir->ins2ImmI(LIR_lshp, idx_ins, 1)), 0, - ACCSET_OTHER, LOAD_CONST); - guard(true, lir->ins2ImmI(LIR_ltui, ch_ins, UNIT_STRING_LIMIT), snapshot(MISMATCH_EXIT)); - LIns *unitstr_ins = lir->ins2(LIR_addp, - INS_CONSTPTR(JSString::unitStringTable), - lir->ins2ImmI(LIR_lshp, - lir->insUI2P(ch_ins), - (sizeof(JSString) == 16) ? 4 : 5)); - if (mode == JSOP_GETELEM) - return unitstr_ins; - - lir->insStore(LIR_stp, unitstr_ins, phi_ins, 0, ACCSET_OTHER); - label(br); - - return lir->insLoad(LIR_ldp, phi_ins, 0, ACCSET_OTHER); + return ret; } // Typed array tracing depends on EXPANDED_LOADSTORE and F2I diff --git a/js/src/jstracer.h b/js/src/jstracer.h index cf5280b24d03..9d3746cb1a9e 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1235,6 +1235,7 @@ class TraceRecorder JS_REQUIRES_STACK nanojit::LIns* getStringChars(nanojit::LIns* str_ins); JS_REQUIRES_STACK nanojit::LIns* getCharCodeAt(JSString *str, nanojit::LIns* str_ins, nanojit::LIns* idx_ins); + JS_REQUIRES_STACK nanojit::LIns* getUnitString(nanojit::LIns* str_ins, nanojit::LIns* idx_ins); JS_REQUIRES_STACK nanojit::LIns* getCharAt(JSString *str, nanojit::LIns* str_ins, nanojit::LIns* idx_ins, JSOp mode); From a40c13317aeb2f6d3aa2c7964bff4f8fa747fa29 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Tue, 5 Oct 2010 12:14:44 +0900 Subject: [PATCH 043/284] Bug 601355 - follow up bug 587707. Crashes consistently at start-up except if JM is disabled or in safe mode. r=bhackett1024, r=dvander --- js/src/methodjit/TrampolineMasmX64.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/methodjit/TrampolineMasmX64.asm b/js/src/methodjit/TrampolineMasmX64.asm index b8353a94a3cf..3c5dac520c7d 100644 --- a/js/src/methodjit/TrampolineMasmX64.asm +++ b/js/src/methodjit/TrampolineMasmX64.asm @@ -158,7 +158,7 @@ JaegerThrowpoline ENDP InjectJaegerReturn PROC FRAME .ENDPROLOG mov rcx, qword ptr [rbx+30h] ; load fp->rval_ into typeReg - mov rax, qword ptr [rbx+50h] ; fp->ncode_ + mov rax, qword ptr [rbx+28h] ; fp->ncode_ ; Reimplementation of PunboxAssembler::loadValueAsComponents() mov rdx, r14 From 55fabd9f6ef7ec73cca1e4cf20edba92d79c5248 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Oct 2010 20:24:43 -0700 Subject: [PATCH 044/284] Bustage fix for bug 589398 new opcode fallout. --- js/src/jspropertycache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jspropertycache.cpp b/js/src/jspropertycache.cpp index d7fcc5dc8416..aa359990489f 100644 --- a/js/src/jspropertycache.cpp +++ b/js/src/jspropertycache.cpp @@ -308,7 +308,7 @@ GetAtomFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs // The method JIT's implementation of instanceof contains an internal lookup // of the prototype property. - if (op == JSOP_INSTANCEOF) + if (op == JSOP_INSTANCEOF || op == JSOP_BEGIN) return cx->runtime->atomState.classPrototypeAtom; ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0; From 4f7cb702371b9ea347ef615756d2fb7deac9ae2f Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Mon, 4 Oct 2010 21:19:30 -0700 Subject: [PATCH 045/284] Long day... --- .../tests/js1_8_5/regress/regress-600137.js | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/js/src/tests/js1_8_5/regress/regress-600137.js b/js/src/tests/js1_8_5/regress/regress-600137.js index 62c9bd1c5b10..58db05bfea7d 100644 --- a/js/src/tests/js1_8_5/regress/regress-600137.js +++ b/js/src/tests/js1_8_5/regress/regress-600137.js @@ -4,15 +4,19 @@ * http://creativecommons.org/licenses/publicdomain/ */ -try { - for (var [e] = /x/ in d) { - (function () {}); - } -} catch (e) {} -try { - let(x = Object.freeze(this, /x/)) - e = #0= * .toString - function y() {} -} catch (e) {} +if (typeof evalcx == 'function') { + var src = 'try {\n' + + ' for (var [e] = /x/ in d) {\n' + + ' (function () {});\n' + + ' }\n' + + '} catch (e) {}\n' + + 'try {\n' + + ' let(x = Object.freeze(this, /x/))\n' + + ' e = #0= * .toString\n' + + ' function y() {}\n' + + '} catch (e) {}'; + + evalcx(src); +} reportCompare(0, 0, "don't crash"); From aa190b930f3cc4813d723c4c339c36dc32408690 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Tue, 5 Oct 2010 10:09:50 -0700 Subject: [PATCH 046/284] Bug 600310 - TM: don't perform GC outside of stack quota r=igor --- js/src/jsgc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index e1d10452d66e..b59c51ad88f9 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2675,6 +2675,11 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) RecordNativeStackTopForGC(cx); +#ifdef DEBUG + int stackDummy; + JS_ASSERT(JS_CHECK_STACK_SIZE(cx->stackLimit, &stackDummy)); +#endif + GCTIMER_BEGIN(); do { From a7311bcecaec409eff161dfba8840ec530643028 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 5 Oct 2010 10:25:41 -0700 Subject: [PATCH 047/284] Don't trace apply with overridden argsobj length (bug 541191, r=dmandelin). --- js/src/jstracer.cpp | 5 +++- js/src/trace-test/tests/basic/bug541191-1.js | 23 +++++++++++++++++ js/src/trace-test/tests/basic/bug541191-2.js | 23 +++++++++++++++++ js/src/trace-test/tests/basic/bug541191-3.js | 23 +++++++++++++++++ js/src/trace-test/tests/basic/bug541191-4.js | 27 ++++++++++++++++++++ js/src/trace-test/tests/basic/bug541191-5.js | 24 +++++++++++++++++ 6 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 js/src/trace-test/tests/basic/bug541191-1.js create mode 100644 js/src/trace-test/tests/basic/bug541191-2.js create mode 100644 js/src/trace-test/tests/basic/bug541191-3.js create mode 100644 js/src/trace-test/tests/basic/bug541191-4.js create mode 100644 js/src/trace-test/tests/basic/bug541191-5.js diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 061bad6b7148..3e4e2a814f74 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -13472,6 +13472,9 @@ TraceRecorder::record_JSOP_APPLY() JSStackFrame *afp = guardArguments(aobj, aobj_ins, &depth); if (!afp) RETURN_STOP_A("can't reach arguments object's frame"); + if (aobj->isArgsLengthOverridden()) + RETURN_STOP_A("can't trace arguments with overridden length"); + guardArgsLengthNotAssigned(aobj_ins); length = afp->numActualArgs(); } else { RETURN_STOP_A("arguments parameter of apply is not a dense array or argments object"); @@ -15233,7 +15236,7 @@ TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins) // ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden. LIns *len_ins = stobj_get_fslot_uint32(argsobj_ins, JSObject::JSSLOT_ARGS_LENGTH); LIns *ovr_ins = lir->ins2(LIR_andi, len_ins, INS_CONST(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT)); - guard(true, lir->insEqI_0(ovr_ins), snapshot(BRANCH_EXIT)); + guard(true, lir->insEqI_0(ovr_ins), snapshot(MISMATCH_EXIT)); return len_ins; } diff --git a/js/src/trace-test/tests/basic/bug541191-1.js b/js/src/trace-test/tests/basic/bug541191-1.js new file mode 100644 index 000000000000..78f0caf10ad8 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug541191-1.js @@ -0,0 +1,23 @@ +/* vim: set ts=4 sw=4 tw=99 et: */ + +function g(a, b, c, d) { + return "" + a + b + c + d; +} + +var x = 1; +function f(a, b, c) { + arguments[1] = 2; + arguments[2] = 3; + arguments[3] = 4; + if (!x) + arguments.length = 4; + var k; + for (var i = 0; i < 10; i++) + k = g.apply(this, arguments); + return k; +} + +assertEq(f(1), "1undefinedundefinedundefined"); +x = 0; +assertEq(f(1), "1234"); + diff --git a/js/src/trace-test/tests/basic/bug541191-2.js b/js/src/trace-test/tests/basic/bug541191-2.js new file mode 100644 index 000000000000..5be269a06082 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug541191-2.js @@ -0,0 +1,23 @@ +/* vim: set ts=4 sw=4 tw=99 et: */ + +function g(a, b, c, d) { + return "" + a + b + c + d; +} + +var x = 1; +function f(a, b, c) { + arguments[1] = 2; + arguments[2] = 3; + arguments[3] = 4; + if (x) + arguments.length = 4; + var k; + for (var i = 0; i < RUNLOOP; i++) + k = g.apply(this, arguments); + return k; +} + +assertEq(f(1), "1234"); +x = 0; +assertEq(f(1), "1undefinedundefinedundefined"); + diff --git a/js/src/trace-test/tests/basic/bug541191-3.js b/js/src/trace-test/tests/basic/bug541191-3.js new file mode 100644 index 000000000000..f4e01b9e1802 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug541191-3.js @@ -0,0 +1,23 @@ +/* vim: set ts=4 sw=4 tw=99 et: */ + +function g(a, b, c, d) { + return "" + a + b + c + d; +} + +var x = 1; +function f(a, b, c) { + arguments[1] = 2; + arguments[2] = 3; + arguments[3] = 4; + if (x) + arguments.length = 1; + var k; + for (var i = 0; i < 10; i++) + k = g.apply(this, arguments); + return k; +} + +assertEq(f(1), "1undefinedundefinedundefined"); +x = 0; +assertEq(f(1), "1undefinedundefinedundefined"); + diff --git a/js/src/trace-test/tests/basic/bug541191-4.js b/js/src/trace-test/tests/basic/bug541191-4.js new file mode 100644 index 000000000000..0ecbb9177425 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug541191-4.js @@ -0,0 +1,27 @@ +/* vim: set ts=4 sw=4 tw=99 et: */ + +function g(a, b, c, d) { + return "" + a + b + c + d; +} + +var x = 1; +function f(a, b, c) { + arguments[1] = 2; + arguments[2] = 3; + arguments[3] = 4; + if (x) { + arguments.length = 1; + delete arguments.length; + arguments.__defineGetter__('length', function () { return eval('1'); }); + } + var k; + for (var i = 0; i < 10; i++) + k = g.apply(this, arguments); + return k; +} + +assertEq(f(1), "1undefinedundefinedundefined"); +x = 0; +assertEq(f(1), "1undefinedundefinedundefined"); + + diff --git a/js/src/trace-test/tests/basic/bug541191-5.js b/js/src/trace-test/tests/basic/bug541191-5.js new file mode 100644 index 000000000000..a2f9e5714f0c --- /dev/null +++ b/js/src/trace-test/tests/basic/bug541191-5.js @@ -0,0 +1,24 @@ +/* vim: set ts=4 sw=4 tw=99 et: */ + +function g(a, b, c, d) { + return "" + a + b + c + d; +} + +var x = 1; +function f(a, b, c) { + arguments[1] = 2; + arguments[2] = 3; + arguments[3] = 4; + if (x) + arguments.length = 1; + var k; + for (var i = 0; i < 10; i++) + k = g.apply(this, arguments); + return k; +} + +assertEq(f(1), "1undefinedundefinedundefined"); +x = 0; +assertEq(f(1), "1undefinedundefinedundefined"); + + From 522426556d51b80c53a301f61cb861e17d02dffb Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Tue, 5 Oct 2010 10:47:11 -0700 Subject: [PATCH 048/284] Bug 601537 - Add a reminder about JSXDR_BYTECODE_VERSION (r=brendan) --- js/src/jsopcode.tbl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 5707ea478dc8..0f02588b5823 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -43,6 +43,8 @@ * for a name of the form JSOP_UNUSED* and claim it. Otherwise, always add at * the end of the table. * + * When changing the bytecode, don't forget to update JSXDR_BYTECODE_VERSION! + * * Includers must define an OPDEF macro of the following form: * * #define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) ... @@ -623,3 +625,4 @@ OPDEF(JSOP_NULLBLOCKCHAIN,248,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_BEGIN, 249,"begin", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_TMPSLOT) +/* When adding bytecodes, don't forget to update JSXDR_BYTECODE_VERSION. */ From 4c44a4adbff13b0b024329c3f2098c5aa0727fc5 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Tue, 5 Oct 2010 10:49:27 -0700 Subject: [PATCH 049/284] Bug 601393 - Ensure that js_EmitTree decrements emitLevel (r=brendan) --- js/src/jsemit.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index d857d1859a1d..3a505a9bcdf8 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -4451,6 +4451,17 @@ EmitEndInit(JSContext *cx, JSCodeGenerator *cg, uint32 count) JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1); JS_STATIC_ASSERT(JSOP_POP_LENGTH == 1); +class EmitLevelManager +{ +private: + JSCodeGenerator *cg; + +public: + EmitLevelManager(JSCodeGenerator *cg) : cg(cg) { cg->emitLevel++; } + + ~EmitLevelManager() { cg->emitLevel--; } +}; + JSBool js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) { @@ -4468,6 +4479,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JSOp op; TokenKind type; uint32 argc; + EmitLevelManager elm(cg); #if JS_HAS_SHARP_VARS jsint sharpnum; #endif @@ -4475,7 +4487,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JS_CHECK_RECURSION(cx, return JS_FALSE); ok = JS_TRUE; - cg->emitLevel++; pn->pn_offset = top = CG_OFFSET(cg); /* Emit notes to tell the current bytecode's source line number. */ @@ -6848,7 +6859,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * MakeDefIntoUse in jsparse.cpp. */ if (pn->pn_op == JSOP_NOP) - return JS_TRUE; + break; if (!EmitNameOp(cx, cg, pn, JS_FALSE)) return JS_FALSE; break; @@ -7049,7 +7060,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JS_ASSERT(0); } - if (ok && --cg->emitLevel == 0) { + /* cg->emitLevel == 1 means we're last on the stack, so finish up. */ + if (ok && cg->emitLevel == 1) { if (cg->spanDeps) ok = OptimizeSpanDeps(cx, cg); if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.end.lineno)) From 1e1a68443190bee0f6419357a03e4e00363c7695 Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Tue, 5 Oct 2010 17:08:01 -0400 Subject: [PATCH 050/284] Eliminate syncData() constant hack. b=601657, r=dvander. --- js/src/methodjit/FastOps.cpp | 5 +- js/src/methodjit/FrameEntry.h | 6 +-- js/src/methodjit/FrameState-inl.h | 43 ++++++--------- js/src/methodjit/FrameState.cpp | 84 +++++++++++++----------------- js/src/methodjit/NunboxAssembler.h | 19 ++++--- js/src/methodjit/PunboxAssembler.h | 57 +++++++++++--------- 6 files changed, 99 insertions(+), 115 deletions(-) diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 50d3fb4d1ea3..7c835e006a7e 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1653,10 +1653,13 @@ mjit::Compiler::jsop_stricteq(JSOp op) masm.set32(cond, frame.tempRegForType(test), Imm32(mask), result); #elif defined JS_CPU_X64 RegisterID maskReg = frame.allocReg(); - masm.move(Imm64(known->getKnownShiftedTag()), maskReg); + frame.pinReg(maskReg); + masm.move(ImmTag(known->getKnownTag()), maskReg); RegisterID r = frame.tempRegForType(test); masm.setPtr(cond, r, maskReg, result); + + frame.unpinReg(maskReg); frame.freeReg(maskReg); #endif frame.popn(2); diff --git a/js/src/methodjit/FrameEntry.h b/js/src/methodjit/FrameEntry.h index cb411488f38a..fc189f05ea59 100644 --- a/js/src/methodjit/FrameEntry.h +++ b/js/src/methodjit/FrameEntry.h @@ -82,7 +82,7 @@ class FrameEntry return v_.s.tag; } #elif defined JS_PUNBOX64 - JSValueShiftedTag getKnownShiftedTag() const { + JSValueShiftedTag getKnownTag() const { return JSValueShiftedTag(v_.asBits & JSVAL_TAG_MASK); } #endif @@ -98,12 +98,12 @@ class FrameEntry } #if defined JS_NUNBOX32 - uint32 getPayload32() const { + uint32 getPayload() const { //JS_ASSERT(!Valueify(v_.asBits).isDouble() || type.synced()); return v_.s.payload.u32; } #elif defined JS_PUNBOX64 - uint64 getPayload64() const { + uint64 getPayload() const { return v_.asBits & JSVAL_PAYLOAD_MASK; } #endif diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index faad1822de65..036c24aa717a 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -491,12 +491,13 @@ FrameState::syncType(const FrameEntry *fe, Address to, Assembler &masm) const fe->isCopied() && addressOf(fe).offset != to.offset); JS_ASSERT(fe->type.inRegister() || fe->type.isConstant()); - if (fe->type.isConstant()) { - JS_ASSERT(fe->isTypeKnown()); + /* Store a double's type bits, even though !isTypeKnown(). */ + if (fe->isConstant()) + masm.storeTypeTag(ImmTag(fe->getKnownTag()), to); + else if (fe->isTypeKnown()) masm.storeTypeTag(ImmType(fe->getKnownType()), to); - } else { + else masm.storeTypeTag(fe->type.reg(), to); - } } inline void @@ -507,18 +508,10 @@ FrameState::syncData(const FrameEntry *fe, Address to, Assembler &masm) const !fe->data.synced()); JS_ASSERT(fe->data.inRegister() || fe->data.isConstant()); - if (fe->data.isConstant()) { - if (!fe->type.synced()) - masm.storeValue(fe->getValue(), to); - else -#if defined JS_NUNBOX32 - masm.storePayload(Imm32(fe->getPayload32()), to); -#elif defined JS_PUNBOX64 - masm.storePayload(Imm64(fe->getPayload64()), to); -#endif - } else { + if (fe->data.isConstant()) + masm.storePayload(ImmPayload(fe->getPayload()), to); + else masm.storePayload(fe->data.reg(), to); - } } inline void @@ -850,18 +843,12 @@ FrameState::loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) cons return; } - Address address = addressOf(fe); - do { - if (!fe->data.synced()) { - syncData(fe, address, masm); - if (fe->isConstant()) - break; - } - if (!fe->type.synced()) - syncType(fe, address, masm); - } while (0); + if (!fe->data.synced()) + syncData(fe, addressOf(fe), masm); + if (!fe->type.synced()) + syncType(fe, addressOf(fe), masm); - masm.loadDouble(address, fpReg); + masm.loadDouble(addressOf(fe), fpReg); } inline bool @@ -870,8 +857,8 @@ FrameState::isClosedVar(uint32 slot) return closedVars[slot]; } -} /* namspace mjit */ -} /* namspace js */ +} /* namespace mjit */ +} /* namespace js */ #endif /* include */ diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 838f638fc510..5e269810e6db 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -481,13 +481,10 @@ FrameState::sync(Assembler &masm, Uses uses) const * already been synced. Otherwise, see if a constant needs to be * synced. */ - if (fe->data.inRegister()) { + if (fe->data.inRegister()) avail.putReg(fe->data.reg()); - } else if (!fe->data.synced()) { + else if (!fe->data.synced()) syncData(fe, address, masm); - if (fe->isConstant()) - continue; - } if (fe->type.inRegister()) avail.putReg(fe->type.reg()); @@ -577,9 +574,7 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) tempRegForData(backing); syncData(backing, address, masm); fe->data.sync(); - if (fe->isConstant() && !fe->type.synced()) { - fe->type.sync(); - } else if (fe->data.inRegister() && kill.hasReg(fe->data.reg())) { + if (fe->data.inRegister() && kill.hasReg(fe->data.reg())) { forgetReg(fe->data.reg()); fe->data.setMemory(); } @@ -790,17 +785,10 @@ FrameState::copyEntryIntoFPReg(Assembler &masm, FrameEntry *fe, FPRegisterID fpr fe = fe->copyOf(); /* The entry must be synced to memory. */ - if (fe->data.isConstant()) { - if (!fe->data.synced()) - syncData(fe, addressOf(fe), masm); - if (!fe->type.synced()) - syncType(fe, addressOf(fe), masm); - } else { - if (fe->data.inRegister() && !fe->data.synced()) - syncData(fe, addressOf(fe), masm); - if (fe->type.inRegister() && !fe->type.synced()) - syncType(fe, addressOf(fe), masm); - } + if (!fe->data.synced()) + syncData(fe, addressOf(fe), masm); + if (!fe->type.synced()) + syncType(fe, addressOf(fe), masm); masm.loadDouble(addressOf(fe), fpreg); return fpreg; @@ -1114,39 +1102,37 @@ FrameState::storeLocal(uint32 n, bool popGuaranteed, bool typeChange) storeTop(local, popGuaranteed, typeChange); bool closed = eval || isClosedVar(n); - if (closed || inTryBlock) { - /* Ensure that the local variable remains synced. */ - if (local->isCopy()) { - FrameEntry *backing = local->copyOf(); - if (!local->data.synced()) { - if (backing->data.inMemory()) - tempRegForData(backing); - syncData(backing, addressOf(local), masm); - } - if (!local->type.synced()) { - if (backing->type.inMemory()) - tempRegForType(backing); - syncType(backing, addressOf(local), masm); - } - } else if (local->isConstant()) { - if (!local->data.synced()) - syncData(local, addressOf(local), masm); - } else { - if (!local->data.synced()) { - syncData(local, addressOf(local), masm); - local->data.sync(); - } - if (!local->type.synced()) { - syncType(local, addressOf(local), masm); - local->type.sync(); - } - if (closed) - forgetEntry(local); - } + if (!closed && !inTryBlock) + return; + /* Ensure that the local variable remains synced. */ + if (local->isCopy()) { + FrameEntry *backing = local->copyOf(); + if (!local->data.synced()) { + if (backing->data.inMemory()) + tempRegForData(backing); + syncData(backing, addressOf(local), masm); + } + if (!local->type.synced()) { + if (backing->type.inMemory()) + tempRegForType(backing); + syncType(backing, addressOf(local), masm); + } + } else { + if (!local->data.synced()) { + syncData(local, addressOf(local), masm); + local->data.sync(); + } + if (!local->type.synced()) { + syncType(local, addressOf(local), masm); + local->type.sync(); + } if (closed) - local->resetSynced(); + forgetEntry(local); } + + if (closed) + local->resetSynced(); } void diff --git a/js/src/methodjit/NunboxAssembler.h b/js/src/methodjit/NunboxAssembler.h index f3f781fbcf9b..1303361f8220 100644 --- a/js/src/methodjit/NunboxAssembler.h +++ b/js/src/methodjit/NunboxAssembler.h @@ -47,13 +47,9 @@ namespace js { namespace mjit { -/* - * Don't use ImmTag. Use ImmType instead. - * TODO: ImmTag should really just be for internal use... - */ -class ImmTag : public JSC::MacroAssembler::Imm32 +/* Don't use ImmTag. Use ImmType instead. */ +struct ImmTag : JSC::MacroAssembler::Imm32 { - public: ImmTag(JSValueTag mask) : Imm32(int32(mask)) { } @@ -66,6 +62,13 @@ struct ImmType : ImmTag { } }; +struct ImmPayload : JSC::MacroAssembler::Imm32 +{ + ImmPayload(uint32 payload) + : Imm32(payload) + { } +}; + class Assembler : public BaseAssembler { static const uint32 PAYLOAD_OFFSET = 0; @@ -111,7 +114,7 @@ class Assembler : public BaseAssembler } template - void storeTypeTag(ImmType imm, T address) { + void storeTypeTag(ImmTag imm, T address) { store32(imm, tagOf(address)); } @@ -131,7 +134,7 @@ class Assembler : public BaseAssembler } template - void storePayload(Imm32 imm, T address) { + void storePayload(ImmPayload imm, T address) { store32(imm, payloadOf(address)); } diff --git a/js/src/methodjit/PunboxAssembler.h b/js/src/methodjit/PunboxAssembler.h index bde46d72a306..1bdaaa5b80fa 100644 --- a/js/src/methodjit/PunboxAssembler.h +++ b/js/src/methodjit/PunboxAssembler.h @@ -47,27 +47,32 @@ namespace js { namespace mjit { -class Imm64 : public JSC::MacroAssembler::ImmPtr +struct Imm64 : JSC::MacroAssembler::ImmPtr { - public: Imm64(uint64 u) : ImmPtr((const void *)u) { } }; -class ImmShiftedTag : public JSC::MacroAssembler::ImmPtr +/* Tag stored in shifted format. */ +struct ImmTag : JSC::MacroAssembler::ImmPtr { - public: - ImmShiftedTag(JSValueShiftedTag shtag) + ImmTag(JSValueShiftedTag shtag) : ImmPtr((const void *)shtag) { } }; -class ImmType : public ImmShiftedTag +struct ImmType : ImmTag { - public: ImmType(JSValueType type) - : ImmShiftedTag(JSValueShiftedTag(JSVAL_TYPE_TO_SHIFTED_TAG(type))) + : ImmTag(JSValueShiftedTag(JSVAL_TYPE_TO_SHIFTED_TAG(type))) + { } +}; + +struct ImmPayload : Imm64 +{ + ImmPayload(uint64 payload) + : Imm64(payload) { } }; @@ -138,7 +143,7 @@ class Assembler : public BaseAssembler } template - void storeValueFromComponents(ImmShiftedTag type, RegisterID payload, T address) { + void storeValueFromComponents(ImmTag type, RegisterID payload, T address) { move(type, Registers::ValueReg); orPtr(payload, Registers::ValueReg); storeValue(Registers::ValueReg, address); @@ -151,7 +156,7 @@ class Assembler : public BaseAssembler } template - void storeTypeTag(ImmShiftedTag imm, T address) { + void storeTypeTag(ImmTag imm, T address) { loadValue(address, Registers::ValueReg); convertValueToPayload(Registers::ValueReg); orPtr(imm, Registers::ValueReg); @@ -183,7 +188,7 @@ class Assembler : public BaseAssembler } template - void storePayload(Imm64 imm, T address) { + void storePayload(ImmPayload imm, T address) { /* Not for doubles. */ storePtr(imm, valueOf(address)); } @@ -223,7 +228,7 @@ class Assembler : public BaseAssembler } Jump testNull(Assembler::Condition cond, RegisterID reg) { - return branchPtr(cond, reg, ImmShiftedTag(JSVAL_SHIFTED_TAG_NULL)); + return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_NULL)); } Jump testNull(Assembler::Condition cond, Address address) { @@ -232,47 +237,47 @@ class Assembler : public BaseAssembler } Jump testInt32(Assembler::Condition cond, RegisterID reg) { - return branchPtr(cond, reg, ImmShiftedTag(JSVAL_SHIFTED_TAG_INT32)); + return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_INT32)); } Jump testInt32(Assembler::Condition cond, Address address) { loadValue(address, Registers::ValueReg); convertValueToType(Registers::ValueReg); - return branchPtr(cond, Registers::ValueReg, ImmShiftedTag(JSVAL_SHIFTED_TAG_INT32)); + return branchPtr(cond, Registers::ValueReg, ImmTag(JSVAL_SHIFTED_TAG_INT32)); } Jump testNumber(Assembler::Condition cond, RegisterID reg) { cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above; - return branchPtr(cond, reg, ImmShiftedTag(JSVAL_SHIFTED_TAG_INT32)); + return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_INT32)); } Jump testNumber(Assembler::Condition cond, Address address) { cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above; loadValue(address, Registers::ValueReg); convertValueToType(Registers::ValueReg); - return branchPtr(cond, Registers::ValueReg, ImmShiftedTag(JSVAL_SHIFTED_TAG_INT32)); + return branchPtr(cond, Registers::ValueReg, ImmTag(JSVAL_SHIFTED_TAG_INT32)); } Jump testPrimitive(Assembler::Condition cond, RegisterID reg) { cond = (cond == Assembler::NotEqual) ? Assembler::AboveOrEqual : Assembler::Below; - return branchPtr(cond, reg, ImmShiftedTag(JSVAL_SHIFTED_TAG_OBJECT)); + return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_OBJECT)); } Jump testPrimitive(Assembler::Condition cond, Address address) { cond = (cond == Assembler::NotEqual) ? Assembler::AboveOrEqual : Assembler::Below; loadValue(address, Registers::ValueReg); convertValueToType(Registers::ValueReg); - return branchPtr(cond, Registers::ValueReg, ImmShiftedTag(JSVAL_SHIFTED_TAG_OBJECT)); + return branchPtr(cond, Registers::ValueReg, ImmTag(JSVAL_SHIFTED_TAG_OBJECT)); } Jump testObject(Assembler::Condition cond, RegisterID reg) { - return branchPtr(cond, reg, ImmShiftedTag(JSVAL_SHIFTED_TAG_OBJECT)); + return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_OBJECT)); } Jump testObject(Assembler::Condition cond, Address address) { loadValue(address, Registers::ValueReg); convertValueToType(Registers::ValueReg); - return branchPtr(cond, Registers::ValueReg, ImmShiftedTag(JSVAL_SHIFTED_TAG_OBJECT)); + return branchPtr(cond, Registers::ValueReg, ImmTag(JSVAL_SHIFTED_TAG_OBJECT)); } Jump testDouble(Assembler::Condition cond, RegisterID reg) { @@ -281,7 +286,7 @@ class Assembler : public BaseAssembler opcond = Assembler::Below; else opcond = Assembler::AboveOrEqual; - return branchPtr(opcond, reg, ImmShiftedTag(JSVAL_SHIFTED_TAG_MAX_DOUBLE)); + return branchPtr(opcond, reg, ImmTag(JSVAL_SHIFTED_TAG_MAX_DOUBLE)); } Jump testDouble(Assembler::Condition cond, Address address) { @@ -292,27 +297,27 @@ class Assembler : public BaseAssembler opcond = Assembler::AboveOrEqual; loadValue(address, Registers::ValueReg); convertValueToType(Registers::ValueReg); - return branchPtr(opcond, Registers::ValueReg, ImmShiftedTag(JSVAL_SHIFTED_TAG_MAX_DOUBLE)); + return branchPtr(opcond, Registers::ValueReg, ImmTag(JSVAL_SHIFTED_TAG_MAX_DOUBLE)); } Jump testBoolean(Assembler::Condition cond, RegisterID reg) { - return branchPtr(cond, reg, ImmShiftedTag(JSVAL_SHIFTED_TAG_BOOLEAN)); + return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_BOOLEAN)); } Jump testBoolean(Assembler::Condition cond, Address address) { loadValue(address, Registers::ValueReg); convertValueToType(Registers::ValueReg); - return branchPtr(cond, Registers::ValueReg, ImmShiftedTag(JSVAL_SHIFTED_TAG_BOOLEAN)); + return branchPtr(cond, Registers::ValueReg, ImmTag(JSVAL_SHIFTED_TAG_BOOLEAN)); } Jump testString(Assembler::Condition cond, RegisterID reg) { - return branchPtr(cond, reg, ImmShiftedTag(JSVAL_SHIFTED_TAG_STRING)); + return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_STRING)); } Jump testString(Assembler::Condition cond, Address address) { loadValue(address, Registers::ValueReg); convertValueToType(Registers::ValueReg); - return branchPtr(cond, Registers::ValueReg, ImmShiftedTag(JSVAL_SHIFTED_TAG_BOOLEAN)); + return branchPtr(cond, Registers::ValueReg, ImmTag(JSVAL_SHIFTED_TAG_BOOLEAN)); } }; From a2316e8005dd7971b974c95de8f5be2a5c4b7eb3 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 4 Oct 2010 16:22:26 -0700 Subject: [PATCH 051/284] Bug 601505 - Error instances don't have [[Construct]] (r=brendan) --HG-- extra : rebase_source : 12c4c0432ca571f57993cabe3309810cfa3866d1 --- js/src/jsexn.cpp | 2 +- js/src/tests/ecma_3/Exceptions/15.11.5.js | 65 +++++++++++++++++++++ js/src/tests/ecma_3/Exceptions/jstests.list | 1 + 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 js/src/tests/ecma_3/Exceptions/15.11.5.js diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index c21b0c396560..d7b5e414a74b 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -101,7 +101,7 @@ Class js_ErrorClass = { NULL, /* reserved0 */ NULL, /* checkAccess */ NULL, /* call */ - Exception, /* construct */ + NULL, /* construct */ NULL, /* xdrObject */ NULL, /* hasInstance */ JS_CLASS_TRACE(exn_trace) diff --git a/js/src/tests/ecma_3/Exceptions/15.11.5.js b/js/src/tests/ecma_3/Exceptions/15.11.5.js new file mode 100644 index 000000000000..351a82bc95a0 --- /dev/null +++ b/js/src/tests/ecma_3/Exceptions/15.11.5.js @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is JavaScript Engine testing utilities. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * + * Date: 4 Oct 2010 + * SUMMARY: Error instances have no special properties beyond those inherited + * from the Error prototype object + */ +//----------------------------------------------------------------------------- +var summary = 'Error instances have no special properties beyond those inherited the Error prototype object'; + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + enterFunc ('test'); + printStatus (summary); + + var actual = ''; + var expect = 'TypeError: Error.prototype is not a constructor'; + try { + new Error.prototype; + } catch (e) { + actual = '' + e; + } + + reportCompare(actual, expect, "not a constructor"); + + exitFunc ('test'); +} diff --git a/js/src/tests/ecma_3/Exceptions/jstests.list b/js/src/tests/ecma_3/Exceptions/jstests.list index 941b1aadf308..fc7aa883e5d7 100644 --- a/js/src/tests/ecma_3/Exceptions/jstests.list +++ b/js/src/tests/ecma_3/Exceptions/jstests.list @@ -1,6 +1,7 @@ url-prefix ../../jsreftest.html?test=ecma_3/Exceptions/ script 15.11.1.1.js script 15.11.4.4-1.js +script 15.11.5.js script 15.11.7.6-001.js script 15.11.7.6-002.js script 15.11.7.6-003.js From 1b445aec596c74b76713f28847932511edbccaba Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Tue, 5 Oct 2010 14:41:55 -0700 Subject: [PATCH 052/284] Bug 598682, part 1 - Minor cleanups to jsinterp.h and jsinterpinlines.h (r=lw) --HG-- extra : rebase_source : ce5adce0b4f5155afd958f2a083085baf4dbe643 --- js/src/jsinterp.h | 14 ++++++++++---- js/src/jsinterpinlines.h | 14 ++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 9b09ddaa1f40..a1937da7190c 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -347,8 +347,13 @@ struct JSStackFrame * lazily created, so a given function frame may or may not have one. */ + /* True if this frame has arguments. Contrast with hasArgsObj. */ + bool hasArgs() const { + return isFunctionFrame() && !isEvalFrame(); + } + uintN numFormalArgs() const { - JS_ASSERT(isFunctionFrame() && !isEvalFrame()); + JS_ASSERT(hasArgs()); return fun()->nargs; } @@ -358,12 +363,12 @@ struct JSStackFrame } js::Value *formalArgs() const { - JS_ASSERT(isFunctionFrame() && !isEvalFrame()); + JS_ASSERT(hasArgs()); return (js::Value *)this - numFormalArgs(); } js::Value *formalArgsEnd() const { - JS_ASSERT(isFunctionFrame() && !isEvalFrame()); + JS_ASSERT(hasArgs()); return (js::Value *)this; } @@ -381,6 +386,7 @@ struct JSStackFrame template inline void forEachCanonicalActualArg(Op op); template inline void forEachFormalArg(Op op); + /* True if we have created an arguments object for this frame; implies hasArgs(). */ bool hasArgsObj() const { return !!(flags_ & JSFRAME_HAS_ARGS_OBJ); } @@ -420,7 +426,7 @@ struct JSStackFrame } JSObject &constructorThis() const { - JS_ASSERT(isFunctionFrame() && !isEvalFrame()); + JS_ASSERT(hasArgs()); return formalArgs()[-1].toObject(); } diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 87d5f3d87ad5..f8f02f756c9f 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -276,7 +276,7 @@ JSStackFrame::varobj(JSContext *cx) const inline uintN JSStackFrame::numActualArgs() const { - JS_ASSERT(isFunctionFrame() && !isEvalFrame()); + JS_ASSERT(hasArgs()); if (JS_UNLIKELY(flags_ & (JSFRAME_OVERFLOW_ARGS | JSFRAME_UNDERFLOW_ARGS))) return hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual; return numFormalArgs(); @@ -285,8 +285,8 @@ JSStackFrame::numActualArgs() const inline js::Value * JSStackFrame::actualArgs() const { - JS_ASSERT(isFunctionFrame() && !isEvalFrame()); - js::Value *argv = formalArgsEnd() - numFormalArgs(); + JS_ASSERT(hasArgs()); + js::Value *argv = formalArgs(); if (JS_UNLIKELY(flags_ & JSFRAME_OVERFLOW_ARGS)) { uintN nactual = hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual; return argv - (2 + nactual); @@ -297,12 +297,10 @@ JSStackFrame::actualArgs() const inline js::Value * JSStackFrame::actualArgsEnd() const { - JS_ASSERT(isFunctionFrame() && !isEvalFrame()); + JS_ASSERT(hasArgs()); if (JS_UNLIKELY(flags_ & JSFRAME_OVERFLOW_ARGS)) - return formalArgsEnd() - (2 + numFormalArgs()); - uintN argc = numActualArgs(); - uintN nmissing = numFormalArgs() - argc; - return formalArgsEnd() - nmissing; + return formalArgs() - 2; + return formalArgs() + numActualArgs(); } inline void From 676ddcba58bf1e100357c4b2ee866da7ca01445c Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 5 Oct 2010 14:41:58 -0700 Subject: [PATCH 053/284] Bug 598682, part 2 - Minor cleanups to jsinterp.h and jsinterpinlines.h (r=bhackett) --HG-- extra : rebase_source : bd664d9cd752992faa280d8d3a848058883f57e4 --- js/src/jscntxtinlines.h | 4 ---- js/src/jsinterp.cpp | 2 +- js/src/jsinterp.h | 28 +++++----------------------- js/src/jsinterpinlines.h | 36 +++++++++++++++++++++++++++++------- js/src/jsiter.cpp | 2 +- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index a06cbf0253b8..4cd3787eec04 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -324,8 +324,6 @@ StackSpace::pushInvokeFrame(JSContext *cx, const CallArgs &args, { JS_ASSERT(firstUnused() == args.argv() + args.argc()); - JSStackFrame *fp = fg->regs_.fp; - fp->setPrev(cx->regs); if (JS_UNLIKELY(!currentSegment->inContext())) { cx->pushSegmentAndFrame(currentSegment, fg->regs_); } else { @@ -392,8 +390,6 @@ StackSpace::pushInlineFrame(JSContext *cx, JSScript *script, JSStackFrame *fp, JS_ASSERT(isCurrentAndActive(cx)); JS_ASSERT(cx->regs == regs && script == fp->script()); - fp->setPrev(regs); - regs->fp = fp; regs->pc = script->code; regs->sp = fp->slots() + script->nfixed; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 9a155087dbf9..ea2898080cd2 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -830,7 +830,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script, JSObject *initialVarObj; if (prev) { JS_ASSERT(chain == &prev->scopeChain()); - frame.fp()->initEvalFrame(script, prev, prev->pc(cx), flags); + frame.fp()->initEvalFrame(cx, script, prev, flags); /* * We want to call |prev->varobj()|, but this requires knowing the diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index a1937da7190c..f5f9de5f827e 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -137,6 +137,8 @@ struct JSStackFrame friend class js::FrameRegsIter; friend struct JSContext; + inline void initPrev(JSContext *cx); + public: /* * Stack frame sort (see JSStackFrame comment above) @@ -195,8 +197,8 @@ struct JSStackFrame inline void initCallFrameLatePrologue(); /* Used for eval. */ - inline void initEvalFrame(JSScript *script, JSStackFrame *prev, - jsbytecode *prevpc, uint32 flags); + inline void initEvalFrame(JSContext *cx, JSScript *script, JSStackFrame *prev, + uint32 flags); inline void initGlobalFrame(JSScript *script, JSObject &chain, uint32 flags); /* Used when activating generators. */ @@ -223,27 +225,7 @@ struct JSStackFrame return prev_; } - void setPrev(JSStackFrame *prev, jsbytecode *prevpc) { - JS_ASSERT(flags_ & JSFRAME_HAS_PREVPC); - prev_ = prev; - if (prev) { - prevpc_ = prevpc; - JS_ASSERT_IF(!prev->isDummyFrame() && !prev->hasImacropc(), - uint32(prevpc - prev->script()->code) < prev->script()->length); - } - } - - void setPrev(JSFrameRegs *regs) { - JS_ASSERT(flags_ & JSFRAME_HAS_PREVPC); - if (regs) { - prev_ = regs->fp; - prevpc_ = regs->pc; - JS_ASSERT_IF(!prev_->isDummyFrame() && !prev_->hasImacropc(), - uint32(prevpc_ - prev_->script()->code) < prev_->script()->length); - } else { - prev_ = NULL; - } - } + inline void resetGeneratorPrev(JSContext *cx); /* * Frame slots diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index f8f02f756c9f..41447266d34b 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -40,6 +40,29 @@ #ifndef jsinterpinlines_h__ #define jsinterpinlines_h__ +inline void +JSStackFrame::initPrev(JSContext *cx) +{ + JS_ASSERT(flags_ & JSFRAME_HAS_PREVPC); + if (JSFrameRegs *regs = cx->regs) { + prev_ = regs->fp; + prevpc_ = regs->pc; + JS_ASSERT_IF(!prev_->isDummyFrame() && !prev_->hasImacropc(), + uint32(prevpc_ - prev_->script()->code) < prev_->script()->length); + } else { + prev_ = NULL; +#ifdef DEBUG + prevpc_ = (jsbytecode *)0xbadc; +#endif + } +} + +inline void +JSStackFrame::resetGeneratorPrev(JSContext *cx) +{ + initPrev(cx); +} + inline void JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun, uint32 nactual, uint32 flagsArg) @@ -54,7 +77,7 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun, exec.fun = fun; args.nactual = nactual; /* only need to write if over/under-flow */ scopeChain_ = callee.getParent(); - /* prevpc_, prev_ initialized by push*Frame */ + initPrev(cx); JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); JS_ASSERT(annotation() == NULL); @@ -89,7 +112,6 @@ JSStackFrame::initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flag inline void JSStackFrame::initCallFrameEarlyPrologue(JSFunction *fun, void *ncode) { - /* Initialize state that gets set early in a jitted function's prologue. */ exec.fun = fun; ncode_ = ncode; } @@ -105,8 +127,7 @@ JSStackFrame::initCallFrameLatePrologue() } inline void -JSStackFrame::initEvalFrame(JSScript *script, JSStackFrame *prev, - jsbytecode *prevpc, uint32 flagsArg) +JSStackFrame::initEvalFrame(JSContext *cx, JSScript *script, JSStackFrame *prev, uint32 flagsArg) { JS_ASSERT(flagsArg & JSFRAME_EVAL); JS_ASSERT((flagsArg & ~(JSFRAME_EVAL | JSFRAME_DEBUGGER)) == 0); @@ -133,10 +154,12 @@ JSStackFrame::initEvalFrame(JSScript *script, JSStackFrame *prev, } else { exec.script = script; } + scopeChain_ = &prev->scopeChain(); JS_ASSERT_IF(isFunctionFrame(), &callObj() == &prev->callObj()); - setPrev(prev, prevpc); + prev_ = prev; + prevpc_ = prev->pc(cx); JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); setAnnotation(prev->annotation()); @@ -157,7 +180,6 @@ JSStackFrame::initGlobalFrame(JSScript *script, JSObject &chain, uint32 flagsArg exec.script = script; args.script = (JSScript *)0xbad; scopeChain_ = &chain; - prev_ = NULL; JS_ASSERT(!hasImacropc()); JS_ASSERT(!hasHookData()); @@ -169,7 +191,7 @@ JSStackFrame::initDummyFrame(JSContext *cx, JSObject &chain) { js::PodZero(this); flags_ = JSFRAME_DUMMY | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN; - setPrev(cx->regs); + initPrev(cx); chain.isGlobal(); setScopeChainNoCallObj(chain); } diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index a5d18f38abcf..772415a27df6 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1271,7 +1271,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, /* Copy frame onto the stack. */ stackfp->stealFrameAndSlots(stackvp, genfp, genvp, gen->regs.sp); - stackfp->setPrev(cx->regs); + stackfp->resetGeneratorPrev(cx); stackfp->unsetFloatingGenerator(); RebaseRegsFromTo(&gen->regs, genfp, stackfp); MUST_FLOW_THROUGH("restore"); From 7893e3acd96f931968551d784bfe2870b09bd272 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 5 Oct 2010 14:50:15 -0700 Subject: [PATCH 054/284] Bug 601771 - TM: possibly uninitialized variable in jsclone.cpp. r=jorendorff via IRC. --- js/src/jsclone.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsclone.cpp b/js/src/jsclone.cpp index 20121586ee6f..a5c1a910528b 100644 --- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -159,7 +159,7 @@ SCInput::read(uint64_t *p) bool SCInput::readPair(uint32_t *tagp, uint32_t *datap) { - uint64_t u; + uint64_t u = 0; /* initialize to shut GCC up */ bool ok = read(&u); if (ok) { *tagp = uint32_t(u >> 32); From 7c039a3ea230a1ce0ca9945dfc4d1ca9e94ca51f Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Fri, 1 Oct 2010 11:12:01 -0700 Subject: [PATCH 055/284] Bug 595963: notify iterators about property deletion in array_splice, r=gal --HG-- extra : rebase_source : de2700e0d3d7bce1453a73155c569d28cfbd4482 --- js/src/jsarray.cpp | 4 ++ js/src/jsiter.cpp | 65 ++++++++++++++++---- js/src/jsiter.h | 3 + js/src/trace-test/tests/basic/bug595963-1.js | 19 ++++++ js/src/trace-test/tests/basic/bug595963-2.js | 19 ++++++ 5 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 js/src/trace-test/tests/basic/bug595963-1.js create mode 100644 js/src/trace-test/tests/basic/bug595963-2.js diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 3b95c57d4ec8..4542b07de976 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2338,6 +2338,7 @@ array_splice(JSContext *cx, uintN argc, Value *vp) JSObject *obj = ComputeThisFromVp(cx, vp); if (!obj || !js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; + jsuint origlength = length; /* Convert the first argument into a starting index. */ jsdouble d; @@ -2452,6 +2453,9 @@ array_splice(JSContext *cx, uintN argc, Value *vp) length -= delta; } + if (length < origlength && !js_SuppressDeletedIndexProperties(cx, obj, length, origlength)) + return JS_FALSE; + /* * Copy from argv into the hole to complete the splice, and update length in * case we deleted elements from the end. diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 772415a27df6..96e04aad7bcb 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -871,17 +871,25 @@ js_CloseIterator(JSContext *cx, JSObject *obj) } /* - * Suppress enumeration of deleted properties. We maintain a list of all active - * non-escaping for-in enumerators. Whenever a property is deleted, we check - * whether any active enumerator contains the (obj, id) pair and has not - * enumerated id yet. If so, we delete the id from the list (or advance the - * cursor if it is the next id to be enumerated). + * Suppress enumeration of deleted properties. This function must be called + * when a property is deleted and there might be active enumerators. + * + * We maintain a list of active non-escaping for-in enumerators. To suppress + * a property, we check whether each active enumerator contains the (obj, id) + * pair and has not yet enumerated |id|. If so, and |id| is the next property, + * we simply advance the cursor. Otherwise, we delete |id| from the list. * * We do not suppress enumeration of a property deleted along an object's * prototype chain. Only direct deletions on the object are handled. + * + * This function can suppress multiple properties at once. The |predicate| + * argument is an object which can be called on an id and returns true or + * false. It also must have a method |matchesAtMostOne| which allows us to + * stop searching after the first deletion if true. */ -bool -js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) +template +static bool +SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicate) { JSObject *iterobj = cx->enumerators; while (iterobj) { @@ -893,7 +901,7 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) jsid *props_cursor = ni->currentKey(); jsid *props_end = ni->endKey(); for (jsid *idp = props_cursor; idp < props_end; ++idp) { - if (*idp == id) { + if (predicate(*idp)) { /* * Check whether another property along the prototype chain * became visible as a result of this deletion. @@ -902,14 +910,14 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) AutoObjectRooter proto(cx, obj->getProto()); AutoObjectRooter obj2(cx); JSProperty *prop; - if (!proto.object()->lookupProperty(cx, id, obj2.addr(), &prop)) + if (!proto.object()->lookupProperty(cx, *idp, obj2.addr(), &prop)) return false; if (prop) { uintN attrs; if (obj2.object()->isNative()) { attrs = ((Shape *) prop)->attributes(); JS_UNLOCK_OBJ(cx, obj2.object()); - } else if (!obj2.object()->getAttributes(cx, id, &attrs)) { + } else if (!obj2.object()->getAttributes(cx, *idp, &attrs)) { return false; } if (attrs & JSPROP_ENUMERATE) @@ -925,7 +933,7 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) goto again; /* - * No property along the prototype chain steppeded in to take the + * No property along the prototype chain stepped in to take the * property's place, so go ahead and delete id from the list. * If it is the next property to be enumerated, just skip it. */ @@ -935,7 +943,8 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) memmove(idp, idp + 1, (props_end - (idp + 1)) * sizeof(jsid)); ni->props_end = ni->endKey() - 1; } - break; + if (predicate.matchesAtMostOne()) + break; } } } @@ -944,6 +953,38 @@ js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) return true; } +class SingleIdPredicate { + jsid id; +public: + SingleIdPredicate(jsid id) : id(id) {} + + bool operator()(jsid id) { return id == this->id; } + bool matchesAtMostOne() { return true; } +}; + +bool +js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) +{ + return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id)); +} + +class IndexRangePredicate { + jsint begin, end; +public: + IndexRangePredicate(jsint begin, jsint end) : begin(begin), end(end) {} + + bool operator()(jsid id) { + return JSID_IS_INT(id) && begin <= JSID_TO_INT(id) && JSID_TO_INT(id) < end; + } + bool matchesAtMostOne() { return false; } +}; + +bool +js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end) +{ + return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end)); +} + JSBool js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval) { diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 022369e8fe5e..d47cb565f284 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -171,6 +171,9 @@ js_CloseIterator(JSContext *cx, JSObject *iterObj); bool js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id); +bool +js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end); + /* * IteratorMore() indicates whether another value is available. It might * internally call iterobj.next() and then cache the value until its diff --git a/js/src/trace-test/tests/basic/bug595963-1.js b/js/src/trace-test/tests/basic/bug595963-1.js new file mode 100644 index 000000000000..2cccfac400d1 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug595963-1.js @@ -0,0 +1,19 @@ +function remove(k, L) { + for (var i in k) { + if (i == L) + k.splice(L, 1); + } +} +function f(k) { + var L = 0; + for (var i in k) { + if (L == 1) + remove(k, L); + L++; + assertEq(k[i], 3); + } + assertEq(L, 6); +} + +var a = [3, 3, 3, 3, 3, 3, 3]; +f(a); diff --git a/js/src/trace-test/tests/basic/bug595963-2.js b/js/src/trace-test/tests/basic/bug595963-2.js new file mode 100644 index 000000000000..651a38064ea3 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug595963-2.js @@ -0,0 +1,19 @@ +function remove(k, L) { + for (var i in k) { + if (i == L) + k.splice(L, 3); + } +} +function f(k) { + var L = 0; + for (var i in k) { + if (L == 1) + remove(k, L); + L++; + assertEq(k[i], 3); + } + assertEq(L, 4); +} + +var a = [3, 3, 3, 3, 3, 3, 3]; +f(a); From b86507bf21fdecd130abf2bc531291e28dc2bc25 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 5 Oct 2010 16:41:06 -0700 Subject: [PATCH 056/284] Fix crash in AtSafePoint (bug 589398, r=dmandelin). --- js/src/jsinterp.cpp | 2 +- js/src/jsscript.h | 36 +++++++++++++++++++-- js/src/methodjit/Compiler.cpp | 2 +- js/src/methodjit/InvokeHelpers.cpp | 34 ++++++------------- js/src/methodjit/MethodJIT.cpp | 2 ++ js/src/methodjit/MethodJIT.h | 20 ------------ js/src/methodjit/StubCalls.cpp | 31 +++++++++--------- js/src/trace-test/tests/jaeger/bug601982.js | 33 +++++++++++++++++++ 8 files changed, 97 insertions(+), 63 deletions(-) create mode 100644 js/src/trace-test/tests/jaeger/bug601982.js diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index ea2898080cd2..ac71e3bd87ce 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2303,7 +2303,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN do { \ JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \ if (leaveOnSafePoint && !regs.fp->hasImacropc() && \ - script->hasNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \ + script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \ JS_ASSERT(!TRACE_RECORDER(cx)); \ interpReturnOK = true; \ goto stop_recording; \ diff --git a/js/src/jsscript.h b/js/src/jsscript.h index b95dc9e5f337..cef37ec6af31 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -294,12 +294,44 @@ struct JSScript { js::mjit::JITScript *jitNormal; /* Extra JIT info for normal scripts */ js::mjit::JITScript *jitCtor; /* Extra JIT info for constructors */ + void **nmapNormal; + void **nmapCtor; + bool hasJITCode() { return jitNormal || jitCtor; } - inline void **maybeNativeMap(bool constructing); - inline bool hasNativeCodeForPC(bool constructing, jsbytecode *pc); + void setNativeMap(bool constructing, void **map) { + if (constructing) + nmapCtor = map; + else + nmapNormal = map; + } + + void **maybeNativeMap(bool constructing) { + return constructing ? nmapCtor : nmapNormal; + } + + void **nativeMap(bool constructing) { + void **nmap = maybeNativeMap(constructing); + JS_ASSERT(nmap); + return nmap; + } + + void *maybeNativeCodeForPC(bool constructing, jsbytecode *pc) { + void **nmap = maybeNativeMap(constructing); + if (!nmap) + return NULL; + JS_ASSERT(pc >= code && pc < code + length); + return nmap[pc - code]; + } + + void *nativeCodeForPC(bool constructing, jsbytecode *pc) { + void **nmap = nativeMap(constructing); + JS_ASSERT(pc >= code && pc < code + length); + JS_ASSERT(nmap[pc - code]); + return nmap[pc - code]; + } js::mjit::JITScript *getJIT(bool constructing) { return constructing ? jitCtor : jitNormal; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 06a7a8d49108..1fbb3332c563 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -370,7 +370,6 @@ mjit::Compiler::finishThisUp(JITScript **jitp) /* Build the pc -> ncode mapping. */ void **nmap = (void **)cursor; - jit->nmap = nmap; cursor += sizeof(void *) * script->length; for (size_t i = 0; i < script->length; i++) { @@ -590,6 +589,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp) JS_ASSERT(size_t(cursor - (uint8*)jit) == totalBytes); + script->setNativeMap(isConstructing, nmap); *jitp = jit; return Compile_Okay; diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index 801128ca202f..bcbe3a3308dd 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -588,8 +588,7 @@ js_InternalThrow(VMFrame &f) JSStackFrame *fp = cx->fp(); JSScript *script = fp->script(); - JITScript *jit = script->getJIT(fp->isConstructing()); - return jit->nmap[pc - script->code]; + return script->nativeCodeForPC(fp->isConstructing(), pc); } void JS_FASTCALL @@ -677,7 +676,7 @@ HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp) return returnOK; } -static inline bool +static inline void * AtSafePoint(JSContext *cx) { JSStackFrame *fp = cx->fp(); @@ -685,12 +684,7 @@ AtSafePoint(JSContext *cx) return false; JSScript *script = fp->script(); - JITScript *jit = script->getJIT(fp->isConstructing()); - if (!jit->nmap) - return false; - - JS_ASSERT(cx->regs->pc >= script->code && cx->regs->pc < script->code + script->length); - return !!jit->nmap[cx->regs->pc - script->code]; + return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc); } static inline JSBool @@ -700,9 +694,9 @@ PartialInterpret(VMFrame &f) JSStackFrame *fp = cx->fp(); #ifdef DEBUG - JITScript *jit = fp->script()->getJIT(fp->isConstructing()); - JS_ASSERT(fp->hasImacropc() || !jit->nmap || - !jit->nmap[cx->regs->pc - fp->script()->code]); + JSScript *script = fp->script(); + JS_ASSERT(fp->hasImacropc() || + !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc)); #endif JSBool ok = JS_TRUE; @@ -729,12 +723,8 @@ FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame) { JSContext *cx = f.cx; while (cx->fp() != entryFrame || entryFrame->hasImacropc()) { - JSStackFrame *fp = cx->fp(); - - if (AtSafePoint(cx)) { - JSScript *script = fp->script(); - JITScript *jit = script->getJIT(fp->isConstructing()); - if (!JaegerShotAtSafePoint(cx, jit->nmap[cx->regs->pc - script->code])) { + if (void *ncode = AtSafePoint(cx)) { + if (!JaegerShotAtSafePoint(cx, ncode)) { if (!HandleErrorInExcessFrames(f, entryFrame)) return false; @@ -887,12 +877,8 @@ RunTracer(VMFrame &f) JS_ASSERT(!entryFrame->hasImacropc()); /* Step 2. If entryFrame is at a safe point, just leave. */ - if (AtSafePoint(cx)) { - JITScript *jit = entryFrame->script()->getJIT(entryFrame->isConstructing()); - uint32 offs = uint32(cx->regs->pc - entryFrame->script()->code); - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; - } + if (void *ncode = AtSafePoint(cx)) + return ncode; /* Step 3. If entryFrame is at a RETURN, then leave slightly differently. */ if (JSOp op = FrameIsFinished(cx)) { diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index c457f6f995db..e82193df99e6 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -829,6 +829,7 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) script->jitArityCheckNormal = NULL; cx->free(script->jitNormal); script->jitNormal = NULL; + script->nmapNormal = NULL; } if (script->jitCtor) { @@ -836,6 +837,7 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) script->jitArityCheckCtor = NULL; cx->free(script->jitCtor); script->jitCtor = NULL; + script->nmapCtor = NULL; } } diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 7e672c6f3c10..a3618f9281d9 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -190,7 +190,6 @@ struct JITScript { js::mjit::CallSite *callSites; uint32 nCallSites; - void **nmap; /* scripted pc to native code map. */ #ifdef JS_MONOIC ic::MICInfo *mics; /* MICs in this script. */ uint32 nMICs; /* number of MonoICs */ @@ -263,25 +262,6 @@ struct CallSite } /* namespace js */ -inline void ** -JSScript::maybeNativeMap(bool constructing) -{ - js::mjit::JITScript *jit = constructing ? jitCtor : jitNormal; - if (!jit) - return NULL; - return jit->nmap; -} - -inline bool -JSScript::hasNativeCodeForPC(bool constructing, jsbytecode *pc) -{ - js::mjit::JITScript *jit = getJIT(constructing); - if (!jit) - return false; - JS_ASSERT(pc >= code && pc < code + length); - return !!jit->nmap[pc - code]; -} - #ifdef _MSC_VER extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame); #else diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index a0231f1ed997..322cde30b956 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -2533,15 +2533,15 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) { jsbytecode *jpc = pc; JSScript *script = f.fp()->script(); - JITScript *jit = script->getJIT(f.fp()->isConstructing()); + void **nmap = script->nativeMap(f.fp()->isConstructing()); /* This is correct because the compiler adjusts the stack beforehand. */ Value lval = f.regs.sp[-1]; if (!lval.isPrimitive()) { ptrdiff_t offs = (pc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(nmap[offs]); + return nmap[offs]; } JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH); @@ -2561,8 +2561,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) JSString *rhs = rval.toString(); if (rhs == str || js_EqualStrings(str, rhs)) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(nmap[offs]); + return nmap[offs]; } } pc += JUMP_OFFSET_LEN; @@ -2574,8 +2574,8 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (rval.isNumber() && d == rval.toNumber()) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(nmap[offs]); + return nmap[offs]; } pc += JUMP_OFFSET_LEN; } @@ -2585,16 +2585,16 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) pc += INDEX_LEN; if (lval == rval) { ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(pc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(nmap[offs]); + return nmap[offs]; } pc += JUMP_OFFSET_LEN; } } ptrdiff_t offs = (jpc + GET_JUMP_OFFSET(jpc)) - script->code; - JS_ASSERT(jit->nmap[offs]); - return jit->nmap[offs]; + JS_ASSERT(nmap[offs]); + return nmap[offs]; } void * JS_FASTCALL @@ -2602,8 +2602,6 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) { jsbytecode * const originalPC = origPc; jsbytecode *pc = originalPC; - JSScript *script = f.fp()->script(); - JITScript *jit = script->getJIT(f.fp()->isConstructing()); uint32 jumpOffset = GET_JUMP_OFFSET(pc); pc += JUMP_OFFSET_LEN; @@ -2641,10 +2639,13 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) } finally: + JSScript *script = f.fp()->script(); + void **nmap = script->nativeMap(f.fp()->isConstructing()); + /* Provide the native address. */ ptrdiff_t offset = (originalPC + jumpOffset) - script->code; - JS_ASSERT(jit->nmap[offset]); - return jit->nmap[offset]; + JS_ASSERT(nmap[offset]); + return nmap[offset]; } void JS_FASTCALL diff --git a/js/src/trace-test/tests/jaeger/bug601982.js b/js/src/trace-test/tests/jaeger/bug601982.js new file mode 100644 index 000000000000..b87444492933 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug601982.js @@ -0,0 +1,33 @@ +/* vim: set ts=4 sw=4 tw=99 et: */ + +function J(i) { + /* Cause a branch to build. */ + if (i % 3) + +} + +function h(i) { + J(i); + + /* Generate a safe point in the method JIT. */ + if (1 == 14) { eval(); } + + return J(i); +} + +function g(i) { + /* Method JIT will try to remove this frame. */ + if (i == 14) { } + return h(i); +} + +function f() { + for (var i = 0; i < RUNLOOP * 2; i++) { + g(i); + } +} + +f(); + +/* Don't crash. */ + From 547d789b0ab33e615331f1fa98441ea5a7d38f82 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 5 Oct 2010 17:15:06 -0700 Subject: [PATCH 057/284] Don't build debug structures if not in debug mode (bug 596804, r=dmandelin). --- js/src/methodjit/Compiler.cpp | 9 +++++---- js/src/methodjit/Compiler.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 1fbb3332c563..37a97f13a407 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -64,7 +64,7 @@ using namespace js::mjit; using namespace js::mjit::ic; #endif -#define ADD_CALLSITE(stub) addCallSite(__LINE__, (stub)) +#define ADD_CALLSITE(stub) if (debugMode) addCallSite(__LINE__, (stub)) #if defined(JS_METHODJIT_SPEW) static const char *OpcodeNames[] = { @@ -96,7 +96,8 @@ mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp) callPatches(ContextAllocPolicy(cx)), callSites(ContextAllocPolicy(cx)), doubleList(ContextAllocPolicy(cx)), - stubcc(cx, *this, frame, script) + stubcc(cx, *this, frame, script), + debugMode(cx->compartment->debugMode) #if defined JS_TRACER ,addTraceHints(cx->traceJitEnabled) #endif @@ -173,7 +174,7 @@ mjit::Compiler::performCompilation(JITScript **jitp) PC = script->code; #ifdef JS_METHODJIT - script->debugMode = cx->compartment->debugMode; + script->debugMode = debugMode; #endif for (uint32 i = 0; i < script->nClosedVars; i++) @@ -2092,7 +2093,7 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) FrameEntry *fe = frame.peek(-int(argc + 2)); /* Currently, we don't support constant functions. */ - if (fe->isConstant() || fe->isNotType(JSVAL_TYPE_OBJECT) || script->debugMode) { + if (fe->isConstant() || fe->isNotType(JSVAL_TYPE_OBJECT) || debugMode) { emitUncachedCall(argc, callingNew); return; } diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 5b8d5ecd3bff..7c702145e3d9 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -225,6 +225,7 @@ class Compiler : public BaseCompiler StubCompiler stubcc; Label invokeLabel; Label arityLabel; + bool debugMode; bool addTraceHints; public: From 693026324a4be3de7e873488aac23048d4897034 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Tue, 5 Oct 2010 11:02:05 -0700 Subject: [PATCH 058/284] Bug 601256: optimize access to globals in global eval using GNAME ops, r=dvander --HG-- extra : rebase_source : 7c4bd486cc9dff1c65eda717fe34dd0fc36fe171 --- js/src/jsemit.cpp | 75 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 3a505a9bcdf8..cdfe329636d0 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -2031,6 +2031,39 @@ MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg) return true; } +/* + * Try to convert a *NAME op to a *GNAME op, which optimizes access to + * undeclared globals. Return true if a conversion was made. + * + * This conversion is not made if we are in strict mode, because the + * access to an undeclared global would be an error. + */ +static bool +TryConvertToGname(JSCodeGenerator *cg, JSParseNode *pn, JSOp *op) +{ + if (cg->compileAndGo() && + cg->compiler()->globalScope->globalObj && + !pn->isDeoptimized() && + !(cg->flags & TCF_STRICT_MODE_CODE)) { + switch (*op) { + case JSOP_NAME: *op = JSOP_GETGNAME; break; + case JSOP_SETNAME: *op = JSOP_SETGNAME; break; + case JSOP_INCNAME: *op = JSOP_INCGNAME; break; + case JSOP_NAMEINC: *op = JSOP_GNAMEINC; break; + case JSOP_DECNAME: *op = JSOP_DECGNAME; break; + case JSOP_NAMEDEC: *op = JSOP_GNAMEDEC; break; + case JSOP_SETCONST: + case JSOP_DELNAME: + case JSOP_FORNAME: + /* Not supported. */ + return false; + default: JS_NOT_REACHED("gname"); + } + return true; + } + return false; +} + /* * BindNameToSlot attempts to optimize name gets and sets to stack slot loads * and stores, given the compile-time information in cg and a TOK_NAME node pn. @@ -2140,6 +2173,21 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_TRUE; JS_ASSERT(caller->isScriptFrame()); + + /* + * If this is an eval in the global scope, then unbound variables + * must be globals, so try to use GNAME ops. + */ + if (caller->isGlobalFrame() && TryConvertToGname(cg, pn, &op)) { + ale = cg->atomList.add(cg->parser, atom); + if (!ale) + return JS_FALSE; + + pn->pn_op = op; + pn->pn_dflags |= PND_BOUND; + return JS_TRUE; + } + if (!caller->isFunctionFrame()) return JS_TRUE; @@ -2178,30 +2226,9 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return MakeUpvarForEval(pn, cg); } - /* - * Optimize accesses to undeclared globals, but only if we are in - * compile-and-go mode, the global is the same as the scope chain, - * and we are not in strict mode. - */ - if (cg->compileAndGo() && - cg->compiler()->globalScope->globalObj && - !pn->isDeoptimized() && - !(cg->flags & TCF_STRICT_MODE_CODE)) { - switch (op) { - case JSOP_NAME: op = JSOP_GETGNAME; break; - case JSOP_SETNAME: op = JSOP_SETGNAME; break; - case JSOP_INCNAME: op = JSOP_INCGNAME; break; - case JSOP_NAMEINC: op = JSOP_GNAMEINC; break; - case JSOP_DECNAME: op = JSOP_DECGNAME; break; - case JSOP_NAMEDEC: op = JSOP_GNAMEDEC; break; - case JSOP_SETCONST: - case JSOP_DELNAME: - case JSOP_FORNAME: - /* Not supported. */ - return JS_TRUE; - default: JS_NOT_REACHED("gname"); - } - } + /* Optimize accesses to undeclared globals. */ + if (!TryConvertToGname(cg, pn, &op)) + return JS_TRUE; ale = cg->atomList.add(cg->parser, atom); if (!ale) From 511c981da1850ad6dcbcb4518f2520fce865601c Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 6 Oct 2010 00:00:28 -0400 Subject: [PATCH 059/284] Bug 601968. Trace call(null). r=gal, a=sayrer --- js/src/jstracer.cpp | 4 ++-- js/src/trace-test/tests/basic/testCallNull.js | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 js/src/trace-test/tests/basic/testCallNull.js diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 3e4e2a814f74..26748a7b0577 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -13435,9 +13435,9 @@ TraceRecorder::record_JSOP_APPLY() /* * We don't trace apply and call with a primitive 'this', which is the - * first positional parameter. + * first positional parameter, unless 'this' is null. That's ok. */ - if (argc > 0 && !vp[2].isObject()) + if (argc > 0 && !vp[2].isObjectOrNull()) return record_JSOP_CALL(); /* diff --git a/js/src/trace-test/tests/basic/testCallNull.js b/js/src/trace-test/tests/basic/testCallNull.js new file mode 100644 index 000000000000..53f11cd99843 --- /dev/null +++ b/js/src/trace-test/tests/basic/testCallNull.js @@ -0,0 +1,9 @@ +function f() {} +for (var i = 0; i < HOTLOOP; ++i) { + f.call(null); +} + +checkStats({ + recorderStarted: 1, + recorderAborted: 0 +}); From 5b59133393d42a8e01e00bc01663068b2925e794 Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Wed, 6 Oct 2010 00:46:21 -0400 Subject: [PATCH 060/284] Define isOOLPath if JaegerSpew is enabled. --- js/src/assembler/assembler/ARMAssembler.h | 2 +- js/src/assembler/assembler/X86Assembler.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/assembler/assembler/ARMAssembler.h b/js/src/assembler/assembler/ARMAssembler.h index 44c2e3f46f31..682a6365ecd0 100644 --- a/js/src/assembler/assembler/ARMAssembler.h +++ b/js/src/assembler/assembler/ARMAssembler.h @@ -132,7 +132,7 @@ namespace JSC { class ARMAssembler { public: -#ifdef DEBUG +#ifdef JS_METHODJIT_SPEW bool isOOLPath; // Assign a default value to keep Valgrind quiet. ARMAssembler() : isOOLPath(false) { } diff --git a/js/src/assembler/assembler/X86Assembler.h b/js/src/assembler/assembler/X86Assembler.h index 2a373769c27b..6147ab95dc36 100644 --- a/js/src/assembler/assembler/X86Assembler.h +++ b/js/src/assembler/assembler/X86Assembler.h @@ -307,7 +307,7 @@ private: class X86InstructionFormatter; public: -#ifdef DEBUG +#ifdef JS_METHODJIT_SPEW bool isOOLPath; #endif @@ -355,7 +355,7 @@ public: }; X86Assembler() -#ifdef DEBUG +#ifdef JS_METHODJIT_SPEW : isOOLPath(false) #endif { From 5fcaf2d864b787b77728b580a77e029eeb34327f Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Wed, 6 Oct 2010 10:09:40 -0700 Subject: [PATCH 061/284] Bug 601733 - respect deleted arguments-object properties in JSOP_GETELEM (r=dvander,dmandelin) --- js/src/jsfun.cpp | 12 ++--- js/src/jsinterp.cpp | 15 ++---- js/src/jstracer.cpp | 52 +++++++++++++++++-- js/src/jstracer.h | 2 + js/src/methodjit/StubCalls.cpp | 15 ++---- .../trace-test/tests/arguments/testDelArg1.js | 13 +++++ .../trace-test/tests/arguments/testDelArg2.js | 13 +++++ 7 files changed, 87 insertions(+), 35 deletions(-) create mode 100644 js/src/trace-test/tests/arguments/testDelArg1.js create mode 100644 js/src/trace-test/tests/arguments/testDelArg2.js diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index ad9572d3bd36..4324270f253b 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -518,15 +518,11 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp) */ uintN arg = uintN(JSID_TO_INT(id)); if (arg < obj->getArgsInitialLength()) { - JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); - if (fp) { - JS_ASSERT(fp->numActualArgs() == obj->getArgsInitialLength()); + JS_ASSERT(!obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)); + if (JSStackFrame *fp = (JSStackFrame *) obj->getPrivate()) *vp = fp->canonicalActualArg(arg); - } else { - const Value &v = obj->getArgsElement(arg); - if (!v.isMagic(JS_ARGS_HOLE)) - *vp = v; - } + else + *vp = obj->getArgsElement(arg); } } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { if (!obj->isArgsLengthOverridden()) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index ac71e3bd87ce..2d89821be48b 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -4378,24 +4378,17 @@ BEGIN_CASE(JSOP_GETELEM) copyFrom = obj->addressOfDenseArrayElement(idx); if (!copyFrom->isMagic()) goto end_getelem; - - /* Reload retval from the stack in the rare hole case. */ - copyFrom = ®s.sp[-1]; } } else if (obj->isArguments()) { uint32 arg = uint32(i); if (arg < obj->getArgsInitialLength()) { - JSStackFrame *afp = (JSStackFrame *) obj->getPrivate(); - if (afp) { - copyFrom = &afp->canonicalActualArg(arg); + copyFrom = obj->addressOfArgsElement(arg); + if (!copyFrom->isMagic(JS_ARGS_HOLE)) { + if (JSStackFrame *afp = (JSStackFrame *) obj->getPrivate()) + copyFrom = &afp->canonicalActualArg(arg); goto end_getelem; } - - copyFrom = obj->addressOfArgsElement(arg); - if (!copyFrom->isMagic()) - goto end_getelem; - copyFrom = ®s.sp[-1]; } } if (JS_LIKELY(INT_FITS_IN_JSID(i))) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 26748a7b0577..0eb877dace16 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -9732,6 +9732,13 @@ TraceRecorder::is_boxed_true(LIns *vaddr_ins, AccSet accSet) return lir->ins2(LIR_andi, bool_ins, payload_ins); } +LIns* +TraceRecorder::is_boxed_magic(LIns *vaddr_ins, JSWhyMagic why, AccSet accSet) +{ + LIns *tag_ins = lir->insLoad(LIR_ldi, vaddr_ins, sTagOffset, accSet); + return lir->ins2(LIR_eqi, tag_ins, INS_CONSTU(JSVAL_TAG_MAGIC)); +} + void TraceRecorder::box_value_into(const Value &v, LIns *v_ins, LIns *dstaddr_ins, ptrdiff_t offset, AccSet accSet) @@ -9903,6 +9910,13 @@ TraceRecorder::is_boxed_true(LIns *vaddr_ins, AccSet accSet) return lir->ins2(LIR_eqq, v_ins, lir->insImmQ(JSVAL_BITS(JSVAL_TRUE))); } +LIns* +TraceRecorder::is_boxed_magic(LIns *vaddr_ins, JSWhyMagic why, AccSet accSet) +{ + LIns *v_ins = lir->insLoad(LIR_ldq, vaddr_ins, 0, accSet); + return lir->ins2(LIR_eqq, v_ins, INS_CONSTQWORD(BUILD_JSVAL(JSVAL_TAG_MAGIC, why))); +} + LIns* TraceRecorder::box_value_for_native_call(const Value &v, LIns *v_ins) { @@ -10362,14 +10376,18 @@ TraceRecorder::record_EnterFrame() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_LeaveFrame() { + JSStackFrame *fp = cx->fp(); + debug_only_stmt( debug_only_printf(LC_TMTracer, "LeaveFrame (back to %s), callDepth=%d\n", - js_AtomToPrintableString(cx, cx->fp()->fun()->atom), + fp->isFunctionFrame() + ? js_AtomToPrintableString(cx, fp->fun()->atom) + : "global code", callDepth); ); - JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, cx->fp()->script(), + JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, fp->script(), cx->regs->pc)].length == JSOP_CALL_LENGTH); if (callDepth-- <= 0) @@ -12547,6 +12565,21 @@ static bool OkToTraceTypedArrays = true; static bool OkToTraceTypedArrays = false; #endif +JS_REQUIRES_STACK void +TraceRecorder::guardNotHole(LIns *argsobj_ins, LIns *idx_ins) +{ + // vp = &argsobj->fslots[JSSLOT_ARGS_DATA].slots[idx] + LIns* argsData_ins = stobj_get_fslot_private_ptr(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); + LIns* slotOffset_ins = lir->ins2(LIR_addp, + INS_CONSTWORD(offsetof(ArgumentsData, slots)), + lir->insUI2P(lir->ins2ImmI(LIR_muli, idx_ins, sizeof(Value)))); + LIns* vp_ins = lir->ins2(LIR_addp, argsData_ins, slotOffset_ins); + + guard(false, + addName(is_boxed_magic(vp_ins, JS_ARGS_HOLE, ACCSET_OTHER), "guard(not deleted arg)"), + MISMATCH_EXIT); +} + JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_GETELEM() { @@ -12587,14 +12620,21 @@ TraceRecorder::record_JSOP_GETELEM() } if (obj->isArguments()) { + // Don't even try to record if out of range or reading a deleted arg + int32 int_idx = idx.toInt32(); + if (int_idx < 0 || int_idx >= (int32)obj->getArgsInitialLength()) + RETURN_STOP_A("cannot trace arguments with out of range index"); + if (obj->getArgsElement(int_idx).isMagic(JS_ARGS_HOLE)) + RETURN_STOP_A("reading deleted args element"); + + // Only trace reading arguments out of active, tracked frame unsigned depth; JSStackFrame *afp = guardArguments(obj, obj_ins, &depth); if (afp) { - int32 int_idx = idx.toInt32(); - if (int_idx < 0 || int_idx >= (int32)afp->numActualArgs()) - RETURN_STOP_A("cannot trace arguments with out of range index"); Value* vp = &afp->canonicalActualArg(int_idx); if (idx_ins->isImmD()) { + JS_ASSERT(int_idx == (int32)idx_ins->immD()); + guardNotHole(obj_ins, INS_CONST(int_idx)); v_ins = get(vp); } else { // If the index is not a constant expression, we generate LIR to load the value from @@ -12611,6 +12651,8 @@ TraceRecorder::record_JSOP_GETELEM() "guard(upvar index in range)"), MISMATCH_EXIT); + guardNotHole(obj_ins, idx_ins); + JSValueType type = getCoercedType(*vp); // Guard that the argument has the same type on trace as during recording. diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 9d3746cb1a9e..a0416fab114f 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1285,6 +1285,7 @@ class TraceRecorder void unbox_any_object(nanojit::LIns* vaddr_ins, nanojit::LIns** obj_ins, nanojit::LIns** is_obj_ins, nanojit::AccSet accSet); nanojit::LIns* is_boxed_true(nanojit::LIns* vaddr_ins, nanojit::AccSet accSet); + nanojit::LIns* is_boxed_magic(nanojit::LIns* vaddr_ins, JSWhyMagic why, nanojit::AccSet accSet); nanojit::LIns* is_string_id(nanojit::LIns* id_ins); nanojit::LIns* unbox_string_id(nanojit::LIns* id_ins); @@ -1327,6 +1328,7 @@ class TraceRecorder JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins, unsigned *depthp); JS_REQUIRES_STACK nanojit::LIns* guardArgsLengthNotAssigned(nanojit::LIns* argsobj_ins); + JS_REQUIRES_STACK void guardNotHole(nanojit::LIns *argsobj_ins, nanojit::LIns *ids_ins); JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSObject* ctor, nanojit::LIns*& proto_ins); JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSProtoKey key, diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 322cde30b956..bd8012518a48 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -531,24 +531,17 @@ stubs::GetElem(VMFrame &f) copyFrom = obj->addressOfDenseArrayElement(idx); if (!copyFrom->isMagic()) goto end_getelem; - - /* Reload retval from the stack in the rare hole case. */ - copyFrom = ®s.sp[-1]; } } else if (obj->isArguments()) { uint32 arg = uint32(i); if (arg < obj->getArgsInitialLength()) { - JSStackFrame *afp = (JSStackFrame *) obj->getPrivate(); - if (afp) { - copyFrom = &afp->canonicalActualArg(arg); + copyFrom = obj->addressOfArgsElement(arg); + if (!copyFrom->isMagic()) { + if (JSStackFrame *afp = (JSStackFrame *) obj->getPrivate()) + copyFrom = &afp->canonicalActualArg(arg); goto end_getelem; } - - copyFrom = obj->addressOfArgsElement(arg); - if (!copyFrom->isMagic()) - goto end_getelem; - /* Otherwise, fall to getProperty(). */ } } if (JS_LIKELY(INT_FITS_IN_JSID(i))) diff --git a/js/src/trace-test/tests/arguments/testDelArg1.js b/js/src/trace-test/tests/arguments/testDelArg1.js new file mode 100644 index 000000000000..3603dd478251 --- /dev/null +++ b/js/src/trace-test/tests/arguments/testDelArg1.js @@ -0,0 +1,13 @@ +function f(x,y,z) { + z = 9; + delete arguments[2]; + assertEq(arguments[2], undefined); + o = arguments; + assertEq(o[2], undefined); + assertEq(o[2] == undefined, true); +} + +for (var i = 0; i < HOTLOOP+2; ++i) { + print(i); + f(1,2,3) +} diff --git a/js/src/trace-test/tests/arguments/testDelArg2.js b/js/src/trace-test/tests/arguments/testDelArg2.js new file mode 100644 index 000000000000..7b4bce8b488e --- /dev/null +++ b/js/src/trace-test/tests/arguments/testDelArg2.js @@ -0,0 +1,13 @@ +function f(del) { + o = arguments; + if (del) + delete o[2]; + for (var i = 0; i < HOTLOOP+2; ++i) + assertEq(o[2] == undefined, del); +} + +// record without arg deleted +f(false, 1,2,3,4); + +// run with arg deleted +f(true, 1,2,3,4); From bf34ff421bce64c6f661e05de5b22ecfc92935b8 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 6 Oct 2010 10:40:07 -0700 Subject: [PATCH 062/284] Bug 601398 - Use new instructions, rather than source notes, to obtain block chain (r=igor) --- js/src/jsemit.cpp | 32 ++++++++++++-- js/src/jsinterp.cpp | 43 +++++++++---------- .../jaeger/bug563000/trap-self-as-parent.js | 2 +- .../jaeger/bug563000/trap-self-from-trap.js | 2 +- 4 files changed, 51 insertions(+), 28 deletions(-) diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index cdfe329636d0..0c6a3440be70 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -1514,15 +1514,20 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt) } static JSBool -EmitBlockChain(JSContext *cx, JSCodeGenerator *cg) +EmitKnownBlockChain(JSContext *cx, JSCodeGenerator *cg, JSObjectBox *box) { - JSObjectBox *box = cg->blockChainBox; if (box) return EmitIndexOp(cx, JSOP_BLOCKCHAIN, box->index, cg); else return js_Emit1(cx, cg, JSOP_NULLBLOCKCHAIN) >= 0; } +static JSBool +EmitBlockChain(JSContext *cx, JSCodeGenerator *cg) +{ + return EmitKnownBlockChain(cx, cg, cg->blockChainBox); +} + static ptrdiff_t EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, ptrdiff_t *lastp, JSAtomListElement *label, JSSrcNoteType noteType) @@ -1541,7 +1546,14 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, if (index < 0) return -1; - return EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp); + ptrdiff_t result = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp); + if (result < 0) + return result; + + if (!EmitBlockChain(cx, cg)) + return -1; + + return result; } static JSBool @@ -5368,16 +5380,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) tryEnd = CG_OFFSET(cg); + JSObjectBox *prevBox = NULL; /* If this try has a catch block, emit it. */ pn2 = pn->pn_kid2; lastCatch = NULL; if (pn2) { - JSObjectBox *prevBox = NULL; uintN count = 0; /* previous catch block's population */ /* * The emitted code for a catch block looks like: * + * blockchain * [throwing] only if 2nd+ catch block * [leaveblock] only if 2nd+ catch block * enterblock with SRC_CATCH @@ -5403,6 +5416,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JS_ASSERT(cg->stackDepth == depth); guardJump = GUARDJUMP(stmtInfo); if (guardJump != -1) { + if (EmitKnownBlockChain(cx, cg, prevBox) < 0) + return JS_FALSE; + /* Fix up and clean up previous catch block. */ CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump); @@ -5487,6 +5503,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * stack unbalanced. */ if (lastCatch && lastCatch->pn_kid2) { + if (EmitKnownBlockChain(cx, cg, prevBox) < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo)); /* Sync the stack to take into account pushed exception. */ @@ -5501,6 +5520,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) js_Emit1(cx, cg, JSOP_THROW) < 0) { return JS_FALSE; } + + if (EmitBlockChain(cx, cg) < 0) + return JS_FALSE; } JS_ASSERT(cg->stackDepth == depth); @@ -5682,6 +5704,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) CG_BASE(cg)[top] = JSOP_SETRVAL; if (js_Emit1(cx, cg, JSOP_RETRVAL) < 0) return JS_FALSE; + if (EmitBlockChain(cx, cg) < 0) + return JS_FALSE; } break; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 2d89821be48b..ac4e5da81a43 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -181,28 +181,29 @@ js_GetBlockChain(JSContext *cx, JSStackFrame *fp) jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx); JS_ASSERT(pc >= start && pc < script->code + script->length); - - ptrdiff_t oplen; - JSObject *blockChain = NULL; - for (jsbytecode *p = start; p < pc; p += oplen) { - JSOp op = js_GetOpcode(cx, script, p); - const JSCodeSpec *cs = &js_CodeSpec[op]; - oplen = cs->length; - if (oplen < 0) - oplen = js_GetVariableBytecodeLength(p); - if (op == JSOP_ENTERBLOCK) { - blockChain = script->getObject(GET_INDEX(p)); - } else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) { - /* - * Some LEAVEBLOCK instructions are due to early exits via - * return/break/etc. from block-scoped loops and functions. - * We should ignore these instructions, since they don't really - * signal the end of the block. - */ - jssrcnote *sn = js_GetSrcNote(script, p); - if (!(sn && SN_TYPE(sn) == SRC_HIDDEN)) + JSObject *blockChain = NULL; + if (*pc == JSOP_BLOCKCHAIN) { + blockChain = script->getObject(GET_INDEX(pc)); + } else if (*pc == JSOP_NULLBLOCKCHAIN) { + blockChain = NULL; + } else { + ptrdiff_t oplen; + for (jsbytecode *p = start; p < pc; p += oplen) { + JSOp op = js_GetOpcode(cx, script, p); + const JSCodeSpec *cs = &js_CodeSpec[op]; + oplen = cs->length; + if (oplen < 0) + oplen = js_GetVariableBytecodeLength(p); + + if (op == JSOP_ENTERBLOCK) + blockChain = script->getObject(GET_INDEX(p)); + else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) blockChain = blockChain->getParent(); + else if (op == JSOP_BLOCKCHAIN) + blockChain = script->getObject(GET_INDEX(p)); + else if (op == JSOP_NULLBLOCKCHAIN) + blockChain = NULL; } } @@ -6746,8 +6747,6 @@ END_CASE(JSOP_ARRAYPUSH) switch (tn->kind) { case JSTRY_CATCH: - JS_ASSERT(js_GetOpcode(cx, regs.fp->script(), regs.pc) == JSOP_ENTERBLOCK); - #if JS_HAS_GENERATORS /* Catch cannot intercept the closing of a generator. */ if (JS_UNLIKELY(cx->exception.isMagic(JS_GENERATOR_CLOSING))) diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js index 6c302163e197..65d3e73135e6 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js @@ -4,7 +4,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* noop call in myparent */ - trap(myparent, 49, "success()"); + trap(myparent, 50, "success()"); } else { myparent(true); x = "failure"; diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js index b01d5ac3b12c..46de9b96e4c6 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js @@ -13,7 +13,7 @@ function myparent(nested) { } } /* JSOP_CALL to doNothing in myparent with nested = false. */ -trap(myparent, 35, "myparent(true)"); +trap(myparent, 36, "myparent(true)"); function success() { x = "success"; From 46d6cc0182c4e18f92a58b9511297b4b66894038 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 6 Oct 2010 10:41:36 -0700 Subject: [PATCH 063/284] Bug 601986 - Make blockchain determination faster for with, flat closures (r=igor) --- js/src/jsemit.cpp | 9 +++++++++ js/src/jsfun.cpp | 4 ++-- js/src/jsfun.h | 3 ++- js/src/jsinterp.cpp | 25 ++++++++++++++++++------- js/src/jsinterp.h | 2 +- js/src/methodjit/StubCalls.cpp | 4 ++-- 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 0c6a3440be70..81c24f2787e3 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -4648,6 +4648,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) CG_SWITCH_TO_PROLOG(cg); op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFFUN_FC : JSOP_DEFFUN; EMIT_INDEX_OP(op, index); + /* Make blockChain determination quicker. */ if (EmitBlockChain(cx, cg) < 0) return JS_FALSE; @@ -5315,6 +5316,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) js_PushStatement(cg, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) return JS_FALSE; + + /* Make blockChain determination quicker. */ + if (EmitBlockChain(cx, cg) < 0) + return JS_FALSE; if (!js_EmitTree(cx, cg, pn->pn_right)) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) @@ -6476,6 +6481,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); if (EmitJump(cx, cg, JSOP_ENDFILTER, top - CG_OFFSET(cg)) < 0) return JS_FALSE; + + /* Make blockChain determination quicker. */ + if (EmitBlockChain(cx, cg) < 0) + return JS_FALSE; break; #endif diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 4324270f253b..05322d158c8e 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2981,13 +2981,13 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure, CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY) JS_REQUIRES_STACK JSObject * -js_NewFlatClosure(JSContext *cx, JSFunction *fun) +js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen) { /* * Flat closures can be partial, they may need to search enclosing scope * objects via JSOP_NAME, etc. */ - JSObject *scopeChain = js_GetScopeChain(cx, cx->fp()); + JSObject *scopeChain = js_GetScopeChainFast(cx, cx->fp(), op, oplen); if (!scopeChain) return NULL; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 9bf82edb5341..8fc6d487fecf 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -47,6 +47,7 @@ #include "jsobj.h" #include "jsatom.h" #include "jsstr.h" +#include "jsopcode.h" /* * The high two bits of JSFunction.flags encode whether the function is native @@ -517,7 +518,7 @@ extern JSObject * JS_FASTCALL js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain); extern JS_REQUIRES_STACK JSObject * -js_NewFlatClosure(JSContext *cx, JSFunction *fun); +js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen); extern JS_REQUIRES_STACK JSObject * js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index ac4e5da81a43..2b53764f854b 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1266,7 +1266,7 @@ ValueToId(JSContext *cx, const Value &v, jsid *idp) * of the with block with sp + stackIndex. */ JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool -js_EnterWith(JSContext *cx, jsint stackIndex) +js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen) { JSStackFrame *fp = cx->fp(); Value *sp = cx->regs->sp; @@ -1283,7 +1283,7 @@ js_EnterWith(JSContext *cx, jsint stackIndex) sp[-1].setObject(*obj); } - JSObject *parent = js_GetScopeChain(cx, fp); + JSObject *parent = js_GetScopeChainFast(cx, fp, op, oplen); if (!parent) return JS_FALSE; @@ -2614,7 +2614,7 @@ BEGIN_CASE(JSOP_POPV) END_CASE(JSOP_POPV) BEGIN_CASE(JSOP_ENTERWITH) - if (!js_EnterWith(cx, -1)) + if (!js_EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH)) goto error; /* @@ -5411,7 +5411,7 @@ BEGIN_CASE(JSOP_DEFFUN_DBGFC) LOAD_FUNCTION(0); JSObject *obj = (op == JSOP_DEFFUN_FC) - ? js_NewFlatClosure(cx, fun) + ? js_NewFlatClosure(cx, fun, JSOP_DEFFUN_FC, JSOP_DEFFUN_FC_LENGTH) : js_NewDebuggableFlatClosure(cx, fun); if (!obj) goto error; @@ -5484,7 +5484,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN_FC) JSFunction *fun; LOAD_FUNCTION(SLOTNO_LEN); - JSObject *obj = js_NewFlatClosure(cx, fun); + JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH); if (!obj) goto error; @@ -5536,12 +5536,23 @@ BEGIN_CASE(JSOP_LAMBDA) * break from the outer do-while(0). */ if (op2 == JSOP_INITMETHOD) { +#ifdef DEBUG + const Value &lref = regs.sp[-1]; + JS_ASSERT(lref.isObject()); + JSObject *obj2 = &lref.toObject(); + JS_ASSERT(obj2->getClass() == &js_ObjectClass); +#endif + fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc))); JS_FUNCTION_METER(cx, joinedinitmethod); break; } if (op2 == JSOP_SETMETHOD) { +#ifdef DEBUG + op2 = JSOp(pc2[JSOP_SETMETHOD_LENGTH]); + JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV); +#endif const Value &lref = regs.sp[-1]; if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) { fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc))); @@ -5628,7 +5639,7 @@ BEGIN_CASE(JSOP_LAMBDA_FC) JSFunction *fun; LOAD_FUNCTION(0); - JSObject *obj = js_NewFlatClosure(cx, fun); + JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH); if (!obj) goto error; @@ -6351,7 +6362,7 @@ BEGIN_CASE(JSOP_ENDFILTER) * temporaries. */ JS_ASSERT(IsXML(regs.sp[-1])); - if (!js_EnterWith(cx, -2)) + if (!js_EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH)) goto error; regs.sp--; len = GET_JUMP_OFFSET(regs.pc); diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index f5f9de5f827e..416a01629021 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -1036,7 +1036,7 @@ GetUpvar(JSContext *cx, uintN level, js::UpvarCookie cookie); # define JS_STATIC_INTERPRET extern JS_REQUIRES_STACK JSBool -js_EnterWith(JSContext *cx, jsint stackIndex); +js_EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen); extern JS_REQUIRES_STACK void js_LeaveWith(JSContext *cx); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index bd8012518a48..89d68b81f261 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1545,7 +1545,7 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun) JSObject * JS_FASTCALL stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun) { - JSObject *obj = js_NewFlatClosure(f.cx, fun); + JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH); if (!obj) THROWV(NULL); return obj; @@ -2390,7 +2390,7 @@ stubs::Throw(VMFrame &f) JSObject * JS_FASTCALL stubs::FlatLambda(VMFrame &f, JSFunction *fun) { - JSObject *obj = js_NewFlatClosure(f.cx, fun); + JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH); if (!obj) THROWV(NULL); return obj; From 6b12494bf3f671a813a2dd6e34491608fada4b31 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 6 Oct 2010 11:06:18 -0700 Subject: [PATCH 064/284] Fix assert in InvokeConstructor (bug 602088, r=luke). --- js/src/jsinterp.cpp | 2 +- js/src/trace-test/tests/basic/bug602088.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 js/src/trace-test/tests/basic/bug602088.js diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 2b53764f854b..d6cd973398c5 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -762,7 +762,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) PutActivationObjects(cx, fp); args.rval() = fp->returnValue(); - JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !args.rval().isPrimitive()); + JS_ASSERT_IF(ok && (flags & JSINVOKE_CONSTRUCT), !args.rval().isPrimitive()); return ok; } diff --git a/js/src/trace-test/tests/basic/bug602088.js b/js/src/trace-test/tests/basic/bug602088.js new file mode 100644 index 000000000000..f868dd9b53ce --- /dev/null +++ b/js/src/trace-test/tests/basic/bug602088.js @@ -0,0 +1,6 @@ +// |trace-test| error: TypeError +/* vim: set ts=4 sw=4 tw=99 et: */ + +var p = Proxy.createFunction({}, function(x, y) { undefined.x(); }); +print(new p(1, 2)); + From 095ae8501a454b233492a4401b417cb23e9e1794 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 6 Oct 2010 11:58:33 -0700 Subject: [PATCH 065/284] Fix trap instruction in trace-test, bump XDR version (r=dvander) --- js/src/jsxdrapi.h | 2 +- js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index e53bc9392211..34294aedb630 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -205,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); * before deserialization of bytecode. If the saved version does not match * the current version, abort deserialization and invalidate the file. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 72) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 73) /* * Library-private functions. diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js index 16a5a11c445e..18378b7808f7 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js @@ -3,7 +3,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* myparent call in myparent. */ - trap(myparent, 38, "failure()"); + trap(myparent, 39, "failure()"); } else { x = "success"; myparent(true); From 60375ecd5e82ba84e967fd3eb96f81e5a618bcb3 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Wed, 6 Oct 2010 12:04:31 -0700 Subject: [PATCH 066/284] Bug 601689 - Optimize GetArrayElement for arguments objects (r=lw) --HG-- extra : rebase_source : 6360a47b95660da477a1f312ab8654a23e4019c1 --- js/src/jsarray.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 4542b07de976..e1f8c9927673 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -103,8 +103,9 @@ #include "jsvector.h" #include "jsatominlines.h" -#include "jsobjinlines.h" #include "jscntxtinlines.h" +#include "jsinterpinlines.h" +#include "jsobjinlines.h" using namespace js; using namespace js::gc; @@ -456,6 +457,17 @@ GetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, *hole = JS_FALSE; return JS_TRUE; } + if (obj->isArguments() && + index < obj->getArgsInitialLength() && + !(*vp = obj->getArgsElement(uint32(index))).isMagic(JS_ARRAY_HOLE)) { + *hole = JS_FALSE; + JSStackFrame *fp = (JSStackFrame *)obj->getPrivate(); + if (fp != JS_ARGUMENTS_OBJECT_ON_TRACE) { + if (fp) + *vp = fp->canonicalActualArg(index); + return JS_TRUE; + } + } AutoIdRooter idr(cx); From 447d62bbdc0e2bec4834d0100837f9772438caec Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 6 Oct 2010 15:11:30 -0500 Subject: [PATCH 067/284] Bug 592644 - Indirect eval should be allowed under ES5. r=brendan. --- js/src/jsobj.cpp | 291 ++++++++---------- js/src/jsversion.h | 5 - js/src/tests/ecma_5/Global/eval-01.js | 38 +++ js/src/tests/ecma_5/Global/eval-02.js | 38 +++ js/src/tests/ecma_5/Global/jstests.list | 2 + js/src/tests/js1_5/extensions/jstests.list | 1 - .../tests/js1_5/extensions/regress-382509.js | 117 ------- js/src/tests/js1_6/Regress/jstests.list | 1 - js/src/tests/js1_6/Regress/regress-382509.js | 77 ----- 9 files changed, 203 insertions(+), 367 deletions(-) create mode 100644 js/src/tests/ecma_5/Global/eval-01.js create mode 100644 js/src/tests/ecma_5/Global/eval-02.js delete mode 100644 js/src/tests/js1_5/extensions/regress-382509.js delete mode 100644 js/src/tests/js1_6/Regress/regress-382509.js diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index cf4ae4d336ba..d8e5dac9dcb6 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -998,71 +998,112 @@ EvalCacheHash(JSContext *cx, JSString *str) return &JS_SCRIPTS_TO_GC(cx)[h]; } +static JS_ALWAYS_INLINE JSScript * +EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel, + JSPrincipals *principals, JSObject *scopeobj, JSScript ***bucketp) +{ + /* + * Cache local eval scripts indexed by source qualified by scope. + * + * An eval cache entry should never be considered a hit unless its + * strictness matches that of the new eval code. The existing code takes + * care of this, because hits are qualified by the function from which + * eval was called, whose strictness doesn't change. Scripts produced by + * calls to eval from global code are not cached. + */ + JSScript **bucket = EvalCacheHash(cx, str); + *bucketp = bucket; + uintN count = 0; + JSScript **scriptp = bucket; + + EVAL_CACHE_METER(probe); + JSVersion version = cx->findVersion(); + JSScript *script; + while ((script = *scriptp) != NULL) { + if (script->savedCallerFun && + script->staticLevel == staticLevel && + script->version == version && + (script->principals == principals || + (principals->subsume(principals, script->principals) && + script->principals->subsume(script->principals, principals)))) { + /* + * Get the prior (cache-filling) eval's saved caller function. + * See Compiler::compileScript in jsparse.cpp. + */ + JSFunction *fun = script->getFunction(0); + + if (fun == caller->fun()) { + /* + * Get the source string passed for safekeeping in the + * atom map by the prior eval to Compiler::compileScript. + */ + JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); + + if (src == str || js_EqualStrings(src, str)) { + /* + * Source matches, qualify by comparing scopeobj to the + * COMPILE_N_GO-memoized parent of the first literal + * function or regexp object if any. If none, then this + * script has no compiled-in dependencies on the prior + * eval's scopeobj. + */ + JSObjectArray *objarray = script->objects(); + int i = 1; + + if (objarray->length == 1) { + if (script->regexpsOffset != 0) { + objarray = script->regexps(); + i = 0; + } else { + EVAL_CACHE_METER(noscope); + i = -1; + } + } + if (i < 0 || + objarray->vector[i]->getParent() == scopeobj) { + JS_ASSERT(staticLevel == script->staticLevel); + EVAL_CACHE_METER(hit); + *scriptp = script->u.nextToGC; + script->u.nextToGC = NULL; + return script; + } + } + } + } + + if (++count == EVAL_CACHE_CHAIN_LIMIT) + return NULL; + EVAL_CACHE_METER(step); + scriptp = &script->u.nextToGC; + } + return NULL; +} + static JSBool obj_eval(JSContext *cx, uintN argc, Value *vp) { if (argc < 1) { vp->setUndefined(); - return JS_TRUE; + return true; } JSStackFrame *caller = js_GetScriptedCaller(cx, NULL); - if (!caller) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, js_eval_str); - return JS_FALSE; - } - - jsbytecode *callerPC = caller->pc(cx); - bool indirectCall = (callerPC && *callerPC != JSOP_EVAL); + jsbytecode *callerPC; + bool directCall = caller && + (callerPC = caller->pc(cx)) != NULL && + js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL; /* - * If the callee was originally a cross-compartment wrapper, this should - * be an indirect call. + * If the callee was originally a cross-compartment wrapper, this is an + * indirect call. */ - if (caller->scopeChain().compartment() != vp[0].toObject().compartment()) - indirectCall = true; - - /* - * Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....)) - * that attempt to use a non-global object as the scope object. - * - * This ban is a bit silly, since we could just disregard the this-argument - * entirely and comply with ES5, which supports indirect eval. See bug - * 592664. - */ - { - JSObject *obj = ComputeThisFromVp(cx, vp); - if (!obj) - return JS_FALSE; - - /* - * This call to JSObject::wrappedObject is safe because the result is - * only used for this check. - */ - obj = obj->wrappedObject(cx); - - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - - JSObject *parent = obj->getParent(); - if (indirectCall || parent) { - uintN flags = parent - ? JSREPORT_ERROR - : JSREPORT_STRICT | JSREPORT_WARNING; - if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, - js_eval_str)) { - return JS_FALSE; - } - } - } + if (directCall && caller->scopeChain().compartment() != vp[0].toObject().compartment()) + directCall = false; Value *argv = JS_ARGV(cx, vp); if (!argv[0].isString()) { *vp = argv[0]; - return JS_TRUE; + return true; } /* @@ -1075,7 +1116,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) "Support for eval(code, scopeObject) has been removed. " "Use |with (scopeObject) eval(code);| instead."; if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING)) - return JS_FALSE; + return false; caller->script()->warnedAboutTwoArgumentEval = true; } @@ -1083,55 +1124,45 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) MUST_FLOW_THROUGH("out"); uintN staticLevel = caller->script()->staticLevel + 1; + JSObject *scopeobj; + /* - * Bring fp->scopeChain up to date. We're either going to use - * it (direct call) or save it and restore it (indirect call). + * Per ES5, if we see an indirect call, then run in the global scope. + * (eval is specified this way so that the compiler can make assumptions + * about what bindings may or may not exist in the current frame if it + * doesn't see 'eval'.) */ - JSObject *callerScopeChain; - - if (callerPC && *callerPC == JSOP_EVAL) - callerScopeChain = js_GetScopeChainFast(cx, caller, JSOP_EVAL, - JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); - else - callerScopeChain = js_GetScopeChain(cx, caller); - - if (!callerScopeChain) - return JS_FALSE; - - JSObject *scopeobj = NULL; - -#if JS_HAS_EVAL_THIS_SCOPE - /* - * If we see an indirect call, then run eval in the global scope. We do - * this so the compiler can make assumptions about what bindings may or - * may not exist in the current frame if it doesn't see 'eval'. - */ - if (indirectCall) { - /* Pretend that we're top level. */ - staticLevel = 0; - scopeobj = vp[0].toObject().getGlobal(); - } else { + if (directCall) { /* * Compile using the caller's current scope object. * * NB: This means that the C API must not be used to call eval. */ + scopeobj = js_GetScopeChainFast(cx, caller, JSOP_EVAL, + JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); + if (!scopeobj) + return false; + JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj()); - scopeobj = callerScopeChain; + } else { + /* Pretend that we're top level. */ + staticLevel = 0; + scopeobj = vp[0].toObject().getGlobal(); } -#endif /* Ensure we compile this eval with the right object in the scope chain. */ JSObject *result = CheckScopeChainValidity(cx, scopeobj, js_eval_str); - JS_ASSERT_IF(result, result == scopeobj); if (!result) - return JS_FALSE; + return false; + JS_ASSERT(result == scopeobj); - // CSP check: is eval() allowed at all? - // report errors via CSP is done in the script security mgr. + /* + * CSP check: Is eval() allowed at all? + * Report errors via CSP is done in the script security mgr. + */ if (!js_CheckContentSecurityPolicy(cx)) { JS_ReportError(cx, "call to eval() blocked by CSP"); - return JS_FALSE; + return false; } JSObject *callee = &vp[0].toObject(); @@ -1140,8 +1171,6 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) const char *file = js_ComputeFilename(cx, caller, principals, &line); JSString *str = argv[0].toString(); - JSScript *script = NULL; - const jschar *chars; size_t length; str->getCharsAndLength(chars, length); @@ -1160,86 +1189,14 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) ok = js_ConsumeJSONText(cx, jp, chars+1, length-2); ok &= js_FinishJSONParse(cx, jp, NullValue()); if (ok) - return JS_TRUE; + return true; } } - /* - * Cache local eval scripts indexed by source qualified by scope. - * - * An eval cache entry should never be considered a hit unless its - * strictness matches that of the new eval code. The existing code takes - * care of this, because hits are qualified by the function from which - * eval was called, whose strictness doesn't change. Scripts produced by - * calls to eval from global code are not cached. - */ - JSScript **bucket = EvalCacheHash(cx, str); - if (!indirectCall && caller->isFunctionFrame()) { - uintN count = 0; - JSScript **scriptp = bucket; - - EVAL_CACHE_METER(probe); - JSVersion version = cx->findVersion(); - while ((script = *scriptp) != NULL) { - if (script->savedCallerFun && - script->staticLevel == staticLevel && - script->version == version && - (script->principals == principals || - (principals->subsume(principals, script->principals) && - script->principals->subsume(script->principals, principals)))) { - /* - * Get the prior (cache-filling) eval's saved caller function. - * See Compiler::compileScript in jsparse.cpp. - */ - JSFunction *fun = script->getFunction(0); - - if (fun == caller->fun()) { - /* - * Get the source string passed for safekeeping in the - * atom map by the prior eval to Compiler::compileScript. - */ - JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); - - if (src == str || js_EqualStrings(src, str)) { - /* - * Source matches, qualify by comparing scopeobj to the - * COMPILE_N_GO-memoized parent of the first literal - * function or regexp object if any. If none, then this - * script has no compiled-in dependencies on the prior - * eval's scopeobj. - */ - JSObjectArray *objarray = script->objects(); - int i = 1; - - if (objarray->length == 1) { - if (script->regexpsOffset != 0) { - objarray = script->regexps(); - i = 0; - } else { - EVAL_CACHE_METER(noscope); - i = -1; - } - } - if (i < 0 || - objarray->vector[i]->getParent() == scopeobj) { - JS_ASSERT(staticLevel == script->staticLevel); - EVAL_CACHE_METER(hit); - *scriptp = script->u.nextToGC; - script->u.nextToGC = NULL; - break; - } - } - } - } - - if (++count == EVAL_CACHE_CHAIN_LIMIT) { - script = NULL; - break; - } - EVAL_CACHE_METER(step); - scriptp = &script->u.nextToGC; - } - } + JSScript *script = NULL; + JSScript **bucket = NULL; + if (directCall && caller->isFunctionFrame()) + script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, &bucket); /* * We can't have a callerFrame (down in js_Execute's terms) if we're in @@ -1254,7 +1211,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) chars, length, NULL, file, line, str, staticLevel); if (!script) - return JS_FALSE; + return false; } assertSameCompartment(cx, scopeobj, script); @@ -1267,8 +1224,10 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) cx->runtime->atomState.evalAtom) && Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp); - script->u.nextToGC = *bucket; - *bucket = script; + if (bucket) { + script->u.nextToGC = *bucket; + *bucket = script; + } #ifdef CHECK_SCRIPT_OWNER script->owner = NULL; #endif diff --git a/js/src/jsversion.h b/js/src/jsversion.h index 293365172bdc..73969bd3b12a 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -86,7 +86,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_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 0 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ @@ -115,7 +114,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ @@ -140,7 +138,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ @@ -165,7 +162,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ @@ -190,7 +186,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ diff --git a/js/src/tests/ecma_5/Global/eval-01.js b/js/src/tests/ecma_5/Global/eval-01.js new file mode 100644 index 000000000000..50bb95ffdbad --- /dev/null +++ b/js/src/tests/ecma_5/Global/eval-01.js @@ -0,0 +1,38 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var a = 9; +var global = this; + +function test() { + var a = 0; + + // direct eval sees local a + assertEq(eval('a+1'), 1); + assertEq(eval('eval("a+1")'), 1); + + // indirect: using a name other than 'eval' + var foo = eval; + assertEq(foo('a+1'), 10); + assertEq(eval('foo("a+1")'), 10); // outer eval is direct, inner foo("a+1") is indirect + + // indirect: qualified method call + assertEq(this.eval("a+1"), 10); + assertEq(global.eval("a+1"), 10); + var obj = {foo: eval, eval: eval}; + assertEq(obj.foo('a+1'), 10); + assertEq(obj.eval('a+1'), 10); + var name = "eval"; + assertEq(obj[name]('a+1'), 10); + assertEq([eval][0]('a+1'), 10); + + // indirect: not called from a CallExpression at all + assertEq(eval.call(undefined, 'a+1'), 10); + assertEq(eval.call(global, 'a+1'), 10); + assertEq(eval.apply(undefined, ['a+1']), 10); + assertEq(eval.apply(global, ['a+1']), 10); + assertEq(['a+1'].map(eval)[0], 10); +} + +test(); +reportCompare(0, 0); diff --git a/js/src/tests/ecma_5/Global/eval-02.js b/js/src/tests/ecma_5/Global/eval-02.js new file mode 100644 index 000000000000..e1a315949c67 --- /dev/null +++ b/js/src/tests/ecma_5/Global/eval-02.js @@ -0,0 +1,38 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var a = 9; + +function directArg(eval, s) { + var a = 1; + return eval(s); +} + +function directVar(f, s) { + var eval = f; + var a = 1; + return eval(s); +} + +function directWith(obj, s) { + var f; + with (obj) { + f = function () { + var a = 1; + return eval(s); + }; + } + return f(); +} + +// direct eval, even though 'eval' is an argument +assertEq(directArg(eval, 'a+1'), 2); + +// direct eval, even though 'eval' is a var +assertEq(directVar(eval, 'a+1'), 2); + +// direct eval, even though 'eval' is found via a with block +assertEq(directWith(this, 'a+1'), 2); +assertEq(directWith({eval: eval, a: -1000}, 'a+1'), 2); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_5/Global/jstests.list b/js/src/tests/ecma_5/Global/jstests.list index 8a1f31710e56..42a4f6e9cbbd 100644 --- a/js/src/tests/ecma_5/Global/jstests.list +++ b/js/src/tests/ecma_5/Global/jstests.list @@ -1,2 +1,4 @@ url-prefix ../../jsreftest.html?test=ecma_5/Global/ script parseInt-01.js +script eval-01.js +script eval-02.js diff --git a/js/src/tests/js1_5/extensions/jstests.list b/js/src/tests/js1_5/extensions/jstests.list index 547027d1bc2e..c6b68e0b4470 100644 --- a/js/src/tests/js1_5/extensions/jstests.list +++ b/js/src/tests/js1_5/extensions/jstests.list @@ -146,7 +146,6 @@ script regress-380581.js script regress-380889.js script regress-381211.js script regress-381304.js -script regress-382509.js script regress-384680.js script regress-385134.js script regress-385393-02.js diff --git a/js/src/tests/js1_5/extensions/regress-382509.js b/js/src/tests/js1_5/extensions/regress-382509.js deleted file mode 100644 index eb9ffb17f4a1..000000000000 --- a/js/src/tests/js1_5/extensions/regress-382509.js +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is JavaScript Engine testing utilities. - * - * The Initial Developer of the Original Code is - * Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 382509; -var summary = 'Disallow non-global indirect eval'; -var actual = ''; -var expect = ''; - -var global = typeof window == 'undefined' ? this : window; -var object = {}; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if (options().match(/strict/)) - { - options('strict'); - } - if (options().match(/werror/)) - { - options('werror'); - } - - global.foo = eval; - global.a = 'global'; - expect = 'global indirect'; - actual = global.foo('a+" indirect"'); - reportCompare(expect, actual, summary + ': global indirect'); - - object.foo = eval; - object.a = 'local'; - expect = 'EvalError: function eval must be called directly, and not by way of a function of another name'; - try - { - actual = object.foo('a+" indirect"'); - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': local indirect'); - - options('strict'); - options('werror'); - - try - { - var foo = eval; - print("foo(1+1)" + foo('1+1')); - actual = 'No Error'; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': strict, rename warning'); - - options('strict'); - options('werror'); - - expect = 'No Error'; - try - { - var foo = eval; - foo('1+1'); - actual = 'No Error'; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': not strict, no rename warning'); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_6/Regress/jstests.list b/js/src/tests/js1_6/Regress/jstests.list index 82264b4fa82f..b1ed21ea3c1b 100644 --- a/js/src/tests/js1_6/Regress/jstests.list +++ b/js/src/tests/js1_6/Regress/jstests.list @@ -11,6 +11,5 @@ script regress-353078.js script regress-355002.js script regress-372565.js script regress-378492.js -script regress-382509.js script regress-475469.js script regress-476655.js diff --git a/js/src/tests/js1_6/Regress/regress-382509.js b/js/src/tests/js1_6/Regress/regress-382509.js deleted file mode 100644 index 84a052ac0831..000000000000 --- a/js/src/tests/js1_6/Regress/regress-382509.js +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is JavaScript Engine testing utilities. - * - * The Initial Developer of the Original Code is - * Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 382509; -var summary = 'Disallow non-global indirect eval'; -var actual = ''; -var expect = ''; - -var global = typeof window == 'undefined' ? this : window; -var object = {}; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - global.foo = eval; - global.a = 'global'; - expect = 'global indirect'; - actual = String(['a+" indirect"'].map(global.foo)); - reportCompare(expect, actual, summary + ': global indirect'); - - object.foo = eval; - object.a = 'local'; - expect = 'EvalError: function eval must be called directly, and not by way of a function of another name'; - try - { - actual = String(['a+" indirect"'].map(object.foo, object)); - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': local indirect'); - - exitFunc ('test'); -} From 1344bee2a560738222e1596e53cebe38db980a22 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 6 Oct 2010 15:51:14 -0500 Subject: [PATCH 068/284] Back out changeset 89006937466d (crashing tests on tinderbox). --- js/src/jsobj.cpp | 291 ++++++++++-------- js/src/jsversion.h | 5 + js/src/tests/ecma_5/Global/eval-01.js | 38 --- js/src/tests/ecma_5/Global/eval-02.js | 38 --- js/src/tests/ecma_5/Global/jstests.list | 2 - js/src/tests/js1_5/extensions/jstests.list | 1 + .../tests/js1_5/extensions/regress-382509.js | 117 +++++++ js/src/tests/js1_6/Regress/jstests.list | 1 + js/src/tests/js1_6/Regress/regress-382509.js | 77 +++++ 9 files changed, 367 insertions(+), 203 deletions(-) delete mode 100644 js/src/tests/ecma_5/Global/eval-01.js delete mode 100644 js/src/tests/ecma_5/Global/eval-02.js create mode 100644 js/src/tests/js1_5/extensions/regress-382509.js create mode 100644 js/src/tests/js1_6/Regress/regress-382509.js diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index d8e5dac9dcb6..cf4ae4d336ba 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -998,112 +998,71 @@ EvalCacheHash(JSContext *cx, JSString *str) return &JS_SCRIPTS_TO_GC(cx)[h]; } -static JS_ALWAYS_INLINE JSScript * -EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel, - JSPrincipals *principals, JSObject *scopeobj, JSScript ***bucketp) -{ - /* - * Cache local eval scripts indexed by source qualified by scope. - * - * An eval cache entry should never be considered a hit unless its - * strictness matches that of the new eval code. The existing code takes - * care of this, because hits are qualified by the function from which - * eval was called, whose strictness doesn't change. Scripts produced by - * calls to eval from global code are not cached. - */ - JSScript **bucket = EvalCacheHash(cx, str); - *bucketp = bucket; - uintN count = 0; - JSScript **scriptp = bucket; - - EVAL_CACHE_METER(probe); - JSVersion version = cx->findVersion(); - JSScript *script; - while ((script = *scriptp) != NULL) { - if (script->savedCallerFun && - script->staticLevel == staticLevel && - script->version == version && - (script->principals == principals || - (principals->subsume(principals, script->principals) && - script->principals->subsume(script->principals, principals)))) { - /* - * Get the prior (cache-filling) eval's saved caller function. - * See Compiler::compileScript in jsparse.cpp. - */ - JSFunction *fun = script->getFunction(0); - - if (fun == caller->fun()) { - /* - * Get the source string passed for safekeeping in the - * atom map by the prior eval to Compiler::compileScript. - */ - JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); - - if (src == str || js_EqualStrings(src, str)) { - /* - * Source matches, qualify by comparing scopeobj to the - * COMPILE_N_GO-memoized parent of the first literal - * function or regexp object if any. If none, then this - * script has no compiled-in dependencies on the prior - * eval's scopeobj. - */ - JSObjectArray *objarray = script->objects(); - int i = 1; - - if (objarray->length == 1) { - if (script->regexpsOffset != 0) { - objarray = script->regexps(); - i = 0; - } else { - EVAL_CACHE_METER(noscope); - i = -1; - } - } - if (i < 0 || - objarray->vector[i]->getParent() == scopeobj) { - JS_ASSERT(staticLevel == script->staticLevel); - EVAL_CACHE_METER(hit); - *scriptp = script->u.nextToGC; - script->u.nextToGC = NULL; - return script; - } - } - } - } - - if (++count == EVAL_CACHE_CHAIN_LIMIT) - return NULL; - EVAL_CACHE_METER(step); - scriptp = &script->u.nextToGC; - } - return NULL; -} - static JSBool obj_eval(JSContext *cx, uintN argc, Value *vp) { if (argc < 1) { vp->setUndefined(); - return true; + return JS_TRUE; } JSStackFrame *caller = js_GetScriptedCaller(cx, NULL); - jsbytecode *callerPC; - bool directCall = caller && - (callerPC = caller->pc(cx)) != NULL && - js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL; + if (!caller) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INDIRECT_CALL, js_eval_str); + return JS_FALSE; + } + + jsbytecode *callerPC = caller->pc(cx); + bool indirectCall = (callerPC && *callerPC != JSOP_EVAL); /* - * If the callee was originally a cross-compartment wrapper, this is an - * indirect call. + * If the callee was originally a cross-compartment wrapper, this should + * be an indirect call. */ - if (directCall && caller->scopeChain().compartment() != vp[0].toObject().compartment()) - directCall = false; + if (caller->scopeChain().compartment() != vp[0].toObject().compartment()) + indirectCall = true; + + /* + * Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....)) + * that attempt to use a non-global object as the scope object. + * + * This ban is a bit silly, since we could just disregard the this-argument + * entirely and comply with ES5, which supports indirect eval. See bug + * 592664. + */ + { + JSObject *obj = ComputeThisFromVp(cx, vp); + if (!obj) + return JS_FALSE; + + /* + * This call to JSObject::wrappedObject is safe because the result is + * only used for this check. + */ + obj = obj->wrappedObject(cx); + + OBJ_TO_INNER_OBJECT(cx, obj); + if (!obj) + return JS_FALSE; + + JSObject *parent = obj->getParent(); + if (indirectCall || parent) { + uintN flags = parent + ? JSREPORT_ERROR + : JSREPORT_STRICT | JSREPORT_WARNING; + if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL, + JSMSG_BAD_INDIRECT_CALL, + js_eval_str)) { + return JS_FALSE; + } + } + } Value *argv = JS_ARGV(cx, vp); if (!argv[0].isString()) { *vp = argv[0]; - return true; + return JS_TRUE; } /* @@ -1116,7 +1075,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) "Support for eval(code, scopeObject) has been removed. " "Use |with (scopeObject) eval(code);| instead."; if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING)) - return false; + return JS_FALSE; caller->script()->warnedAboutTwoArgumentEval = true; } @@ -1124,45 +1083,55 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) MUST_FLOW_THROUGH("out"); uintN staticLevel = caller->script()->staticLevel + 1; - JSObject *scopeobj; - /* - * Per ES5, if we see an indirect call, then run in the global scope. - * (eval is specified this way so that the compiler can make assumptions - * about what bindings may or may not exist in the current frame if it - * doesn't see 'eval'.) + * Bring fp->scopeChain up to date. We're either going to use + * it (direct call) or save it and restore it (indirect call). */ - if (directCall) { + JSObject *callerScopeChain; + + if (callerPC && *callerPC == JSOP_EVAL) + callerScopeChain = js_GetScopeChainFast(cx, caller, JSOP_EVAL, + JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); + else + callerScopeChain = js_GetScopeChain(cx, caller); + + if (!callerScopeChain) + return JS_FALSE; + + JSObject *scopeobj = NULL; + +#if JS_HAS_EVAL_THIS_SCOPE + /* + * If we see an indirect call, then run eval in the global scope. We do + * this so the compiler can make assumptions about what bindings may or + * may not exist in the current frame if it doesn't see 'eval'. + */ + if (indirectCall) { + /* Pretend that we're top level. */ + staticLevel = 0; + scopeobj = vp[0].toObject().getGlobal(); + } else { /* * Compile using the caller's current scope object. * * NB: This means that the C API must not be used to call eval. */ - scopeobj = js_GetScopeChainFast(cx, caller, JSOP_EVAL, - JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); - if (!scopeobj) - return false; - JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj()); - } else { - /* Pretend that we're top level. */ - staticLevel = 0; - scopeobj = vp[0].toObject().getGlobal(); + scopeobj = callerScopeChain; } +#endif /* Ensure we compile this eval with the right object in the scope chain. */ JSObject *result = CheckScopeChainValidity(cx, scopeobj, js_eval_str); + JS_ASSERT_IF(result, result == scopeobj); if (!result) - return false; - JS_ASSERT(result == scopeobj); + return JS_FALSE; - /* - * CSP check: Is eval() allowed at all? - * Report errors via CSP is done in the script security mgr. - */ + // CSP check: is eval() allowed at all? + // report errors via CSP is done in the script security mgr. if (!js_CheckContentSecurityPolicy(cx)) { JS_ReportError(cx, "call to eval() blocked by CSP"); - return false; + return JS_FALSE; } JSObject *callee = &vp[0].toObject(); @@ -1171,6 +1140,8 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) const char *file = js_ComputeFilename(cx, caller, principals, &line); JSString *str = argv[0].toString(); + JSScript *script = NULL; + const jschar *chars; size_t length; str->getCharsAndLength(chars, length); @@ -1189,14 +1160,86 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) ok = js_ConsumeJSONText(cx, jp, chars+1, length-2); ok &= js_FinishJSONParse(cx, jp, NullValue()); if (ok) - return true; + return JS_TRUE; } } - JSScript *script = NULL; - JSScript **bucket = NULL; - if (directCall && caller->isFunctionFrame()) - script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, &bucket); + /* + * Cache local eval scripts indexed by source qualified by scope. + * + * An eval cache entry should never be considered a hit unless its + * strictness matches that of the new eval code. The existing code takes + * care of this, because hits are qualified by the function from which + * eval was called, whose strictness doesn't change. Scripts produced by + * calls to eval from global code are not cached. + */ + JSScript **bucket = EvalCacheHash(cx, str); + if (!indirectCall && caller->isFunctionFrame()) { + uintN count = 0; + JSScript **scriptp = bucket; + + EVAL_CACHE_METER(probe); + JSVersion version = cx->findVersion(); + while ((script = *scriptp) != NULL) { + if (script->savedCallerFun && + script->staticLevel == staticLevel && + script->version == version && + (script->principals == principals || + (principals->subsume(principals, script->principals) && + script->principals->subsume(script->principals, principals)))) { + /* + * Get the prior (cache-filling) eval's saved caller function. + * See Compiler::compileScript in jsparse.cpp. + */ + JSFunction *fun = script->getFunction(0); + + if (fun == caller->fun()) { + /* + * Get the source string passed for safekeeping in the + * atom map by the prior eval to Compiler::compileScript. + */ + JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); + + if (src == str || js_EqualStrings(src, str)) { + /* + * Source matches, qualify by comparing scopeobj to the + * COMPILE_N_GO-memoized parent of the first literal + * function or regexp object if any. If none, then this + * script has no compiled-in dependencies on the prior + * eval's scopeobj. + */ + JSObjectArray *objarray = script->objects(); + int i = 1; + + if (objarray->length == 1) { + if (script->regexpsOffset != 0) { + objarray = script->regexps(); + i = 0; + } else { + EVAL_CACHE_METER(noscope); + i = -1; + } + } + if (i < 0 || + objarray->vector[i]->getParent() == scopeobj) { + JS_ASSERT(staticLevel == script->staticLevel); + EVAL_CACHE_METER(hit); + *scriptp = script->u.nextToGC; + script->u.nextToGC = NULL; + break; + } + } + } + } + + if (++count == EVAL_CACHE_CHAIN_LIMIT) { + script = NULL; + break; + } + EVAL_CACHE_METER(step); + scriptp = &script->u.nextToGC; + } + } /* * We can't have a callerFrame (down in js_Execute's terms) if we're in @@ -1211,7 +1254,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) chars, length, NULL, file, line, str, staticLevel); if (!script) - return false; + return JS_FALSE; } assertSameCompartment(cx, scopeobj, script); @@ -1224,10 +1267,8 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) cx->runtime->atomState.evalAtom) && Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp); - if (bucket) { - script->u.nextToGC = *bucket; - *bucket = script; - } + script->u.nextToGC = *bucket; + *bucket = script; #ifdef CHECK_SCRIPT_OWNER script->owner = NULL; #endif diff --git a/js/src/jsversion.h b/js/src/jsversion.h index 73969bd3b12a..293365172bdc 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -86,6 +86,7 @@ #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_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 0 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ @@ -114,6 +115,7 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ @@ -138,6 +140,7 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ @@ -162,6 +165,7 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ @@ -186,6 +190,7 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ diff --git a/js/src/tests/ecma_5/Global/eval-01.js b/js/src/tests/ecma_5/Global/eval-01.js deleted file mode 100644 index 50bb95ffdbad..000000000000 --- a/js/src/tests/ecma_5/Global/eval-01.js +++ /dev/null @@ -1,38 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var a = 9; -var global = this; - -function test() { - var a = 0; - - // direct eval sees local a - assertEq(eval('a+1'), 1); - assertEq(eval('eval("a+1")'), 1); - - // indirect: using a name other than 'eval' - var foo = eval; - assertEq(foo('a+1'), 10); - assertEq(eval('foo("a+1")'), 10); // outer eval is direct, inner foo("a+1") is indirect - - // indirect: qualified method call - assertEq(this.eval("a+1"), 10); - assertEq(global.eval("a+1"), 10); - var obj = {foo: eval, eval: eval}; - assertEq(obj.foo('a+1'), 10); - assertEq(obj.eval('a+1'), 10); - var name = "eval"; - assertEq(obj[name]('a+1'), 10); - assertEq([eval][0]('a+1'), 10); - - // indirect: not called from a CallExpression at all - assertEq(eval.call(undefined, 'a+1'), 10); - assertEq(eval.call(global, 'a+1'), 10); - assertEq(eval.apply(undefined, ['a+1']), 10); - assertEq(eval.apply(global, ['a+1']), 10); - assertEq(['a+1'].map(eval)[0], 10); -} - -test(); -reportCompare(0, 0); diff --git a/js/src/tests/ecma_5/Global/eval-02.js b/js/src/tests/ecma_5/Global/eval-02.js deleted file mode 100644 index e1a315949c67..000000000000 --- a/js/src/tests/ecma_5/Global/eval-02.js +++ /dev/null @@ -1,38 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var a = 9; - -function directArg(eval, s) { - var a = 1; - return eval(s); -} - -function directVar(f, s) { - var eval = f; - var a = 1; - return eval(s); -} - -function directWith(obj, s) { - var f; - with (obj) { - f = function () { - var a = 1; - return eval(s); - }; - } - return f(); -} - -// direct eval, even though 'eval' is an argument -assertEq(directArg(eval, 'a+1'), 2); - -// direct eval, even though 'eval' is a var -assertEq(directVar(eval, 'a+1'), 2); - -// direct eval, even though 'eval' is found via a with block -assertEq(directWith(this, 'a+1'), 2); -assertEq(directWith({eval: eval, a: -1000}, 'a+1'), 2); - -reportCompare(0, 0); diff --git a/js/src/tests/ecma_5/Global/jstests.list b/js/src/tests/ecma_5/Global/jstests.list index 42a4f6e9cbbd..8a1f31710e56 100644 --- a/js/src/tests/ecma_5/Global/jstests.list +++ b/js/src/tests/ecma_5/Global/jstests.list @@ -1,4 +1,2 @@ url-prefix ../../jsreftest.html?test=ecma_5/Global/ script parseInt-01.js -script eval-01.js -script eval-02.js diff --git a/js/src/tests/js1_5/extensions/jstests.list b/js/src/tests/js1_5/extensions/jstests.list index c6b68e0b4470..547027d1bc2e 100644 --- a/js/src/tests/js1_5/extensions/jstests.list +++ b/js/src/tests/js1_5/extensions/jstests.list @@ -146,6 +146,7 @@ script regress-380581.js script regress-380889.js script regress-381211.js script regress-381304.js +script regress-382509.js script regress-384680.js script regress-385134.js script regress-385393-02.js diff --git a/js/src/tests/js1_5/extensions/regress-382509.js b/js/src/tests/js1_5/extensions/regress-382509.js new file mode 100644 index 000000000000..eb9ffb17f4a1 --- /dev/null +++ b/js/src/tests/js1_5/extensions/regress-382509.js @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is JavaScript Engine testing utilities. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 382509; +var summary = 'Disallow non-global indirect eval'; +var actual = ''; +var expect = ''; + +var global = typeof window == 'undefined' ? this : window; +var object = {}; + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + enterFunc ('test'); + printBugNumber(BUGNUMBER); + printStatus (summary); + + if (options().match(/strict/)) + { + options('strict'); + } + if (options().match(/werror/)) + { + options('werror'); + } + + global.foo = eval; + global.a = 'global'; + expect = 'global indirect'; + actual = global.foo('a+" indirect"'); + reportCompare(expect, actual, summary + ': global indirect'); + + object.foo = eval; + object.a = 'local'; + expect = 'EvalError: function eval must be called directly, and not by way of a function of another name'; + try + { + actual = object.foo('a+" indirect"'); + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': local indirect'); + + options('strict'); + options('werror'); + + try + { + var foo = eval; + print("foo(1+1)" + foo('1+1')); + actual = 'No Error'; + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': strict, rename warning'); + + options('strict'); + options('werror'); + + expect = 'No Error'; + try + { + var foo = eval; + foo('1+1'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': not strict, no rename warning'); + + exitFunc ('test'); +} diff --git a/js/src/tests/js1_6/Regress/jstests.list b/js/src/tests/js1_6/Regress/jstests.list index b1ed21ea3c1b..82264b4fa82f 100644 --- a/js/src/tests/js1_6/Regress/jstests.list +++ b/js/src/tests/js1_6/Regress/jstests.list @@ -11,5 +11,6 @@ script regress-353078.js script regress-355002.js script regress-372565.js script regress-378492.js +script regress-382509.js script regress-475469.js script regress-476655.js diff --git a/js/src/tests/js1_6/Regress/regress-382509.js b/js/src/tests/js1_6/Regress/regress-382509.js new file mode 100644 index 000000000000..84a052ac0831 --- /dev/null +++ b/js/src/tests/js1_6/Regress/regress-382509.js @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is JavaScript Engine testing utilities. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 382509; +var summary = 'Disallow non-global indirect eval'; +var actual = ''; +var expect = ''; + +var global = typeof window == 'undefined' ? this : window; +var object = {}; + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + enterFunc ('test'); + printBugNumber(BUGNUMBER); + printStatus (summary); + + global.foo = eval; + global.a = 'global'; + expect = 'global indirect'; + actual = String(['a+" indirect"'].map(global.foo)); + reportCompare(expect, actual, summary + ': global indirect'); + + object.foo = eval; + object.a = 'local'; + expect = 'EvalError: function eval must be called directly, and not by way of a function of another name'; + try + { + actual = String(['a+" indirect"'].map(object.foo, object)); + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary + ': local indirect'); + + exitFunc ('test'); +} From b9e01bbcf2592a971c1f154ffd8ec542f6d91d5c Mon Sep 17 00:00:00 2001 From: Chris Leary Date: Tue, 5 Oct 2010 18:20:06 -0700 Subject: [PATCH 069/284] Bug 596775: re-enable YARR-JIT on ARM. (r=vladimir) --- js/src/assembler/wtf/Platform.h | 2 + js/src/yarr/yarr/RegexJIT.cpp | 271 +++++++++++++++++++------------- js/src/yarr/yarr/RegexPattern.h | 37 ++++- 3 files changed, 193 insertions(+), 117 deletions(-) diff --git a/js/src/assembler/wtf/Platform.h b/js/src/assembler/wtf/Platform.h index b4227754de30..a675f4b286e4 100644 --- a/js/src/assembler/wtf/Platform.h +++ b/js/src/assembler/wtf/Platform.h @@ -900,6 +900,8 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ /* YARR supports x86 & x86-64, and has been tested on Mac and Windows. */ #if (WTF_CPU_X86 \ || WTF_CPU_X86_64 \ + || WTF_CPU_ARM_TRADITIONAL \ + || WTF_CPU_ARM_THUMB2 \ || WTF_CPU_X86) #define ENABLE_YARR_JIT 1 #else diff --git a/js/src/yarr/yarr/RegexJIT.cpp b/js/src/yarr/yarr/RegexJIT.cpp index 41202e3f46af..6b7caa3ea6f3 100644 --- a/js/src/yarr/yarr/RegexJIT.cpp +++ b/js/src/yarr/yarr/RegexJIT.cpp @@ -994,7 +994,9 @@ class RegexGenerator : private MacroAssembler { parenthesesState.linkAlternativeBacktracks(this); if (term.invertOrCapture) { store32(Imm32(-1), Address(output, (term.parentheses.subpatternId << 1) * sizeof(int))); +#if DEBUG store32(Imm32(-1), Address(output, ((term.parentheses.subpatternId << 1) + 1) * sizeof(int))); +#endif } if (term.quantityType == QuantifierGreedy) @@ -1213,21 +1215,26 @@ class RegexGenerator : private MacroAssembler { TermGenerationState state(disjunction, 0); state.resetAlternative(); - // Plant a check to see if there is sufficient input available to run the first alternative. - // Jumping back to the label 'firstAlternative' will get to this check, jumping to - // 'firstAlternativeInputChecked' will jump directly to matching the alternative having - // skipped this check. - - Label firstAlternative(this); - // check availability for the next alternative int countCheckedForCurrentAlternative = 0; int countToCheckForFirstAlternative = 0; bool hasShorterAlternatives = false; + bool setRepeatAlternativeLabels = false; JumpList notEnoughInputForPreviousAlternative; + Label firstAlternative; + Label firstAlternativeInputChecked; + // The label 'firstAlternative' is used to plant a check to see if there is + // sufficient input available to run the first repeating alternative. + // The label 'firstAlternativeInputChecked' will jump directly to matching + // the first repeating alternative having skipped this check. + if (state.alternativeValid()) { PatternAlternative* alternative = state.alternative(); + if (!alternative->onceThrough()) { + firstAlternative = Label(this); + setRepeatAlternativeLabels = true; + } countToCheckForFirstAlternative = alternative->m_minimumSize; state.checkedTotal += countToCheckForFirstAlternative; if (countToCheckForFirstAlternative) @@ -1235,15 +1242,17 @@ class RegexGenerator : private MacroAssembler { countCheckedForCurrentAlternative = countToCheckForFirstAlternative; } - Label firstAlternativeInputChecked(this); + if (setRepeatAlternativeLabels) + firstAlternativeInputChecked = Label(this); while (state.alternativeValid()) { - // Track whether any alternatives are shorter than the first one. - hasShorterAlternatives = hasShorterAlternatives || (countCheckedForCurrentAlternative < countToCheckForFirstAlternative); - PatternAlternative* alternative = state.alternative(); optimizeAlternative(alternative); + // Track whether any alternatives are shorter than the first one. + if (!alternative->onceThrough()) + hasShorterAlternatives = hasShorterAlternatives || (countCheckedForCurrentAlternative < countToCheckForFirstAlternative); + for (state.resetTerm(); state.termValid(); state.nextTerm()) generateTerm(state); @@ -1270,47 +1279,75 @@ class RegexGenerator : private MacroAssembler { // if there are any more alternatives, plant the check for input before looping. if (state.alternativeValid()) { PatternAlternative* nextAlternative = state.alternative(); - int countToCheckForNextAlternative = nextAlternative->m_minimumSize; - - if (countCheckedForCurrentAlternative > countToCheckForNextAlternative) { // CASE 1: current alternative was longer than the next one. + if (!setRepeatAlternativeLabels && !nextAlternative->onceThrough()) { + // We have handled non-repeating alternatives, jump to next iteration + // and loop over repeating alternatives. + state.jumpToBacktrack(jump(), this); + + countToCheckForFirstAlternative = nextAlternative->m_minimumSize; + // If we get here, there the last input checked failed. notEnoughInputForPreviousAlternative.link(this); - - // Check if sufficent input available to run the next alternative - notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForNextAlternative - countCheckedForCurrentAlternative)); - // We are now in the correct state to enter the next alternative; this add is only required - // to mirror and revert operation of the sub32, just below. - add32(Imm32(countCheckedForCurrentAlternative - countToCheckForNextAlternative), index); - - // If we get here, there the last input checked passed. + state.linkAlternativeBacktracks(this); - // No need to check if we can run the next alternative, since it is shorter - - // just update index. - sub32(Imm32(countCheckedForCurrentAlternative - countToCheckForNextAlternative), index); - } else if (countCheckedForCurrentAlternative < countToCheckForNextAlternative) { // CASE 2: next alternative is longer than the current one. - // If we get here, there the last input checked failed. - // If there is insufficient input to run the current alternative, and the next alternative is longer, - // then there is definitely not enough input to run it - don't even check. Just adjust index, as if - // we had checked. - notEnoughInputForPreviousAlternative.link(this); - add32(Imm32(countToCheckForNextAlternative - countCheckedForCurrentAlternative), index); - notEnoughInputForPreviousAlternative.append(jump()); - // The next alternative is longer than the current one; check the difference. - state.linkAlternativeBacktracks(this); - notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForNextAlternative - countCheckedForCurrentAlternative)); - } else { // CASE 3: Both alternatives are the same length. - ASSERT(countCheckedForCurrentAlternative == countToCheckForNextAlternative); + // Back up to start the looping alternatives. + if (countCheckedForCurrentAlternative) + sub32(Imm32(countCheckedForCurrentAlternative), index); + + firstAlternative = Label(this); + + state.checkedTotal = countToCheckForFirstAlternative; + if (countToCheckForFirstAlternative) + notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForFirstAlternative)); + + countCheckedForCurrentAlternative = countToCheckForFirstAlternative; + + firstAlternativeInputChecked = Label(this); - // If the next alterative is the same length as this one, then no need to check the input - - // if there was sufficent input to run the current alternative then there is sufficient - // input to run the next one; if not, there isn't. - state.linkAlternativeBacktracks(this); + setRepeatAlternativeLabels = true; + } else { + int countToCheckForNextAlternative = nextAlternative->m_minimumSize; + + if (countCheckedForCurrentAlternative > countToCheckForNextAlternative) { // CASE 1: current alternative was longer than the next one. + // If we get here, then the last input checked failed. + notEnoughInputForPreviousAlternative.link(this); + + // Check if sufficent input available to run the next alternative + notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForNextAlternative - countCheckedForCurrentAlternative)); + // We are now in the correct state to enter the next alternative; this add is only required + // to mirror and revert operation of the sub32, just below. + add32(Imm32(countCheckedForCurrentAlternative - countToCheckForNextAlternative), index); + + // If we get here, then the last input checked passed. + state.linkAlternativeBacktracks(this); + // No need to check if we can run the next alternative, since it is shorter - + // just update index. + sub32(Imm32(countCheckedForCurrentAlternative - countToCheckForNextAlternative), index); + } else if (countCheckedForCurrentAlternative < countToCheckForNextAlternative) { // CASE 2: next alternative is longer than the current one. + // If we get here, then the last input checked failed. + // If there is insufficient input to run the current alternative, and the next alternative is longer, + // then there is definitely not enough input to run it - don't even check. Just adjust index, as if + // we had checked. + notEnoughInputForPreviousAlternative.link(this); + add32(Imm32(countToCheckForNextAlternative - countCheckedForCurrentAlternative), index); + notEnoughInputForPreviousAlternative.append(jump()); + + // The next alternative is longer than the current one; check the difference. + state.linkAlternativeBacktracks(this); + notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForNextAlternative - countCheckedForCurrentAlternative)); + } else { // CASE 3: Both alternatives are the same length. + ASSERT(countCheckedForCurrentAlternative == countToCheckForNextAlternative); + + // If the next alterative is the same length as this one, then no need to check the input - + // if there was sufficent input to run the current alternative then there is sufficient + // input to run the next one; if not, there isn't. + state.linkAlternativeBacktracks(this); + } + state.checkedTotal -= countCheckedForCurrentAlternative; + countCheckedForCurrentAlternative = countToCheckForNextAlternative; + state.checkedTotal += countCheckedForCurrentAlternative; } - - state.checkedTotal -= countCheckedForCurrentAlternative; - countCheckedForCurrentAlternative = countToCheckForNextAlternative; - state.checkedTotal += countCheckedForCurrentAlternative; } } @@ -1318,76 +1355,84 @@ class RegexGenerator : private MacroAssembler { state.checkedTotal -= countCheckedForCurrentAlternative; - // How much more input need there be to be able to retry from the first alternative? - // examples: - // /yarr_jit/ or /wrec|pcre/ - // In these examples we need check for one more input before looping. - // /yarr_jit|pcre/ - // In this case we need check for 5 more input to loop (+4 to allow for the first alterative - // being four longer than the last alternative checked, and another +1 to effectively move - // the start position along by one). - // /yarr|rules/ or /wrec|notsomuch/ - // In these examples, provided that there was sufficient input to have just been matching for - // the second alternative we can loop without checking for available input (since the second - // alternative is longer than the first). In the latter example we need to decrement index - // (by 4) so the start position is only progressed by 1 from the last iteration. - int incrementForNextIter = (countToCheckForFirstAlternative - countCheckedForCurrentAlternative) + 1; - - // First, deal with the cases where there was sufficient input to try the last alternative. - if (incrementForNextIter > 0) // We need to check for more input anyway, fall through to the checking below. - state.linkAlternativeBacktracks(this); - else if (m_pattern.m_body->m_hasFixedSize && !incrementForNextIter) // No need to update anything, link these backtracks straight to the to pof the loop! - state.linkAlternativeBacktracksTo(firstAlternativeInputChecked, this); - else { // no need to check the input, but we do have some bookkeeping to do first. + if (!setRepeatAlternativeLabels) { + // If there are no alternatives that need repeating (all are marked 'onceThrough') then just link + // the match failures to this point, and fall through to the return below. state.linkAlternativeBacktracks(this); + notEnoughInputForPreviousAlternative.link(this); + } else { + // How much more input need there be to be able to retry from the first alternative? + // examples: + // /yarr_jit/ or /wrec|pcre/ + // In these examples we need check for one more input before looping. + // /yarr_jit|pcre/ + // In this case we need check for 5 more input to loop (+4 to allow for the first alterative + // being four longer than the last alternative checked, and another +1 to effectively move + // the start position along by one). + // /yarr|rules/ or /wrec|notsomuch/ + // In these examples, provided that there was sufficient input to have just been matching for + // the second alternative we can loop without checking for available input (since the second + // alternative is longer than the first). In the latter example we need to decrement index + // (by 4) so the start position is only progressed by 1 from the last iteration. + int incrementForNextIter = (countToCheckForFirstAlternative - countCheckedForCurrentAlternative) + 1; - // Where necessary update our preserved start position. - if (!m_pattern.m_body->m_hasFixedSize) { - move(index, regT0); - sub32(Imm32(countCheckedForCurrentAlternative - 1), regT0); - store32(regT0, Address(output)); + // First, deal with the cases where there was sufficient input to try the last alternative. + if (incrementForNextIter > 0) // We need to check for more input anyway, fall through to the checking below. + state.linkAlternativeBacktracks(this); + else if (m_pattern.m_body->m_hasFixedSize && !incrementForNextIter) // No need to update anything, link these backtracks straight to the to pof the loop! + state.linkAlternativeBacktracksTo(firstAlternativeInputChecked, this); + else { // no need to check the input, but we do have some bookkeeping to do first. + state.linkAlternativeBacktracks(this); + + // Where necessary update our preserved start position. + if (!m_pattern.m_body->m_hasFixedSize) { + move(index, regT0); + sub32(Imm32(countCheckedForCurrentAlternative - 1), regT0); + store32(regT0, Address(output)); + } + + // Update index if necessary, and loop (without checking). + if (incrementForNextIter) + add32(Imm32(incrementForNextIter), index); + jump().linkTo(firstAlternativeInputChecked, this); } - // Update index if necessary, and loop (without checking). - if (incrementForNextIter) - add32(Imm32(incrementForNextIter), index); - jump().linkTo(firstAlternativeInputChecked, this); + notEnoughInputForPreviousAlternative.link(this); + // Update our idea of the start position, if we're tracking this. + if (!m_pattern.m_body->m_hasFixedSize) { + if (countCheckedForCurrentAlternative - 1) { + move(index, regT0); + sub32(Imm32(countCheckedForCurrentAlternative - 1), regT0); + store32(regT0, Address(output)); + } else + store32(index, Address(output)); + } + + // Check if there is sufficent input to run the first alternative again. + jumpIfAvailableInput(incrementForNextIter).linkTo(firstAlternativeInputChecked, this); + // No - insufficent input to run the first alteranative, are there any other alternatives we + // might need to check? If so, the last check will have left the index incremented by + // (countToCheckForFirstAlternative + 1), so we need test whether countToCheckForFirstAlternative + // LESS input is available, to have the effect of just progressing the start position by 1 + // from the last iteration. If this check passes we can just jump up to the check associated + // with the first alternative in the loop. This is a bit sad, since we'll end up trying the + // first alternative again, and this check will fail (otherwise the check planted just above + // here would have passed). This is a bit sad, however it saves trying to do something more + // complex here in compilation, and in the common case we should end up coallescing the checks. + // + // FIXME: a nice improvement here may be to stop trying to match sooner, based on the least + // of the minimum-alternative-lengths. E.g. if I have two alternatives of length 200 and 150, + // and a string of length 100, we'll end up looping index from 0 to 100, checking whether there + // is sufficient input to run either alternative (constantly failing). If there had been only + // one alternative, or if the shorter alternative had come first, we would have terminated + // immediately. :-/ + if (hasShorterAlternatives) + jumpIfAvailableInput(-countToCheckForFirstAlternative).linkTo(firstAlternative, this); + // index will now be a bit garbled (depending on whether 'hasShorterAlternatives' is true, + // it has either been incremented by 1 or by (countToCheckForFirstAlternative + 1) ... + // but since we're about to return a failure this doesn't really matter!) } - notEnoughInputForPreviousAlternative.link(this); - // Update our idea of the start position, if we're tracking this. - if (!m_pattern.m_body->m_hasFixedSize) { - if (countCheckedForCurrentAlternative - 1) { - move(index, regT0); - sub32(Imm32(countCheckedForCurrentAlternative - 1), regT0); - store32(regT0, Address(output)); - } else - store32(index, Address(output)); - } - // Check if there is sufficent input to run the first alternative again. - jumpIfAvailableInput(incrementForNextIter).linkTo(firstAlternativeInputChecked, this); - // No - insufficent input to run the first alteranative, are there any other alternatives we - // might need to check? If so, the last check will have left the index incremented by - // (countToCheckForFirstAlternative + 1), so we need test whether countToCheckForFirstAlternative - // LESS input is available, to have the effect of just progressing the start position by 1 - // from the last iteration. If this check passes we can just jump up to the check associated - // with the first alternative in the loop. This is a bit sad, since we'll end up trying the - // first alternative again, and this check will fail (otherwise the check planted just above - // here would have passed). This is a bit sad, however it saves trying to do something more - // complex here in compilation, and in the common case we should end up coallescing the checks. - // - // FIXME: a nice improvement here may be to stop trying to match sooner, based on the least - // of the minimum-alternative-lengths. E.g. if I have two alternatives of length 200 and 150, - // and a string of length 100, we'll end up looping index from 0 to 100, checking whether there - // is sufficient input to run either alternative (constantly failing). If there had been only - // one alternative, or if the shorter alternative had come first, we would have terminated - // immediately. :-/ - if (hasShorterAlternatives) - jumpIfAvailableInput(-countToCheckForFirstAlternative).linkTo(firstAlternative, this); - // index will now be a bit garbled (depending on whether 'hasShorterAlternatives' is true, - // it has either been incremented by 1 or by (countToCheckForFirstAlternative + 1) ... - // but since we're about to return a failure this doesn't really matter!) - if (m_pattern.m_body->m_callFrameSize) addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); @@ -1422,6 +1467,9 @@ class RegexGenerator : private MacroAssembler { push(ARMRegisters::r4); push(ARMRegisters::r5); push(ARMRegisters::r6); +#if WTF_CPU_ARM_TRADITIONAL + push(ARMRegisters::r8); // scratch register +#endif move(ARMRegisters::r3, output); #elif WTF_CPU_MIPS // Do nothing. @@ -1439,6 +1487,9 @@ class RegexGenerator : private MacroAssembler { pop(X86Registers::ebx); pop(X86Registers::ebp); #elif WTF_CPU_ARM +#if WTF_CPU_ARM_TRADITIONAL + pop(ARMRegisters::r8); // scratch register +#endif pop(ARMRegisters::r6); pop(ARMRegisters::r5); pop(ARMRegisters::r4); diff --git a/js/src/yarr/yarr/RegexPattern.h b/js/src/yarr/yarr/RegexPattern.h index ec9bfe7cb5a6..77800f6d786f 100644 --- a/js/src/yarr/yarr/RegexPattern.h +++ b/js/src/yarr/yarr/RegexPattern.h @@ -225,6 +225,10 @@ struct PatternTerm { struct PatternAlternative { PatternAlternative(PatternDisjunction* disjunction) : m_parent(disjunction) + , m_onceThrough(false) + , m_hasFixedSize(false) + , m_startsWithBOL(false) + , m_containsBOL(false) { } @@ -239,16 +243,30 @@ struct PatternAlternative { JS_ASSERT(m_terms.length()); m_terms.popBack(); } + + void setOnceThrough() + { + m_onceThrough = true; + } + + bool onceThrough() + { + return m_onceThrough; + } js::Vector m_terms; PatternDisjunction* m_parent; unsigned m_minimumSize; - bool m_hasFixedSize; + bool m_onceThrough : 1; + bool m_hasFixedSize : 1; + bool m_startsWithBOL : 1; + bool m_containsBOL : 1; }; struct PatternDisjunction { PatternDisjunction(PatternAlternative* parent = 0) : m_parent(parent) + , m_hasFixedSize(false) { } @@ -287,9 +305,10 @@ struct RegexPattern { RegexPattern(bool ignoreCase, bool multiline) : m_ignoreCase(ignoreCase) , m_multiline(multiline) + , m_containsBackreferences(false) + , m_containsBOL(false) , m_numSubpatterns(0) , m_maxBackReference(0) - , m_containsBackreferences(false) , newlineCached(0) , digitsCached(0) , spacesCached(0) @@ -312,6 +331,7 @@ struct RegexPattern { m_maxBackReference = 0; m_containsBackreferences = false; + m_containsBOL = false; newlineCached = 0; digitsCached = 0; @@ -375,14 +395,17 @@ struct RegexPattern { return nonwordcharCached; } - bool m_ignoreCase; - bool m_multiline; + typedef js::Vector PatternDisjunctions; + typedef js::Vector CharacterClasses; + bool m_ignoreCase : 1; + bool m_multiline : 1; + bool m_containsBackreferences : 1; + bool m_containsBOL : 1; unsigned m_numSubpatterns; unsigned m_maxBackReference; - bool m_containsBackreferences; PatternDisjunction *m_body; - js::Vector m_disjunctions; - js::Vector m_userCharacterClasses; + PatternDisjunctions m_disjunctions; + CharacterClasses m_userCharacterClasses; private: CharacterClass* newlineCached; From e46a11c243f791dc1e6655ad0705a96133a694e9 Mon Sep 17 00:00:00 2001 From: Chris Leary Date: Wed, 6 Oct 2010 15:15:54 -0700 Subject: [PATCH 070/284] Fix trace_tests.py when showing output. (r=jorendorff) --- js/src/trace-test/trace_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/trace-test/trace_test.py b/js/src/trace-test/trace_test.py index e432d4d65f19..e729ece0c6d7 100644 --- a/js/src/trace-test/trace_test.py +++ b/js/src/trace-test/trace_test.py @@ -182,7 +182,7 @@ def run_test(test, lib_dir): if OPTIONS.show_output: sys.stdout.write(out) sys.stdout.write(err) - sys.stdout.write('Exit code: ' + str(p.returncode) + "\n") + sys.stdout.write('Exit code: %s\n' % code) if test.valgrind: sys.stdout.write(err) return (check_output(out, err, code, test.allow_oom, test.error), From d0384d584f89756c2e28042185ed67a8f1d544fd Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 6 Oct 2010 16:14:01 -0700 Subject: [PATCH 071/284] Bug 593931 - inline js_GetPropertyHelper() and friends more aggressively. r=cdleary. --- js/src/jsobj.cpp | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index cf4ae4d336ba..7fd59d5f156d 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4653,9 +4653,9 @@ cleanup: return ok; } -int -js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp) +static JS_ALWAYS_INLINE int +js_LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp) { /* Convert string indices to integers if appropriate. */ id = js_CheckForStringIndex(id); @@ -4708,6 +4708,13 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, return protoIndex; } +int +js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp) +{ + return js_LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp); +} + PropertyCacheEntry * js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, JSObject **objp, JSObject **pobjp, JSProperty **propp) @@ -4891,9 +4898,9 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) return obj; } -JSBool -js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow, - Value *vp) +static JS_ALWAYS_INLINE JSBool +js_NativeGetInline(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow, + Value *vp) { LeaveTraceIfGlobalObject(cx, pobj); @@ -4939,6 +4946,13 @@ js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, u return true; } +JSBool +js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow, + Value *vp) +{ + return js_NativeGetInline(cx, obj, pobj, shape, getHow, vp); +} + JSBool js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, Value *vp) { @@ -5014,8 +5028,9 @@ js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, jsid id, id = js_CheckForStringIndex(id); aobj = js_GetProtoIfDenseArray(obj); - protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags, - &obj2, &prop); + /* This call site is hot -- use the always-inlined variant of js_LookupPropertyWithFlags(). */ + protoIndex = js_LookupPropertyWithFlagsInline(cx, aobj, id, cx->resolveFlags, + &obj2, &prop); if (protoIndex < 0) return JS_FALSE; @@ -5093,7 +5108,8 @@ js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, jsid id, JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, shape); } - if (!js_NativeGet(cx, obj, obj2, shape, getHow, vp)) + /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */ + if (!js_NativeGetInline(cx, obj, obj2, shape, getHow, vp)) return JS_FALSE; JS_UNLOCK_OBJ(cx, obj2); @@ -5108,18 +5124,25 @@ js_GetPropertyHelperWithShape(JSContext *cx, JSObject *obj, jsid id, return js_GetPropertyHelperWithShapeInline(cx, obj, id, getHow, vp, shapeOut, holderOut); } -extern JSBool -js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32 getHow, Value *vp) +static JS_ALWAYS_INLINE JSBool +js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, jsid id, uint32 getHow, Value *vp) { const Shape *shape; JSObject *holder; return js_GetPropertyHelperWithShapeInline(cx, obj, id, getHow, vp, &shape, &holder); } +extern JSBool +js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32 getHow, Value *vp) +{ + return js_GetPropertyHelperInline(cx, obj, id, getHow, vp); +} + JSBool js_GetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - return js_GetPropertyHelper(cx, obj, id, JSGET_METHOD_BARRIER, vp); + /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */ + return js_GetPropertyHelperInline(cx, obj, id, JSGET_METHOD_BARRIER, vp); } JSBool From 9eceb8db2c65c7f7aa88630a268f58da2c9f564c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 6 Oct 2010 16:16:11 -0700 Subject: [PATCH 072/284] Fix compiler warning. No bug, r=jwalden via IRC. --- js/src/jstracer.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 0eb877dace16..68c746d65031 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -10376,16 +10376,14 @@ TraceRecorder::record_EnterFrame() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_LeaveFrame() { - JSStackFrame *fp = cx->fp(); + debug_only_stmt(JSStackFrame *fp = cx->fp();) - debug_only_stmt( - debug_only_printf(LC_TMTracer, - "LeaveFrame (back to %s), callDepth=%d\n", - fp->isFunctionFrame() - ? js_AtomToPrintableString(cx, fp->fun()->atom) - : "global code", - callDepth); - ); + debug_only_printf(LC_TMTracer, + "LeaveFrame (back to %s), callDepth=%d\n", + fp->isFunctionFrame() + ? js_AtomToPrintableString(cx, fp->fun()->atom) + : "global code", + callDepth); JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, fp->script(), cx->regs->pc)].length == JSOP_CALL_LENGTH); From 170aae6161b69248bcc8e59fdc75dc9012964706 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 6 Oct 2010 15:30:56 -0700 Subject: [PATCH 073/284] Bug 599251 - nanojit: make Register a non-numeric type. r=edwsmith. --HG-- extra : convert_revision : c7275693cde4c740f1a53ab73b9ec1963f6fe0cc --- js/src/nanojit/Assembler.cpp | 26 +-- js/src/nanojit/LIR.h | 18 ++- js/src/nanojit/Native.h | 4 +- js/src/nanojit/NativeARM.h | 70 ++++---- js/src/nanojit/NativeMIPS.h | 119 +++++++++++--- js/src/nanojit/NativePPC.h | 151 +++++++++--------- js/src/nanojit/NativeSH4.h | 88 +++++----- js/src/nanojit/NativeSparc.h | 156 +++++++++--------- js/src/nanojit/NativeX64.cpp | 89 ++++++----- js/src/nanojit/NativeX64.h | 101 ++++++------ js/src/nanojit/Nativei386.cpp | 291 +++++++++++++++++----------------- js/src/nanojit/Nativei386.h | 123 +++++++------- js/src/nanojit/RegAlloc.h | 38 ++--- 13 files changed, 701 insertions(+), 573 deletions(-) diff --git a/js/src/nanojit/Assembler.cpp b/js/src/nanojit/Assembler.cpp index 0a57ac05b0aa..0173d2ff0cbb 100755 --- a/js/src/nanojit/Assembler.cpp +++ b/js/src/nanojit/Assembler.cpp @@ -362,8 +362,8 @@ namespace nanojit // we enforce this condition between all pairs of instructions, but this is // overly restrictive, and would fail if we did not generate unreachable x87 // stack pops following unconditional branches. - NanoAssert((_allocator.active[FST0] && _fpuStkDepth == -1) || - (!_allocator.active[FST0] && _fpuStkDepth == 0)); + NanoAssert((_allocator.active[REGNUM(FST0)] && _fpuStkDepth == -1) || + (!_allocator.active[REGNUM(FST0)] && _fpuStkDepth == 0)); #endif _activation.checkForResourceConsistency(_allocator); registerConsistencyCheck(); @@ -391,7 +391,7 @@ namespace nanojit for (Register r = lsReg(not_managed); not_managed; r = nextLsReg(not_managed, r)) { // A register not managed by register allocation must be // neither free nor active. - if (r <= LastReg) { + if (REGNUM(r) <= LastRegNum) { NanoAssert(!_allocator.isFree(r)); NanoAssert(!_allocator.getActive(r)); } @@ -2272,7 +2272,7 @@ namespace nanojit // 'tosave' is a binary heap stored in an array. The root is tosave[0], // left child is at i+1, right child is at i+2. - Register tosave[LastReg-FirstReg+1]; + Register tosave[LastRegNum - FirstRegNum + 1]; int len=0; RegAlloc *regs = &_allocator; RegisterMask evict_set = regs->activeMask() & GpRegs & ~ignore; @@ -2354,17 +2354,17 @@ namespace nanojit */ void Assembler::intersectRegisterState(RegAlloc& saved) { - Register regsTodo[LastReg + 1]; - LIns* insTodo[LastReg + 1]; + Register regsTodo[LastRegNum + 1]; + LIns* insTodo[LastRegNum + 1]; int nTodo = 0; // Do evictions and pops first. verbose_only(bool shouldMention=false; ) - // The obvious thing to do here is to iterate from FirstReg to LastReg. - // However, on ARM that causes lower-numbered integer registers - // to be be saved at higher addresses, which inhibits the formation - // of load/store multiple instructions. Hence iterate the loop the - // other way. + // The obvious thing to do here is to iterate from FirstRegNum to + // LastRegNum. However, on ARM that causes lower-numbered integer + // registers to be be saved at higher addresses, which inhibits the + // formation of load/store multiple instructions. Hence iterate the + // loop the other way. RegisterMask reg_set = _allocator.activeMask() | saved.activeMask(); for (Register r = msReg(reg_set); reg_set; r = nextMsReg(reg_set, r)) { @@ -2415,8 +2415,8 @@ namespace nanojit */ void Assembler::unionRegisterState(RegAlloc& saved) { - Register regsTodo[LastReg + 1]; - LIns* insTodo[LastReg + 1]; + Register regsTodo[LastRegNum + 1]; + LIns* insTodo[LastRegNum + 1]; int nTodo = 0; // Do evictions and pops first. diff --git a/js/src/nanojit/LIR.h b/js/src/nanojit/LIR.h index dd72a21c3874..4abdba7b9828 100644 --- a/js/src/nanojit/LIR.h +++ b/js/src/nanojit/LIR.h @@ -547,7 +547,7 @@ namespace nanojit inline RegisterMask rmask(Register r) { - return RegisterMask(1) << r; + return RegisterMask(1) << REGNUM(r); } //----------------------------------------------------------------------- @@ -656,7 +656,7 @@ namespace nanojit private: // SharedFields: fields shared by all LIns kinds. // - // The .inReg, .reg, .inAr and .arIndex fields form a "reservation" + // The .inReg, .regnum, .inAr and .arIndex fields form a "reservation" // that is used temporarily during assembly to record information // relating to register allocation. See class RegAlloc for more // details. Note: all combinations of .inReg/.inAr are possible, ie. @@ -668,7 +668,7 @@ namespace nanojit // struct SharedFields { uint32_t inReg:1; // if 1, 'reg' is active - Register reg:7; + uint32_t regnum:7; uint32_t inAr:1; // if 1, 'arIndex' is active uint32_t isResultLive:1; // if 1, the instruction's result is live @@ -758,7 +758,12 @@ namespace nanojit } Register deprecated_getReg() { NanoAssert(isExtant()); - return ( isInReg() ? sharedFields.reg : deprecated_UnknownReg ); + if (isInReg()) { + Register r = { sharedFields.regnum }; + return r; + } else { + return deprecated_UnknownReg; + } } uint32_t deprecated_getArIndex() { NanoAssert(isExtant()); @@ -782,11 +787,12 @@ namespace nanojit } Register getReg() { NanoAssert(isInReg()); - return sharedFields.reg; + Register r = { sharedFields.regnum }; + return r; } void setReg(Register r) { sharedFields.inReg = 1; - sharedFields.reg = r; + sharedFields.regnum = REGNUM(r); } void clearReg() { sharedFields.inReg = 0; diff --git a/js/src/nanojit/Native.h b/js/src/nanojit/Native.h index 2e70e8457460..8397d1aac09c 100644 --- a/js/src/nanojit/Native.h +++ b/js/src/nanojit/Native.h @@ -137,7 +137,7 @@ namespace nanojit { #ifdef NJ_NO_VARIADIC_MACROS static void asm_output(const char *f, ...) {} - #define gpn(r) regNames[(r)] + #define gpn(r) regNames[(REGNUM(n))] #elif defined(NJ_VERBOSE) // Used for printing native instructions. Like Assembler::outputf(), // but only outputs if LC_Native is set. Also prepends the output @@ -150,7 +150,7 @@ namespace nanojit { output(); \ } \ } while (0) /* no semi */ - #define gpn(r) regNames[(r)] + #define gpn(r) regNames[(REGNUM(r))] #else #define asm_output(...) #define gpn(r) diff --git a/js/src/nanojit/NativeARM.h b/js/src/nanojit/NativeARM.h index d0a671881920..973e4362cca2 100644 --- a/js/src/nanojit/NativeARM.h +++ b/js/src/nanojit/NativeARM.h @@ -99,49 +99,57 @@ typedef int NIns; const size_t LARGEST_BRANCH_PATCH = 2 * sizeof(NIns); /* ARM registers */ -typedef enum { - R0 = 0, - R1 = 1, - R2 = 2, - R3 = 3, - R4 = 4, - R5 = 5, - R6 = 6, - R7 = 7, - R8 = 8, - R9 = 9, - R10 = 10, - FP = 11, - IP = 12, - SP = 13, - LR = 14, - PC = 15, +typedef uint32_t Register; +static const Register + R0 = { 0 }, + R1 = { 1 }, + R2 = { 2 }, + R3 = { 3 }, + R4 = { 4 }, + R5 = { 5 }, + R6 = { 6 }, + R7 = { 7 }, + R8 = { 8 }, + R9 = { 9 }, + R10 = { 10 }, + FP = { 11 }, + IP = { 12 }, + SP = { 13 }, + LR = { 14 }, + PC = { 15 }, // VFP regs (we currently only use D0-D6 and S14) - D0 = 16, - D1 = 17, - D2 = 18, - D3 = 19, - D4 = 20, - D5 = 21, - D6 = 22, + D0 = { 16 }, + D1 = { 17 }, + D2 = { 18 }, + D3 = { 19 }, + D4 = { 20 }, + D5 = { 21 }, + D6 = { 22 }, // S14 overlaps with D7 and is hard-coded into i2d and u2f operations, but // D7 is still listed here for completeness and to facilitate assertions. - D7 = 23, + D7 = { 23 }, // D8-D15 are caller-saved registers that we don't currently handle. FirstFloatReg = D0, LastFloatReg = D6, - FirstReg = R0, - LastReg = D6, - deprecated_UnknownReg = 32, // XXX: remove eventually, see bug 538924 + deprecated_UnknownReg = { 32 }, // XXX: remove eventually, see bug 538924 - S14 = 24, + S14 = { 24 }, - SBZ = 0 // Used for 'should-be-zero' fields in instructions with + SBZ = { 0 } ; // Used for 'should-be-zero' fields in instructions with // unused register fields. -} Register; + +static const uint32_t FirstRegNum = R0; +static const uint32_t LastRegNum = D6; +} + +#define NJ_USE_UINT32_REGISTER 1 +#include "NativeCommon.h" + +namespace nanojit +{ /* ARM condition codes */ typedef enum { diff --git a/js/src/nanojit/NativeMIPS.h b/js/src/nanojit/NativeMIPS.h index fb6d426a395d..c4ba9fd40bb6 100644 --- a/js/src/nanojit/NativeMIPS.h +++ b/js/src/nanojit/NativeMIPS.h @@ -65,33 +65,112 @@ namespace nanojit static const int NJ_ALIGN_STACK = 8; typedef uint32_t NIns; // REQ: Instruction count - typedef uint64_t RegisterMask; // REQ: Large enough to hold LastReg-FirstReg bits + typedef uint64_t RegisterMask; // REQ: Large enough to hold LastRegNum-FirstRegNum bits #define _rmask_(r) (1LL<<(r)) - typedef enum { // REQ: Register identifiers - // Register numbers for Native code generator - ZERO = 0, AT = 1, V0 = 2, V1 = 3, A0 = 4, A1 = 5, A2 = 6, A3 = 7, - T0 = 8, T1 = 9, T2 = 10, T3 = 11, T4 = 12, T5 = 13, T6 = 14, T7 = 15, - S0 = 16, S1 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23, - T8 = 24, T9 = 25, K0 = 26, K1 = 27, GP = 28, SP = 29, FP = 30, RA = 31, - F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39, - F8 = 40, F9 = 41, F10 = 42, F11 = 43, F12 = 44, F13 = 45, F14 = 46, F15 = 47, - F16 = 48, F17 = 49, F18 = 50, F19 = 51, F20 = 52, F21 = 53, F22 = 54, F23 = 55, - F24 = 56, F25 = 57, F26 = 58, F27 = 59, F28 = 60, F29 = 61, F30 = 62, F31 = 63, + typedef uint32_t Register; // REQ: Register identifiers + // Register numbers for Native code generator + static const Register + ZERO = { 0 }, + AT = { 1 }, + V0 = { 2 }, + V1 = { 3 }, + A0 = { 4 }, + A1 = { 5 }, + A2 = { 6 }, + A3 = { 7 }, + + T0 = { 8 }, + T1 = { 9 }, + T2 = { 10 }, + T3 = { 11 }, + T4 = { 12 }, + T5 = { 13 }, + T6 = { 14 }, + T7 = { 15 }, + + S0 = { 16 }, + S1 = { 17 }, + S2 = { 18 }, + S3 = { 19 }, + S4 = { 20 }, + S5 = { 21 }, + S6 = { 22 }, + S7 = { 23 }, + + T8 = { 24 }, + T9 = { 25 }, + K0 = { 26 }, + K1 = { 27 }, + GP = { 28 }, + SP = { 29 }, + FP = { 30 }, + RA = { 31 }, + + F0 = { 32 }, + F1 = { 33 }, + F2 = { 34 }, + F3 = { 35 }, + F4 = { 36 }, + F5 = { 37 }, + F6 = { 38 }, + F7 = { 39 }, + + F8 = { 40 }, + F9 = { 41 }, + F10 = { 42 }, + F11 = { 43 }, + F12 = { 44 }, + F13 = { 45 }, + F14 = { 46 }, + F15 = { 47 }, + + F16 = { 48 }, + F17 = { 49 }, + F18 = { 50 }, + F19 = { 51 }, + F20 = { 52 }, + F21 = { 53 }, + F22 = { 54 }, + F23 = { 55 }, + + F24 = { 56 }, + F25 = { 57 }, + F26 = { 58 }, + F27 = { 59 }, + F28 = { 60 }, + F29 = { 61 }, + F30 = { 62 }, + F31 = { 63 }, // FP register aliases - FV0 = F0, FV1 = F2, - FA0 = F12, FA1 = F14, - FT0 = F4, FT1 = F6, FT2 = F8, FT3 = F10, FT4 = F16, FT5 = F18, - FS0 = F20, FS1 = F22, FS2 = F24, FS3 = F26, FS4 = F28, FS5 = F30, + FV0 = F0, + FV1 = F2, + FA0 = F12, + FA1 = F14, + FT0 = F4, + FT1 = F6, + FT2 = F8, + FT3 = F10, + FT4 = F16, + FT5 = F18, + FS0 = F20, + FS1 = F22, + FS2 = F24, + FS3 = F26, + FS4 = F28, + FS5 = F30, - // Wellknown register names used by code generator - FirstReg = ZERO, - LastReg = F31, - deprecated_UnknownReg = 127 // XXX: remove eventually, see bug 538924 + deprecated_UnknownReg = { 127 }; // XXX: remove eventually, see bug 538924 - } Register; + static const uint32_t FirstRegNum = ZERO; + static const uint32_t LastRegNum = F31; +} +#define NJ_USE_UINT32_REGISTER 1 +#include "NativeCommon.h" + +namespace nanojit { // REQ: register names verbose_only(extern const char* regNames[];) diff --git a/js/src/nanojit/NativePPC.h b/js/src/nanojit/NativePPC.h index 306f2f4686be..a5c7be17fa03 100644 --- a/js/src/nanojit/NativePPC.h +++ b/js/src/nanojit/NativePPC.h @@ -87,86 +87,93 @@ namespace nanojit BO_false = 4, // branch if false }; - enum Register { + typedef uint32_t Register; + static const Register // general purpose 32bit regs - R0 = 0, // scratch or the value 0, excluded from regalloc - SP = 1, // stack pointer, excluded from regalloc - R2 = 2, // scratch on MacOSX, rtoc pointer elsewhere - R3 = 3, // this, return value, MSW of int64 return - R4 = 4, // param, LSW of int64 return - R5 = 5, // param - R6 = 6, // param - R7 = 7, // param - R8 = 8, // param - R9 = 9, // param - R10 = 10, // param - R11 = 11, // scratch in leaf funcs, outgoing arg ptr otherwise - R12 = 12, // scratch - R13 = 13, // ppc32: saved, ppc64: thread-specific storage - R14 = 14, // saved - R15 = 15, - R16 = 16, - R17 = 17, - R18 = 18, - R19 = 19, - R20 = 20, - R21 = 21, - R22 = 22, - R23 = 23, - R24 = 24, - R25 = 25, - R26 = 26, - R27 = 27, - R28 = 28, - R29 = 29, - R30 = 30, - R31 = 31, // excluded from regalloc since we use it as FP + R0 = { 0 }, // scratch or the value 0, excluded from regalloc + SP = { 1 }, // stack pointer, excluded from regalloc + R2 = { 2 }, // scratch on MacOSX, rtoc pointer elsewhere + R3 = { 3 }, // this, return value, MSW of int64 return + R4 = { 4 }, // param, LSW of int64 return + R5 = { 5 }, // param + R6 = { 6 }, // param + R7 = { 7 }, // param + R8 = { 8 }, // param + R9 = { 9 }, // param + R10 = { 10 }, // param + R11 = { 11 }, // scratch in leaf funcs, outgoing arg ptr otherwise + R12 = { 12 }, // scratch + R13 = { 13 }, // ppc32: saved, ppc64: thread-specific storage + R14 = { 14 }, // saved + R15 = { 15 }, + R16 = { 16 }, + R17 = { 17 }, + R18 = { 18 }, + R19 = { 19 }, + R20 = { 20 }, + R21 = { 21 }, + R22 = { 22 }, + R23 = { 23 }, + R24 = { 24 }, + R25 = { 25 }, + R26 = { 26 }, + R27 = { 27 }, + R28 = { 28 }, + R29 = { 29 }, + R30 = { 30 }, + R31 = { 31 }, // excluded from regalloc since we use it as FP FP = R31, // FP regs - F0 = 32, // scratch, excluded from reg alloc - F1 = 33, // param, double return value - F2 = 34, // param - F3 = 35, // param - F4 = 36, // param - F5 = 37, // param - F6 = 38, // param - F7 = 39, // param - F8 = 40, // param - F9 = 41, // param - F10 = 42, // param - F11 = 43, // param - F12 = 44, // param - F13 = 45, // param - F14 = 46, // F14-31 saved - F15 = 47, - F16 = 48, - F17 = 49, - F18 = 50, - F19 = 51, - F20 = 52, - F21 = 53, - F22 = 54, - F23 = 55, - F24 = 56, - F25 = 57, - F26 = 58, - F27 = 59, - F28 = 60, - F29 = 61, - F30 = 62, - F31 = 63, + F0 = { 32 }, // scratch, excluded from reg alloc + F1 = { 33 }, // param, double return value + F2 = { 34 }, // param + F3 = { 35 }, // param + F4 = { 36 }, // param + F5 = { 37 }, // param + F6 = { 38 }, // param + F7 = { 39 }, // param + F8 = { 40 }, // param + F9 = { 41 }, // param + F10 = { 42 }, // param + F11 = { 43 }, // param + F12 = { 44 }, // param + F13 = { 45 }, // param + F14 = { 46 }, // F14-31 saved + F15 = { 47 }, + F16 = { 48 }, + F17 = { 49 }, + F18 = { 50 }, + F19 = { 51 }, + F20 = { 52 }, + F21 = { 53 }, + F22 = { 54 }, + F23 = { 55 }, + F24 = { 56 }, + F25 = { 57 }, + F26 = { 58 }, + F27 = { 59 }, + F28 = { 60 }, + F29 = { 61 }, + F30 = { 62 }, + F31 = { 63 }, // special purpose registers (SPR) - Rxer = 1, - Rlr = 8, - Rctr = 9, + Rxer = { 1 }, + Rlr = { 8 }, + Rctr = { 9 }, - deprecated_UnknownReg = 127, // XXX: remove eventually, see bug 538924 - FirstReg = R0, - LastReg = F31 - }; + deprecated_UnknownReg = { 127 }; // XXX: remove eventually, see bug 538924 + static const uint32_t FirstRegNum = R0; + static const uint32_t LastRegNum = F31; +} + +#define NJ_USE_UINT32_REGISTER 1 +#include "NativeCommon.h" + +namespace nanojit +{ enum PpcOpcode { // opcodes PPC_add = 0x7C000214, // add diff --git a/js/src/nanojit/NativeSH4.h b/js/src/nanojit/NativeSH4.h index 2ea0f46d2830..29db3a212ef5 100644 --- a/js/src/nanojit/NativeSH4.h +++ b/js/src/nanojit/NativeSH4.h @@ -47,65 +47,71 @@ namespace nanojit */ // General purpose and ABI registers. - enum Register { - + typedef uint32_t Register; + static const Register // Scratch registers (a.k.a caller-saved, a.k.a local). - R0 = 0, - R1, - R2, - R3, // Excluded from the regalloc because of its use as a hyper-scratch. - R4, - R5, - R6, - R7, + R0 = { 0 }, + R1 = { 1 }, + R2 = { 2 }, + R3 = { 3 }, // Excluded from the regalloc because of its use as a hyper-scratch. + R4 = { 4 }, + R5 = { 5 }, + R6 = { 6 }, + R7 = { 7 }, // Saved registers (a.k.a callee-saved, a.k.a global). - R8, - R9, - R10, - R11, - R12, - R13, + R8 = { 8 }, + R9 = { 9 }, + R10 = { 10 }, + R11 = { 11 }, + R12 = { 12 }, + R13 = { 13 }, // ABI registers, excluded from the register allocation. - FP, - SP, + FP = { 14 }, + SP = { 15 }, // Floatting-point registers. - _D0, + _D0 = { 16 }, _F0 = _D0, - _F1, - _D1, + _F1 = { 17 }, + _D1 = { 18 }, _F2 = _D1, - _F3, - _D2, + _F3 = { 19 }, + _D2 = { 20 }, _F4 = _D2, - _F5, - _D3, + _F5 = { 21 }, + _D3 = { 22 }, _F6 = _D3, - _F7, - _D4, + _F7 = { 23 }, + _D4 = { 24 }, _F8 = _D4, - _F9, - _D5, + _F9 = { 25 }, + _D5 = { 26 }, _F10 = _D5, - _F11, - _D6, + _F11 = { 27 }, + _D6 = { 28 }, _F12 = _D6, - _F13, - _D7, + _F13 = { 29 }, + _D7 = { 30 }, _F14 = _D7, // Excluded from the regalloc because of its use as a hyper-scratch. - _F15, + _F15 = { 31 }, // Helpers. - FirstReg = R0, - LastReg = _D7, - deprecated_UnknownReg = LastReg + 2, - UnspecifiedReg = LastReg + 2, - Rtemp = R3, - Dtemp = _D7 - }; + deprecated_UnknownReg = { 32 }, + UnspecifiedReg = { 32 }, + Rtemp = R3, + Dtemp = _D7; + static const uint32_t FirstRegNum = R0; + static const uint32_t LastRegNum = _D7; +} + +#define NJ_USE_UINT32_REGISTER 1 +#include "NativeCommon.h" + +namespace nanojit +{ // There's 16 integer registers + 8 double registers on SH4. typedef uint32_t RegisterMask; diff --git a/js/src/nanojit/NativeSparc.h b/js/src/nanojit/NativeSparc.h index 06143acbd6ec..64243b7ef20c 100644 --- a/js/src/nanojit/NativeSparc.h +++ b/js/src/nanojit/NativeSparc.h @@ -42,7 +42,6 @@ #ifndef __nanojit_NativeSparc__ #define __nanojit_NativeSparc__ - #define count_instr() #define count_ret() #define count_push() @@ -87,89 +86,94 @@ namespace nanojit const size_t LARGEST_BRANCH_PATCH = 2 * sizeof(NIns); // These are used as register numbers in various parts of the code - typedef enum - { - G0 = 0, - G1 = 1, - G2 = 2, - G3 = 3, - G4 = 4, - G5 = 5, // Reserved for system - G6 = 6, // Reserved for system - G7 = 7, // Reserved for system + typedef uint32_t Register; + static const Register + G0 = { 0 }, + G1 = { 1 }, + G2 = { 2 }, + G3 = { 3 }, + G4 = { 4 }, + G5 = { 5 }, // Reserved for system + G6 = { 6 }, // Reserved for system + G7 = { 7 }, // Reserved for system - O0 = 8, - O1 = 9, - O2 = 10, - O3 = 11, - O4 = 12, - O5 = 13, - O6 = 14, // SP - O7 = 15, // Not used. + O0 = { 8 }, + O1 = { 9 }, + O2 = { 10 }, + O3 = { 11 }, + O4 = { 12 }, + O5 = { 13 }, + O6 = { 14 }, // SP + O7 = { 15 }, // Not used. - L0 = 16, - L1 = 17, - L2 = 18, - L3 = 19, - L4 = 20, - L5 = 21, - L6 = 22, - L7 = 23, + L0 = { 16 }, + L1 = { 17 }, + L2 = { 18 }, + L3 = { 19 }, + L4 = { 20 }, + L5 = { 21 }, + L6 = { 22 }, + L7 = { 23 }, - I0 = 24, - I1 = 25, - I2 = 26, - I3 = 27, - I4 = 28, - I5 = 29, - I6 = 30, // FP - I7 = 31, // Not used + I0 = { 24 }, + I1 = { 25 }, + I2 = { 26 }, + I3 = { 27 }, + I4 = { 28 }, + I5 = { 29 }, + I6 = { 30 }, // FP + I7 = { 31 }, // Not used - SP = O6, - FP = I6, + SP = O6, + FP = I6, - F0 = 0, - F1 = 1, - F2 = 2, - F3 = 3, - F4 = 4, - F5 = 5, - F6 = 6, - F7 = 7, - F8 = 8, - F9 = 9, - F10 = 10, - F11 = 11, - F12 = 12, - F13 = 13, - F14 = 14, - F15 = 15, - F16 = 16, - F17 = 17, - F18 = 18, - F19 = 19, - F20 = 20, - F21 = 21, - F22 = 22, - F23 = 23, - F24 = 24, - F25 = 25, - F26 = 26, - F27 = 27, - F28 = 28, - F29 = 29, - F30 = 30, - F31 = 31, + F0 = { 0 }, + F1 = { 1 }, + F2 = { 2 }, + F3 = { 3 }, + F4 = { 4 }, + F5 = { 5 }, + F6 = { 6 }, + F7 = { 7 }, + F8 = { 8 }, + F9 = { 9 }, + F10 = { 10 }, + F11 = { 11 }, + F12 = { 12 }, + F13 = { 13 }, + F14 = { 14 }, + F15 = { 15 }, + F16 = { 16 }, + F17 = { 17 }, + F18 = { 18 }, + F19 = { 19 }, + F20 = { 20 }, + F21 = { 21 }, + F22 = { 22 }, + F23 = { 23 }, + F24 = { 24 }, + F25 = { 25 }, + F26 = { 26 }, + F27 = { 27 }, + F28 = { 28 }, + F29 = { 29 }, + F30 = { 30 }, + F31 = { 31 }, - // helpers - FRAME_PTR = G4, + // helpers + FRAME_PTR = G4, - FirstReg = 0, - LastReg = 29, - deprecated_UnknownReg = 30 // XXX: remove eventually, see bug 538924 - } - Register; + deprecated_UnknownReg = { 30 }; // XXX: remove eventually, see bug 538924 + static const uint32_t FirstRegNum = 0; + static const uint32_t LastRegNum = 29; +} + +#define NJ_USE_UINT32_REGISTER 1 +#include "NativeCommon.h" + +namespace nanojit +{ // We only use 32 registers to be managed. // So we choose some of them. // And other unmanaged registers we can use them directly. diff --git a/js/src/nanojit/NativeX64.cpp b/js/src/nanojit/NativeX64.cpp index b8fa3258e6cc..b999359ad995 100644 --- a/js/src/nanojit/NativeX64.cpp +++ b/js/src/nanojit/NativeX64.cpp @@ -131,14 +131,14 @@ namespace nanojit // encode 2-register rex prefix. dropped if none of its bits are set. static inline uint64_t rexrb(uint64_t op, Register r, Register b) { int shift = 64 - 8*oplen(op); - uint64_t rex = ((op >> shift) & 255) | ((r&8)>>1) | ((b&8)>>3); + uint64_t rex = ((op >> shift) & 255) | ((REGNUM(r)&8)>>1) | ((REGNUM(b)&8)>>3); return rex != 0x40 ? op | rex << shift : op - 1; } // encode 3-register rex prefix. dropped if none of its bits are set. static inline uint64_t rexrxb(uint64_t op, Register r, Register x, Register b) { int shift = 64 - 8*oplen(op); - uint64_t rex = ((op >> shift) & 255) | ((r&8)>>1) | ((x&8)>>2) | ((b&8)>>3); + uint64_t rex = ((op >> shift) & 255) | ((REGNUM(r)&8)>>1) | ((REGNUM(x)&8)>>2) | ((REGNUM(b)&8)>>3); return rex != 0x40 ? op | rex << shift : op - 1; } @@ -146,15 +146,15 @@ namespace nanojit // keep REX if b >= rsp, to allow uniform use of all 16 8bit registers static inline uint64_t rexrb8(uint64_t op, Register r, Register b) { int shift = 64 - 8*oplen(op); - uint64_t rex = ((op >> shift) & 255) | ((r&8)>>1) | ((b&8)>>3); - return ((rex | (b & ~3)) != 0x40) ? (op | (rex << shift)) : op - 1; + uint64_t rex = ((op >> shift) & 255) | ((REGNUM(r)&8)>>1) | ((REGNUM(b)&8)>>3); + return ((rex | (REGNUM(b) & ~3)) != 0x40) ? (op | (rex << shift)) : op - 1; } // encode 2-register rex prefix that follows a manditory prefix (66,F2,F3) // [prefix][rex][opcode] static inline uint64_t rexprb(uint64_t op, Register r, Register b) { int shift = 64 - 8*oplen(op) + 8; - uint64_t rex = ((op >> shift) & 255) | ((r&8)>>1) | ((b&8)>>3); + uint64_t rex = ((op >> shift) & 255) | ((REGNUM(r)&8)>>1) | ((REGNUM(b)&8)>>3); // to drop rex, we replace rex with manditory prefix, and decrement length return rex != 0x40 ? op | rex << shift : ((op & ~(255LL<>(shift-8)&255) << shift) - 1; @@ -162,26 +162,26 @@ namespace nanojit // [rex][opcode][mod-rr] static inline uint64_t mod_rr(uint64_t op, Register r, Register b) { - return op | uint64_t((r&7)<<3 | (b&7))<<56; + return op | uint64_t((REGNUM(r)&7)<<3 | (REGNUM(b)&7))<<56; } // [rex][opcode][modrm=r][sib=xb] static inline uint64_t mod_rxb(uint64_t op, Register r, Register x, Register b) { - return op | /*modrm*/uint64_t((r&7)<<3)<<48 | /*sib*/uint64_t((x&7)<<3|(b&7))<<56; + return op | /*modrm*/uint64_t((REGNUM(r)&7)<<3)<<48 | /*sib*/uint64_t((REGNUM(x)&7)<<3|(REGNUM(b)&7))<<56; } static inline uint64_t mod_disp32(uint64_t op, Register r, Register b, int32_t d) { NanoAssert(IsGpReg(r) && IsGpReg(b)); - NanoAssert((b & 7) != 4); // using RSP or R12 as base requires SIB + NanoAssert((REGNUM(b) & 7) != 4); // using RSP or R12 as base requires SIB uint64_t mod = (((op>>24)&255)>>6); // mod bits in addressing mode: 0,1,2, or 3 if (mod == 2 && isS8(d)) { // op is: 0x[disp32=0][mod=2:r:b][op][rex][len] int len = oplen(op); - op = (op & ~0xff000000LL) | (0x40 | (r&7)<<3 | (b&7))<<24; // replace mod + op = (op & ~0xff000000LL) | (0x40 | (REGNUM(r)&7)<<3 | (REGNUM(b)&7))<<24; // replace mod return op<<24 | int64_t(d)<<56 | (len-3); // shrink disp, add disp8 } else { // op is: 0x[disp32][mod][op][rex][len] - return op | int64_t(d)<<32 | uint64_t((r&7)<<3 | (b&7))<<24; + return op | int64_t(d)<<32 | uint64_t((REGNUM(r)&7)<<3 | (REGNUM(b)&7))<<24; } } @@ -320,8 +320,8 @@ namespace nanojit // op = [rex][opcode][modrm][imm8] void Assembler::emitr_imm8(uint64_t op, Register b, int32_t imm8) { NanoAssert(IsGpReg(b) && isS8(imm8)); - op |= uint64_t(imm8)<<56 | uint64_t(b&7)<<48; // modrm is 2nd to last byte - emit(rexrb(op, (Register)0, b)); + op |= uint64_t(imm8)<<56 | uint64_t(REGNUM(b)&7)<<48; // modrm is 2nd to last byte + emit(rexrb(op, RZero, b)); } void Assembler::emitxm_abs(uint64_t op, Register r, int32_t addr32) @@ -329,8 +329,8 @@ namespace nanojit underrunProtect(4+8); *((int32_t*)(_nIns -= 4)) = addr32; _nvprof("x64-bytes", 4); - op = op | uint64_t((r&7)<<3)<<48; // put rr[0:2] into mod/rm byte - op = rexrb(op, r, (Register)0); // put rr[3] into rex byte + op = op | uint64_t((REGNUM(r)&7)<<3)<<48; // put rr[0:2] into mod/rm byte + op = rexrb(op, r, RZero); // put rr[3] into rex byte emit(op); } @@ -340,7 +340,7 @@ namespace nanojit int32_t d = (int32_t)(addr64 - _nIns); *((int32_t*)(_nIns -= 4)) = d; _nvprof("x64-bytes", 4); - emitrr(op, r, (Register)0); + emitrr(op, r, RZero); } // Succeeds if 'target' is within a signed 8-bit offset from the current @@ -362,10 +362,10 @@ namespace nanojit return isS32(target - _nIns); } -#define RB(r) gpRegNames8[(r)] -#define RS(r) gpRegNames16[(r)] -#define RBhi(r) gpRegNames8hi[(r)] -#define RL(r) gpRegNames32[(r)] +#define RB(r) gpRegNames8[(REGNUM(r))] +#define RS(r) gpRegNames16[(REGNUM(r))] +#define RBhi(r) gpRegNames8hi[(REGNUM(r))] +#define RL(r) gpRegNames32[(REGNUM(r))] #define RQ(r) gpn(r) typedef Register R; @@ -387,12 +387,12 @@ namespace nanojit void Assembler::SARQ(R r) { emitr(X64_sarq, r); asm_output("sarq %s, ecx", RQ(r)); } void Assembler::SHLQ(R r) { emitr(X64_shlq, r); asm_output("shlq %s, ecx", RQ(r)); } - void Assembler::SHRI( R r, I i) { emit8(rexrb(X64_shri | U64(r&7)<<48, (R)0, r), i); asm_output("shrl %s, %d", RL(r), i); } - void Assembler::SARI( R r, I i) { emit8(rexrb(X64_sari | U64(r&7)<<48, (R)0, r), i); asm_output("sarl %s, %d", RL(r), i); } - void Assembler::SHLI( R r, I i) { emit8(rexrb(X64_shli | U64(r&7)<<48, (R)0, r), i); asm_output("shll %s, %d", RL(r), i); } - void Assembler::SHRQI(R r, I i) { emit8(rexrb(X64_shrqi | U64(r&7)<<48, (R)0, r), i); asm_output("shrq %s, %d", RQ(r), i); } - void Assembler::SARQI(R r, I i) { emit8(rexrb(X64_sarqi | U64(r&7)<<48, (R)0, r), i); asm_output("sarq %s, %d", RQ(r), i); } - void Assembler::SHLQI(R r, I i) { emit8(rexrb(X64_shlqi | U64(r&7)<<48, (R)0, r), i); asm_output("shlq %s, %d", RQ(r), i); } + void Assembler::SHRI( R r, I i) { emit8(rexrb(X64_shri | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("shrl %s, %d", RL(r), i); } + void Assembler::SARI( R r, I i) { emit8(rexrb(X64_sari | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("sarl %s, %d", RL(r), i); } + void Assembler::SHLI( R r, I i) { emit8(rexrb(X64_shli | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("shll %s, %d", RL(r), i); } + void Assembler::SHRQI(R r, I i) { emit8(rexrb(X64_shrqi | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("shrq %s, %d", RQ(r), i); } + void Assembler::SARQI(R r, I i) { emit8(rexrb(X64_sarqi | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("sarq %s, %d", RQ(r), i); } + void Assembler::SHLQI(R r, I i) { emit8(rexrb(X64_shlqi | U64(REGNUM(r)&7)<<48, RZero, r), i); asm_output("shlq %s, %d", RQ(r), i); } void Assembler::SETE( R r) { emitr8(X64_sete, r); asm_output("sete %s", RB(r)); } void Assembler::SETL( R r) { emitr8(X64_setl, r); asm_output("setl %s", RB(r)); } @@ -503,7 +503,7 @@ namespace nanojit void Assembler::MOVQI(R r, U64 u64) { emitr_imm64(X64_movqi,r,u64); asm_output("movq %s, %p",RQ(r),(void*)u64); } - void Assembler::LEARIP(R r, I32 d) { emitrm(X64_learip,r,d,(Register)0); asm_output("lea %s, %d(rip)",RQ(r),d); } + void Assembler::LEARIP(R r, I32 d) { emitrm(X64_learip,r,d,RZero); asm_output("lea %s, %d(rip)",RQ(r),d); } void Assembler::LEALRM(R r, I d, R b) { emitrm(X64_lealrm,r,d,b); asm_output("leal %s, %d(%s)",RL(r),d,RL(b)); } void Assembler::LEAQRM(R r, I d, R b) { emitrm(X64_leaqrm,r,d,b); asm_output("leaq %s, %d(%s)",RQ(r),d,RQ(b)); } @@ -530,7 +530,12 @@ namespace nanojit void Assembler::JMP32(S n, NIns* t) { emit_target32(n,X64_jmp, t); asm_output("jmp %p", t); } void Assembler::JMP64(S n, NIns* t) { emit_target64(n,X64_jmpi, t); asm_output("jmp %p", t); } - void Assembler::JMPX(R indexreg, NIns** table) { emitrxb_imm(X64_jmpx, (R)0, indexreg, (Register)5, (int32_t)(uintptr_t)table); asm_output("jmpq [%s*8 + %p]", RQ(indexreg), (void*)table); } + void Assembler::JMPX(R indexreg, NIns** table) + { + Register R5 = { 5 }; + emitrxb_imm(X64_jmpx, RZero, indexreg, R5, (int32_t)(uintptr_t)table); + asm_output("jmpq [%s*8 + %p]", RQ(indexreg), (void*)table); + } void Assembler::JMPXB(R indexreg, R tablereg) { emitxb(X64_jmpxb, indexreg, tablereg); asm_output("jmp [%s*8 + %s]", RQ(indexreg), RQ(tablereg)); } @@ -585,14 +590,14 @@ namespace nanojit void Assembler::CALLRAX() { emit(X64_callrax); asm_output("call (rax)"); } void Assembler::RET() { emit(X64_ret); asm_output("ret"); } - void Assembler::MOVQSPR(I d, R r) { emit(X64_movqspr | U64(d) << 56 | U64((r&7)<<3) << 40 | U64((r&8)>>1) << 24); asm_output("movq %d(rsp), %s", d, RQ(r)); } // insert r into mod/rm and rex bytes + void Assembler::MOVQSPR(I d, R r) { emit(X64_movqspr | U64(d) << 56 | U64((REGNUM(r)&7)<<3) << 40 | U64((REGNUM(r)&8)>>1) << 24); asm_output("movq %d(rsp), %s", d, RQ(r)); } // insert r into mod/rm and rex bytes void Assembler::XORPSA(R r, I32 i32) { emitxm_abs(X64_xorpsa, r, i32); asm_output("xorps %s, (0x%x)",RQ(r), i32); } void Assembler::XORPSM(R r, NIns* a64) { emitxm_rel(X64_xorpsm, r, a64); asm_output("xorps %s, (%p)", RQ(r), a64); } - void Assembler::X86_AND8R(R r) { emit(X86_and8r | U64(r<<3|(r|4))<<56); asm_output("andb %s, %s", RB(r), RBhi(r)); } - void Assembler::X86_SETNP(R r) { emit(X86_setnp | U64(r|4)<<56); asm_output("setnp %s", RBhi(r)); } - void Assembler::X86_SETE(R r) { emit(X86_sete | U64(r)<<56); asm_output("sete %s", RB(r)); } + void Assembler::X86_AND8R(R r) { emit(X86_and8r | U64(REGNUM(r)<<3|(REGNUM(r)|4))<<56); asm_output("andb %s, %s", RB(r), RBhi(r)); } + void Assembler::X86_SETNP(R r) { emit(X86_setnp | U64(REGNUM(r)|4)<<56); asm_output("setnp %s", RBhi(r)); } + void Assembler::X86_SETE(R r) { emit(X86_sete | U64(REGNUM(r))<<56); asm_output("sete %s", RB(r)); } #undef R #undef I @@ -962,14 +967,15 @@ namespace nanojit #ifdef _WIN64 else if (ty == ARGTYPE_D && arg_index < NumArgRegs) { // double goes in XMM reg # based on overall arg_index - asm_regarg(ty, arg, Register(XMM0+arg_index)); + Register rxi = { REGNUM(XMM0) + arg_index }; + asm_regarg(ty, arg, rxi); arg_index++; } #else - else if (ty == ARGTYPE_D && fr < XMM8) { + else if (ty == ARGTYPE_D && REGNUM(fr) < REGNUM(XMM8)) { // double goes in next available XMM register asm_regarg(ty, arg, fr); - fr = Register(fr + 1); + fr = REGINC(fr); } #endif else { @@ -1403,7 +1409,8 @@ namespace nanojit if (op == LIR_eqd) { // result = ZF & !PF, must do logic on flags // r = al|bl|cl|dl, can only use rh without rex prefix - Register r = prepareResultReg(ins, 1<>8); } - inline void Assembler::ALU2m(I32 c, I32 r, I32 d, R b) { + inline void Assembler::ALU2m(I32 c, R r, I32 d, R b) { underrunProtect(9); - MODRMm(r, d, b); + MODRMm(REGNUM(r), d, b); *(--_nIns) = uint8_t(c); *(--_nIns) = uint8_t(c>>8); } - inline void Assembler::ALU2sib(I32 c, Register r, R base, I32 index, I32 scale, I32 disp) { + inline void Assembler::ALU2sib(I32 c, Register r, R base, R index, I32 scale, I32 disp) { underrunProtect(8); - MODRMSIB(r, base, index, scale, disp); + MODRMsib(r, base, index, scale, disp); *(--_nIns) = uint8_t(c); *(--_nIns) = uint8_t(c>>8); } - inline void Assembler::ALUi(I32 c, I32 r, I32 i) { + inline void Assembler::ALUi(I32 c, R r, I32 i) { underrunProtect(6); - NanoAssert(unsigned(r) < 8); + NanoAssert(REGNUM(r) < 8); if (isS8(i)) { *(--_nIns) = uint8_t(i); - MODRM(c>>3, r); + MODRM(c>>3, REGNUM(r)); *(--_nIns) = uint8_t(0x83); } else { IMM32(i); - if ( r == EAX) { + if ( r == rEAX) { *(--_nIns) = uint8_t(c); } else { - MODRM(c >> 3, r); + MODRM(c >> 3, REGNUM(r)); *(--_nIns) = uint8_t(0x81); } } @@ -208,7 +208,7 @@ namespace nanojit inline void Assembler::ALUmi(I32 c, I32 d, Register b, I32 i) { underrunProtect(10); - NanoAssert(unsigned(b) < 8); + NanoAssert(REGNUM(b) < 8); if (isS8(i)) { *(--_nIns) = uint8_t(i); MODRMm(c>>3, d, b); @@ -220,9 +220,9 @@ namespace nanojit } } - inline void Assembler::ALU2(I32 c, I32 d, I32 s) { + inline void Assembler::ALU2(I32 c, R d, R s) { underrunProtect(3); - MODRM(d, s); + MODRM(REGNUM(d), REGNUM(s)); _nIns -= 2; _nIns[0] = uint8_t(c>>8); _nIns[1] = uint8_t(c); @@ -230,41 +230,41 @@ namespace nanojit inline void Assembler::LAHF() { count_alu(); ALU0(0x9F); asm_output("lahf"); } inline void Assembler::SAHF() { count_alu(); ALU0(0x9E); asm_output("sahf"); } - inline void Assembler::OR(R l, R r) { count_alu(); ALU(0x0b, l, r); asm_output("or %s,%s", gpn(l), gpn(r)); } - inline void Assembler::AND(R l, R r) { count_alu(); ALU(0x23, l, r); asm_output("and %s,%s", gpn(l), gpn(r)); } - inline void Assembler::XOR(R l, R r) { count_alu(); ALU(0x33, l, r); asm_output("xor %s,%s", gpn(l), gpn(r)); } - inline void Assembler::ADD(R l, R r) { count_alu(); ALU(0x03, l, r); asm_output("add %s,%s", gpn(l), gpn(r)); } - inline void Assembler::SUB(R l, R r) { count_alu(); ALU(0x2b, l, r); asm_output("sub %s,%s", gpn(l), gpn(r)); } + inline void Assembler::OR(R l, R r) { count_alu(); ALU(0x0b, REGNUM(l), r); asm_output("or %s,%s", gpn(l), gpn(r)); } + inline void Assembler::AND(R l, R r) { count_alu(); ALU(0x23, REGNUM(l), r); asm_output("and %s,%s", gpn(l), gpn(r)); } + inline void Assembler::XOR(R l, R r) { count_alu(); ALU(0x33, REGNUM(l), r); asm_output("xor %s,%s", gpn(l), gpn(r)); } + inline void Assembler::ADD(R l, R r) { count_alu(); ALU(0x03, REGNUM(l), r); asm_output("add %s,%s", gpn(l), gpn(r)); } + inline void Assembler::SUB(R l, R r) { count_alu(); ALU(0x2b, REGNUM(l), r); asm_output("sub %s,%s", gpn(l), gpn(r)); } inline void Assembler::MUL(R l, R r) { count_alu(); ALU2(0x0faf, l, r); asm_output("mul %s,%s", gpn(l), gpn(r)); } - inline void Assembler::DIV(R r) { count_alu(); ALU(0xf7, (Register)7, r); asm_output("idiv edx:eax, %s", gpn(r)); } - inline void Assembler::NOT(R r) { count_alu(); ALU(0xf7, (Register)2, r); asm_output("not %s", gpn(r)); } - inline void Assembler::NEG(R r) { count_alu(); ALU(0xf7, (Register)3, r); asm_output("neg %s", gpn(r)); } + inline void Assembler::DIV(R r) { count_alu(); ALU(0xf7, 7, r); asm_output("idiv edx:eax, %s", gpn(r)); } + inline void Assembler::NOT(R r) { count_alu(); ALU(0xf7, 2, r); asm_output("not %s", gpn(r)); } + inline void Assembler::NEG(R r) { count_alu(); ALU(0xf7, 3, r); asm_output("neg %s", gpn(r)); } inline void Assembler::SHR(R r, R s) { count_alu(); - NanoAssert(s == ECX); (void)s; - ALU(0xd3, (Register)5, r); + NanoAssert(s == rECX); (void)s; + ALU(0xd3, 5, r); asm_output("shr %s,%s", gpn(r), gpn(s)); } inline void Assembler::SAR(R r, R s) { count_alu(); - NanoAssert(s == ECX); (void)s; - ALU(0xd3, (Register)7, r); + NanoAssert(s == rECX); (void)s; + ALU(0xd3, 7, r); asm_output("sar %s,%s", gpn(r), gpn(s)); } inline void Assembler::SHL(R r, R s) { count_alu(); - NanoAssert(s == ECX); (void)s; - ALU(0xd3, (Register)4, r); + NanoAssert(s == rECX); (void)s; + ALU(0xd3, 4, r); asm_output("shl %s,%s", gpn(r), gpn(s)); } inline void Assembler::SHIFT(I32 c, R r, I32 i) { underrunProtect(3); *--_nIns = uint8_t(i); - MODRM((Register)c, r); + MODRM(c, REGNUM(r)); *--_nIns = 0xc1; } @@ -282,23 +282,23 @@ namespace nanojit inline void Assembler::ADDmi(I32 d, R b, I32 i) { count_alust(); ALUmi(0x05, d, b, i); asm_output("add %d(%s), %d", d, gpn(b), i); } - inline void Assembler::TEST(R d, R s) { count_alu(); ALU(0x85, d, s); asm_output("test %s,%s", gpn(d), gpn(s)); } - inline void Assembler::CMP(R l, R r) { count_alu(); ALU(0x3b, l, r); asm_output("cmp %s,%s", gpn(l), gpn(r)); } + inline void Assembler::TEST(R d, R s) { count_alu(); ALU(0x85, REGNUM(d), s); asm_output("test %s,%s", gpn(d), gpn(s)); } + inline void Assembler::CMP(R l, R r) { count_alu(); ALU(0x3b, REGNUM(l), r); asm_output("cmp %s,%s", gpn(l), gpn(r)); } inline void Assembler::CMPi(R r, I32 i) { count_alu(); ALUi(0x3d, r, i); asm_output("cmp %s,%d", gpn(r), i); } - inline void Assembler::LEA(R r, I32 d, R b) { count_alu(); ALUm(0x8d, r, d, b); asm_output("lea %s,%d(%s)", gpn(r), d, gpn(b)); } + inline void Assembler::LEA(R r, I32 d, R b) { count_alu(); ALUm(0x8d, REGNUM(r), d, b); asm_output("lea %s,%d(%s)", gpn(r), d, gpn(b)); } // lea %r, d(%i*4) - // This addressing mode is not supported by the MODRMSIB macro. - inline void Assembler::LEAmi4(R r, I32 d, I32 i) { + // This addressing mode is not supported by the MODRMsib function. + inline void Assembler::LEAmi4(R r, I32 d, R i) { count_alu(); IMM32(int32_t(d)); - *(--_nIns) = uint8_t(2 << 6 | i << 3 | 5); - *(--_nIns) = uint8_t(0 << 6 | r << 3 | 4); + *(--_nIns) = uint8_t(2 << 6 | REGNUM(i) << 3 | 5); + *(--_nIns) = uint8_t(0 << 6 | REGNUM(r) << 3 | 4); *(--_nIns) = 0x8d; asm_output("lea %s, %p(%s*4)", gpn(r), (void*)d, gpn(i)); } - inline void Assembler::CDQ() { SARi(EDX, 31); MR(EDX, EAX); } + inline void Assembler::CDQ() { SARi(rEDX, 31); MR(rEDX, rEAX); } inline void Assembler::INCLi(I32 p) { count_alu(); @@ -337,19 +337,19 @@ namespace nanojit inline void Assembler::LD(R reg, I32 disp, R base) { count_ld(); - ALUm(0x8b, reg, disp, base); + ALUm(0x8b, REGNUM(reg), disp, base); asm_output("mov %s,%d(%s)", gpn(reg), disp, gpn(base)); } inline void Assembler::LDdm(R reg, I32 addr) { count_ld(); - ALUdm(0x8b,reg,addr); + ALUdm(0x8b, reg, addr); asm_output("mov %s,0(%lx)", gpn(reg), (unsigned long)addr); } #define SIBIDX(n) "1248"[n] - inline void Assembler::LDsib(R reg, I32 disp, R base, I32 index, I32 scale) { + inline void Assembler::LDsib(R reg, I32 disp, R base, R index, I32 scale) { count_ld(); ALUsib(0x8b, reg, base, index, scale, disp); asm_output("mov %s,%d(%s+%s*%c)", gpn(reg), disp, gpn(base), gpn(index), SIBIDX(scale)); @@ -374,7 +374,7 @@ namespace nanojit asm_output("movsx16 %s,0(%lx)", gpn(r), (unsigned long)addr); } - inline void Assembler::LD16Ssib(R r, I32 disp, R base, I32 index, I32 scale) { + inline void Assembler::LD16Ssib(R r, I32 disp, R base, R index, I32 scale) { count_ld(); ALU2sib(0x0fbf, r, base, index, scale, disp); asm_output("movsx16 %s,%d(%s+%s*%c)", gpn(r), disp, gpn(base), gpn(index), SIBIDX(scale)); @@ -393,7 +393,7 @@ namespace nanojit asm_output("movzx16 %s,0(%lx)", gpn(r), (unsigned long)addr); } - inline void Assembler::LD16Zsib(R r, I32 disp, R base, I32 index, I32 scale) { + inline void Assembler::LD16Zsib(R r, I32 disp, R base, R index, I32 scale) { count_ld(); ALU2sib(0x0fb7, r, base, index, scale, disp); asm_output("movzx16 %s,%d(%s+%s*%c)", gpn(r), disp, gpn(base), gpn(index), SIBIDX(scale)); @@ -412,7 +412,7 @@ namespace nanojit asm_output("movzx8 %s,0(%lx)", gpn(r), (long unsigned)addr); } - inline void Assembler::LD8Zsib(R r, I32 disp, R base, I32 index, I32 scale) { + inline void Assembler::LD8Zsib(R r, I32 disp, R base, R index, I32 scale) { count_ld(); ALU2sib(0x0fb6, r, base, index, scale, disp); asm_output("movzx8 %s,%d(%s+%s*%c)", gpn(r), disp, gpn(base), gpn(index), SIBIDX(scale)); @@ -431,7 +431,7 @@ namespace nanojit asm_output("movsx8 %s,0(%lx)", gpn(r), (long unsigned)addr); } - inline void Assembler::LD8Ssib(R r, I32 disp, R base, I32 index, I32 scale) { + inline void Assembler::LD8Ssib(R r, I32 disp, R base, R index, I32 scale) { count_ld(); ALU2sib(0x0fbe, r, base, index, scale, disp); asm_output("movsx8 %s,%d(%s+%s*%c)", gpn(r), disp, gpn(base), gpn(index), SIBIDX(scale)); @@ -441,28 +441,28 @@ namespace nanojit count_ld(); underrunProtect(5); IMM32(i); - NanoAssert(unsigned(r) < 8); - *(--_nIns) = uint8_t(0xb8 | r); + NanoAssert(REGNUM(r) < 8); + *(--_nIns) = uint8_t(0xb8 | REGNUM(r)); asm_output("mov %s,%d", gpn(r), i); } // Quirk of x86-32: reg must be a/b/c/d for byte stores here. inline void Assembler::ST8(R base, I32 disp, R reg) { count_st(); - NanoAssert(unsigned(reg) < 4); - ALUm(0x88, reg, disp, base); + NanoAssert(REGNUM(reg) < 4); + ALUm(0x88, REGNUM(reg), disp, base); asm_output("mov8 %d(%s),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), gpn(reg)); } inline void Assembler::ST16(R base, I32 disp, R reg) { count_st(); - ALUm16(0x89, reg, disp, base); + ALUm16(0x89, REGNUM(reg), disp, base); asm_output("mov16 %d(%s),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), gpn(reg)); } inline void Assembler::ST(R base, I32 disp, R reg) { count_st(); - ALUm(0x89, reg, disp, base); + ALUm(0x89, REGNUM(reg), disp, base); asm_output("mov %d(%s),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), gpn(reg)); } @@ -522,8 +522,8 @@ namespace nanojit inline void Assembler::PUSHr(R r) { count_push(); underrunProtect(1); - NanoAssert(unsigned(r) < 8); - *(--_nIns) = uint8_t(0x50 | r); + NanoAssert(REGNUM(r) < 8); + *(--_nIns) = uint8_t(0x50 | REGNUM(r)); asm_output("push %s", gpn(r)); } @@ -536,8 +536,8 @@ namespace nanojit inline void Assembler::POPr(R r) { count_pop(); underrunProtect(1); - NanoAssert(unsigned(r) < 8); - *(--_nIns) = uint8_t(0x58 | r); + NanoAssert(REGNUM(r) < 8); + *(--_nIns) = uint8_t(0x58 | REGNUM(r)); asm_output("pop %s", gpn(r)); } @@ -581,9 +581,9 @@ namespace nanojit underrunProtect(7); IMM32(int32_t(addr)); _nIns -= 3; - _nIns[0] = uint8_t(0xff); /* jmp */ - _nIns[1] = uint8_t(0 << 6 | 4 << 3 | 4); /* modrm: base=sib + disp32 */ - _nIns[2] = uint8_t(ss << 6 | x << 3 | 5); /* sib: x<> 16) & 0xff); _nIns[1] = uint8_t((c >> 8) & 0xff); _nIns[2] = uint8_t(c & 0xff); } - inline void Assembler::SSEm(I32 c, I32 r, I32 d, R b) { + inline void Assembler::SSEm(I32 c, R r, I32 d, R b) { underrunProtect(9); - MODRMm(r, d, b); + MODRMm(REGNUM(r)&7, d, b); _nIns -= 3; _nIns[0] = uint8_t((c >> 16) & 0xff); _nIns[1] = uint8_t((c >> 8) & 0xff); @@ -638,36 +638,36 @@ namespace nanojit count_ldq(); underrunProtect(8); IMM32(int32_t(addr)); - *(--_nIns) = uint8_t((r & 7) << 3 | 5); + *(--_nIns) = uint8_t((REGNUM(r) & 7) << 3 | 5); *(--_nIns) = 0x10; *(--_nIns) = 0x0f; *(--_nIns) = 0xf2; asm_output("movsd %s,(%p) // =%f", gpn(r), (void*)addr, *addr); } - inline void Assembler::SSE_LDSD(R r, I32 d, R b) { count_ldq(); SSEm(0xf20f10, r&7, d, b); asm_output("movsd %s,%d(%s)", gpn(r), d, gpn(b)); } - inline void Assembler::SSE_LDQ( R r, I32 d, R b) { count_ldq(); SSEm(0xf30f7e, r&7, d, b); asm_output("movq %s,%d(%s)", gpn(r), d, gpn(b)); } - inline void Assembler::SSE_LDSS(R r, I32 d, R b) { count_ld(); SSEm(0xf30f10, r&7, d, b); asm_output("movss %s,%d(%s)", gpn(r), d, gpn(b)); } - inline void Assembler::SSE_STSD(I32 d, R b, R r) { count_stq(); SSEm(0xf20f11, r&7, d, b); asm_output("movsd %d(%s),%s", d, gpn(b), gpn(r)); } - inline void Assembler::SSE_STQ( I32 d, R b, R r) { count_stq(); SSEm(0x660fd6, r&7, d, b); asm_output("movq %d(%s),%s", d, gpn(b), gpn(r)); } - inline void Assembler::SSE_STSS(I32 d, R b, R r) { count_st(); SSEm(0xf30f11, r&7, d, b); asm_output("movss %d(%s),%s", d, gpn(b), gpn(r)); } + inline void Assembler::SSE_LDSD(R r, I32 d, R b) { count_ldq(); SSEm(0xf20f10, r, d, b); asm_output("movsd %s,%d(%s)", gpn(r), d, gpn(b)); } + inline void Assembler::SSE_LDQ( R r, I32 d, R b) { count_ldq(); SSEm(0xf30f7e, r, d, b); asm_output("movq %s,%d(%s)", gpn(r), d, gpn(b)); } + inline void Assembler::SSE_LDSS(R r, I32 d, R b) { count_ld(); SSEm(0xf30f10, r, d, b); asm_output("movss %s,%d(%s)", gpn(r), d, gpn(b)); } + inline void Assembler::SSE_STSD(I32 d, R b, R r) { count_stq(); SSEm(0xf20f11, r, d, b); asm_output("movsd %d(%s),%s", d, gpn(b), gpn(r)); } + inline void Assembler::SSE_STQ( I32 d, R b, R r) { count_stq(); SSEm(0x660fd6, r, d, b); asm_output("movq %d(%s),%s", d, gpn(b), gpn(r)); } + inline void Assembler::SSE_STSS(I32 d, R b, R r) { count_st(); SSEm(0xf30f11, r, d, b); asm_output("movss %d(%s),%s", d, gpn(b), gpn(r)); } - inline void Assembler::SSE_CVTSI2SD(R xr, R gr) { count_fpu(); SSE(0xf20f2a, xr&7, gr&7); asm_output("cvtsi2sd %s,%s", gpn(xr), gpn(gr)); } - inline void Assembler::SSE_CVTSD2SI(R gr, R xr) { count_fpu(); SSE(0xf20f2d, gr&7, xr&7); asm_output("cvtsd2si %s,%s", gpn(gr), gpn(xr)); } - inline void Assembler::SSE_CVTSD2SS(R xr, R gr) { count_fpu(); SSE(0xf20f5a, xr&7, gr&7); asm_output("cvtsd2ss %s,%s", gpn(xr), gpn(gr)); } - inline void Assembler::SSE_CVTSS2SD(R xr, R gr) { count_fpu(); SSE(0xf30f5a, xr&7, gr&7); asm_output("cvtss2sd %s,%s", gpn(xr), gpn(gr)); } - inline void Assembler::SSE_CVTDQ2PD(R d, R r) { count_fpu(); SSE(0xf30fe6, d&7, r&7); asm_output("cvtdq2pd %s,%s", gpn(d), gpn(r)); } + inline void Assembler::SSE_CVTSI2SD(R xr, R gr) { count_fpu(); SSE(0xf20f2a, xr, gr); asm_output("cvtsi2sd %s,%s", gpn(xr), gpn(gr)); } + inline void Assembler::SSE_CVTSD2SI(R gr, R xr) { count_fpu(); SSE(0xf20f2d, gr, xr); asm_output("cvtsd2si %s,%s", gpn(gr), gpn(xr)); } + inline void Assembler::SSE_CVTSD2SS(R xr, R gr) { count_fpu(); SSE(0xf20f5a, xr, gr); asm_output("cvtsd2ss %s,%s", gpn(xr), gpn(gr)); } + inline void Assembler::SSE_CVTSS2SD(R xr, R gr) { count_fpu(); SSE(0xf30f5a, xr, gr); asm_output("cvtss2sd %s,%s", gpn(xr), gpn(gr)); } + inline void Assembler::SSE_CVTDQ2PD(R d, R r) { count_fpu(); SSE(0xf30fe6, d, r); asm_output("cvtdq2pd %s,%s", gpn(d), gpn(r)); } // Move and zero-extend GP reg to XMM reg. inline void Assembler::SSE_MOVD(R d, R s) { count_mov(); if (IsXmmReg(s)) { NanoAssert(IsGpReg(d)); - SSE(0x660f7e, s&7, d&7); + SSE(0x660f7e, s, d); } else { NanoAssert(IsGpReg(s)); NanoAssert(IsXmmReg(d)); - SSE(0x660f6e, d&7, s&7); + SSE(0x660f6e, d, s); } asm_output("movd %s,%s", gpn(d), gpn(s)); } @@ -675,14 +675,14 @@ namespace nanojit inline void Assembler::SSE_MOVSD(R rd, R rs) { count_mov(); NanoAssert(IsXmmReg(rd) && IsXmmReg(rs)); - SSE(0xf20f10, rd&7, rs&7); + SSE(0xf20f10, rd, rs); asm_output("movsd %s,%s", gpn(rd), gpn(rs)); } inline void Assembler::SSE_ADDSD(R rd, R rs) { count_fpu(); NanoAssert(IsXmmReg(rd) && IsXmmReg(rs)); - SSE(0xf20f58, rd&7, rs&7); + SSE(0xf20f58, rd, rs); asm_output("addsd %s,%s", gpn(rd), gpn(rs)); } @@ -692,7 +692,7 @@ namespace nanojit NanoAssert(IsXmmReg(r)); const double* daddr = addr; IMM32(int32_t(daddr)); - *(--_nIns) = uint8_t((r & 7) << 3 | 5); + *(--_nIns) = uint8_t((REGNUM(r) & 7) << 3 | 5); *(--_nIns) = 0x58; *(--_nIns) = 0x0f; *(--_nIns) = 0xf2; @@ -702,28 +702,28 @@ namespace nanojit inline void Assembler::SSE_SUBSD(R rd, R rs) { count_fpu(); NanoAssert(IsXmmReg(rd) && IsXmmReg(rs)); - SSE(0xf20f5c, rd&7, rs&7); + SSE(0xf20f5c, rd, rs); asm_output("subsd %s,%s", gpn(rd), gpn(rs)); } inline void Assembler::SSE_MULSD(R rd, R rs) { count_fpu(); NanoAssert(IsXmmReg(rd) && IsXmmReg(rs)); - SSE(0xf20f59, rd&7, rs&7); + SSE(0xf20f59, rd, rs); asm_output("mulsd %s,%s", gpn(rd), gpn(rs)); } inline void Assembler::SSE_DIVSD(R rd, R rs) { count_fpu(); NanoAssert(IsXmmReg(rd) && IsXmmReg(rs)); - SSE(0xf20f5e, rd&7, rs&7); + SSE(0xf20f5e, rd, rs); asm_output("divsd %s,%s", gpn(rd), gpn(rs)); } inline void Assembler::SSE_UCOMISD(R rl, R rr) { count_fpu(); NanoAssert(IsXmmReg(rl) && IsXmmReg(rr)); - SSE(0x660f2e, rl&7, rr&7); + SSE(0x660f2e, rl, rr); asm_output("ucomisd %s,%s", gpn(rl), gpn(rr)); } @@ -731,7 +731,7 @@ namespace nanojit count_fpuld(); underrunProtect(8); IMM32(int32_t(maskaddr)); - *(--_nIns) = uint8_t((r & 7) << 3 | 5); + *(--_nIns) = uint8_t((REGNUM(r) & 7) << 3 | 5); *(--_nIns) = 0x57; *(--_nIns) = 0x0f; *(--_nIns) = 0x66; @@ -740,7 +740,7 @@ namespace nanojit inline void Assembler::SSE_XORPDr(R rd, R rs) { count_fpu(); - SSE(0x660f57, rd&7, rs&7); + SSE(0x660f57, rd, rs); asm_output("xorpd %s,%s", gpn(rd), gpn(rs)); } @@ -910,7 +910,7 @@ namespace nanojit // If the guard is LIR_xtbl, use a jump table with epilog in every entry if (guard->isop(LIR_xtbl)) { lr = guard->record(); - Register r = EDX; + Register r = rEDX; SwitchInfo* si = guard->record()->exit->switchInfo; if (!_epilogue) _epilogue = genEpilogue(); @@ -938,11 +938,11 @@ namespace nanojit } ) - // Restore ESP from EBP, undoing SUBi(SP,amt) in the prologue + // Restore rESP from rEBP, undoing SUBi(SP,amt) in the prologue MR(SP,FP); // return value is GuardRecord* - asm_immi(EAX, int(lr), /*canClobberCCs*/true); + asm_immi(rEAX, int(lr), /*canClobberCCs*/true); } NIns *Assembler::genEpilogue() @@ -984,7 +984,7 @@ namespace nanojit #if _MSC_VER // msc only provides 4-byte alignment, anything more than 4 on windows - // x86-32 requires dynamic ESP alignment in prolog/epilog and static + // x86-32 requires dynamic rESP alignment in prolog/epilog and static // esp-alignment here. uint32_t align = 4;//NJ_ALIGN_STACK; #else @@ -1017,11 +1017,11 @@ namespace nanojit CALL(call); } else { - // Indirect call. x86 Calling conventions don't use EAX as an - // argument, and do use EAX as a return value. We need a register - // for the address to call, so we use EAX since it will always be + // Indirect call. x86 Calling conventions don't use rEAX as an + // argument, and do use rEAX as a return value. We need a register + // for the address to call, so we use rEAX since it will always be // available. - CALLr(call, EAX); + CALLr(call, rEAX); } // Call this now so that the arg setup can involve 'rr'. @@ -1039,7 +1039,7 @@ namespace nanojit if (indirect) { argc--; - asm_arg(ARGTYPE_P, ins->arg(argc), EAX, stkd); + asm_arg(ARGTYPE_P, ins->arg(argc), rEAX, stkd); if (!_config.i386_fixed_esp) stkd = 0; } @@ -1236,9 +1236,10 @@ namespace nanojit else { // Quirk of x86-32: reg must be a/b/c/d for single-byte stores. - const RegisterMask SrcRegs = (op == LIR_sti2c) ? - (1<isImmI()) { @@ -1483,7 +1484,7 @@ namespace nanojit void Assembler::asm_switch(LIns* ins, NIns* exit) { LIns* diff = ins->oprnd1(); - findSpecificRegFor(diff, EDX); + findSpecificRegFor(diff, rEDX); JMP(exit); } @@ -1691,9 +1692,9 @@ namespace nanojit // Nb: if the div feeds into a mod it will be handled by // asm_div_mod() rather than here. isConstRhs = false; - rb = findRegFor(rhs, (GpRegs & ~(rmask(EAX)|rmask(EDX)))); - allow = rmask(EAX); - evictIfActive(EDX); + rb = findRegFor(rhs, (GpRegs & ~(rmask(rEAX)|rmask(rEDX)))); + allow = rmask(rEAX); + evictIfActive(rEDX); break; case LIR_muli: case LIR_muljovi: @@ -1709,7 +1710,7 @@ namespace nanojit case LIR_rshui: isConstRhs = rhs->isImmI(); if (!isConstRhs) { - rb = findSpecificRegFor(rhs, ECX); + rb = findSpecificRegFor(rhs, rECX); allow &= ~rmask(rb); } break; @@ -1750,7 +1751,7 @@ namespace nanojit case LIR_rshui: SHR(rr, rb); break; case LIR_divi: DIV(rb); - CDQ(); // sign-extend EAX into EDX:EAX + CDQ(); // sign-extend rEAX into rEDX:rEAX break; default: NanoAssert(0); break; } @@ -1800,22 +1801,22 @@ namespace nanojit LIns* divL = div->oprnd1(); LIns* divR = div->oprnd2(); - prepareResultReg(mod, rmask(EDX)); - prepareResultReg(div, rmask(EAX)); + prepareResultReg(mod, rmask(rEDX)); + prepareResultReg(div, rmask(rEAX)); - Register rDivR = findRegFor(divR, (GpRegs & ~(rmask(EAX)|rmask(EDX)))); - Register rDivL = divL->isInReg() ? divL->getReg() : EAX; + Register rDivR = findRegFor(divR, (GpRegs & ~(rmask(rEAX)|rmask(rEDX)))); + Register rDivL = divL->isInReg() ? divL->getReg() : rEAX; DIV(rDivR); - CDQ(); // sign-extend EAX into EDX:EAX - if (EAX != rDivL) - MR(EAX, rDivL); + CDQ(); // sign-extend rEAX into rEDX:rEAX + if (rEAX != rDivL) + MR(rEAX, rDivL); freeResourcesOf(mod); freeResourcesOf(div); if (!divL->isInReg()) { - NanoAssert(rDivL == EAX); - findSpecificRegForUnallocated(divL, EAX); + NanoAssert(rDivL == rEAX); + findSpecificRegForUnallocated(divL, rEAX); } } @@ -2118,7 +2119,7 @@ namespace nanojit prepareResultReg(ins, rmask(argRegs[arg])); // No code to generate. } else { - // Incoming arg is on stack, and EBP points nearby (see genPrologue()). + // Incoming arg is on stack, and rEBP points nearby (see genPrologue()). Register r = prepareResultReg(ins, GpRegs); int d = (arg - abi_regcount) * sizeof(intptr_t) + 8; LD(r, d, FP); @@ -2372,7 +2373,7 @@ namespace nanojit evict(ins); } if (!_config.i386_fixed_esp) - SUBi(ESP, 8); + SUBi(rESP, 8); stkd += sizeof(double); } @@ -2531,7 +2532,7 @@ namespace nanojit } } else { - // Use space just below ESP and use PUSH to avoid writing + // Use space just below rESP and use PUSH to avoid writing // past the end of the stack, see bug 590553. Register ra = findRegFor(lhs, GpRegs); NanoAssert(rr == FST0); @@ -2665,7 +2666,7 @@ namespace nanojit // GREATER_THAN 000 0000_0000 011 SETNP/JNP fails // LESS_THAN 001 0000_0000 011 SETNP/JNP fails - evictIfActive(EAX); + evictIfActive(rEAX); Register ra, rb; findRegFor2(XmmRegs, lhs, ra, XmmRegs, rhs, rb); @@ -2754,14 +2755,14 @@ namespace nanojit default: NanoAssert(0); break; } - evictIfActive(EAX); + evictIfActive(rEAX); bool pop = !lhs->isInReg(); findSpecificRegFor(lhs, FST0); if (lhs == rhs) { // NaN test. TEST_AH(mask); - FNSTSW_AX(); // requires EAX to be free + FNSTSW_AX(); // requires rEAX to be free if (pop) FCOMPP(); else @@ -2769,7 +2770,7 @@ namespace nanojit FLDr(FST0); // DUP } else { TEST_AH(mask); - FNSTSW_AX(); // requires EAX to be free + FNSTSW_AX(); // requires rEAX to be free if (rhs->isImmD()) { const uint64_t* p = findImmDFromPool(rhs->immDasQ()); @@ -2819,7 +2820,7 @@ namespace nanojit { genEpilogue(); - // Restore ESP from EBP, undoing SUBi(SP,amt) in the prologue + // Restore rESP from rEBP, undoing SUBi(SP,amt) in the prologue MR(SP,FP); releaseRegisters(); diff --git a/js/src/nanojit/Nativei386.h b/js/src/nanojit/Nativei386.h index 9c0cdabd6af9..ecba115d97b4 100644 --- a/js/src/nanojit/Nativei386.h +++ b/js/src/nanojit/Nativei386.h @@ -41,6 +41,8 @@ #ifndef __nanojit_Nativei386__ #define __nanojit_Nativei386__ +#include "NativeCommon.h" + #ifdef PERFM #define DOPROF #include "../vprof/vprof.h" @@ -115,60 +117,63 @@ namespace nanojit // Bytes of icache to flush after patch const size_t LARGEST_BRANCH_PATCH = 16 * sizeof(NIns); - // These are used as register numbers in various parts of the code - typedef enum - { - // general purpose 32bit regs - EAX = 0, // return value, scratch - ECX = 1, // this/arg0, scratch - EDX = 2, // arg1, return-msw, scratch - EBX = 3, - ESP = 4, // stack pointer - EBP = 5, // frame pointer - ESI = 6, - EDI = 7, + static const Register + // General purpose 32 bit registers. The names are rEAX, rEBX, etc, + // because EAX, EBX, et al clash with on Solaris (sigh). + // See bug 570726 for details. + rEAX = { 0 }, // return value, scratch + rECX = { 1 }, // this/arg0, scratch + rEDX = { 2 }, // arg1, return-msw, scratch + rEBX = { 3 }, + rESP = { 4 }, // stack pointer + rEBP = { 5 }, // frame pointer + rESI = { 6 }, + rEDI = { 7 }, - SP = ESP, // alias SP to ESP for convenience - FP = EBP, // alias FP to EBP for convenience + SP = rESP, // alias SP to ESP for convenience + FP = rEBP, // alias FP to EBP for convenience // SSE regs come before X87 so we prefer them - XMM0 = 8, - XMM1 = 9, - XMM2 = 10, - XMM3 = 11, - XMM4 = 12, - XMM5 = 13, - XMM6 = 14, - XMM7 = 15, + XMM0 = { 8 }, + XMM1 = { 9 }, + XMM2 = { 10 }, + XMM3 = { 11 }, + XMM4 = { 12 }, + XMM5 = { 13 }, + XMM6 = { 14 }, + XMM7 = { 15 }, // X87 regs - FST0 = 16, + FST0 = { 16 }, - FirstReg = 0, - LastReg = 16, - deprecated_UnknownReg = 17, // XXX: remove eventually, see bug 538924 - UnspecifiedReg = 17 - } - Register; + deprecated_UnknownReg = { 17 }, // XXX: remove eventually, see bug 538924 + UnspecifiedReg = { 17 }; + + static const uint32_t FirstRegNum = 0; + static const uint32_t LastRegNum = 16; typedef int RegisterMask; static const int NumSavedRegs = 3; - static const RegisterMask SavedRegs = 1<> 8) & 0xff); \ }; \ void FPUm(int32_t o, int32_t d, Register b); \ diff --git a/js/src/nanojit/RegAlloc.h b/js/src/nanojit/RegAlloc.h index 801dab8be1c3..ad3e9da749ec 100644 --- a/js/src/nanojit/RegAlloc.h +++ b/js/src/nanojit/RegAlloc.h @@ -80,44 +80,44 @@ namespace nanojit // Count++; NanoAssert(v); NanoAssert(r != deprecated_UnknownReg); - NanoAssert(active[r] == NULL); - active[r] = v; + NanoAssert(active[REGNUM(r)] == NULL); + active[REGNUM(r)] = v; useActive(r); } void useActive(Register r) { NanoAssert(r != deprecated_UnknownReg); - NanoAssert(active[r] != NULL); - usepri[r] = priority++; + NanoAssert(active[REGNUM(r)] != NULL); + usepri[REGNUM(r)] = priority++; } void removeActive(Register r) { //registerReleaseCount++; NanoAssert(r != deprecated_UnknownReg); - NanoAssert(active[r] != NULL); + NanoAssert(active[REGNUM(r)] != NULL); // remove the given register from the active list - active[r] = NULL; + active[REGNUM(r)] = NULL; } void retire(Register r) { NanoAssert(r != deprecated_UnknownReg); - NanoAssert(active[r] != NULL); - active[r] = NULL; + NanoAssert(active[REGNUM(r)] != NULL); + active[REGNUM(r)] = NULL; free |= rmask(r); } int32_t getPriority(Register r) { - NanoAssert(r != deprecated_UnknownReg && active[r]); - return usepri[r]; + NanoAssert(r != deprecated_UnknownReg && active[REGNUM(r)]); + return usepri[REGNUM(r)]; } LIns* getActive(Register r) const { NanoAssert(r != deprecated_UnknownReg); - return active[r]; + return active[REGNUM(r)]; } // Return a mask containing the active registers. For each register @@ -173,8 +173,8 @@ namespace nanojit // * If an LIns's reservation names 'deprecated_UnknownReg' then LIns // should not be in 'active'. // - LIns* active[LastReg + 1]; // active[r] = LIns that defines r - int32_t usepri[LastReg + 1]; // used priority. lower = more likely to spill. + LIns* active[LastRegNum + 1]; // active[REGNUM(r)] = LIns that defines r + int32_t usepri[LastRegNum + 1]; // used priority. lower = more likely to spill. RegisterMask free; // Registers currently free. RegisterMask managed; // Registers under management (invariant). int32_t priority; @@ -186,20 +186,16 @@ namespace nanojit inline Register lsReg(RegisterMask mask) { // This is faster than it looks; we rely on the C++ optimizer // to strip the dead branch and inline just one alternative. - if (sizeof(RegisterMask) == 4) - return (Register) lsbSet32(mask); - else - return (Register) lsbSet64(mask); + Register r = { (sizeof(RegisterMask) == 4) ? lsbSet32(mask) : lsbSet64(mask) }; + return r; } // Return the highest numbered Register in mask. inline Register msReg(RegisterMask mask) { // This is faster than it looks; we rely on the C++ optimizer // to strip the dead branch and inline just one alternative. - if (sizeof(RegisterMask) == 4) - return (Register) msbSet32(mask); - else - return (Register) msbSet64(mask); + Register r = { (sizeof(RegisterMask) == 4) ? msbSet32(mask) : msbSet64(mask) }; + return r; } // Clear bit r in mask, then return lsReg(mask). From e729dced8bf706a5a36d8798c09714d40f6c78ce Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 6 Oct 2010 15:51:01 -0700 Subject: [PATCH 074/284] Bustage fix: add new file that was accidentally omitted for bug 599251. --HG-- extra : convert_revision : 661718b83d398aa4c91f522f7d08ef5cea8e20a9 --- js/src/nanojit/NativeCommon.h | 100 ++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 js/src/nanojit/NativeCommon.h diff --git a/js/src/nanojit/NativeCommon.h b/js/src/nanojit/NativeCommon.h new file mode 100644 index 000000000000..6b452e203604 --- /dev/null +++ b/js/src/nanojit/NativeCommon.h @@ -0,0 +1,100 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** 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 [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nanojit_NativeCommon__ +#define __nanojit_NativeCommon__ + +namespace nanojit +{ + // In debug builds, Register is defined as a non-integer type to avoid + // possible mix-ups with RegisterMask and integer offsets. In non-debug + // builds, it's defined as an integer just in case some compilers fail to + // optimize single-element structs in the obvious way. + // + // Note that in either case, a Register can be initialized like so: + // + // Register r = { 0 }; + // + // In the debug case it's a struct initializer, in the non-debug case it's + // a scalar initializer. + // + // XXX: The exception to all the above is that if NJ_USE_UINT32_REGISTER + // is defined, the back-end is responsible for defining its own Register + // type, which will probably be an enum. This is just to give all the + // back-ends a chance to transition smoothly. +#if defined(NJ_USE_UINT32_REGISTER) + #define REGNUM(r) (r) + +#elif defined(DEBUG) + struct Register { + uint32_t n; // the register number + }; + + static inline uint32_t REGNUM(Register r) { + return r.n; + } + + static inline Register REGINC(Register r) { + r.n++; + return r; + } + + static inline bool operator==(Register r1, Register r2) + { + return r1.n == r2.n; + } + + static inline bool operator!=(Register r1, Register r2) + { + return r1.n != r2.n; + } +#else + typedef uint32_t Register; + + static inline uint32_t REGNUM(Register r) { + return r; + } + + static inline Register REGINC(Register r) { + return r+1; + } +#endif +} // namespace nanojit + +#endif // __nanojit_NativeCommon__ From 234cb58214caf20e4cee0a2d066f01524f23b6b9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 6 Oct 2010 17:28:51 -0700 Subject: [PATCH 075/284] Update nanojit-import-rev stamp. --- js/src/nanojit-import-rev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/nanojit-import-rev b/js/src/nanojit-import-rev index a7ab32eb0ed5..be5dd344e42d 100644 --- a/js/src/nanojit-import-rev +++ b/js/src/nanojit-import-rev @@ -1 +1 @@ -12776aa248b916be646dd7b9c760be1b3fa7ba8a +661718b83d398aa4c91f522f7d08ef5cea8e20a9 From 11275562836d1a0256c86167a9b1feb62f3a87b0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 6 Oct 2010 17:39:28 -0700 Subject: [PATCH 076/284] Bug 599251 - nanojit: make Register a non-numeric type (TM-specific part). r=edwsmith. --- js/src/Makefile.in | 1 + js/src/jstracer.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 291135c22757..2c7aa86e8838 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -254,6 +254,7 @@ INSTALLED_HEADERS += \ avmplus.h \ Fragmento.h \ Native.h \ + NativeCommon.h \ Native$(NANOJIT_ARCH).h \ njconfig.h \ RegAlloc.h \ diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 68c746d65031..561636368100 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2432,7 +2432,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag lir->insParam(i, 1); #ifdef DEBUG for (int i = 0; i < NumSavedRegs; ++i) - addName(lirbuf->savedRegs[i], regNames[Assembler::savedRegs[i]]); + addName(lirbuf->savedRegs[i], regNames[REGNUM(Assembler::savedRegs[i])]); #endif lirbuf->state = addName(lir->insParam(0, 0), "state"); From 0a55eb0a2d6be15b779d4aa41a60146bcbf89550 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Wed, 6 Oct 2010 19:30:02 -0700 Subject: [PATCH 077/284] GetPropertyByName doesn't deep bail (602415, r=dvander). --- js/src/jstracer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 561636368100..017946dbf3d0 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -12209,7 +12209,7 @@ GetPropertyByName(JSContext* cx, JSObject* obj, JSString** namep, Value* vp, PIC bool result = op(cx, obj, id, vp); if (!result) SetBuiltinError(cx); - return result; + return cx->traceState->builtinStatus == 0; } /* Try to hit in the cache. */ From f231551624d8095734922290a0794da8ecb747ba Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Wed, 6 Oct 2010 19:50:46 -0700 Subject: [PATCH 078/284] Fixing bustage. --- js/src/jstracer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 017946dbf3d0..11de0b96ea68 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -12209,7 +12209,7 @@ GetPropertyByName(JSContext* cx, JSObject* obj, JSString** namep, Value* vp, PIC bool result = op(cx, obj, id, vp); if (!result) SetBuiltinError(cx); - return cx->traceState->builtinStatus == 0; + return cx->tracerState->builtinStatus == 0; } /* Try to hit in the cache. */ From 25d5ead26584316c3ad41ab0d94d0b82d483925b Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 7 Oct 2010 09:18:53 -0400 Subject: [PATCH 079/284] Bug 599009. Don't use setProperty for JSOP_DEFFUN unless there's already a property with that name around, to work around the fact that setProperty will define with class-default getter and setter while we always want to define with stub getter and setter if we can. r=igor --- js/src/jsinterp.cpp | 23 ++++++++++++----------- js/src/methodjit/StubCalls.cpp | 21 +++++++++++---------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index d6cd973398c5..af08c43916ee 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -5367,24 +5367,25 @@ BEGIN_CASE(JSOP_DEFFUN) /* * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for function - * declarations JSObject::setProperty, not JSObject::defineProperty, to - * preserve the JSOP_PERMANENT attribute of existing properties and make - * sure that such properties cannot be deleted. + * declarations JSObject::setProperty, not JSObject::defineProperty if the + * property already exists, to preserve the JSOP_PERMANENT attribute of + * existing properties and make sure that such properties cannot be deleted. * * We also use JSObject::setProperty for the existing properties of Call * objects with matching attributes to preserve the native getters and * setters that store the value of the property in the interpreter frame, * see bug 467495. */ - uint32 old; - bool doSet = (attrs == JSPROP_ENUMERATE); - JS_ASSERT_IF(doSet, regs.fp->isEvalFrame()); + bool doSet = false; if (prop) { - if (parent == pobj && - parent->isCall() && - (old = ((Shape *) prop)->attributes(), - !(old & (JSPROP_GETTER|JSPROP_SETTER)) && - (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) { + JS_ASSERT((attrs == JSPROP_ENUMERATE) == regs.fp->isEvalFrame()); + uint32 old; + if (attrs == JSPROP_ENUMERATE || + (parent == pobj && + parent->isCall() && + (old = ((Shape *) prop)->attributes(), + !(old & (JSPROP_GETTER|JSPROP_SETTER)) && + (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs))) { /* * js_CheckRedeclaration must reject attempts to add a getter or * setter to an existing property without a getter or setter. diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 89d68b81f261..e82e588c626f 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -931,23 +931,24 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) /* * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for function - * declarations JSObject::setProperty, not JSObject::defineProperty, to - * preserve the JSOP_PERMANENT attribute of existing properties and make - * sure that such properties cannot be deleted. + * declarations JSObject::setProperty, not JSObject::defineProperty if the + * property already exists, to preserve the JSOP_PERMANENT attribute of + * existing properties and make sure that such properties cannot be deleted. * * We also use JSObject::setProperty for the existing properties of Call * objects with matching attributes to preserve the native getters and * setters that store the value of the property in the interpreter frame, * see bug 467495. */ - doSet = (attrs == JSPROP_ENUMERATE); - JS_ASSERT_IF(doSet, fp->isEvalFrame()); + doSet = false; if (prop) { - if (parent == pobj && - parent->isCall() && - (old = ((Shape *) prop)->attributes(), - !(old & (JSPROP_GETTER|JSPROP_SETTER)) && - (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) { + JS_ASSERT((attrs == JSPROP_PERMANENT) == fp->isEvalFrame()); + if (attrs == JSPROP_ENUMERATE || + (parent == pobj && + parent->isCall() && + (old = ((Shape *) prop)->attributes(), + !(old & (JSPROP_GETTER|JSPROP_SETTER)) && + (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs))) { /* * js_CheckRedeclaration must reject attempts to add a getter or * setter to an existing property without a getter or setter. From a7aff5111bb32403a0a6d48308cc40a91f017a43 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 7 Oct 2010 11:03:41 -0400 Subject: [PATCH 080/284] Bug 599009 followup. Fix typo in assert. --- js/src/methodjit/StubCalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index e82e588c626f..0f4e399b7fde 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -942,7 +942,7 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) */ doSet = false; if (prop) { - JS_ASSERT((attrs == JSPROP_PERMANENT) == fp->isEvalFrame()); + JS_ASSERT((attrs == JSPROP_ENUMERATE) == fp->isEvalFrame()); if (attrs == JSPROP_ENUMERATE || (parent == pobj && parent->isCall() && From e2941e77c7319b425af329996593ce17135cd370 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 7 Oct 2010 13:58:51 -0400 Subject: [PATCH 081/284] Another bug 599009 followup. Don't assert things about uninitialized quantities. --- js/src/jsinterp.cpp | 2 +- js/src/methodjit/StubCalls.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index af08c43916ee..c38d0a3f9582 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -5391,7 +5391,7 @@ BEGIN_CASE(JSOP_DEFFUN) * setter to an existing property without a getter or setter. */ JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); - JS_ASSERT(!(old & JSPROP_READONLY)); + JS_ASSERT_IF(attrs != JSPROP_ENUMERATE, !(old & JSPROP_READONLY)); doSet = true; } pobj->dropProperty(cx, prop); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 0f4e399b7fde..508c2f35dc5f 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -954,7 +954,7 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) * setter to an existing property without a getter or setter. */ JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); - JS_ASSERT(!(old & JSPROP_READONLY)); + JS_ASSERT_IF(attrs != JSPROP_ENUMERATE, !(old & JSPROP_READONLY)); doSet = true; } pobj->dropProperty(cx, prop); From b3fd89a22aa419e1bd7bd235cbbc756985162db4 Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Thu, 7 Oct 2010 10:59:45 -0700 Subject: [PATCH 082/284] bug 584787, r=pcwalton: Node shouldn't subclass Array --- js/narcissus/jsexec.js | 197 ++++++++++++++++++---------- js/narcissus/jsparse.js | 51 +++---- js/narcissus/jsssa.js | 113 ++++++++-------- js/src/shell/njs | 3 +- js/src/tests/narcissus-failures.txt | 26 ++++ 5 files changed, 243 insertions(+), 147 deletions(-) diff --git a/js/narcissus/jsexec.js b/js/narcissus/jsexec.js index 382d894968f4..26f7d89b9391 100644 --- a/js/narcissus/jsexec.js +++ b/js/narcissus/jsexec.js @@ -313,8 +313,8 @@ Narcissus.interpreter = (function() { return; for (var i = 0, j = ps.length; i < j; i++) { - // If the thing we're valuating is already equal to the thing we want - // to valuate it to, we have fully saturated (and have a cycle), and + // If the thing we're evaluating is already equal to the thing we want + // to evaluate it to, we have fully saturated (and have a cycle), and // thus we should break. if (ps[i].v === v) break; @@ -324,7 +324,7 @@ Narcissus.interpreter = (function() { } function execute(n, x) { - var a, f, i, j, r, s, t, u, v; + var a, c, f, i, j, r, s, t, u, v; switch (n.type) { case FUNCTION: @@ -370,8 +370,9 @@ Narcissus.interpreter = (function() { // FALL THROUGH case BLOCK: - for (i = 0, j = n.length; i < j; i++) - execute(n[i], x); + c = n.children; + for (i = 0, j = c.length; i < j; i++) + execute(c[i], x); break; case IF: @@ -405,7 +406,7 @@ Narcissus.interpreter = (function() { } if (u === s) { for (;;) { // this loop exits switch_loop - if (t.statements.length) { + if (t.statements.children.length) { try { execute(t.statements, x); } catch (e if e === BREAK && x.target === n) { @@ -532,11 +533,12 @@ Narcissus.interpreter = (function() { case VAR: case CONST: - for (i = 0, j = n.length; i < j; i++) { - u = n[i].initializer; + c = n.children; + for (i = 0, j = c.length; i < j; i++) { + u = c[i].initializer; if (!u) continue; - t = n[i].name; + t = c[i].name; for (s = x.scope; s; s = s.parent) { if (hasDirectProperty(s.object, t)) break; @@ -565,16 +567,18 @@ Narcissus.interpreter = (function() { break; case COMMA: - for (i = 0, j = n.length; i < j; i++) - v = getValue(execute(n[i], x)); + c = n.children; + for (i = 0, j = c.length; i < j; i++) + v = getValue(execute(c[i], x)); break; case ASSIGN: - r = execute(n[0], x); + c = n.children; + r = execute(c[0], x); t = n.assignOp; if (t) u = getValue(r); - v = getValue(execute(n[1], x)); + v = getValue(execute(c[1], x)); if (t) { switch (t) { case BITWISE_OR: v = u | v; break; @@ -590,73 +594,89 @@ Narcissus.interpreter = (function() { case MOD: v = u % v; break; } } - putValue(r, v, n[0]); + putValue(r, v, c[0]); break; case HOOK: - v = getValue(execute(n[0], x)) ? getValue(execute(n[1], x)) - : getValue(execute(n[2], x)); + c = n.children; + v = getValue(execute(c[0], x)) ? getValue(execute(c[1], x)) + : getValue(execute(c[2], x)); break; case OR: - v = getValue(execute(n[0], x)) || getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) || getValue(execute(c[1], x)); break; case AND: - v = getValue(execute(n[0], x)) && getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) && getValue(execute(c[1], x)); break; case BITWISE_OR: - v = getValue(execute(n[0], x)) | getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) | getValue(execute(c[1], x)); break; case BITWISE_XOR: - v = getValue(execute(n[0], x)) ^ getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) ^ getValue(execute(c[1], x)); break; case BITWISE_AND: - v = getValue(execute(n[0], x)) & getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) & getValue(execute(c[1], x)); break; case EQ: - v = getValue(execute(n[0], x)) == getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) == getValue(execute(c[1], x)); break; case NE: - v = getValue(execute(n[0], x)) != getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) != getValue(execute(c[1], x)); break; case STRICT_EQ: - v = getValue(execute(n[0], x)) === getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) === getValue(execute(c[1], x)); break; case STRICT_NE: - v = getValue(execute(n[0], x)) !== getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) !== getValue(execute(c[1], x)); break; case LT: - v = getValue(execute(n[0], x)) < getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) < getValue(execute(c[1], x)); break; case LE: - v = getValue(execute(n[0], x)) <= getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) <= getValue(execute(c[1], x)); break; case GE: - v = getValue(execute(n[0], x)) >= getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) >= getValue(execute(c[1], x)); break; case GT: - v = getValue(execute(n[0], x)) > getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) > getValue(execute(c[1], x)); break; case IN: - v = getValue(execute(n[0], x)) in getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) in getValue(execute(c[1], x)); break; case INSTANCEOF: - t = getValue(execute(n[0], x)); - u = getValue(execute(n[1], x)); + c = n.children; + t = getValue(execute(c[0], x)); + u = getValue(execute(c[1], x)); if (isObject(u) && typeof u.__hasInstance__ === "function") v = u.__hasInstance__(t); else @@ -664,111 +684,122 @@ Narcissus.interpreter = (function() { break; case LSH: - v = getValue(execute(n[0], x)) << getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) << getValue(execute(c[1], x)); break; case RSH: - v = getValue(execute(n[0], x)) >> getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) >> getValue(execute(c[1], x)); break; case URSH: - v = getValue(execute(n[0], x)) >>> getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) >>> getValue(execute(c[1], x)); break; case PLUS: - v = getValue(execute(n[0], x)) + getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) + getValue(execute(c[1], x)); break; case MINUS: - v = getValue(execute(n[0], x)) - getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) - getValue(execute(c[1], x)); break; case MUL: - v = getValue(execute(n[0], x)) * getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) * getValue(execute(c[1], x)); break; case DIV: - v = getValue(execute(n[0], x)) / getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) / getValue(execute(c[1], x)); break; case MOD: - v = getValue(execute(n[0], x)) % getValue(execute(n[1], x)); + c = n.children; + v = getValue(execute(c[0], x)) % getValue(execute(c[1], x)); break; case DELETE: - t = execute(n[0], x); + t = execute(n.children[0], x); v = !(t instanceof Reference) || delete t.base[t.propertyName]; break; case VOID: - getValue(execute(n[0], x)); + getValue(execute(n.children[0], x)); break; case TYPEOF: - t = execute(n[0], x); + t = execute(n.children[0], x); if (t instanceof Reference) t = t.base ? t.base[t.propertyName] : undefined; v = typeof t; break; case NOT: - v = !getValue(execute(n[0], x)); + v = !getValue(execute(n.children[0], x)); break; case BITWISE_NOT: - v = ~getValue(execute(n[0], x)); + v = ~getValue(execute(n.children[0], x)); break; case UNARY_PLUS: - v = +getValue(execute(n[0], x)); + v = +getValue(execute(n.children[0], x)); break; case UNARY_MINUS: - v = -getValue(execute(n[0], x)); + v = -getValue(execute(n.children[0], x)); break; case INCREMENT: case DECREMENT: - t = execute(n[0], x); + t = execute(n.children[0], x); u = Number(getValue(t)); if (n.postfix) v = u; - putValue(t, (n.type === INCREMENT) ? ++u : --u, n[0]); + putValue(t, (n.type === INCREMENT) ? ++u : --u, n.children[0]); if (!n.postfix) v = u; break; case DOT: - r = execute(n[0], x); + c = n.children; + r = execute(c[0], x); t = getValue(r); - u = n[1].value; - v = new Reference(toObject(t, r, n[0]), u, n); + u = c[1].value; + v = new Reference(toObject(t, r, c[0]), u, n); break; case INDEX: - r = execute(n[0], x); + c = n.children; + r = execute(c[0], x); t = getValue(r); - u = getValue(execute(n[1], x)); - v = new Reference(toObject(t, r, n[0]), String(u), n); + u = getValue(execute(c[1], x)); + v = new Reference(toObject(t, r, c[0]), String(u), n); break; case LIST: // Curse ECMA for specifying that arguments is not an Array object! v = {}; - for (i = 0, j = n.length; i < j; i++) { - u = getValue(execute(n[i], x)); + c = n.children; + for (i = 0, j = c.length; i < j; i++) { + u = getValue(execute(c[i], x)); definitions.defineProperty(v, i, u, false, false, true); } definitions.defineProperty(v, "length", i, false, false, true); break; case CALL: - r = execute(n[0], x); - a = execute(n[1], x); + c = n.children; + r = execute(c[0], x); + a = execute(c[1], x); f = getValue(r); if (isPrimitive(f) || typeof f.__call__ !== "function") { - throw new TypeError(r + " is not callable", - n[0].filename, n[0].lineno); + throw new TypeError(r + " is not callable", c[0].filename, c[0].lineno); } t = (r instanceof Reference) ? r.base : null; if (t instanceof Activation) @@ -778,36 +809,39 @@ Narcissus.interpreter = (function() { case NEW: case NEW_WITH_ARGS: - r = execute(n[0], x); + c = n.children; + r = execute(c[0], x); f = getValue(r); if (n.type === NEW) { a = {}; definitions.defineProperty(a, "length", 0, false, false, true); } else { - a = execute(n[1], x); + a = execute(c[1], x); } if (isPrimitive(f) || typeof f.__construct__ !== "function") { - throw new TypeError(r + " is not a constructor", - n[0].filename, n[0].lineno); + throw new TypeError(r + " is not a constructor", c[0].filename, c[0].lineno); } v = f.__construct__(a, x); break; case ARRAY_INIT: v = []; - for (i = 0, j = n.length; i < j; i++) { - if (n[i]) - v[i] = getValue(execute(n[i], x)); + c = n.children; + for (i = 0, j = c.length; i < j; i++) { + if (c[i]) + v[i] = getValue(execute(c[i], x)); } v.length = j; break; case OBJECT_INIT: v = {}; - for (i = 0, j = n.length; i < j; i++) { - t = n[i]; + c = n.children; + for (i = 0, j = c.length; i < j; i++) { + t = c[i]; if (t.type === PROPERTY_INIT) { - v[t[0].value] = getValue(execute(t[1], x)); + let c2 = t.children; + v[c2[0].value] = getValue(execute(c2[1], x)); } else { f = newFunction(t, x); u = (t.type === GETTER) ? '__defineGetter__' @@ -834,6 +868,9 @@ Narcissus.interpreter = (function() { break; case IDENTIFIER: + if (typeof n.resolve !== "function") + throw n; + // Identifiers with forward pointers that weren't intervened can't be // lvalues, so we safely get the cached value directly. var resolved = n.resolve(); @@ -859,7 +896,7 @@ Narcissus.interpreter = (function() { break; case GROUP: - v = execute(n[0], x); + v = execute(n.children[0], x); break; default: @@ -1132,6 +1169,22 @@ Narcissus.interpreter = (function() { print(e.toString()); } catch (e) { print("internal Narcissus error"); + if (typeof e === "object" && e.stack) { + let st = String(e.stack).split(/\n/); + // beautify stack trace: + // - eliminate blank lines + // - sanitize confusing trace lines for getters and js -e expressions + // - simplify source location reporting + // - indent + for (let i = 0; i < st.length; i++) { + let line = st[i].trim(); + if (line) { + line = line.replace(/^(\(\))?@/, "@"); + line = line.replace(/@(.*\/|\\)?([^\/\\]+:[0-9]+)/, " at $2"); + print(" in " + line); + } + } + } throw e; } } diff --git a/js/narcissus/jsparse.js b/js/narcissus/jsparse.js index e615504a1a02..c3182f3f740b 100644 --- a/js/narcissus/jsparse.js +++ b/js/narcissus/jsparse.js @@ -657,15 +657,15 @@ Narcissus.parser = (function() { }, setCondition: function(n, e) { - n[0] = e; + n.children[0] = e; }, setThenPart: function(n, n2) { - n[1] = n2; + n.children[1] = n2; }, setElsePart: function(n, n2) { - n[2] = n2; + n.children[2] = n2; }, finish: function(n) { @@ -906,11 +906,12 @@ Narcissus.parser = (function() { // Nodes use a tokenizer for debugging (getSource, filename getter). this.tokenizer = t; + this.children = []; for (var i = 2; i < arguments.length; i++) this.push(arguments[i]); } - var Np = Node.prototype = new Array; + var Np = Node.prototype = {}; Np.constructor = Node; Np.toSource = Object.prototype.toSource; @@ -923,7 +924,7 @@ Narcissus.parser = (function() { if (this.end < kid.end) this.end = kid.end; } - return Array.prototype.push.call(this, kid); + return this.children.push(kid); } Node.indentLevel = 0; @@ -959,6 +960,11 @@ Narcissus.parser = (function() { return this.tokenizer.filename; }); + definitions.defineGetter(Np, "length", + function() { + throw new Error("Node.prototype.length is gone; use n.children.length instead"); + }); + definitions.defineProperty(String.prototype, "repeat", function(n) { var s = "", t = this + s; @@ -1027,7 +1033,7 @@ Narcissus.parser = (function() { * Parses a Statement. */ function Statement(t, x) { - var i, label, n, n2, ss, tt = t.get(true); + var i, label, n, n2, c, ss, tt = t.get(true); var builder = x.builder, b, b2, b3; // Cases for statements ending in a right curly return early, avoiding the @@ -1141,17 +1147,19 @@ Narcissus.parser = (function() { b.rebuildForIn(n); b.setObject(n, Expression(t, x)); if (n2.type === VAR || n2.type === LET) { + c = n2.children; + // Destructuring turns one decl into multiples, so either // there must be only one destructuring or only one // decl. - if (n2.length !== 1 && n2.destructurings.length !== 1) { + if (c.length !== 1 && n2.destructurings.length !== 1) { throw new SyntaxError("Invalid for..in left-hand side", t.filename, n2.lineno); } if (n2.destructurings.length > 0) { b.setIterator(n, n2.destructurings[0], n2, forBlock); } else { - b.setIterator(n, n2[0], n2, forBlock); + b.setIterator(n, c[0], n2, forBlock); } } else { if (n2.type === ARRAY_INIT || n2.type === OBJECT_INIT) { @@ -1181,8 +1189,9 @@ Narcissus.parser = (function() { if (forBlock) { builder.BLOCK.finish(forBlock); x.stmtStack.pop(); - for (var i = 0, j = forBlock.length; i < j; i++) { - n.body.unshift(forBlock[i]); + c = forBlock.children; + for (var i = 0, j = c.length; i < j; i++) { + n.body.unshift(c[i]); } } return n; @@ -1726,13 +1735,14 @@ Narcissus.parser = (function() { return; var lhss = {}; - var nn, n2, idx, sub; - for (var i = 0, j = n.length; i < j; i++) { - if (!(nn = n[i])) + var nn, n2, idx, sub, cc, c = n.children; + for (var i = 0, j = c.length; i < j; i++) { + if (!(nn = c[i])) continue; if (nn.type === PROPERTY_INIT) { - sub = nn[1]; - idx = nn[0].value; + cc = nn.children; + sub = cc[1]; + idx = cc[0].value; } else if (n.type === OBJECT_INIT) { // Do we have destructuring shorthand {foo, bar}? sub = nn; @@ -1891,7 +1901,7 @@ Narcissus.parser = (function() { b.addOperand(n2, n); n = n2; do { - n2 = n[n.length-1]; + n2 = n.children[n.children.length-1]; if (n2.type === YIELD && !n2.parenthesized) throw t.newSyntaxError("Yield expression must be parenthesized"); b.addOperand(n, AssignExpression(t, x)); @@ -2233,7 +2243,7 @@ Narcissus.parser = (function() { throw t.newSyntaxError("Yield " + err); if (t.match(FOR)) { n2 = GeneratorExpression(t, x, n2); - if (n.length > 1 || t.peek(true) === COMMA) + if (n.children.length > 1 || t.peek(true) === COMMA) throw t.newSyntaxError("Generator " + err); } b.addOperand(n, n2); @@ -2274,9 +2284,9 @@ Narcissus.parser = (function() { // If we matched exactly one element and got a FOR, we have an // array comprehension. - if (n.length === 1 && t.match(FOR)) { + if (n.children.length === 1 && t.match(FOR)) { n2 = bArrayComp.build(t); - bArrayComp.setExpression(n2, n[0]); + bArrayComp.setExpression(n2, n.children[0]); bArrayComp.setTail(n2, comprehensionTail(t, x)); n = n2; } @@ -2379,9 +2389,6 @@ Narcissus.parser = (function() { parse: parse, Node: Node, DefaultBuilder: DefaultBuilder, - get SSABuilder() { - throw new Error("SSA builder not yet supported"); - }, bindSubBuilders: bindSubBuilders, DECLARED_FORM: DECLARED_FORM, EXPRESSED_FORM: EXPRESSED_FORM, diff --git a/js/narcissus/jsssa.js b/js/narcissus/jsssa.js index 6e9bbd93602b..c3f86ce2a1e7 100644 --- a/js/narcissus/jsssa.js +++ b/js/narcissus/jsssa.js @@ -41,7 +41,7 @@ * SSA builder and optimizations. */ -(function() { +Narcissus.parser.SSABuilder = (function() { const parser = Narcissus.parser; const definitions = Narcissus.definitions; @@ -717,7 +717,7 @@ if (allDashes) { return null; } - rhs.reverse(); + rhs.children.reverse(); e = new Node(ft, ASSIGN); e.push(lhs); @@ -732,20 +732,21 @@ continue; } - rhs = e2[1]; + rhs = e2.children[1]; // Optimize away phis that are only one branch, but we still need // to propagate them! if (branches == 1) { - rhs = rhs[0]; + rhs = rhs.children[0]; } else { // Push a phi use for each operand so the phis can be filled // in during exec. - for (var i = 0, j = rhs.length; i < j; i++) { - if (rhs[i].type == INTERVENED) { + var rhsc = rhs.children; + for (var i = 0, j = rhsc.length; i < j; i++) { + if (rhsc[i].type == INTERVENED) { rhs.intervened = true; } - rhs[i].pushPhiUse(rhs); + rhsc[i].pushPhiUse(rhs); } e.push(e2); } @@ -753,7 +754,7 @@ propagate(x, rhs); } - return e.length > 0 ? e : null; + return e.children.length > 0 ? e : null; } SSAJoin.prototype = { @@ -904,9 +905,10 @@ uu = u[i]; // Phi nodes might have stale branches. if (uu.type == PHI) { - for (var k = 0, l = uu.length; k < l; k++) { - if (uu[k] === old.def) { - uu[k] = rhs; + var uuc = uu.children; + for (var k = 0, l = uuc.length; k < l; k++) { + if (uuc[k] === old.def) { + uuc[k] = rhs; rhs.pushPhiUse(uu); } } @@ -1279,7 +1281,7 @@ // do. var bComma = this.COMMA; - e2.push(n.setup[0]); + e2.push(n.setup.children[0]); n.setup = e2; var comma = bComma.build(t); @@ -1318,7 +1320,7 @@ this.join = breakJoin.parent; // Add update to the top if we were a for-in if (n.type == FOR_IN) { - n.body.unshift(n.update); + n.body.children.unshift(n.update); n.update = null; n.type = FOR; } @@ -1973,7 +1975,7 @@ ASSIGN: { addOperand: function(n, n2) { - if (n.length == 0) { + if (n.children.length === 0) { this.binds.inRHS++; } @@ -1981,15 +1983,16 @@ }, finish: function(n) { - if (n.length == 0) { + if (n.children.length === 0) { return; } var join = this.join; var binds = this.binds; var fb = binds.nearestFunction; - var lhs = n[0]; - var init = n[1]; + var nc = n.children; + var lhs = nc[0]; + var init = nc[1]; var upvars = init.upvars || new Upvars; if (--binds.inRHS > 0) { @@ -2001,7 +2004,7 @@ var t = n.tokenizer; // Rebuild as COMMA. n.type = COMMA; - n.length = 0; + n.children = []; desugarDestructuringAssign(this, n, lhs, init); return; } @@ -2053,12 +2056,11 @@ // Transform op= into a normal assignment only if the // lhs is an identifier we _know_ to be from a var. var nt = n.tokenizer; - var lhs = n[0]; var n2 = mkRawIdentifier(nt, name, null, true); this.PRIMARY.finish(n2); var o = n.assignOp; n.assignOp = undefined; - n.length = 0; + n.children = []; n.push(lhs); n.push(new Node(nt, o, n2, init)); n2.setForward(c.def); @@ -2066,17 +2068,17 @@ } // Clear the forward pointer and upvars on lefthand side. - if (n[0].forward) { - n[0].forward = null; - n[0].upvars = null; + if (lhs.forward) { + lhs.forward = null; + lhs.upvars = null; } // Set local to help decomp to do value numbering. - n[0].local = c.type; + lhs.local = c.type; // Get the rightmost expression in case of compound // assignment. while (init.type == ASSIGN) - init = init[1]; + init = init.children[1]; if (join) { // If the name is not a local let, we need a phi. @@ -2124,21 +2126,21 @@ }, setCondition: function(n, e) { - n[0] = e; + n.children[0] = e; n.rhsUnionUpvars(e); this.join = new SSAJoin(this.join, this.binds, false); }, setThenPart: function(n, n2) { var join = this.join; - n[1] = n2; + n.children[1] = n2; n.rhsUnionUpvars(n2); join.finishBranch(); join.restore(this.binds); }, setElsePart: function(n, n2) { - n[2] = n2; + n.children[2] = n2; n.rhsUnionUpvars(n2); }, @@ -2158,7 +2160,7 @@ }, addOperand: function(n, n2) { - if (n.length == 0) { + if (n.children.length == 0) { // Short circuiting means the right hand expression needs // to be parsed in a new context. var join = this.join = new SSAJoin(this.join, this.binds, false); @@ -2186,7 +2188,7 @@ }, addOperand: function(n, n2) { - if (n.length == 0) { + if (n.children.length == 0) { // Short circuiting means the right hand expression needs // to be parsed in a new context. var join = this.join = new SSAJoin(this.join, this.binds, false); @@ -2213,7 +2215,9 @@ var join = this.join; var binds = this.binds; - if (!(n[0].type == IDENTIFIER && binds.hasCurrent(n[0].value))) + var nc = n.children; + var lhs = nc[0]; + if (!(lhs.type == IDENTIFIER && binds.hasCurrent(lhs.value))) return; // @@ -2230,7 +2234,7 @@ // effect, so we do not duplicate side effects in an unsafe // fashion. // - var name = n[0].value; + var name = lhs.value; var c = binds.current(name); // Don't transform vars inside of withs if (binds.isWith && c.type == VAR) @@ -2255,7 +2259,7 @@ if (n.postfix) { n.parenthesized = true; n.type = COMMA; - n.length = 0; + n.children = []; n.push(mkAssignSimple(this, t, ptmp, mkIdentifier(this, t, name))); } @@ -2270,9 +2274,11 @@ n.push(mkIdentifier(this, t, ptmp)); } else { n.type = ASSIGN; - n.length = 0; - n.push(assign[0]); - n.push(assign[1]); + n.children = []; + + var assignc = assign.children; + n.push(assignc[0]); + n.push(assignc[1]); } } }, @@ -2296,12 +2302,13 @@ var join = this.join; var binds = this.binds; var fb = binds.nearestFunction; + var nc = n.children; if (--binds.inRHS > 0) { if (unionOnRight) { - n.upvars = n[1].upvars; + n.upvars = nc[1].upvars; } else { - n.upvars = n[0].upvars; + n.upvars = nc[0].upvars; } } @@ -2357,8 +2364,9 @@ // local ones, so blast away context. // var inners = this.binds.inners; - var base = baseOfCall(n[0]); - var target = targetOfCall(n[0], IDENTIFIER); + var call = nc[0]; + var base = baseOfCall(call); + var target = targetOfCall(call, IDENTIFIER); if (target == "eval") { escapeEval(join, binds); @@ -2454,7 +2462,7 @@ var unionOnRight = n.type == CALL || n.type == NEW_WITH_ARGS || n.type == INDEX; if (unionOnRight) { - escapeVars(join, binds, n[1].upvars || new Upvars); + escapeVars(join, binds, nc[1].upvars || new Upvars); } } }, @@ -2581,7 +2589,7 @@ }, finish: function(n) { - n.rhsUnionUpvars(n[1]); + n.rhsUnionUpvars(n.children[1]); } }, @@ -2837,9 +2845,9 @@ function baseOfCall(n) { switch (n.type) { case DOT: - return baseOfCall(n[0]); + return baseOfCall(n.children[0]); case INDEX: - return baseOfCall(n[0]); + return baseOfCall(n.children[0]); default: return n; } @@ -2850,9 +2858,9 @@ case ident: return n.value; case DOT: - return targetOfCall(n[1], IDENTIFIER); + return targetOfCall(n.children[1], IDENTIFIER); case INDEX: - return targetOfCall(n[1], STRING); + return targetOfCall(n.children[1], STRING); default: return null; } @@ -2948,9 +2956,9 @@ builder.genDestructuringSym(), e, false); builder.binds.block.push(decl); - decl[0].setForward(e); - - go(n.destructuredNames, decl[0]); + var declc = decl.children[0]; + declc[0].setForward(e); + go(n.destructuredNames, declc[0]); } function desugarDestructuringInit(builder, n, e) { @@ -2984,8 +2992,9 @@ var dtmp = builder.genDestructuringSym(); var decl = mkDecl(builder, "LET", t, dtmp, e, false); block.push(decl); - decl[0].setForward(e); - go(ddecls, decl[0]); + var declc = decl.children[0]; + declc[0].setForward(e); + go(ddecls, declc[0]); } else { // This only happens when we have destructuring for a catch var, // in which case that catch var already has let-scoping, so we @@ -3139,6 +3148,6 @@ this.phiUses.push(p); }; - parser.SSABuilder = SSABuilder; + return SSABuilder; }()); diff --git a/js/src/shell/njs b/js/src/shell/njs index 72322629c60a..6e1a3064346f 100755 --- a/js/src/shell/njs +++ b/js/src/shell/njs @@ -16,6 +16,7 @@ js_cmd = os.path.abspath(os.path.join(THIS_DIR, "js")) narc_jsdefs = os.path.join(NARC_JS_DIR, "jsdefs.js") narc_jslex = os.path.join(NARC_JS_DIR, "jslex.js") narc_jsparse = os.path.join(NARC_JS_DIR, "jsparse.js") +narc_jsssa = os.path.join(NARC_JS_DIR, "jsssa.js") narc_jsexec = os.path.join(NARC_JS_DIR, "jsexec.js") def handler(signum, frame): @@ -62,5 +63,5 @@ if __name__ == '__main__': if options.js_interactive: cmd += 'Narcissus.interpreter.repl();' - Popen([js_cmd, '-f', narc_jsdefs, '-f', narc_jslex, '-f', narc_jsparse, '-f', narc_jsexec, '-e', cmd]).wait() + Popen([js_cmd, '-f', narc_jsdefs, '-f', narc_jslex, '-f', narc_jsparse, '-f', narc_jsssa, '-f', narc_jsexec, '-e', cmd]).wait() diff --git a/js/src/tests/narcissus-failures.txt b/js/src/tests/narcissus-failures.txt index 8ea0dab85fd5..4a48f1350a76 100644 --- a/js/src/tests/narcissus-failures.txt +++ b/js/src/tests/narcissus-failures.txt @@ -259,6 +259,7 @@ e4x/extensions/regress-410192.js e4x/extensions/regress-450871-01.js e4x/extensions/regress-450871-02.js e4x/extensions/regress-462734-01.js +e4x/extensions/extensibility.js ecma/Array/15.4.4.3-1.js ecma/Boolean/15.6.4.1.js ecma/Boolean/15.6.4.2-4-n.js @@ -606,10 +607,13 @@ ecma_5/Object/15.2.3.6-redefinition-2-of-4.js ecma_5/Object/15.2.3.6-redefinition-3-of-4.js ecma_5/Object/15.2.3.6-redefinition-4-of-4.js ecma_5/Object/15.2.3.7-01.js +ecma_5/Object/extensibility-01.js +ecma_5/Object/vacuous-accessor-unqualified-name.js ecma_5/RegExp/15.10.7.5-01.js ecma_5/Types/8.12.5-01.js ecma_5/extensions/8.12.5-01.js ecma_5/extensions/string-literal-getter-setter-decompilation.js +ecma_5/extensions/15.4.4.11.js ecma_5/misc/enumerate-undefined.js ecma_5/misc/global-numeric-properties.js ecma_5/strict/10.4.2.js @@ -629,6 +633,21 @@ ecma_5/strict/8.7.2.js ecma_5/strict/B.1.1.js ecma_5/strict/B.1.2.js ecma_5/strict/regress-532254.js +ecma_5/strict/8.12.5.js +ecma_5/strict/8.12.7.js +ecma_5/strict/10.6.js +ecma_5/strict/15.3.5.1.js +ecma_5/strict/15.3.5.2.js +ecma_5/strict/15.4.4.9.js +ecma_5/strict/15.4.4.6.js +ecma_5/strict/15.4.4.8.js +ecma_5/strict/15.4.4.12.js +ecma_5/strict/15.4.5.1.js +ecma_5/strict/15.4.4.13.js +ecma_5/strict/15.5.5.1.js +ecma_5/strict/15.5.5.2.js +ecma_5/strict/15.10.7.js +ecma_5/strict/function-name-arity.js js1_2/regexp/RegExp_multiline.js js1_2/regexp/RegExp_multiline_as_array.js js1_2/regexp/alphanumeric.js @@ -1335,6 +1354,9 @@ js1_8_5/extensions/destructure-accessor.js js1_8_5/extensions/reflect-parse.js js1_8_5/extensions/scripted-proxies.js js1_8_5/extensions/typedarray.js +js1_8_5/extensions/clone-object.js +js1_8_5/extensions/clone-errors.js +js1_8_5/extensions/clone-typed-array.js js1_8_5/regress/regress-500528.js js1_8_5/regress/regress-533876.js js1_8_5/regress/regress-541255-0.js @@ -1354,6 +1376,10 @@ js1_8_5/regress/regress-577648-1.js js1_8_5/regress/regress-577648-2.js js1_8_5/regress/regress-584355.js js1_8_5/regress/regress-588339.js +js1_8_5/regress/regress-592217.js +js1_8_5/regress/regress-596805-2.js +js1_8_5/regress/regress-597870.js +js1_8_5/regress/regress-597945-1.js narcissus/../ecma/Date/15.9.5.10-2.js narcissus/../ecma/Date/15.9.5.11-2.js narcissus/../ecma/Date/15.9.5.12-2.js From b250c727a3c232f61b55f016ba277038b770d071 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Thu, 7 Oct 2010 11:33:23 -0700 Subject: [PATCH 083/284] Nit-picks, mainly localizing and initializing the oldAttrs var, on patchwork for bug 599009 (r=bz). --- js/src/jsinterp.cpp | 29 +++++++++++++++-------------- js/src/methodjit/StubCalls.cpp | 33 ++++++++++++++++----------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index c38d0a3f9582..1edf625ad46f 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -5379,23 +5379,24 @@ BEGIN_CASE(JSOP_DEFFUN) bool doSet = false; if (prop) { JS_ASSERT((attrs == JSPROP_ENUMERATE) == regs.fp->isEvalFrame()); - uint32 old; - if (attrs == JSPROP_ENUMERATE || - (parent == pobj && - parent->isCall() && - (old = ((Shape *) prop)->attributes(), - !(old & (JSPROP_GETTER|JSPROP_SETTER)) && - (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs))) { - /* - * js_CheckRedeclaration must reject attempts to add a getter or - * setter to an existing property without a getter or setter. - */ - JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); - JS_ASSERT_IF(attrs != JSPROP_ENUMERATE, !(old & JSPROP_READONLY)); - doSet = true; + if (attrs == JSPROP_ENUMERATE || (parent == pobj && parent->isCall())) { + JS_ASSERT(pobj->isNative()); + uintN oldAttrs = ((Shape *) prop)->attributes(); + + if (!(oldAttrs & (JSPROP_GETTER|JSPROP_SETTER)) && + (oldAttrs & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs) { + /* + * js_CheckRedeclaration must reject attempts to add a getter or + * setter to an existing property without a getter or setter. + */ + JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); + JS_ASSERT(!(oldAttrs & JSPROP_READONLY)); + doSet = true; + } } pobj->dropProperty(cx, prop); } + Value rval = ObjectValue(*obj); ok = doSet ? parent->setProperty(cx, id, &rval, script->strictModeCode) diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 508c2f35dc5f..87c60d8e5df5 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -914,9 +914,6 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) */ JSObject *parent = &fp->varobj(cx); - uint32 old; - bool doSet; - /* * Check for a const property of the same name -- or any kind of property * if executing with the strict option. We check here at runtime as well @@ -940,25 +937,27 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) * setters that store the value of the property in the interpreter frame, * see bug 467495. */ - doSet = false; + bool doSet = false; if (prop) { JS_ASSERT((attrs == JSPROP_ENUMERATE) == fp->isEvalFrame()); - if (attrs == JSPROP_ENUMERATE || - (parent == pobj && - parent->isCall() && - (old = ((Shape *) prop)->attributes(), - !(old & (JSPROP_GETTER|JSPROP_SETTER)) && - (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs))) { - /* - * js_CheckRedeclaration must reject attempts to add a getter or - * setter to an existing property without a getter or setter. - */ - JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); - JS_ASSERT_IF(attrs != JSPROP_ENUMERATE, !(old & JSPROP_READONLY)); - doSet = true; + if (attrs == JSPROP_ENUMERATE || (parent == pobj && parent->isCall())) { + JS_ASSERT(pobj->isNative()); + uintN oldAttrs = ((Shape *) prop)->attributes(); + + if (!(oldAttrs & (JSPROP_GETTER|JSPROP_SETTER)) && + (oldAttrs & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs) { + /* + * js_CheckRedeclaration must reject attempts to add a getter or + * setter to an existing property without a getter or setter. + */ + JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); + JS_ASSERT(!(oldAttrs & JSPROP_READONLY)); + doSet = true; + } } pobj->dropProperty(cx, prop); } + Value rval = ObjectValue(*obj); ok = doSet ? parent->setProperty(cx, id, &rval, strict) From c317b353384fe319f1e1468fab668b8e4289551b Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 7 Oct 2010 15:00:09 -0500 Subject: [PATCH 084/284] Bug 592644 - ES5 indirect eval. Part 1, add support and tests. r=brendan. --HG-- extra : rebase_source : 5d07f3e847b2adfb46f30f343af2e06d494012c3 --- js/src/jsobj.cpp | 101 ++++----------- js/src/jsversion.h | 5 - js/src/tests/ecma_5/Global/eval-01.js | 38 ++++++ js/src/tests/ecma_5/Global/eval-02.js | 38 ++++++ js/src/tests/ecma_5/Global/jstests.list | 2 + js/src/tests/js1_5/extensions/jstests.list | 1 - .../tests/js1_5/extensions/regress-382509.js | 117 ------------------ js/src/tests/js1_6/Regress/jstests.list | 1 - js/src/tests/js1_6/Regress/regress-382509.js | 77 ------------ 9 files changed, 104 insertions(+), 276 deletions(-) create mode 100644 js/src/tests/ecma_5/Global/eval-01.js create mode 100644 js/src/tests/ecma_5/Global/eval-02.js delete mode 100644 js/src/tests/js1_5/extensions/regress-382509.js delete mode 100644 js/src/tests/js1_6/Regress/regress-382509.js diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 7fd59d5f156d..ff866569a37e 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1008,56 +1008,21 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) JSStackFrame *caller = js_GetScriptedCaller(cx, NULL); if (!caller) { + /* Eval code needs to inherit principals from the caller. */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL, js_eval_str); return JS_FALSE; } jsbytecode *callerPC = caller->pc(cx); - bool indirectCall = (callerPC && *callerPC != JSOP_EVAL); + bool directCall = (callerPC && js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL); /* - * If the callee was originally a cross-compartment wrapper, this should - * be an indirect call. + * If the callee was originally a cross-compartment wrapper, this is an + * indirect call. */ - if (caller->scopeChain().compartment() != vp[0].toObject().compartment()) - indirectCall = true; - - /* - * Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....)) - * that attempt to use a non-global object as the scope object. - * - * This ban is a bit silly, since we could just disregard the this-argument - * entirely and comply with ES5, which supports indirect eval. See bug - * 592664. - */ - { - JSObject *obj = ComputeThisFromVp(cx, vp); - if (!obj) - return JS_FALSE; - - /* - * This call to JSObject::wrappedObject is safe because the result is - * only used for this check. - */ - obj = obj->wrappedObject(cx); - - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - - JSObject *parent = obj->getParent(); - if (indirectCall || parent) { - uintN flags = parent - ? JSREPORT_ERROR - : JSREPORT_STRICT | JSREPORT_WARNING; - if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, - js_eval_str)) { - return JS_FALSE; - } - } - } + if (directCall && caller->scopeChain().compartment() != vp[0].toObject().compartment()) + directCall = false; Value *argv = JS_ARGV(cx, vp); if (!argv[0].isString()) { @@ -1083,52 +1048,38 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) MUST_FLOW_THROUGH("out"); uintN staticLevel = caller->script()->staticLevel + 1; + JSObject *scopeobj; + /* - * Bring fp->scopeChain up to date. We're either going to use - * it (direct call) or save it and restore it (indirect call). + * Per ES5, if we see an indirect call, then run in the global scope. + * (eval is specified this way so that the compiler can make assumptions + * about what bindings may or may not exist in the current frame if it + * doesn't see 'eval'.) */ - JSObject *callerScopeChain; + if (directCall) { + /* Compile using the caller's current scope object. */ + scopeobj = js_GetScopeChainFast(cx, caller, JSOP_EVAL, + JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); + if (!scopeobj) + return JS_FALSE; - if (callerPC && *callerPC == JSOP_EVAL) - callerScopeChain = js_GetScopeChainFast(cx, caller, JSOP_EVAL, - JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); - else - callerScopeChain = js_GetScopeChain(cx, caller); - - if (!callerScopeChain) - return JS_FALSE; - - JSObject *scopeobj = NULL; - -#if JS_HAS_EVAL_THIS_SCOPE - /* - * If we see an indirect call, then run eval in the global scope. We do - * this so the compiler can make assumptions about what bindings may or - * may not exist in the current frame if it doesn't see 'eval'. - */ - if (indirectCall) { + JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj()); + } else { /* Pretend that we're top level. */ staticLevel = 0; scopeobj = vp[0].toObject().getGlobal(); - } else { - /* - * Compile using the caller's current scope object. - * - * NB: This means that the C API must not be used to call eval. - */ - JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj()); - scopeobj = callerScopeChain; } -#endif /* Ensure we compile this eval with the right object in the scope chain. */ JSObject *result = CheckScopeChainValidity(cx, scopeobj, js_eval_str); - JS_ASSERT_IF(result, result == scopeobj); if (!result) return JS_FALSE; + JS_ASSERT(result == scopeobj); - // CSP check: is eval() allowed at all? - // report errors via CSP is done in the script security mgr. + /* + * CSP check: Is eval() allowed at all? + * Report errors via CSP is done in the script security mgr. + */ if (!js_CheckContentSecurityPolicy(cx)) { JS_ReportError(cx, "call to eval() blocked by CSP"); return JS_FALSE; @@ -1174,7 +1125,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) * calls to eval from global code are not cached. */ JSScript **bucket = EvalCacheHash(cx, str); - if (!indirectCall && caller->isFunctionFrame()) { + if (directCall && caller->isFunctionFrame()) { uintN count = 0; JSScript **scriptp = bucket; diff --git a/js/src/jsversion.h b/js/src/jsversion.h index 293365172bdc..73969bd3b12a 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -86,7 +86,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_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 0 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ @@ -115,7 +114,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ @@ -140,7 +138,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ @@ -165,7 +162,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ @@ -190,7 +186,6 @@ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #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_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ diff --git a/js/src/tests/ecma_5/Global/eval-01.js b/js/src/tests/ecma_5/Global/eval-01.js new file mode 100644 index 000000000000..50bb95ffdbad --- /dev/null +++ b/js/src/tests/ecma_5/Global/eval-01.js @@ -0,0 +1,38 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var a = 9; +var global = this; + +function test() { + var a = 0; + + // direct eval sees local a + assertEq(eval('a+1'), 1); + assertEq(eval('eval("a+1")'), 1); + + // indirect: using a name other than 'eval' + var foo = eval; + assertEq(foo('a+1'), 10); + assertEq(eval('foo("a+1")'), 10); // outer eval is direct, inner foo("a+1") is indirect + + // indirect: qualified method call + assertEq(this.eval("a+1"), 10); + assertEq(global.eval("a+1"), 10); + var obj = {foo: eval, eval: eval}; + assertEq(obj.foo('a+1'), 10); + assertEq(obj.eval('a+1'), 10); + var name = "eval"; + assertEq(obj[name]('a+1'), 10); + assertEq([eval][0]('a+1'), 10); + + // indirect: not called from a CallExpression at all + assertEq(eval.call(undefined, 'a+1'), 10); + assertEq(eval.call(global, 'a+1'), 10); + assertEq(eval.apply(undefined, ['a+1']), 10); + assertEq(eval.apply(global, ['a+1']), 10); + assertEq(['a+1'].map(eval)[0], 10); +} + +test(); +reportCompare(0, 0); diff --git a/js/src/tests/ecma_5/Global/eval-02.js b/js/src/tests/ecma_5/Global/eval-02.js new file mode 100644 index 000000000000..e1a315949c67 --- /dev/null +++ b/js/src/tests/ecma_5/Global/eval-02.js @@ -0,0 +1,38 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var a = 9; + +function directArg(eval, s) { + var a = 1; + return eval(s); +} + +function directVar(f, s) { + var eval = f; + var a = 1; + return eval(s); +} + +function directWith(obj, s) { + var f; + with (obj) { + f = function () { + var a = 1; + return eval(s); + }; + } + return f(); +} + +// direct eval, even though 'eval' is an argument +assertEq(directArg(eval, 'a+1'), 2); + +// direct eval, even though 'eval' is a var +assertEq(directVar(eval, 'a+1'), 2); + +// direct eval, even though 'eval' is found via a with block +assertEq(directWith(this, 'a+1'), 2); +assertEq(directWith({eval: eval, a: -1000}, 'a+1'), 2); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_5/Global/jstests.list b/js/src/tests/ecma_5/Global/jstests.list index 8a1f31710e56..42a4f6e9cbbd 100644 --- a/js/src/tests/ecma_5/Global/jstests.list +++ b/js/src/tests/ecma_5/Global/jstests.list @@ -1,2 +1,4 @@ url-prefix ../../jsreftest.html?test=ecma_5/Global/ script parseInt-01.js +script eval-01.js +script eval-02.js diff --git a/js/src/tests/js1_5/extensions/jstests.list b/js/src/tests/js1_5/extensions/jstests.list index 547027d1bc2e..c6b68e0b4470 100644 --- a/js/src/tests/js1_5/extensions/jstests.list +++ b/js/src/tests/js1_5/extensions/jstests.list @@ -146,7 +146,6 @@ script regress-380581.js script regress-380889.js script regress-381211.js script regress-381304.js -script regress-382509.js script regress-384680.js script regress-385134.js script regress-385393-02.js diff --git a/js/src/tests/js1_5/extensions/regress-382509.js b/js/src/tests/js1_5/extensions/regress-382509.js deleted file mode 100644 index eb9ffb17f4a1..000000000000 --- a/js/src/tests/js1_5/extensions/regress-382509.js +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is JavaScript Engine testing utilities. - * - * The Initial Developer of the Original Code is - * Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 382509; -var summary = 'Disallow non-global indirect eval'; -var actual = ''; -var expect = ''; - -var global = typeof window == 'undefined' ? this : window; -var object = {}; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if (options().match(/strict/)) - { - options('strict'); - } - if (options().match(/werror/)) - { - options('werror'); - } - - global.foo = eval; - global.a = 'global'; - expect = 'global indirect'; - actual = global.foo('a+" indirect"'); - reportCompare(expect, actual, summary + ': global indirect'); - - object.foo = eval; - object.a = 'local'; - expect = 'EvalError: function eval must be called directly, and not by way of a function of another name'; - try - { - actual = object.foo('a+" indirect"'); - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': local indirect'); - - options('strict'); - options('werror'); - - try - { - var foo = eval; - print("foo(1+1)" + foo('1+1')); - actual = 'No Error'; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': strict, rename warning'); - - options('strict'); - options('werror'); - - expect = 'No Error'; - try - { - var foo = eval; - foo('1+1'); - actual = 'No Error'; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': not strict, no rename warning'); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_6/Regress/jstests.list b/js/src/tests/js1_6/Regress/jstests.list index 82264b4fa82f..b1ed21ea3c1b 100644 --- a/js/src/tests/js1_6/Regress/jstests.list +++ b/js/src/tests/js1_6/Regress/jstests.list @@ -11,6 +11,5 @@ script regress-353078.js script regress-355002.js script regress-372565.js script regress-378492.js -script regress-382509.js script regress-475469.js script regress-476655.js diff --git a/js/src/tests/js1_6/Regress/regress-382509.js b/js/src/tests/js1_6/Regress/regress-382509.js deleted file mode 100644 index 84a052ac0831..000000000000 --- a/js/src/tests/js1_6/Regress/regress-382509.js +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is JavaScript Engine testing utilities. - * - * The Initial Developer of the Original Code is - * Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 382509; -var summary = 'Disallow non-global indirect eval'; -var actual = ''; -var expect = ''; - -var global = typeof window == 'undefined' ? this : window; -var object = {}; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - global.foo = eval; - global.a = 'global'; - expect = 'global indirect'; - actual = String(['a+" indirect"'].map(global.foo)); - reportCompare(expect, actual, summary + ': global indirect'); - - object.foo = eval; - object.a = 'local'; - expect = 'EvalError: function eval must be called directly, and not by way of a function of another name'; - try - { - actual = String(['a+" indirect"'].map(object.foo, object)); - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': local indirect'); - - exitFunc ('test'); -} From 0ca869499686d4a5b4698b1356afdc80673a94c2 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 7 Oct 2010 15:01:45 -0500 Subject: [PATCH 085/284] Bug 592644 - ES5 indirect eval. Part 2, factor out EvalCacheLookup and other misc. cleanup. r=brendan. --HG-- extra : rebase_source : 6c0dfea58dbbe3d9ce7aa6315b4c9c17c253a03a --- js/src/jsobj.cpp | 193 ++++++++++++++++++++++++----------------------- 1 file changed, 100 insertions(+), 93 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index ff866569a37e..44e7af02b9b9 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -998,12 +998,93 @@ EvalCacheHash(JSContext *cx, JSString *str) return &JS_SCRIPTS_TO_GC(cx)[h]; } +static JS_ALWAYS_INLINE JSScript * +EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel, + JSPrincipals *principals, JSObject *scopeobj, JSScript ***bucketp) +{ + /* + * Cache local eval scripts indexed by source qualified by scope. + * + * An eval cache entry should never be considered a hit unless its + * strictness matches that of the new eval code. The existing code takes + * care of this, because hits are qualified by the function from which + * eval was called, whose strictness doesn't change. Scripts produced by + * calls to eval from global code are not cached. + */ + JSScript **bucket = EvalCacheHash(cx, str); + *bucketp = bucket; + uintN count = 0; + JSScript **scriptp = bucket; + + EVAL_CACHE_METER(probe); + JSVersion version = cx->findVersion(); + JSScript *script; + while ((script = *scriptp) != NULL) { + if (script->savedCallerFun && + script->staticLevel == staticLevel && + script->version == version && + (script->principals == principals || + (principals->subsume(principals, script->principals) && + script->principals->subsume(script->principals, principals)))) { + /* + * Get the prior (cache-filling) eval's saved caller function. + * See Compiler::compileScript in jsparse.cpp. + */ + JSFunction *fun = script->getFunction(0); + + if (fun == caller->fun()) { + /* + * Get the source string passed for safekeeping in the + * atom map by the prior eval to Compiler::compileScript. + */ + JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); + + if (src == str || js_EqualStrings(src, str)) { + /* + * Source matches, qualify by comparing scopeobj to the + * COMPILE_N_GO-memoized parent of the first literal + * function or regexp object if any. If none, then this + * script has no compiled-in dependencies on the prior + * eval's scopeobj. + */ + JSObjectArray *objarray = script->objects(); + int i = 1; + + if (objarray->length == 1) { + if (script->regexpsOffset != 0) { + objarray = script->regexps(); + i = 0; + } else { + EVAL_CACHE_METER(noscope); + i = -1; + } + } + if (i < 0 || + objarray->vector[i]->getParent() == scopeobj) { + JS_ASSERT(staticLevel == script->staticLevel); + EVAL_CACHE_METER(hit); + *scriptp = script->u.nextToGC; + script->u.nextToGC = NULL; + return script; + } + } + } + } + + if (++count == EVAL_CACHE_CHAIN_LIMIT) + return NULL; + EVAL_CACHE_METER(step); + scriptp = &script->u.nextToGC; + } + return NULL; +} + static JSBool obj_eval(JSContext *cx, uintN argc, Value *vp) { if (argc < 1) { vp->setUndefined(); - return JS_TRUE; + return true; } JSStackFrame *caller = js_GetScriptedCaller(cx, NULL); @@ -1011,7 +1092,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) /* Eval code needs to inherit principals from the caller. */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL, js_eval_str); - return JS_FALSE; + return false; } jsbytecode *callerPC = caller->pc(cx); @@ -1027,7 +1108,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) Value *argv = JS_ARGV(cx, vp); if (!argv[0].isString()) { *vp = argv[0]; - return JS_TRUE; + return true; } /* @@ -1040,28 +1121,25 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) "Support for eval(code, scopeObject) has been removed. " "Use |with (scopeObject) eval(code);| instead."; if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING)) - return JS_FALSE; + return false; caller->script()->warnedAboutTwoArgumentEval = true; } - /* From here on, control must exit through label out with ok set. */ - MUST_FLOW_THROUGH("out"); - uintN staticLevel = caller->script()->staticLevel + 1; - - JSObject *scopeobj; - /* * Per ES5, if we see an indirect call, then run in the global scope. * (eval is specified this way so that the compiler can make assumptions * about what bindings may or may not exist in the current frame if it * doesn't see 'eval'.) */ + uintN staticLevel; + JSObject *scopeobj; if (directCall) { /* Compile using the caller's current scope object. */ + staticLevel = caller->script()->staticLevel + 1; scopeobj = js_GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); if (!scopeobj) - return JS_FALSE; + return false; JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj()); } else { @@ -1073,7 +1151,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) /* Ensure we compile this eval with the right object in the scope chain. */ JSObject *result = CheckScopeChainValidity(cx, scopeobj, js_eval_str); if (!result) - return JS_FALSE; + return false; JS_ASSERT(result == scopeobj); /* @@ -1082,7 +1160,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) */ if (!js_CheckContentSecurityPolicy(cx)) { JS_ReportError(cx, "call to eval() blocked by CSP"); - return JS_FALSE; + return false; } JSObject *callee = &vp[0].toObject(); @@ -1111,86 +1189,13 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) ok = js_ConsumeJSONText(cx, jp, chars+1, length-2); ok &= js_FinishJSONParse(cx, jp, NullValue()); if (ok) - return JS_TRUE; + return true; } } - /* - * Cache local eval scripts indexed by source qualified by scope. - * - * An eval cache entry should never be considered a hit unless its - * strictness matches that of the new eval code. The existing code takes - * care of this, because hits are qualified by the function from which - * eval was called, whose strictness doesn't change. Scripts produced by - * calls to eval from global code are not cached. - */ - JSScript **bucket = EvalCacheHash(cx, str); - if (directCall && caller->isFunctionFrame()) { - uintN count = 0; - JSScript **scriptp = bucket; - - EVAL_CACHE_METER(probe); - JSVersion version = cx->findVersion(); - while ((script = *scriptp) != NULL) { - if (script->savedCallerFun && - script->staticLevel == staticLevel && - script->version == version && - (script->principals == principals || - (principals->subsume(principals, script->principals) && - script->principals->subsume(script->principals, principals)))) { - /* - * Get the prior (cache-filling) eval's saved caller function. - * See Compiler::compileScript in jsparse.cpp. - */ - JSFunction *fun = script->getFunction(0); - - if (fun == caller->fun()) { - /* - * Get the source string passed for safekeeping in the - * atom map by the prior eval to Compiler::compileScript. - */ - JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); - - if (src == str || js_EqualStrings(src, str)) { - /* - * Source matches, qualify by comparing scopeobj to the - * COMPILE_N_GO-memoized parent of the first literal - * function or regexp object if any. If none, then this - * script has no compiled-in dependencies on the prior - * eval's scopeobj. - */ - JSObjectArray *objarray = script->objects(); - int i = 1; - - if (objarray->length == 1) { - if (script->regexpsOffset != 0) { - objarray = script->regexps(); - i = 0; - } else { - EVAL_CACHE_METER(noscope); - i = -1; - } - } - if (i < 0 || - objarray->vector[i]->getParent() == scopeobj) { - JS_ASSERT(staticLevel == script->staticLevel); - EVAL_CACHE_METER(hit); - *scriptp = script->u.nextToGC; - script->u.nextToGC = NULL; - break; - } - } - } - } - - if (++count == EVAL_CACHE_CHAIN_LIMIT) { - script = NULL; - break; - } - EVAL_CACHE_METER(step); - scriptp = &script->u.nextToGC; - } - } + JSScript **bucket; + if (directCall && caller->isFunctionFrame()) + script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, &bucket); /* * We can't have a callerFrame (down in js_Execute's terms) if we're in @@ -1205,7 +1210,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) chars, length, NULL, file, line, str, staticLevel); if (!script) - return JS_FALSE; + return false; } assertSameCompartment(cx, scopeobj, script); @@ -1218,8 +1223,10 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) cx->runtime->atomState.evalAtom) && Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp); - script->u.nextToGC = *bucket; - *bucket = script; + if (bucket) { + script->u.nextToGC = *bucket; + *bucket = script; + } #ifdef CHECK_SCRIPT_OWNER script->owner = NULL; #endif From b9a95a11dbb0437e50d0d6134eb2b086f263d83c Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Thu, 7 Oct 2010 13:17:03 -0700 Subject: [PATCH 086/284] Back out my last push (599009 botched followup). --- js/src/jsinterp.cpp | 29 ++++++++++++++--------------- js/src/methodjit/StubCalls.cpp | 33 +++++++++++++++++---------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 1edf625ad46f..c38d0a3f9582 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -5379,24 +5379,23 @@ BEGIN_CASE(JSOP_DEFFUN) bool doSet = false; if (prop) { JS_ASSERT((attrs == JSPROP_ENUMERATE) == regs.fp->isEvalFrame()); - if (attrs == JSPROP_ENUMERATE || (parent == pobj && parent->isCall())) { - JS_ASSERT(pobj->isNative()); - uintN oldAttrs = ((Shape *) prop)->attributes(); - - if (!(oldAttrs & (JSPROP_GETTER|JSPROP_SETTER)) && - (oldAttrs & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs) { - /* - * js_CheckRedeclaration must reject attempts to add a getter or - * setter to an existing property without a getter or setter. - */ - JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); - JS_ASSERT(!(oldAttrs & JSPROP_READONLY)); - doSet = true; - } + uint32 old; + if (attrs == JSPROP_ENUMERATE || + (parent == pobj && + parent->isCall() && + (old = ((Shape *) prop)->attributes(), + !(old & (JSPROP_GETTER|JSPROP_SETTER)) && + (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs))) { + /* + * js_CheckRedeclaration must reject attempts to add a getter or + * setter to an existing property without a getter or setter. + */ + JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); + JS_ASSERT_IF(attrs != JSPROP_ENUMERATE, !(old & JSPROP_READONLY)); + doSet = true; } pobj->dropProperty(cx, prop); } - Value rval = ObjectValue(*obj); ok = doSet ? parent->setProperty(cx, id, &rval, script->strictModeCode) diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 87c60d8e5df5..508c2f35dc5f 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -914,6 +914,9 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) */ JSObject *parent = &fp->varobj(cx); + uint32 old; + bool doSet; + /* * Check for a const property of the same name -- or any kind of property * if executing with the strict option. We check here at runtime as well @@ -937,27 +940,25 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) * setters that store the value of the property in the interpreter frame, * see bug 467495. */ - bool doSet = false; + doSet = false; if (prop) { JS_ASSERT((attrs == JSPROP_ENUMERATE) == fp->isEvalFrame()); - if (attrs == JSPROP_ENUMERATE || (parent == pobj && parent->isCall())) { - JS_ASSERT(pobj->isNative()); - uintN oldAttrs = ((Shape *) prop)->attributes(); - - if (!(oldAttrs & (JSPROP_GETTER|JSPROP_SETTER)) && - (oldAttrs & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs) { - /* - * js_CheckRedeclaration must reject attempts to add a getter or - * setter to an existing property without a getter or setter. - */ - JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); - JS_ASSERT(!(oldAttrs & JSPROP_READONLY)); - doSet = true; - } + if (attrs == JSPROP_ENUMERATE || + (parent == pobj && + parent->isCall() && + (old = ((Shape *) prop)->attributes(), + !(old & (JSPROP_GETTER|JSPROP_SETTER)) && + (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs))) { + /* + * js_CheckRedeclaration must reject attempts to add a getter or + * setter to an existing property without a getter or setter. + */ + JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); + JS_ASSERT_IF(attrs != JSPROP_ENUMERATE, !(old & JSPROP_READONLY)); + doSet = true; } pobj->dropProperty(cx, prop); } - Value rval = ObjectValue(*obj); ok = doSet ? parent->setProperty(cx, id, &rval, strict) From 2eddcbcb39358563ec4b1dae8b00fb1a72aa67dc Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 7 Oct 2010 15:35:08 -0500 Subject: [PATCH 087/284] Back out changeset 7598b7ab2e76. --HG-- extra : rebase_source : e3571c6433d01e53ba31639d4dc082ced93775f0 --- js/src/jsobj.cpp | 193 +++++++++++++++++++++++------------------------ 1 file changed, 93 insertions(+), 100 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 44e7af02b9b9..ff866569a37e 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -998,93 +998,12 @@ EvalCacheHash(JSContext *cx, JSString *str) return &JS_SCRIPTS_TO_GC(cx)[h]; } -static JS_ALWAYS_INLINE JSScript * -EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel, - JSPrincipals *principals, JSObject *scopeobj, JSScript ***bucketp) -{ - /* - * Cache local eval scripts indexed by source qualified by scope. - * - * An eval cache entry should never be considered a hit unless its - * strictness matches that of the new eval code. The existing code takes - * care of this, because hits are qualified by the function from which - * eval was called, whose strictness doesn't change. Scripts produced by - * calls to eval from global code are not cached. - */ - JSScript **bucket = EvalCacheHash(cx, str); - *bucketp = bucket; - uintN count = 0; - JSScript **scriptp = bucket; - - EVAL_CACHE_METER(probe); - JSVersion version = cx->findVersion(); - JSScript *script; - while ((script = *scriptp) != NULL) { - if (script->savedCallerFun && - script->staticLevel == staticLevel && - script->version == version && - (script->principals == principals || - (principals->subsume(principals, script->principals) && - script->principals->subsume(script->principals, principals)))) { - /* - * Get the prior (cache-filling) eval's saved caller function. - * See Compiler::compileScript in jsparse.cpp. - */ - JSFunction *fun = script->getFunction(0); - - if (fun == caller->fun()) { - /* - * Get the source string passed for safekeeping in the - * atom map by the prior eval to Compiler::compileScript. - */ - JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); - - if (src == str || js_EqualStrings(src, str)) { - /* - * Source matches, qualify by comparing scopeobj to the - * COMPILE_N_GO-memoized parent of the first literal - * function or regexp object if any. If none, then this - * script has no compiled-in dependencies on the prior - * eval's scopeobj. - */ - JSObjectArray *objarray = script->objects(); - int i = 1; - - if (objarray->length == 1) { - if (script->regexpsOffset != 0) { - objarray = script->regexps(); - i = 0; - } else { - EVAL_CACHE_METER(noscope); - i = -1; - } - } - if (i < 0 || - objarray->vector[i]->getParent() == scopeobj) { - JS_ASSERT(staticLevel == script->staticLevel); - EVAL_CACHE_METER(hit); - *scriptp = script->u.nextToGC; - script->u.nextToGC = NULL; - return script; - } - } - } - } - - if (++count == EVAL_CACHE_CHAIN_LIMIT) - return NULL; - EVAL_CACHE_METER(step); - scriptp = &script->u.nextToGC; - } - return NULL; -} - static JSBool obj_eval(JSContext *cx, uintN argc, Value *vp) { if (argc < 1) { vp->setUndefined(); - return true; + return JS_TRUE; } JSStackFrame *caller = js_GetScriptedCaller(cx, NULL); @@ -1092,7 +1011,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) /* Eval code needs to inherit principals from the caller. */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL, js_eval_str); - return false; + return JS_FALSE; } jsbytecode *callerPC = caller->pc(cx); @@ -1108,7 +1027,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) Value *argv = JS_ARGV(cx, vp); if (!argv[0].isString()) { *vp = argv[0]; - return true; + return JS_TRUE; } /* @@ -1121,25 +1040,28 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) "Support for eval(code, scopeObject) has been removed. " "Use |with (scopeObject) eval(code);| instead."; if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING)) - return false; + return JS_FALSE; caller->script()->warnedAboutTwoArgumentEval = true; } + /* From here on, control must exit through label out with ok set. */ + MUST_FLOW_THROUGH("out"); + uintN staticLevel = caller->script()->staticLevel + 1; + + JSObject *scopeobj; + /* * Per ES5, if we see an indirect call, then run in the global scope. * (eval is specified this way so that the compiler can make assumptions * about what bindings may or may not exist in the current frame if it * doesn't see 'eval'.) */ - uintN staticLevel; - JSObject *scopeobj; if (directCall) { /* Compile using the caller's current scope object. */ - staticLevel = caller->script()->staticLevel + 1; scopeobj = js_GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); if (!scopeobj) - return false; + return JS_FALSE; JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj()); } else { @@ -1151,7 +1073,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) /* Ensure we compile this eval with the right object in the scope chain. */ JSObject *result = CheckScopeChainValidity(cx, scopeobj, js_eval_str); if (!result) - return false; + return JS_FALSE; JS_ASSERT(result == scopeobj); /* @@ -1160,7 +1082,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) */ if (!js_CheckContentSecurityPolicy(cx)) { JS_ReportError(cx, "call to eval() blocked by CSP"); - return false; + return JS_FALSE; } JSObject *callee = &vp[0].toObject(); @@ -1189,13 +1111,86 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) ok = js_ConsumeJSONText(cx, jp, chars+1, length-2); ok &= js_FinishJSONParse(cx, jp, NullValue()); if (ok) - return true; + return JS_TRUE; } } - JSScript **bucket; - if (directCall && caller->isFunctionFrame()) - script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, &bucket); + /* + * Cache local eval scripts indexed by source qualified by scope. + * + * An eval cache entry should never be considered a hit unless its + * strictness matches that of the new eval code. The existing code takes + * care of this, because hits are qualified by the function from which + * eval was called, whose strictness doesn't change. Scripts produced by + * calls to eval from global code are not cached. + */ + JSScript **bucket = EvalCacheHash(cx, str); + if (directCall && caller->isFunctionFrame()) { + uintN count = 0; + JSScript **scriptp = bucket; + + EVAL_CACHE_METER(probe); + JSVersion version = cx->findVersion(); + while ((script = *scriptp) != NULL) { + if (script->savedCallerFun && + script->staticLevel == staticLevel && + script->version == version && + (script->principals == principals || + (principals->subsume(principals, script->principals) && + script->principals->subsume(script->principals, principals)))) { + /* + * Get the prior (cache-filling) eval's saved caller function. + * See Compiler::compileScript in jsparse.cpp. + */ + JSFunction *fun = script->getFunction(0); + + if (fun == caller->fun()) { + /* + * Get the source string passed for safekeeping in the + * atom map by the prior eval to Compiler::compileScript. + */ + JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); + + if (src == str || js_EqualStrings(src, str)) { + /* + * Source matches, qualify by comparing scopeobj to the + * COMPILE_N_GO-memoized parent of the first literal + * function or regexp object if any. If none, then this + * script has no compiled-in dependencies on the prior + * eval's scopeobj. + */ + JSObjectArray *objarray = script->objects(); + int i = 1; + + if (objarray->length == 1) { + if (script->regexpsOffset != 0) { + objarray = script->regexps(); + i = 0; + } else { + EVAL_CACHE_METER(noscope); + i = -1; + } + } + if (i < 0 || + objarray->vector[i]->getParent() == scopeobj) { + JS_ASSERT(staticLevel == script->staticLevel); + EVAL_CACHE_METER(hit); + *scriptp = script->u.nextToGC; + script->u.nextToGC = NULL; + break; + } + } + } + } + + if (++count == EVAL_CACHE_CHAIN_LIMIT) { + script = NULL; + break; + } + EVAL_CACHE_METER(step); + scriptp = &script->u.nextToGC; + } + } /* * We can't have a callerFrame (down in js_Execute's terms) if we're in @@ -1210,7 +1205,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) chars, length, NULL, file, line, str, staticLevel); if (!script) - return false; + return JS_FALSE; } assertSameCompartment(cx, scopeobj, script); @@ -1223,10 +1218,8 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) cx->runtime->atomState.evalAtom) && Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp); - if (bucket) { - script->u.nextToGC = *bucket; - *bucket = script; - } + script->u.nextToGC = *bucket; + *bucket = script; #ifdef CHECK_SCRIPT_OWNER script->owner = NULL; #endif From 450b5d785683f28e024729c3c1202072e66b6203 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 7 Oct 2010 15:01:45 -0500 Subject: [PATCH 088/284] Bug 592644 - ES5 indirect eval. Part 2, factor out EvalCacheLookup and other misc. cleanup. r=brendan. --- js/src/jsobj.cpp | 193 ++++++++++++++++++++++++----------------------- 1 file changed, 100 insertions(+), 93 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index ff866569a37e..e8c828e2826a 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -998,12 +998,93 @@ EvalCacheHash(JSContext *cx, JSString *str) return &JS_SCRIPTS_TO_GC(cx)[h]; } +static JS_ALWAYS_INLINE JSScript * +EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel, + JSPrincipals *principals, JSObject *scopeobj, JSScript ***bucketp) +{ + /* + * Cache local eval scripts indexed by source qualified by scope. + * + * An eval cache entry should never be considered a hit unless its + * strictness matches that of the new eval code. The existing code takes + * care of this, because hits are qualified by the function from which + * eval was called, whose strictness doesn't change. Scripts produced by + * calls to eval from global code are not cached. + */ + JSScript **bucket = EvalCacheHash(cx, str); + *bucketp = bucket; + uintN count = 0; + JSScript **scriptp = bucket; + + EVAL_CACHE_METER(probe); + JSVersion version = cx->findVersion(); + JSScript *script; + while ((script = *scriptp) != NULL) { + if (script->savedCallerFun && + script->staticLevel == staticLevel && + script->version == version && + (script->principals == principals || + (principals->subsume(principals, script->principals) && + script->principals->subsume(script->principals, principals)))) { + /* + * Get the prior (cache-filling) eval's saved caller function. + * See Compiler::compileScript in jsparse.cpp. + */ + JSFunction *fun = script->getFunction(0); + + if (fun == caller->fun()) { + /* + * Get the source string passed for safekeeping in the + * atom map by the prior eval to Compiler::compileScript. + */ + JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); + + if (src == str || js_EqualStrings(src, str)) { + /* + * Source matches, qualify by comparing scopeobj to the + * COMPILE_N_GO-memoized parent of the first literal + * function or regexp object if any. If none, then this + * script has no compiled-in dependencies on the prior + * eval's scopeobj. + */ + JSObjectArray *objarray = script->objects(); + int i = 1; + + if (objarray->length == 1) { + if (script->regexpsOffset != 0) { + objarray = script->regexps(); + i = 0; + } else { + EVAL_CACHE_METER(noscope); + i = -1; + } + } + if (i < 0 || + objarray->vector[i]->getParent() == scopeobj) { + JS_ASSERT(staticLevel == script->staticLevel); + EVAL_CACHE_METER(hit); + *scriptp = script->u.nextToGC; + script->u.nextToGC = NULL; + return script; + } + } + } + } + + if (++count == EVAL_CACHE_CHAIN_LIMIT) + return NULL; + EVAL_CACHE_METER(step); + scriptp = &script->u.nextToGC; + } + return NULL; +} + static JSBool obj_eval(JSContext *cx, uintN argc, Value *vp) { if (argc < 1) { vp->setUndefined(); - return JS_TRUE; + return true; } JSStackFrame *caller = js_GetScriptedCaller(cx, NULL); @@ -1011,7 +1092,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) /* Eval code needs to inherit principals from the caller. */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL, js_eval_str); - return JS_FALSE; + return false; } jsbytecode *callerPC = caller->pc(cx); @@ -1027,7 +1108,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) Value *argv = JS_ARGV(cx, vp); if (!argv[0].isString()) { *vp = argv[0]; - return JS_TRUE; + return true; } /* @@ -1040,28 +1121,25 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) "Support for eval(code, scopeObject) has been removed. " "Use |with (scopeObject) eval(code);| instead."; if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING)) - return JS_FALSE; + return false; caller->script()->warnedAboutTwoArgumentEval = true; } - /* From here on, control must exit through label out with ok set. */ - MUST_FLOW_THROUGH("out"); - uintN staticLevel = caller->script()->staticLevel + 1; - - JSObject *scopeobj; - /* * Per ES5, if we see an indirect call, then run in the global scope. * (eval is specified this way so that the compiler can make assumptions * about what bindings may or may not exist in the current frame if it * doesn't see 'eval'.) */ + uintN staticLevel; + JSObject *scopeobj; if (directCall) { /* Compile using the caller's current scope object. */ + staticLevel = caller->script()->staticLevel + 1; scopeobj = js_GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); if (!scopeobj) - return JS_FALSE; + return false; JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj()); } else { @@ -1073,7 +1151,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) /* Ensure we compile this eval with the right object in the scope chain. */ JSObject *result = CheckScopeChainValidity(cx, scopeobj, js_eval_str); if (!result) - return JS_FALSE; + return false; JS_ASSERT(result == scopeobj); /* @@ -1082,7 +1160,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) */ if (!js_CheckContentSecurityPolicy(cx)) { JS_ReportError(cx, "call to eval() blocked by CSP"); - return JS_FALSE; + return false; } JSObject *callee = &vp[0].toObject(); @@ -1111,86 +1189,13 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) ok = js_ConsumeJSONText(cx, jp, chars+1, length-2); ok &= js_FinishJSONParse(cx, jp, NullValue()); if (ok) - return JS_TRUE; + return true; } } - /* - * Cache local eval scripts indexed by source qualified by scope. - * - * An eval cache entry should never be considered a hit unless its - * strictness matches that of the new eval code. The existing code takes - * care of this, because hits are qualified by the function from which - * eval was called, whose strictness doesn't change. Scripts produced by - * calls to eval from global code are not cached. - */ - JSScript **bucket = EvalCacheHash(cx, str); - if (directCall && caller->isFunctionFrame()) { - uintN count = 0; - JSScript **scriptp = bucket; - - EVAL_CACHE_METER(probe); - JSVersion version = cx->findVersion(); - while ((script = *scriptp) != NULL) { - if (script->savedCallerFun && - script->staticLevel == staticLevel && - script->version == version && - (script->principals == principals || - (principals->subsume(principals, script->principals) && - script->principals->subsume(script->principals, principals)))) { - /* - * Get the prior (cache-filling) eval's saved caller function. - * See Compiler::compileScript in jsparse.cpp. - */ - JSFunction *fun = script->getFunction(0); - - if (fun == caller->fun()) { - /* - * Get the source string passed for safekeeping in the - * atom map by the prior eval to Compiler::compileScript. - */ - JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); - - if (src == str || js_EqualStrings(src, str)) { - /* - * Source matches, qualify by comparing scopeobj to the - * COMPILE_N_GO-memoized parent of the first literal - * function or regexp object if any. If none, then this - * script has no compiled-in dependencies on the prior - * eval's scopeobj. - */ - JSObjectArray *objarray = script->objects(); - int i = 1; - - if (objarray->length == 1) { - if (script->regexpsOffset != 0) { - objarray = script->regexps(); - i = 0; - } else { - EVAL_CACHE_METER(noscope); - i = -1; - } - } - if (i < 0 || - objarray->vector[i]->getParent() == scopeobj) { - JS_ASSERT(staticLevel == script->staticLevel); - EVAL_CACHE_METER(hit); - *scriptp = script->u.nextToGC; - script->u.nextToGC = NULL; - break; - } - } - } - } - - if (++count == EVAL_CACHE_CHAIN_LIMIT) { - script = NULL; - break; - } - EVAL_CACHE_METER(step); - scriptp = &script->u.nextToGC; - } - } + JSScript **bucket = NULL; + if (directCall && caller->isFunctionFrame()) + script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, &bucket); /* * We can't have a callerFrame (down in js_Execute's terms) if we're in @@ -1205,7 +1210,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) chars, length, NULL, file, line, str, staticLevel); if (!script) - return JS_FALSE; + return false; } assertSameCompartment(cx, scopeobj, script); @@ -1218,8 +1223,10 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) cx->runtime->atomState.evalAtom) && Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp); - script->u.nextToGC = *bucket; - *bucket = script; + if (bucket) { + script->u.nextToGC = *bucket; + *bucket = script; + } #ifdef CHECK_SCRIPT_OWNER script->owner = NULL; #endif From 9e9b0bfd945a9e0b67852e8ca687ec523c8e926e Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 1 Oct 2010 18:40:11 -0700 Subject: [PATCH 089/284] Bug 601307 - Add a test for directness of the eval in |with (...) eval(...)| (assuming eval is the global eval function). r=jimb --HG-- extra : rebase_source : 9a6b233cd99a3f9016c760f1249497d5a39d0c55 --- .../Global/eval-inside-with-is-direct.js | 28 +++++++++++++++++++ js/src/tests/ecma_5/Global/jstests.list | 1 + 2 files changed, 29 insertions(+) create mode 100644 js/src/tests/ecma_5/Global/eval-inside-with-is-direct.js diff --git a/js/src/tests/ecma_5/Global/eval-inside-with-is-direct.js b/js/src/tests/ecma_5/Global/eval-inside-with-is-direct.js new file mode 100644 index 000000000000..d5d66483e0d9 --- /dev/null +++ b/js/src/tests/ecma_5/Global/eval-inside-with-is-direct.js @@ -0,0 +1,28 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 601307; +var summary = "with (...) eval(...) is a direct eval"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var t = "global"; +function test() +{ + var t = "local"; + with ({}) + return eval("t"); +} +assertEq(test(), "local"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/ecma_5/Global/jstests.list b/js/src/tests/ecma_5/Global/jstests.list index 42a4f6e9cbbd..44ffdbc69a71 100644 --- a/js/src/tests/ecma_5/Global/jstests.list +++ b/js/src/tests/ecma_5/Global/jstests.list @@ -2,3 +2,4 @@ url-prefix ../../jsreftest.html?test=ecma_5/Global/ script parseInt-01.js script eval-01.js script eval-02.js +script eval-inside-with-is-direct.js From 94009aa8b6460e473913035418fedf5d5808b150 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 28 Sep 2010 17:09:40 -0700 Subject: [PATCH 090/284] Bug 600128 - Properly record attempted addition of properties to non-extensible objects. r=jimb --HG-- extra : rebase_source : af730b39d1f5d6b299e74278ec22d1920ec96e2c --- js/src/jsobj.cpp | 5 +++ js/src/tests/js1_8_5/extensions/jstests.list | 1 + .../extensions/set-property-non-extensible.js | 37 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 js/src/tests/js1_8_5/extensions/set-property-non-extensible.js diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index e8c828e2826a..d8a50d6d6567 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -5339,6 +5339,11 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, added = false; if (!shape) { if (!obj->isExtensible()) { + if (defineHow & JSDNP_CACHE_RESULT) { + JS_ASSERT_NOT_ON_TRACE(cx); + TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, shape); + } + /* Error in strict mode code, warn with strict option, otherwise do nothing. */ if (strict) return obj->reportNotExtensible(cx); diff --git a/js/src/tests/js1_8_5/extensions/jstests.list b/js/src/tests/js1_8_5/extensions/jstests.list index 317a33067484..ba12a1cdecfa 100644 --- a/js/src/tests/js1_8_5/extensions/jstests.list +++ b/js/src/tests/js1_8_5/extensions/jstests.list @@ -20,3 +20,4 @@ skip-if(!xulRuntime.shell) script clone-regexp.js skip-if(!xulRuntime.shell) script clone-object.js skip-if(!xulRuntime.shell) script clone-typed-array.js skip-if(!xulRuntime.shell) script clone-errors.js +skip-if(!xulRuntime.shell) script set-property-non-extensible.js # methodjit mis-implements extensibility checking, bug 602441 diff --git a/js/src/tests/js1_8_5/extensions/set-property-non-extensible.js b/js/src/tests/js1_8_5/extensions/set-property-non-extensible.js new file mode 100644 index 000000000000..79ec7b18b612 --- /dev/null +++ b/js/src/tests/js1_8_5/extensions/set-property-non-extensible.js @@ -0,0 +1,37 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 600128; +var summary = + "Properly handle attempted addition of properties to non-extensible objects"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +if (typeof options === "function" && + options().split(",").indexOf("methodjit") < 0) +{ + var o = Object.freeze({}); + for (var i = 0; i < 10; i++) + print(o.u = ""); +} +else +{ + print("non-extensible+loop adding property disabled, bug 602441"); +} + +Object.freeze(this); +for (let j = 0; j < 10; j++) + print(u = ""); + + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); From ebb0ade8cbda68da7bd78f8ccc721cf4ce8eb755 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 7 Oct 2010 15:51:57 -0700 Subject: [PATCH 091/284] Bug 601009 - TM: allow for guards that always exit. r=dmandelin. --- js/src/jstracer.cpp | 176 ++++++++++++++++++++++++++++---------------- js/src/jstracer.h | 19 +++-- 2 files changed, 125 insertions(+), 70 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 11de0b96ea68..c325302efe6a 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -4397,18 +4397,33 @@ TraceRecorder::createGuardRecord(VMSideExit* exit) /* * Emit a guard for condition (cond), expecting to evaluate to boolean result - * (expected) and using the supplied side exit if the conditon doesn't hold. + * (expected) and using the supplied side exit if the condition doesn't hold. + * + * Callers shouldn't generate guards that always exit (which can occur due to + * optimization of the guard condition) because it's bad for both compile-time + * speed (all the code generated after the guard is dead) and run-time speed + * (fragment that always exit are slow). This function has two modes for + * handling an always-exit guard; which mode is used depends on the value of + * abortIfAlwaysExits: + * + * - abortIfAlwaysExits == false: This is the default mode. If the guard + * will always exit, we assert (in debug builds) as a signal that we are + * generating bad traces. (In optimized builds that lack assertions the + * guard will be generated correctly, so the code will be slow but safe.) In + * this mode, the caller is responsible for not generating an always-exit + * guard. The return value will always be RECORD_CONTINUE, so the caller + * need not check it. + * + * - abortIfAlwaysExits == true: If the guard will always exit, we abort + * recording and return RECORD_STOP; otherwise we generate the guard + * normally and return RECORD_CONTINUE. This mode can be used when the + * caller doesn't know ahead of time whether the guard will always exit. In + * this mode, the caller must check the return value. */ -JS_REQUIRES_STACK void -TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit) +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit, + bool abortIfAlwaysExits/* = false */) { - debug_only_printf(LC_TMRecorder, - " About to try emitting guard code for " - "SideExit=%p exitType=%s\n", - (void*)exit, getExitName(exit->exitType)); - - GuardRecord* guardRec = createGuardRecord(exit); - if (exit->exitType == LOOP_EXIT) tree->sideExits.add(exit); @@ -4417,12 +4432,42 @@ TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit) cond = cond->isI() ? lir->insEqI_0(cond) : lir->insEqP_0(cond); } - LIns* guardIns = - lir->insGuard(expected ? LIR_xf : LIR_xt, cond, guardRec); - if (!guardIns) { - debug_only_print0(LC_TMRecorder, - " redundant guard, eliminated, no codegen\n"); + if ((cond->isImmI(0) && expected) || (cond->isImmI(1) && !expected)) { + if (abortIfAlwaysExits) { + /* The guard always exits, the caller must check for an abort. */ + RETURN_STOP("Constantly false guard detected"); + } + /* + * If this assertion fails, first decide if you want recording to + * abort in the case where the guard always exits. If not, find a way + * to detect that case and avoid calling guard(). Otherwise, change + * the invocation of guard() so it passes in abortIfAlwaysExits=true, + * and have the caller check the return value, eg. using + * CHECK_STATUS(). (In optimized builds, we'll fall through to the + * insGuard() below and an always-exits guard will be inserted, which + * is correct but sub-optimal.) + */ + JS_ASSERT(0); } + + /* + * Nb: if the guard is never taken, no instruction will be created and + * insGuard() will return NULL. This is a good thing. + * */ + lir->insGuard(expected ? LIR_xf : LIR_xt, cond, createGuardRecord(exit)); + return RECORD_CONTINUE; +} + +/* + * Emit a guard for condition (cond), expecting to evaluate to boolean result + * (expected) and generate a side exit with type exitType to jump to if the + * condition does not hold. + */ +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType, + bool abortIfAlwaysExits/* = false */) +{ + return guard(expected, cond, snapshot(exitType), abortIfAlwaysExits); } /* @@ -4481,17 +4526,6 @@ TraceRecorder::copy(VMSideExit* copy) return exit; } -/* - * Emit a guard for condition (cond), expecting to evaluate to boolean result - * (expected) and generate a side exit with type exitType to jump to if the - * condition does not hold. - */ -JS_REQUIRES_STACK void -TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType) -{ - guard(expected, cond, snapshot(exitType)); -} - /* * Determine whether any context associated with the same thread as cx is * executing native code. @@ -8533,21 +8567,22 @@ TraceRecorder::f2u(LIns* f) return lir->insCall(&js_DoubleToUint32_ci, &f); } -JS_REQUIRES_STACK LIns* -TraceRecorder::makeNumberInt32(LIns* f) +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::makeNumberInt32(LIns* d, LIns** out) { - JS_ASSERT(f->isD()); - LIns* x; - if (!isPromote(f)) { - // This means "convert double to int if it's integral, otherwise - // exit". We first convert the double to an int, then convert it back - // and exit if the two doubles don't match. - x = d2i(f, /* resultCanBeImpreciseIfFractional = */true); - guard(true, lir->ins2(LIR_eqd, f, lir->ins1(LIR_i2d, x)), MISMATCH_EXIT); - } else { - x = demote(lir, f); + JS_ASSERT(d->isD()); + if (isPromote(d)) { + *out = demote(lir, d); + return RECORD_CONTINUE; } - return x; + + // This means "convert double to int if it's integral, otherwise + // exit". We first convert the double to an int, then convert it back + // and exit if the two doubles don't match. If 'f' is a non-integral + // immediate we'll end up aborting. + *out = d2i(d, /* resultCanBeImpreciseIfFractional = */true); + return guard(true, lir->ins2(LIR_eqd, d, i2d(*out)), MISMATCH_EXIT, + /* abortIfAlwaysExits = */true); } JS_REQUIRES_STACK LIns* @@ -11265,9 +11300,14 @@ TraceRecorder::callNative(uintN argc, JSOp mode) if (vp[1].isString()) { JSString *str = vp[1].toString(); if (native == js_str_charAt) { + jsdouble i = vp[2].toNumber(); + if (i < 0 || i >= str->length()) + RETURN_STOP("charAt out of bounds"); LIns* str_ins = get(&vp[1]); LIns* idx_ins = get(&vp[2]); - set(&vp[0], getCharAt(str, str_ins, idx_ins, mode)); + LIns* char_ins; + CHECK_STATUS(getCharAt(str, str_ins, idx_ins, mode, &char_ins)); + set(&vp[0], char_ins); pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; return RECORD_CONTINUE; } else if (native == js_str_charCodeAt) { @@ -11276,7 +11316,9 @@ TraceRecorder::callNative(uintN argc, JSOp mode) RETURN_STOP("charCodeAt out of bounds"); LIns* str_ins = get(&vp[1]); LIns* idx_ins = get(&vp[2]); - set(&vp[0], getCharCodeAt(str, str_ins, idx_ins)); + LIns* charCode_ins; + CHECK_STATUS(getCharCodeAt(str, str_ins, idx_ins, &charCode_ins)); + set(&vp[0], charCode_ins); pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; return RECORD_CONTINUE; } @@ -11649,7 +11691,9 @@ TraceRecorder::record_JSOP_DELELEM() enterDeepBailCall(); if (hasInt32Repr(idx)) { - LIns* args[] = { strictModeCode_ins, makeNumberInt32(get(&idx)), get(&lval), cx_ins }; + LIns* num_ins; + CHECK_STATUS_A(makeNumberInt32(get(&idx), &num_ins)); + LIns* args[] = { strictModeCode_ins, num_ins, get(&lval), cx_ins }; rval_ins = lir->insCall(&DeleteIntKey_ci, args); } else if (idx.isString()) { LIns* args[] = { strictModeCode_ins, get(&idx), get(&lval), cx_ins }; @@ -12312,7 +12356,7 @@ JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyByIndex, CONTEXT, OBJECT, INT JS_REQUIRES_STACK RecordingStatus TraceRecorder::getPropertyByIndex(LIns* obj_ins, LIns* index_ins, Value* outp) { - index_ins = makeNumberInt32(index_ins); + CHECK_STATUS(makeNumberInt32(index_ins, &index_ins)); // See note in getPropertyByName about vp. enterDeepBailCall(); @@ -12475,10 +12519,11 @@ TraceRecorder::getStringChars(LIns* str_ins) ACCSET_OTHER), "chars"); } -JS_REQUIRES_STACK LIns* -TraceRecorder::getCharCodeAt(JSString *str, LIns* str_ins, LIns* idx_ins) +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::getCharCodeAt(JSString *str, LIns* str_ins, LIns* idx_ins, LIns** out) { - idx_ins = lir->insUI2P(makeNumberInt32(idx_ins)); + CHECK_STATUS(makeNumberInt32(idx_ins, &idx_ins)); + idx_ins = lir->insUI2P(idx_ins); LIns *length_ins = lir->insLoad(LIR_ldp, str_ins, offsetof(JSString, mLengthAndFlags), ACCSET_OTHER); LIns *br = lir->insBranch(LIR_jt, @@ -12493,9 +12538,10 @@ TraceRecorder::getCharCodeAt(JSString *str, LIns* str_ins, LIns* idx_ins) lir->ins2(LIR_ltup, idx_ins, lir->ins2ImmI(LIR_rshup, length_ins, JSString::FLAGS_LENGTH_SHIFT)), snapshot(MISMATCH_EXIT)); LIns *chars_ins = getStringChars(str_ins); - return i2d(lir->insLoad(LIR_ldus2ui, + *out = i2d(lir->insLoad(LIR_ldus2ui, lir->ins2(LIR_addp, chars_ins, lir->ins2ImmI(LIR_lshp, idx_ins, 1)), 0, ACCSET_OTHER, LOAD_CONST)); + return RECORD_CONTINUE; } JS_STATIC_ASSERT(sizeof(JSString) == 16 || sizeof(JSString) == 32); @@ -12518,10 +12564,11 @@ TraceRecorder::getUnitString(LIns* str_ins, LIns* idx_ins) (sizeof(JSString) == 16) ? 4 : 5)); } -JS_REQUIRES_STACK LIns* -TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode) +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode, LIns** out) { - idx_ins = lir->insUI2P(makeNumberInt32(idx_ins)); + CHECK_STATUS(makeNumberInt32(idx_ins, &idx_ins)); + idx_ins = lir->insUI2P(idx_ins); LIns *length_ins = lir->insLoad(LIR_ldp, str_ins, offsetof(JSString, mLengthAndFlags), ACCSET_OTHER); @@ -12537,11 +12584,10 @@ TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode) idx_ins, lir->ins2ImmI(LIR_rshup, length_ins, JSString::FLAGS_LENGTH_SHIFT)); - LIns* ret; if (mode == JSOP_GETELEM) { guard(true, inRange, MISMATCH_EXIT); - ret = getUnitString(str_ins, idx_ins); + *out = getUnitString(str_ins, idx_ins); } else { LIns *phi_ins = lir->insAlloc(sizeof(JSString *)); lir->insStore(LIR_stp, INS_CONSTSTR(cx->runtime->emptyString), phi_ins, 0, ACCSET_OTHER); @@ -12551,9 +12597,9 @@ TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode) lir->insStore(LIR_stp, unitstr_ins, phi_ins, 0, ACCSET_OTHER); label(br2); - ret = lir->insLoad(LIR_ldp, phi_ins, 0, ACCSET_OTHER); + *out = lir->insLoad(LIR_ldp, phi_ins, 0, ACCSET_OTHER); } - return ret; + return RECORD_CONTINUE; } // Typed array tracing depends on EXPANDED_LOADSTORE and F2I @@ -12596,7 +12642,9 @@ TraceRecorder::record_JSOP_GETELEM() int i = asInt32(idx); if (size_t(i) >= lval.toString()->length()) RETURN_STOP_A("Invalid string index in JSOP_GETELEM"); - set(&lval, getCharAt(lval.toString(), obj_ins, idx_ins, JSOP_GETELEM)); + LIns* char_ins; + CHECK_STATUS_A(getCharAt(lval.toString(), obj_ins, idx_ins, JSOP_GETELEM, &char_ins)); + set(&lval, char_ins); return ARECORD_CONTINUE; } @@ -12638,7 +12686,7 @@ TraceRecorder::record_JSOP_GETELEM() // If the index is not a constant expression, we generate LIR to load the value from // the native stack area. The guard on js_ArgumentClass above ensures the up-to-date // value has been written back to the native stack area. - idx_ins = makeNumberInt32(idx_ins); + CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins)); /* * For small nactual, @@ -12832,7 +12880,7 @@ JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, InitPropertyByIndex, CONTEXT, OBJECT, IN JS_REQUIRES_STACK RecordingStatus TraceRecorder::initOrSetPropertyByIndex(LIns* obj_ins, LIns* index_ins, Value* rvalp, bool init) { - index_ins = makeNumberInt32(index_ins); + CHECK_STATUS(makeNumberInt32(index_ins, &index_ins)); if (init) { LIns* rval_ins = box_value_for_native_call(*rvalp, get(rvalp)); @@ -12891,7 +12939,7 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) // The index was on the stack and is therefore a LIR float; force it to // be an integer. - idx_ins = makeNumberInt32(idx_ins); + CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins)); // Ensure idx >= 0 && idx < length (by using uint32) lir->insGuard(LIR_xf, @@ -13006,7 +13054,7 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) // The index was on the stack and is therefore a LIR float. Force it to // be an integer. - idx_ins = makeNumberInt32(idx_ins); + CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins)); if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) { /* @@ -13874,7 +13922,8 @@ TraceRecorder::denseArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_ JSObject* obj = &oval.toObject(); LIns* obj_ins = get(&oval); jsint idx = ival.toInt32(); - LIns* idx_ins = makeNumberInt32(get(&ival)); + LIns* idx_ins; + CHECK_STATUS(makeNumberInt32(get(&ival), &idx_ins)); /* * Arrays have both a length and a capacity, but we only need to check @@ -13938,7 +13987,8 @@ TraceRecorder::typedArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_ JSObject* obj = &oval.toObject(); LIns* obj_ins = get(&oval); jsint idx = ival.toInt32(); - LIns* idx_ins = makeNumberInt32(get(&ival)); + LIns* idx_ins; + CHECK_STATUS_A(makeNumberInt32(get(&ival), &idx_ins)); LIns* pidx_ins = lir->insUI2P(idx_ins); js::TypedArray* tarray = js::TypedArray::fromJSObject(obj); @@ -14856,7 +14906,9 @@ TraceRecorder::record_JSOP_IN() if (lval.isInt32()) { if (!js_Int32ToId(cx, lval.toInt32(), &id)) RETURN_ERROR_A("OOM converting left operand of JSOP_IN to string"); - LIns* args[] = { makeNumberInt32(get(&lval)), obj_ins, cx_ins }; + LIns* num_ins; + CHECK_STATUS_A(makeNumberInt32(get(&lval), &num_ins)); + LIns* args[] = { num_ins, obj_ins, cx_ins }; x = lir->insCall(&js_HasNamedPropertyInt32_ci, args); } else if (lval.isString()) { if (!js_ValueToStringId(cx, lval, &id)) diff --git a/js/src/jstracer.h b/js/src/jstracer.h index a0416fab114f..749209e035e0 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1032,8 +1032,10 @@ class TraceRecorder JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot); JS_REQUIRES_STACK void importGlobalSlot(unsigned slot); - JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, ExitType exitType); - JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, VMSideExit* exit); + JS_REQUIRES_STACK RecordingStatus guard(bool expected, nanojit::LIns* cond, ExitType exitType, + bool abortIfAlwaysExits = false); + JS_REQUIRES_STACK RecordingStatus guard(bool expected, nanojit::LIns* cond, VMSideExit* exit, + bool abortIfAlwaysExits = false); JS_REQUIRES_STACK nanojit::LIns* guard_xov(nanojit::LOpcode op, nanojit::LIns* d0, nanojit::LIns* d1, VMSideExit* exit); @@ -1118,7 +1120,7 @@ class TraceRecorder nanojit::LIns* i2d(nanojit::LIns* i); nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false); nanojit::LIns* f2u(nanojit::LIns* f); - JS_REQUIRES_STACK nanojit::LIns* makeNumberInt32(nanojit::LIns* f); + JS_REQUIRES_STACK RecordingStatus makeNumberInt32(nanojit::LIns* d, nanojit::LIns** num_ins); JS_REQUIRES_STACK nanojit::LIns* stringify(const Value& v); JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins, bool strict); @@ -1233,12 +1235,13 @@ class TraceRecorder JS_REQUIRES_STACK nanojit::LIns* getStringLength(nanojit::LIns* str_ins); JS_REQUIRES_STACK nanojit::LIns* getStringChars(nanojit::LIns* str_ins); - JS_REQUIRES_STACK nanojit::LIns* getCharCodeAt(JSString *str, - nanojit::LIns* str_ins, nanojit::LIns* idx_ins); + JS_REQUIRES_STACK RecordingStatus getCharCodeAt(JSString *str, + nanojit::LIns* str_ins, nanojit::LIns* idx_ins, + nanojit::LIns** out_ins); JS_REQUIRES_STACK nanojit::LIns* getUnitString(nanojit::LIns* str_ins, nanojit::LIns* idx_ins); - JS_REQUIRES_STACK nanojit::LIns* getCharAt(JSString *str, - nanojit::LIns* str_ins, nanojit::LIns* idx_ins, - JSOp mode); + JS_REQUIRES_STACK RecordingStatus getCharAt(JSString *str, + nanojit::LIns* str_ins, nanojit::LIns* idx_ins, + JSOp mode, nanojit::LIns** out_ins); JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins, const js::Shape* shape, From 87255fd8167706937f4a04a2a71b249457d9b9c4 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Thu, 7 Oct 2010 16:33:40 -0700 Subject: [PATCH 092/284] Remove obsolete declarations for functions never defined or called --- js/src/jscompartment.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 2c01b6ae3e87..6e8b24c6dcec 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -91,10 +91,6 @@ struct JSCompartment { bool wrapException(JSContext *cx); void sweep(JSContext *cx); -#ifdef JS_METHODJIT - bool addScript(JSContext *cx, JSScript *script); - void removeScript(JSScript *script); -#endif void purge(JSContext *cx); void finishArenaLists(); bool arenaListsAreEmpty(); From 8b91a371acba81c834ce84c567e0b7117686e44e Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Wed, 6 Oct 2010 11:23:14 -0700 Subject: [PATCH 093/284] Bug 602003: add jsd API to query valid script begin and end PCs, r=sayrer,jjb --- js/jsd/idl/jsdIDebuggerService.idl | 11 +++++++++++ js/jsd/jsd.h | 3 +++ js/jsd/jsd_scpt.c | 16 ++++++---------- js/jsd/jsd_xpc.cpp | 23 ++++++++++++++++++++++- js/jsd/jsd_xpc.h | 1 + js/jsd/jsdebug.c | 8 ++++++++ js/jsd/jsdebug.h | 7 +++++++ js/src/jscntxt.cpp | 4 +++- js/src/jsdbgapi.cpp | 14 +++++++++++--- js/src/jsdbgapi.h | 3 +++ 10 files changed, 75 insertions(+), 15 deletions(-) diff --git a/js/jsd/idl/jsdIDebuggerService.idl b/js/jsd/idl/jsdIDebuggerService.idl index 27cd0cd0cc4c..cd6b443ffc8c 100644 --- a/js/jsd/idl/jsdIDebuggerService.idl +++ b/js/jsd/idl/jsdIDebuggerService.idl @@ -1001,6 +1001,17 @@ interface jsdIScript : jsdIEphemeral * The |pcmap| argument specifies which pc to source line map to use. */ boolean isLineExecutable (in unsigned long line, in unsigned long pcmap); + /** + * Get the first valid PC in the script. This will be either + * (a) the first bytecode in the script, or (b) the next bytecode + * in the script, iff the first bytecode is a JSOP_BEGIN. + */ + unsigned long getFirstValidPC (); + /** + * Return the last valid PC in the script (i.e., the PC just after + * the last bytecode). + */ + unsigned long getEndValidPC (); /** * Set a breakpoint at a PC in this script. */ diff --git a/js/jsd/jsd.h b/js/jsd/jsd.h index 58b54ef7a8f7..7516a1948fbe 100644 --- a/js/jsd/jsd.h +++ b/js/jsd/jsd.h @@ -478,6 +478,9 @@ jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) extern jsuword jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line); +extern jsuword +jsd_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript); + extern uintN jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc); diff --git a/js/jsd/jsd_scpt.c b/js/jsd/jsd_scpt.c index 01e96c0668fb..29833c6f0ba1 100644 --- a/js/jsd/jsd_scpt.c +++ b/js/jsd/jsd_scpt.c @@ -500,20 +500,16 @@ jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript) jsuword jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) { -#ifdef LIVEWIRE - if( jsdscript && jsdscript->lwscript ) - { - uintN newline; - jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline); - if( line != newline ) - line = newline; - } -#endif - return (jsuword) JS_LineNumberToPC(jsdc->dumbContext, jsdscript->script, line ); } +jsuword +jsd_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript) +{ + return (jsuword) JS_EndPC(jsdc->dumbContext, jsdscript->script ); +} + uintN jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) { diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 582852fb0fe3..27f3e6af9612 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -958,7 +958,8 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE), mBaseLineNumber(0), mLineExtent(0), mPPLineMap(0), - mFirstPC(0) + mFirstPC(0), + mEndPC(0) { DEBUG_CREATE ("jsdScript", gScriptCount); @@ -972,6 +973,7 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE), mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript); mLineExtent = JSD_GetScriptLineExtent(mCx, mScript); mFirstPC = JSD_GetClosestPC(mCx, mScript, 0); + mEndPC = JSD_GetEndPC(mCx, mScript); JSD_UnlockScriptSubsystem(mCx); mValid = PR_TRUE; @@ -1475,6 +1477,25 @@ jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval) return NS_OK; } +NS_IMETHODIMP +jsdScript::GetFirstValidPC(PRUint32 *_rval) +{ + ASSERT_VALID_EPHEMERAL; + jsbytecode *pc = (jsbytecode *) mFirstPC; + if (*pc == JSOP_BEGIN) + ++pc; + *_rval = PRUint32(pc); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetEndValidPC(PRUint32 *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = PRUint32(mEndPC); + return NS_OK; +} + NS_IMETHODIMP jsdScript::SetBreakpoint(PRUint32 aPC) { diff --git a/js/jsd/jsd_xpc.h b/js/jsd/jsd_xpc.h index 92f08799242c..331a955769a9 100644 --- a/js/jsd/jsd_xpc.h +++ b/js/jsd/jsd_xpc.h @@ -183,6 +183,7 @@ class jsdScript : public jsdIScript PCMapEntry *mPPLineMap; PRUint32 mPCMapSize; jsuword mFirstPC; + jsuword mEndPC; }; PRUint32 jsdScript::LastTag = 0; diff --git a/js/jsd/jsdebug.c b/js/jsd/jsdebug.c index 173c9d060fe4..f5d1c84eb9a3 100644 --- a/js/jsd/jsdebug.c +++ b/js/jsd/jsdebug.c @@ -348,6 +348,14 @@ JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) return jsd_GetClosestPC(jsdc, jsdscript, line); } +JSD_PUBLIC_API(jsuword) +JSD_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetEndPC(jsdc, jsdscript); +} + JSD_PUBLIC_API(uintN) JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) { diff --git a/js/jsd/jsdebug.h b/js/jsd/jsdebug.h index fcdc2e04ee88..55ef1022c97c 100644 --- a/js/jsd/jsdebug.h +++ b/js/jsd/jsdebug.h @@ -486,6 +486,13 @@ JSD_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) extern JSD_PUBLIC_API(jsuword) JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line); +/* +* Get the 'Program Counter' value just after the last byte of the script. +* 0 is returned for invalid scripts. +*/ +extern JSD_PUBLIC_API(jsuword) +JSD_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript); + /* * Get the source line number for a given 'Program Counter' location. * Returns 0 if no source line information is appropriate (or available) for diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 949561b06261..1b2ac3d535e8 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -475,7 +475,9 @@ AllFramesIter::operator++() { JS_ASSERT(!done()); if (curfp == curcs->getInitialFrame()) { - curcs = curcs->getPreviousInMemory(); + do { + curcs = curcs->getPreviousInMemory(); + } while (curcs && !curcs->inContext()); curfp = curcs ? curcs->getCurrentFrame() : NULL; } else { curfp = curfp->prev(); diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index e6fc2e91fec5..cb7132c19581 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -235,9 +235,11 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, return JS_FALSE; } - // Do not trap BEGIN, it's a special prologue opcode. - if (JSOp(*pc) == JSOP_BEGIN) - pc += JSOP_BEGIN_LENGTH; + if (JSOp(*pc) == JSOP_BEGIN) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, + NULL, JSMSG_READ_ONLY, "trap invalid on BEGIN opcode"); + return JS_FALSE; + } JS_ASSERT((JSOp) *pc != JSOP_TRAP); junk = NULL; @@ -1019,6 +1021,12 @@ JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno) return js_LineNumberToPC(script, lineno); } +JS_PUBLIC_API(jsbytecode *) +JS_EndPC(JSContext *cx, JSScript *script) +{ + return script->code + script->length; +} + JS_PUBLIC_API(uintN) JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun) { diff --git a/js/src/jsdbgapi.h b/js/src/jsdbgapi.h index 5855f18c7839..51676f181317 100644 --- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -159,6 +159,9 @@ JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); extern JS_PUBLIC_API(jsbytecode *) JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); +extern JS_PUBLIC_API(jsbytecode *) +JS_EndPC(JSContext *cx, JSScript *script); + extern JS_PUBLIC_API(uintN) JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun); From 4e2d76fbc7fc03e69321a5e3b0c2ca3fcc6cb3f6 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Thu, 7 Oct 2010 17:03:01 -0700 Subject: [PATCH 094/284] Backed out changeset af020f2b9293 due to x64 build bustage. --- js/jsd/idl/jsdIDebuggerService.idl | 11 ----------- js/jsd/jsd.h | 3 --- js/jsd/jsd_scpt.c | 16 ++++++++++------ js/jsd/jsd_xpc.cpp | 23 +---------------------- js/jsd/jsd_xpc.h | 1 - js/jsd/jsdebug.c | 8 -------- js/jsd/jsdebug.h | 7 ------- js/src/jscntxt.cpp | 4 +--- js/src/jsdbgapi.cpp | 14 +++----------- js/src/jsdbgapi.h | 3 --- 10 files changed, 15 insertions(+), 75 deletions(-) diff --git a/js/jsd/idl/jsdIDebuggerService.idl b/js/jsd/idl/jsdIDebuggerService.idl index cd6b443ffc8c..27cd0cd0cc4c 100644 --- a/js/jsd/idl/jsdIDebuggerService.idl +++ b/js/jsd/idl/jsdIDebuggerService.idl @@ -1001,17 +1001,6 @@ interface jsdIScript : jsdIEphemeral * The |pcmap| argument specifies which pc to source line map to use. */ boolean isLineExecutable (in unsigned long line, in unsigned long pcmap); - /** - * Get the first valid PC in the script. This will be either - * (a) the first bytecode in the script, or (b) the next bytecode - * in the script, iff the first bytecode is a JSOP_BEGIN. - */ - unsigned long getFirstValidPC (); - /** - * Return the last valid PC in the script (i.e., the PC just after - * the last bytecode). - */ - unsigned long getEndValidPC (); /** * Set a breakpoint at a PC in this script. */ diff --git a/js/jsd/jsd.h b/js/jsd/jsd.h index 7516a1948fbe..58b54ef7a8f7 100644 --- a/js/jsd/jsd.h +++ b/js/jsd/jsd.h @@ -478,9 +478,6 @@ jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) extern jsuword jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line); -extern jsuword -jsd_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript); - extern uintN jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc); diff --git a/js/jsd/jsd_scpt.c b/js/jsd/jsd_scpt.c index 29833c6f0ba1..01e96c0668fb 100644 --- a/js/jsd/jsd_scpt.c +++ b/js/jsd/jsd_scpt.c @@ -500,16 +500,20 @@ jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript) jsuword jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) { +#ifdef LIVEWIRE + if( jsdscript && jsdscript->lwscript ) + { + uintN newline; + jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline); + if( line != newline ) + line = newline; + } +#endif + return (jsuword) JS_LineNumberToPC(jsdc->dumbContext, jsdscript->script, line ); } -jsuword -jsd_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript) -{ - return (jsuword) JS_EndPC(jsdc->dumbContext, jsdscript->script ); -} - uintN jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) { diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 27f3e6af9612..582852fb0fe3 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -958,8 +958,7 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE), mBaseLineNumber(0), mLineExtent(0), mPPLineMap(0), - mFirstPC(0), - mEndPC(0) + mFirstPC(0) { DEBUG_CREATE ("jsdScript", gScriptCount); @@ -973,7 +972,6 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE), mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript); mLineExtent = JSD_GetScriptLineExtent(mCx, mScript); mFirstPC = JSD_GetClosestPC(mCx, mScript, 0); - mEndPC = JSD_GetEndPC(mCx, mScript); JSD_UnlockScriptSubsystem(mCx); mValid = PR_TRUE; @@ -1477,25 +1475,6 @@ jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval) return NS_OK; } -NS_IMETHODIMP -jsdScript::GetFirstValidPC(PRUint32 *_rval) -{ - ASSERT_VALID_EPHEMERAL; - jsbytecode *pc = (jsbytecode *) mFirstPC; - if (*pc == JSOP_BEGIN) - ++pc; - *_rval = PRUint32(pc); - return NS_OK; -} - -NS_IMETHODIMP -jsdScript::GetEndValidPC(PRUint32 *_rval) -{ - ASSERT_VALID_EPHEMERAL; - *_rval = PRUint32(mEndPC); - return NS_OK; -} - NS_IMETHODIMP jsdScript::SetBreakpoint(PRUint32 aPC) { diff --git a/js/jsd/jsd_xpc.h b/js/jsd/jsd_xpc.h index 331a955769a9..92f08799242c 100644 --- a/js/jsd/jsd_xpc.h +++ b/js/jsd/jsd_xpc.h @@ -183,7 +183,6 @@ class jsdScript : public jsdIScript PCMapEntry *mPPLineMap; PRUint32 mPCMapSize; jsuword mFirstPC; - jsuword mEndPC; }; PRUint32 jsdScript::LastTag = 0; diff --git a/js/jsd/jsdebug.c b/js/jsd/jsdebug.c index f5d1c84eb9a3..173c9d060fe4 100644 --- a/js/jsd/jsdebug.c +++ b/js/jsd/jsdebug.c @@ -348,14 +348,6 @@ JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) return jsd_GetClosestPC(jsdc, jsdscript, line); } -JSD_PUBLIC_API(jsuword) -JSD_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript) -{ - JSD_ASSERT_VALID_CONTEXT(jsdc); - JSD_ASSERT_VALID_SCRIPT(jsdscript); - return jsd_GetEndPC(jsdc, jsdscript); -} - JSD_PUBLIC_API(uintN) JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) { diff --git a/js/jsd/jsdebug.h b/js/jsd/jsdebug.h index 55ef1022c97c..fcdc2e04ee88 100644 --- a/js/jsd/jsdebug.h +++ b/js/jsd/jsdebug.h @@ -486,13 +486,6 @@ JSD_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) extern JSD_PUBLIC_API(jsuword) JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line); -/* -* Get the 'Program Counter' value just after the last byte of the script. -* 0 is returned for invalid scripts. -*/ -extern JSD_PUBLIC_API(jsuword) -JSD_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript); - /* * Get the source line number for a given 'Program Counter' location. * Returns 0 if no source line information is appropriate (or available) for diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 1b2ac3d535e8..949561b06261 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -475,9 +475,7 @@ AllFramesIter::operator++() { JS_ASSERT(!done()); if (curfp == curcs->getInitialFrame()) { - do { - curcs = curcs->getPreviousInMemory(); - } while (curcs && !curcs->inContext()); + curcs = curcs->getPreviousInMemory(); curfp = curcs ? curcs->getCurrentFrame() : NULL; } else { curfp = curfp->prev(); diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index cb7132c19581..e6fc2e91fec5 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -235,11 +235,9 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, return JS_FALSE; } - if (JSOp(*pc) == JSOP_BEGIN) { - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, - NULL, JSMSG_READ_ONLY, "trap invalid on BEGIN opcode"); - return JS_FALSE; - } + // Do not trap BEGIN, it's a special prologue opcode. + if (JSOp(*pc) == JSOP_BEGIN) + pc += JSOP_BEGIN_LENGTH; JS_ASSERT((JSOp) *pc != JSOP_TRAP); junk = NULL; @@ -1021,12 +1019,6 @@ JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno) return js_LineNumberToPC(script, lineno); } -JS_PUBLIC_API(jsbytecode *) -JS_EndPC(JSContext *cx, JSScript *script) -{ - return script->code + script->length; -} - JS_PUBLIC_API(uintN) JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun) { diff --git a/js/src/jsdbgapi.h b/js/src/jsdbgapi.h index 51676f181317..5855f18c7839 100644 --- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -159,9 +159,6 @@ JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); extern JS_PUBLIC_API(jsbytecode *) JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); -extern JS_PUBLIC_API(jsbytecode *) -JS_EndPC(JSContext *cx, JSScript *script); - extern JS_PUBLIC_API(uintN) JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun); From dde8e36f2a796b4ff286300b2394fe52a3e9331a Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 7 Oct 2010 19:15:45 -0500 Subject: [PATCH 095/284] Bug 592664, Epilogue: caching eval scripts that we will never use again is necessary to avoid leaking them. rs=Waldo. (Note that my 4 previous commits today were all for bug 592664, not "592644" as the commit messages say.) --HG-- extra : rebase_source : e7337a31313a4ef3d2d676be41291c832afa4c15 --- js/src/jsobj.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index d8a50d6d6567..58e3d128c9a7 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1000,7 +1000,7 @@ EvalCacheHash(JSContext *cx, JSString *str) static JS_ALWAYS_INLINE JSScript * EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel, - JSPrincipals *principals, JSObject *scopeobj, JSScript ***bucketp) + JSPrincipals *principals, JSObject *scopeobj, JSScript **bucket) { /* * Cache local eval scripts indexed by source qualified by scope. @@ -1011,8 +1011,6 @@ EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN static * eval was called, whose strictness doesn't change. Scripts produced by * calls to eval from global code are not cached. */ - JSScript **bucket = EvalCacheHash(cx, str); - *bucketp = bucket; uintN count = 0; JSScript **scriptp = bucket; @@ -1193,9 +1191,9 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) } } - JSScript **bucket = NULL; + JSScript **bucket = EvalCacheHash(cx, str); if (directCall && caller->isFunctionFrame()) - script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, &bucket); + script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, bucket); /* * We can't have a callerFrame (down in js_Execute's terms) if we're in @@ -1223,10 +1221,8 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) cx->runtime->atomState.evalAtom) && Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp); - if (bucket) { - script->u.nextToGC = *bucket; - *bucket = script; - } + script->u.nextToGC = *bucket; + *bucket = script; #ifdef CHECK_SCRIPT_OWNER script->owner = NULL; #endif From d112487c3d3604736bab02bf72b3bd4f1f1809dc Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Wed, 6 Oct 2010 11:23:14 -0700 Subject: [PATCH 096/284] Bug 602003: add jsd API to query valid script begin and end PCs, r=sayrer,jjb --- js/jsd/idl/jsdIDebuggerService.idl | 11 ++++++ js/jsd/jsd.h | 6 +++ js/jsd/jsd_scpt.c | 22 ++++++----- js/jsd/jsd_xpc.cpp | 22 ++++++++++- js/jsd/jsd_xpc.h | 4 +- js/jsd/jsdebug.c | 16 ++++++++ js/jsd/jsdebug.h | 13 +++++++ js/jsd/test/Makefile.in | 1 + js/jsd/test/test_bug602003.html | 62 ++++++++++++++++++++++++++++++ js/src/jscntxt.cpp | 4 +- js/src/jsdbgapi.cpp | 21 ++++++++-- js/src/jsdbgapi.h | 6 +++ 12 files changed, 172 insertions(+), 16 deletions(-) create mode 100644 js/jsd/test/test_bug602003.html diff --git a/js/jsd/idl/jsdIDebuggerService.idl b/js/jsd/idl/jsdIDebuggerService.idl index 27cd0cd0cc4c..cd6b443ffc8c 100644 --- a/js/jsd/idl/jsdIDebuggerService.idl +++ b/js/jsd/idl/jsdIDebuggerService.idl @@ -1001,6 +1001,17 @@ interface jsdIScript : jsdIEphemeral * The |pcmap| argument specifies which pc to source line map to use. */ boolean isLineExecutable (in unsigned long line, in unsigned long pcmap); + /** + * Get the first valid PC in the script. This will be either + * (a) the first bytecode in the script, or (b) the next bytecode + * in the script, iff the first bytecode is a JSOP_BEGIN. + */ + unsigned long getFirstValidPC (); + /** + * Return the last valid PC in the script (i.e., the PC just after + * the last bytecode). + */ + unsigned long getEndValidPC (); /** * Set a breakpoint at a PC in this script. */ diff --git a/js/jsd/jsd.h b/js/jsd/jsd.h index 58b54ef7a8f7..f39070b5eb97 100644 --- a/js/jsd/jsd.h +++ b/js/jsd/jsd.h @@ -478,6 +478,12 @@ jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) extern jsuword jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line); +extern jsuword +jsd_GetFirstValidPC(JSDContext* jsdc, JSDScript* jsdscript); + +extern jsuword +jsd_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript); + extern uintN jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc); diff --git a/js/jsd/jsd_scpt.c b/js/jsd/jsd_scpt.c index 01e96c0668fb..e599a7310c8a 100644 --- a/js/jsd/jsd_scpt.c +++ b/js/jsd/jsd_scpt.c @@ -500,20 +500,22 @@ jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript) jsuword jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) { -#ifdef LIVEWIRE - if( jsdscript && jsdscript->lwscript ) - { - uintN newline; - jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline); - if( line != newline ) - line = newline; - } -#endif - return (jsuword) JS_LineNumberToPC(jsdc->dumbContext, jsdscript->script, line ); } +jsuword +jsd_GetFirstValidPC(JSDContext* jsdc, JSDScript* jsdscript) +{ + return (jsuword) JS_FirstValidPC(jsdc->dumbContext, jsdscript->script ); +} + +jsuword +jsd_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript) +{ + return (jsuword) JS_EndPC(jsdc->dumbContext, jsdscript->script ); +} + uintN jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) { diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 582852fb0fe3..a2effbb6355f 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -958,7 +958,9 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE), mBaseLineNumber(0), mLineExtent(0), mPPLineMap(0), - mFirstPC(0) + mFirstValidPC(0), + mFirstPC(0), + mEndPC(0) { DEBUG_CREATE ("jsdScript", gScriptCount); @@ -972,6 +974,8 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE), mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript); mLineExtent = JSD_GetScriptLineExtent(mCx, mScript); mFirstPC = JSD_GetClosestPC(mCx, mScript, 0); + mFirstValidPC = JSD_GetFirstValidPC(mCx, mScript); + mEndPC = JSD_GetEndPC(mCx, mScript); JSD_UnlockScriptSubsystem(mCx); mValid = PR_TRUE; @@ -1475,6 +1479,22 @@ jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval) return NS_OK; } +NS_IMETHODIMP +jsdScript::GetFirstValidPC(PRUint32 *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = PRUint32(mFirstValidPC - mFirstPC); + return NS_OK; +} + +NS_IMETHODIMP +jsdScript::GetEndValidPC(PRUint32 *_rval) +{ + ASSERT_VALID_EPHEMERAL; + *_rval = PRUint32(mEndPC - mFirstPC); + return NS_OK; +} + NS_IMETHODIMP jsdScript::SetBreakpoint(PRUint32 aPC) { diff --git a/js/jsd/jsd_xpc.h b/js/jsd/jsd_xpc.h index 92f08799242c..5751034b6f4b 100644 --- a/js/jsd/jsd_xpc.h +++ b/js/jsd/jsd_xpc.h @@ -182,7 +182,9 @@ class jsdScript : public jsdIScript PRUint32 mBaseLineNumber, mLineExtent; PCMapEntry *mPPLineMap; PRUint32 mPCMapSize; - jsuword mFirstPC; + jsuword mFirstPC; /* address of first PC in script */ + jsuword mFirstValidPC; /* address of first valid bkpt PC */ + jsuword mEndPC; /* address of end of script code */ }; PRUint32 jsdScript::LastTag = 0; diff --git a/js/jsd/jsdebug.c b/js/jsd/jsdebug.c index 173c9d060fe4..4d6c07b80615 100644 --- a/js/jsd/jsdebug.c +++ b/js/jsd/jsdebug.c @@ -348,6 +348,22 @@ JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line) return jsd_GetClosestPC(jsdc, jsdscript, line); } +JSD_PUBLIC_API(jsuword) +JSD_GetFirstValidPC(JSDContext* jsdc, JSDScript* jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetFirstValidPC(jsdc, jsdscript); +} + +JSD_PUBLIC_API(jsuword) +JSD_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript) +{ + JSD_ASSERT_VALID_CONTEXT(jsdc); + JSD_ASSERT_VALID_SCRIPT(jsdscript); + return jsd_GetEndPC(jsdc, jsdscript); +} + JSD_PUBLIC_API(uintN) JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc) { diff --git a/js/jsd/jsdebug.h b/js/jsd/jsdebug.h index fcdc2e04ee88..7f4ee911103d 100644 --- a/js/jsd/jsdebug.h +++ b/js/jsd/jsdebug.h @@ -486,6 +486,19 @@ JSD_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) extern JSD_PUBLIC_API(jsuword) JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line); +/* +* Get the first 'Program Counter' value where a breakpoint can be set. +*/ +extern JSD_PUBLIC_API(jsuword) +JSD_GetFirstValidPC(JSDContext* jsdc, JSDScript* jsdscript); + +/* +* Get the 'Program Counter' value just after the last byte of the script. +* 0 is returned for invalid scripts. +*/ +extern JSD_PUBLIC_API(jsuword) +JSD_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript); + /* * Get the source line number for a given 'Program Counter' location. * Returns 0 if no source line information is appropriate (or available) for diff --git a/js/jsd/test/Makefile.in b/js/jsd/test/Makefile.in index d4d75a26b146..a37da51a2a01 100644 --- a/js/jsd/test/Makefile.in +++ b/js/jsd/test/Makefile.in @@ -48,6 +48,7 @@ MODULE = jsdebug include $(topsrcdir)/config/rules.mk _TEST_FILES = test_bug507448.html \ + test_bug602003.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/js/jsd/test/test_bug602003.html b/js/jsd/test/test_bug602003.html new file mode 100644 index 000000000000..f4714f90f173 --- /dev/null +++ b/js/jsd/test/test_bug602003.html @@ -0,0 +1,62 @@ + + + + + Test for Bug 602003 + + + + + +Mozilla Bug 507448 +

+ +
+
+
+
+
+ + diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 949561b06261..1b2ac3d535e8 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -475,7 +475,9 @@ AllFramesIter::operator++() { JS_ASSERT(!done()); if (curfp == curcs->getInitialFrame()) { - curcs = curcs->getPreviousInMemory(); + do { + curcs = curcs->getPreviousInMemory(); + } while (curcs && !curcs->inContext()); curfp = curcs ? curcs->getCurrentFrame() : NULL; } else { curfp = curfp->prev(); diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index e6fc2e91fec5..4d6a40be8050 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -235,9 +235,11 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, return JS_FALSE; } - // Do not trap BEGIN, it's a special prologue opcode. - if (JSOp(*pc) == JSOP_BEGIN) - pc += JSOP_BEGIN_LENGTH; + if (JSOp(*pc) == JSOP_BEGIN) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, + NULL, JSMSG_READ_ONLY, "trap invalid on BEGIN opcode"); + return JS_FALSE; + } JS_ASSERT((JSOp) *pc != JSOP_TRAP); junk = NULL; @@ -1019,6 +1021,19 @@ JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno) return js_LineNumberToPC(script, lineno); } +JS_PUBLIC_API(jsbytecode *) +JS_FirstValidPC(JSContext *cx, JSScript *script) +{ + jsbytecode *pc = script->code; + return *pc == JSOP_BEGIN ? pc + JSOP_BEGIN_LENGTH : pc; +} + +JS_PUBLIC_API(jsbytecode *) +JS_EndPC(JSContext *cx, JSScript *script) +{ + return script->code + script->length; +} + JS_PUBLIC_API(uintN) JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun) { diff --git a/js/src/jsdbgapi.h b/js/src/jsdbgapi.h index 5855f18c7839..03028145714b 100644 --- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -159,6 +159,12 @@ JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); extern JS_PUBLIC_API(jsbytecode *) JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); +extern JS_PUBLIC_API(jsbytecode *) +JS_FirstValidPC(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(jsbytecode *) +JS_EndPC(JSContext *cx, JSScript *script); + extern JS_PUBLIC_API(uintN) JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun); From 0b3cfbebc09446afa93725d5f5a3228e99926031 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Thu, 7 Oct 2010 18:59:18 -0700 Subject: [PATCH 097/284] Bug 602744: fix test case for JSOP_BEGIN with trap, r=dvander --- .../tests/jaeger/bug563000/trap-parent-from-trap.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js b/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js index d6ded288128c..f1b626b81129 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js @@ -10,8 +10,8 @@ function child() { function parent() { x = "failure2"; } -/* First op in parent. */ -trap(parent, 0, "child()"); +/* First op in parent: because of JSOP_BEGIN, it is op 1. */ +trap(parent, 1, "child()"); function success() { x = "success"; From 187009cc8f09b30878350c348bff42db6f8e6d19 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Thu, 7 Oct 2010 13:52:58 -0700 Subject: [PATCH 098/284] Clean up JSOP_DEFFUN and duplicated methodjit StubCall logic, fixing latent arguments override bug (602621, r=igor). --- js/src/jsfun.cpp | 6 +- js/src/jsinterp.cpp | 56 +++++++++-------- js/src/methodjit/StubCalls.cpp | 61 +++++++++++-------- js/src/tests/js1_8_5/regress/jstests.list | 1 + .../tests/js1_8_5/regress/regress-602621.js | 15 +++++ 5 files changed, 88 insertions(+), 51 deletions(-) create mode 100644 js/src/tests/js1_8_5/regress/regress-602621.js diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 05322d158c8e..c009148681d5 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1377,11 +1377,15 @@ call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, /* * Resolve arguments so that we never store a particular Call object's * arguments object reference in a Call prototype's |arguments| slot. + * + * Include JSPROP_ENUMERATE for consistency with all other Call object + * properties; see JSFunction::addLocal and js::Interpret's JSOP_DEFFUN + * rebinding-Call-property logic. */ if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) { if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(), GetCallArguments, SetCallArguments, - JSPROP_PERMANENT | JSPROP_SHARED, + JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE, 0, 0, NULL, JSDNP_DONT_PURGE)) { return JS_FALSE; } diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index c38d0a3f9582..7b15f610933b 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -179,7 +179,7 @@ js_GetBlockChain(JSContext *cx, JSStackFrame *fp) jsbytecode *start = script->code; /* Assume that imacros don't affect blockChain */ jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx); - + JS_ASSERT(pc >= start && pc < script->code + script->length); JSObject *blockChain = NULL; @@ -195,7 +195,7 @@ js_GetBlockChain(JSContext *cx, JSStackFrame *fp) oplen = cs->length; if (oplen < 0) oplen = js_GetVariableBytecodeLength(p); - + if (op == JSOP_ENTERBLOCK) blockChain = script->getObject(GET_INDEX(p)); else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) @@ -232,7 +232,7 @@ js_GetBlockChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen) JSScript *script = fp->script(); JS_ASSERT(js_GetOpcode(cx, script, pc) == op); - + JSObject *blockChain; JSOp opNext = js_GetOpcode(cx, script, pc + oplen); if (opNext == JSOP_BLOCKCHAIN) { @@ -5366,36 +5366,44 @@ BEGIN_CASE(JSOP_DEFFUN) goto error; /* - * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for function - * declarations JSObject::setProperty, not JSObject::defineProperty if the - * property already exists, to preserve the JSOP_PERMANENT attribute of - * existing properties and make sure that such properties cannot be deleted. + * We deviate from ES3 10.1.3, ES5 10.5, by using JSObject::setProperty not + * JSObject::defineProperty for a function declaration in eval code whose + * id is already bound to a JSPROP_PERMANENT property, to ensure that such + * properties can't be deleted. * * We also use JSObject::setProperty for the existing properties of Call - * objects with matching attributes to preserve the native getters and - * setters that store the value of the property in the interpreter frame, - * see bug 467495. + * objects with matching attributes to preserve the internal (JSPropertyOp) + * getters and setters that update the value of the property in the stack + * frame. See bug 467495. */ bool doSet = false; if (prop) { + JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT))); JS_ASSERT((attrs == JSPROP_ENUMERATE) == regs.fp->isEvalFrame()); - uint32 old; - if (attrs == JSPROP_ENUMERATE || - (parent == pobj && - parent->isCall() && - (old = ((Shape *) prop)->attributes(), - !(old & (JSPROP_GETTER|JSPROP_SETTER)) && - (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs))) { - /* - * js_CheckRedeclaration must reject attempts to add a getter or - * setter to an existing property without a getter or setter. - */ - JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); - JS_ASSERT_IF(attrs != JSPROP_ENUMERATE, !(old & JSPROP_READONLY)); + + if (attrs == JSPROP_ENUMERATE) { + /* In eval code: assign rather than (re-)define, always. */ doSet = true; + } else if (parent->isCall()) { + JS_ASSERT(parent == pobj); + + uintN oldAttrs = ((Shape *) prop)->attributes(); + JS_ASSERT(!(oldAttrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER))); + + /* + * We may be processing a function sub-statement or declaration in + * function code: we assign rather than redefine if the essential + * JSPROP_PERMANENT (not [[Configurable]] in ES5 terms) attribute + * is not changing (note that JSPROP_ENUMERATE is set for all Call + * object properties). + */ + JS_ASSERT(oldAttrs & attrs & JSPROP_ENUMERATE); + if (oldAttrs & JSPROP_PERMANENT) + doSet = true; } pobj->dropProperty(cx, prop); } + Value rval = ObjectValue(*obj); ok = doSet ? parent->setProperty(cx, id, &rval, script->strictModeCode) @@ -5543,7 +5551,7 @@ BEGIN_CASE(JSOP_LAMBDA) JSObject *obj2 = &lref.toObject(); JS_ASSERT(obj2->getClass() == &js_ObjectClass); #endif - + fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc))); JS_FUNCTION_METER(cx, joinedinitmethod); break; diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 508c2f35dc5f..c39d29685c6d 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -525,7 +525,7 @@ stubs::GetElem(VMFrame &f) int32_t i = rref.toInt32(); if (obj->isDenseArray()) { jsuint idx = jsuint(i); - + if (idx < obj->getArrayLength() && idx < obj->getDenseArrayCapacity()) { copyFrom = obj->addressOfDenseArrayElement(idx); @@ -617,7 +617,7 @@ stubs::SetElem(VMFrame &f) Value &objval = regs.sp[-3]; Value &idval = regs.sp[-2]; Value retval = regs.sp[-1]; - + JSObject *obj; jsid id; @@ -930,35 +930,44 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) THROW(); /* - * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for function - * declarations JSObject::setProperty, not JSObject::defineProperty if the - * property already exists, to preserve the JSOP_PERMANENT attribute of - * existing properties and make sure that such properties cannot be deleted. + * We deviate from ES3 10.1.3, ES5 10.5, by using JSObject::setProperty not + * JSObject::defineProperty for a function declaration in eval code whose + * id is already bound to a JSPROP_PERMANENT property, to ensure that such + * properties can't be deleted. * * We also use JSObject::setProperty for the existing properties of Call - * objects with matching attributes to preserve the native getters and - * setters that store the value of the property in the interpreter frame, - * see bug 467495. + * objects with matching attributes to preserve the internal (JSPropertyOp) + * getters and setters that update the value of the property in the stack + * frame. See bug 467495. */ doSet = false; if (prop) { + JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT))); JS_ASSERT((attrs == JSPROP_ENUMERATE) == fp->isEvalFrame()); - if (attrs == JSPROP_ENUMERATE || - (parent == pobj && - parent->isCall() && - (old = ((Shape *) prop)->attributes(), - !(old & (JSPROP_GETTER|JSPROP_SETTER)) && - (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs))) { - /* - * js_CheckRedeclaration must reject attempts to add a getter or - * setter to an existing property without a getter or setter. - */ - JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); - JS_ASSERT_IF(attrs != JSPROP_ENUMERATE, !(old & JSPROP_READONLY)); + + if (attrs == JSPROP_ENUMERATE) { + /* In eval code: assign rather than (re-)define, always. */ doSet = true; + } else if (parent->isCall()) { + JS_ASSERT(parent == pobj); + + uintN oldAttrs = ((Shape *) prop)->attributes(); + JS_ASSERT(!(oldAttrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER))); + + /* + * We may be processing a function sub-statement or declaration in + * function code: we assign rather than redefine if the essential + * JSPROP_PERMANENT (not [[Configurable]] in ES5 terms) attribute + * is not changing (note that JSPROP_ENUMERATE is set for all Call + * object properties). + */ + JS_ASSERT(oldAttrs & attrs & JSPROP_ENUMERATE); + if (oldAttrs & JSPROP_PERMANENT) + doSet = true; } pobj->dropProperty(cx, prop); } + Value rval = ObjectValue(*obj); ok = doSet ? parent->setProperty(cx, id, &rval, strict) @@ -1112,7 +1121,7 @@ StubEqualityOp(VMFrame &f) /* * The string==string case is repeated because DefaultValue() can - * convert lval/rval to strings. + * convert lval/rval to strings. */ if (lval.isString() && rval.isString()) { JSString *l = lval.toString(); @@ -1124,7 +1133,7 @@ StubEqualityOp(VMFrame &f) !ValueToNumber(cx, rval, &r)) { return false; } - + if (EQ) cond = JSDOUBLE_COMPARE(l, ==, r, false); else @@ -1447,7 +1456,7 @@ stubs::NewInitObject(VMFrame &f) { JSContext *cx = f.cx; - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); if (!obj) THROWV(NULL); @@ -2222,7 +2231,7 @@ stubs::Iter(VMFrame &f, uint32 flags) JS_ASSERT(!f.regs.sp[-1].isPrimitive()); } -static void +static void InitPropOrMethod(VMFrame &f, JSAtom *atom, JSOp op) { JSContext *cx = f.cx; @@ -2539,7 +2548,7 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) } JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH); - + pc += JUMP_OFFSET_LEN; uint32 npairs = GET_UINT16(pc); pc += UINT16_LEN; diff --git a/js/src/tests/js1_8_5/regress/jstests.list b/js/src/tests/js1_8_5/regress/jstests.list index 6b812d62e8ff..6c757e175a59 100644 --- a/js/src/tests/js1_8_5/regress/jstests.list +++ b/js/src/tests/js1_8_5/regress/jstests.list @@ -47,3 +47,4 @@ script regress-597945-2.js script regress-598176.js script regress-600067.js script regress-600137.js +script regress-602621.js diff --git a/js/src/tests/js1_8_5/regress/regress-602621.js b/js/src/tests/js1_8_5/regress/regress-602621.js new file mode 100644 index 000000000000..21dc8541596e --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-602621.js @@ -0,0 +1,15 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +function test(arg) { + eval(arg); + { + function arguments() { return 1; } + } + return arguments; +} + +reportCompare("function", typeof test("42"), "function sub-statement must override arguments"); From e1ab8d55d11a5d5184e533e6243ef5fea5a32428 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Fri, 8 Oct 2010 16:25:57 -0700 Subject: [PATCH 099/284] Bug 600139 - Delayed marking can skip marking live objects r=igor --- js/src/jsgc.cpp | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index b59c51ad88f9..fed0e77e4361 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1440,12 +1440,20 @@ GCMarker::markDelayedChildren() { while (Arena *a = unmarkedArenaStackTop) { /* - * The following assert verifies that the current arena belongs to the - * unmarked stack, since DelayMarkingChildren ensures that even for - * the stack's bottom, prevUnmarked != 0 but rather points to - * itself. + * markingDelay->link == current arena indicates last arena on stack. + * If marking gets delayed at the same arena again, the arena is pushed + * again in delayMarkingChildren. markingDelay->link has to be cleared, + * otherwise the arena is not pushed again. */ MarkingDelay *markingDelay = a->getMarkingDelay(); + unmarkedArenaStackTop = (markingDelay->link != a) + ? markingDelay->link + : NULL; + markingDelay->link = NULL; +#ifdef DEBUG + markLaterCount -= Arena::ThingsPerArena; +#endif + switch (a->header()->thingKind) { case FINALIZE_OBJECT: reinterpret_cast *>(a)->markDelayedChildren(this); @@ -1473,22 +1481,7 @@ GCMarker::markDelayedChildren() break; #endif default: - JS_ASSERT(false); - } - - /* - * Pop the arena off the stack. If we try to mark a thing on the same - * arena and that marking gets delayed, the arena will be put back - * into the worklist. - */ - if (unmarkedArenaStackTop == a) { - unmarkedArenaStackTop = (markingDelay->link != a) - ? markingDelay->link - : NULL; - markingDelay->link = NULL; -#ifdef DEBUG - markLaterCount -= Arena::ThingsPerArena; -#endif + JS_NOT_REACHED("wrong thingkind"); } } JS_ASSERT(markLaterCount == 0); From e37369b1b84eaf44676e8a88757475aceb5cb786 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 28 Sep 2010 15:23:43 -0700 Subject: [PATCH 100/284] Bug 581893 - build a js::Invoke Gatling gun (r=waldo) --- js/src/jsarray.cpp | 66 ++++++-------- js/src/jscntxt.h | 8 +- js/src/jscntxtinlines.h | 8 +- js/src/jsinterp.cpp | 131 ++++++++++++++++++++------- js/src/jsinterp.h | 34 ++++++- js/src/jsinterpinlines.h | 156 +++++++++++++++++++++++++++++++++ js/src/jsprvtd.h | 1 + js/src/jsstr.cpp | 59 +++++++------ js/src/methodjit/MethodJIT.cpp | 32 ++++--- js/src/methodjit/MethodJIT.h | 6 ++ 10 files changed, 378 insertions(+), 123 deletions(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index e1f8c9927673..f0247e813052 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1766,12 +1766,11 @@ js_MergeSort(void *src, size_t nel, size_t elsize, struct CompareArgs { - JSContext *context; - Value fval; - InvokeArgsGuard args; + JSContext *context; + InvokeSessionGuard session; - CompareArgs(JSContext *cx, const Value &fval) - : context(cx), fval(fval) + CompareArgs(JSContext *cx) + : context(cx) {} }; @@ -1792,17 +1791,15 @@ sort_compare(void *arg, const void *a, const void *b, int *result) if (!JS_CHECK_OPERATION_LIMIT(cx)) return JS_FALSE; - CallArgs &args = ca->args; - args.callee() = ca->fval; - args.thisv().setNull(); - args[0] = *av; - args[1] = *bv; + InvokeSessionGuard &session = ca->session; + session[0] = *av; + session[1] = *bv; - if (!Invoke(cx, ca->args, 0)) + if (!session.invoke(cx)) return JS_FALSE; jsdouble cmp; - if (!ValueToNumber(cx, args.rval(), &cmp)) + if (!ValueToNumber(cx, session.rval(), &cmp)) return JS_FALSE; /* Clamp cmp to -1, 0, 1. */ @@ -2040,10 +2037,8 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) } while (++i != newlen); } } else { - LeaveTrace(cx); - - CompareArgs ca(cx, fval); - if (!cx->stack().pushInvokeArgs(cx, 2, &ca.args)) + CompareArgs ca(cx); + if (!ca.session.start(cx, fval, NullValue(), 2)) return false; if (!js_MergeSort(vec, size_t(newlen), sizeof(Value), @@ -2819,34 +2814,31 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) if (length == 0) return JS_TRUE; - JSObject *thisp; + Value thisv; if (argc > 1 && !REDUCE_MODE(mode)) { + JSObject *thisp; if (!js_ValueToObjectOrNull(cx, argv[1], &thisp)) return JS_FALSE; - argv[1].setObjectOrNull(thisp); + thisv.setObjectOrNull(thisp); } else { - thisp = NULL; + thisv.setNull(); } /* * For all but REDUCE, we call with 3 args (value, index, array). REDUCE * requires 4 args (accum, value, index, array). */ - LeaveTrace(cx); argc = 3 + REDUCE_MODE(mode); - InvokeArgsGuard args; - if (!cx->stack().pushInvokeArgs(cx, argc, &args)) + InvokeSessionGuard session; + if (!session.start(cx, ObjectValue(*callable), thisv, argc)) return JS_FALSE; MUST_FLOW_THROUGH("out"); JSBool ok = JS_TRUE; JSBool cond; - Value calleev, thisv, objv; - calleev.setObject(*callable); - thisv.setObjectOrNull(thisp); - objv.setObject(*obj); + Value objv = ObjectValue(*obj); AutoValueRooter tvr(cx); for (jsint i = start; i != end; i += step) { JSBool hole; @@ -2861,22 +2853,22 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) * Push callable and 'this', then args. We must do this for every * iteration around the loop since Invoke clobbers its arguments. */ - args.callee() = calleev; - args.thisv() = thisv; - Value *sp = args.argv(); + uintN argi = 0; if (REDUCE_MODE(mode)) - *sp++ = *vp; - sp[0] = tvr.value(); - sp[1].setInt32(i); - sp[2] = objv; + session[argi++] = *vp; + session[argi++] = tvr.value(); + session[argi++] = Int32Value(i); + session[argi] = objv; /* Do the call. */ - ok = Invoke(cx, args, 0); + ok = session.invoke(cx); if (!ok) break; + const Value &rval = session.rval(); + if (mode > MAP) - cond = js_ValueToBoolean(args.rval()); + cond = js_ValueToBoolean(rval); #ifdef __GNUC__ /* quell GCC overwarning */ else cond = JS_FALSE; @@ -2887,10 +2879,10 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) break; case REDUCE: case REDUCE_RIGHT: - *vp = args.rval(); + *vp = rval; break; case MAP: - ok = SetArrayElement(cx, newarr, i, args.rval()); + ok = SetArrayElement(cx, newarr, i, rval); if (!ok) goto out; break; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 894a599dafcd..e6c9d5bcb170 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -541,9 +541,8 @@ class InvokeArgsGuard : public CallArgs JSStackFrame *prevInvokeFrame; #endif public: - inline InvokeArgsGuard() : cx(NULL), seg(NULL) {} - inline InvokeArgsGuard(JSContext *cx, Value *vp, uintN argc); - inline ~InvokeArgsGuard(); + InvokeArgsGuard() : cx(NULL), seg(NULL) {} + ~InvokeArgsGuard(); bool pushed() const { return cx != NULL; } }; @@ -565,8 +564,9 @@ class InvokeFrameGuard JSFrameRegs *prevRegs_; public: InvokeFrameGuard() : cx_(NULL) {} - JS_REQUIRES_STACK ~InvokeFrameGuard(); + ~InvokeFrameGuard() { if (pushed()) pop(); } bool pushed() const { return cx_ != NULL; } + void pop(); JSStackFrame *fp() const { return regs_.fp; } }; diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 4cd3787eec04..c4daa0fcd33a 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -352,12 +352,12 @@ StackSpace::popInvokeFrame(const InvokeFrameGuard &fg) } } -JS_REQUIRES_STACK JS_ALWAYS_INLINE -InvokeFrameGuard::~InvokeFrameGuard() +JS_ALWAYS_INLINE void +InvokeFrameGuard::pop() { - if (JS_UNLIKELY(!pushed())) - return; + JS_ASSERT(pushed()); cx_->stack().popInvokeFrame(*this); + cx_ = NULL; } JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame * diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 7b15f610933b..9dbda4bd914f 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -586,37 +586,6 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags) namespace js { -class AutoPreserveEnumerators { - JSContext *cx; - JSObject *enumerators; - - public: - AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators) - { - } - - ~AutoPreserveEnumerators() - { - cx->enumerators = enumerators; - } -}; - -struct AutoInterpPreparer { - JSContext *cx; - JSScript *script; - - AutoInterpPreparer(JSContext *cx, JSScript *script) - : cx(cx), script(script) - { - cx->interpLevel++; - } - - ~AutoInterpPreparer() - { - --cx->interpLevel; - } -}; - JS_REQUIRES_STACK bool RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) { @@ -652,6 +621,8 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) JS_REQUIRES_STACK bool Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) { + /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */ + CallArgs args = argsRef; JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX); @@ -685,10 +656,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) return CallJSNative(cx, fun->u.n.native, args.argc(), args.base()); } - JS_ASSERT(fun->isInterpreted()); - JSScript *script = fun->u.i.script; - /* Handle the empty-script special case. */ + JSScript *script = fun->script(); if (JS_UNLIKELY(script->isEmpty())) { if (flags & JSINVOKE_CONSTRUCT) { JSObject *obj = js_CreateThisForFunction(cx, &callee); @@ -719,6 +688,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) return false; /* + * FIXME bug 592992: hoist to ExternalInvoke + * * Compute |this|. Currently, this must happen after the frame is pushed * and fp->scopeChain is correct because the thisObject hook may call * JS_GetScopeChain. @@ -767,6 +738,98 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) return ok; } +bool +InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc) +{ +#ifdef JS_TRACER + if (TRACE_RECORDER(cx)) + AbortRecording(cx, "attempt to reenter VM while recording"); + LeaveTrace(cx); +#endif + + /* Always push arguments, regardless of optimized/normal invoke. */ + StackSpace &stack = cx->stack(); + if (!stack.pushInvokeArgs(cx, argc, &args_)) + return false; + + do { + /* Hoist dynamic checks from scripted Invoke. */ + if (!calleev.isObject()) + break; + JSObject &callee = calleev.toObject(); + if (callee.getClass() != &js_FunctionClass) + break; + JSFunction *fun = callee.getFunctionPrivate(); + if (fun->isNative()) + break; + script_ = fun->script(); + if (fun->isHeavyweight() || script_->isEmpty() || cx->compartment->debugMode) + break; + + /* Set (callee, this) once for the session (before args are duped). */ + args_.callee().setObject(callee); + args_.thisv() = thisv; + + /* Push the stack frame once for the session. */ + uint32 flags = 0; + if (!stack.getInvokeFrame(cx, args_, fun, script_, &flags, &frame_)) + return false; + JSStackFrame *fp = frame_.fp(); + fp->initCallFrame(cx, calleev.toObject(), fun, argc, flags); + stack.pushInvokeFrame(cx, args_, &frame_); + + // FIXME bug 592992: hoist thisObject hook to ExternalInvoke + if (thisv.isObject()) { + JSObject *thisp = thisv.toObject().thisObject(cx); + if (!thisp) + return false; + JS_ASSERT(IsSaneThisObject(*thisp)); + fp->functionThis().setObject(*thisp); + } + +#ifdef JS_METHODJIT + /* Hoist dynamic checks from RunScript. */ + mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp); + if (status == mjit::Compile_Error) + return false; + if (status != mjit::Compile_Okay) + break; + code_ = script_->getJIT(fp->isConstructing())->invokeEntry; + + /* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */ + JS_CHECK_RECURSION(cx, return JS_FALSE); + stackLimit_ = stack.getStackLimit(cx); + if (!stackLimit_) + return false; + + stop_ = script_->code + script_->length - 1; + JS_ASSERT(*stop_ == JSOP_STOP); +#endif + + /* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */ + nformals_ = fp->numFormalArgs(); + formals_ = fp->formalArgs(); + actuals_ = args_.argv(); + JS_ASSERT(actuals_ == fp->actualArgs()); + return true; + } while (0); + + /* + * Use the normal invoke path. + * + * The callee slot gets overwritten during an unoptimized Invoke, so we + * cache it here and restore it before every Invoke call. The 'this' value + * does not get overwritten, so we can fill it here once. + */ + if (frame_.pushed()) + frame_.pop(); + args_.thisv() = thisv; + savedCallee_ = calleev; + formals_ = actuals_ = args_.argv(); + nformals_ = (unsigned)-1; + return true; +} + bool ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value *argv, Value *rval) diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 416a01629021..453485677cd6 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -191,6 +191,9 @@ struct JSStackFrame inline void initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun, uint32 nactual, uint32 flags); + /* Used for SessionInvoke. */ + inline void resetInvokeCallFrame(); + /* Called by method-jit stubs and serve as a specification for jit-code. */ inline void initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flags); inline void initCallFrameEarlyPrologue(JSFunction *fun, void *ncode); @@ -368,7 +371,8 @@ struct JSStackFrame template inline void forEachCanonicalActualArg(Op op); template inline void forEachFormalArg(Op op); - /* True if we have created an arguments object for this frame; implies hasArgs(). */ + inline void clearMissingArgs(); + bool hasArgsObj() const { return !!(flags_ & JSFRAME_HAS_ARGS_OBJ); } @@ -558,7 +562,7 @@ struct JSStackFrame /* Return value */ - const js::Value& returnValue() { + const js::Value &returnValue() { if (!(flags_ & JSFRAME_HAS_RVAL)) rval_.setUndefined(); return rval_; @@ -885,6 +889,32 @@ struct CallArgs extern JS_REQUIRES_STACK bool Invoke(JSContext *cx, const CallArgs &args, uint32 flags); +/* + * Natives like sort/forEach/replace call Invoke repeatedly with the same + * callee, this, and number of arguments. To optimize this, such natives can + * start an "invoke session" to factor out much of the dynamic setup logic + * required by a normal Invoke. Usage is: + * + * InvokeSessionGuard session(cx); + * if (!session.start(cx, callee, thisp, argc, &session)) + * ... + * + * while (...) { + * // write actual args (not callee, this) + * session[0] = ... + * ... + * session[argc - 1] = ... + * + * if (!session.invoke(cx, session)) + * ... + * + * ... = session.rval(); + * } + * + * // session ended by ~InvokeSessionGuard + */ +class InvokeSessionGuard; + /* * Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that * we can share bits stored in JSStackFrame.flags and passed to: diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 41447266d34b..b5bd787a3583 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -40,6 +40,9 @@ #ifndef jsinterpinlines_h__ #define jsinterpinlines_h__ +#include "jsprobes.h" +#include "methodjit/MethodJIT.h" + inline void JSStackFrame::initPrev(JSContext *cx) { @@ -85,6 +88,42 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun, JS_ASSERT(!hasCallObj()); } +inline void +JSStackFrame::resetInvokeCallFrame() +{ + /* Undo changes to frame made during execution; see initCallFrame */ + + if (hasArgsObj()) + args.nactual = argsObj().getArgsInitialLength(); + + JS_ASSERT(!(flags_ & ~(JSFRAME_FUNCTION | + JSFRAME_OVERFLOW_ARGS | + JSFRAME_UNDERFLOW_ARGS | + JSFRAME_HAS_CALL_OBJ | + JSFRAME_HAS_ARGS_OBJ | + JSFRAME_OVERRIDE_ARGS | + JSFRAME_HAS_PREVPC | + JSFRAME_HAS_RVAL | + JSFRAME_HAS_SCOPECHAIN | + JSFRAME_HAS_ANNOTATION | + JSFRAME_BAILED_AT_RETURN))); + flags_ &= JSFRAME_FUNCTION | + JSFRAME_OVERFLOW_ARGS | + JSFRAME_HAS_PREVPC | + JSFRAME_UNDERFLOW_ARGS; + + JS_ASSERT_IF(!hasCallObj(), scopeChain_ == calleeValue().toObject().getParent()); + JS_ASSERT_IF(hasCallObj(), scopeChain_ == callObj().getParent()); + if (hasCallObj()) + scopeChain_ = callObj().getParent(); + + JS_ASSERT(exec.fun == calleeValue().toObject().getFunctionPrivate()); + JS_ASSERT(!hasImacropc()); + JS_ASSERT(!hasHookData()); + JS_ASSERT(annotation() == NULL); + JS_ASSERT(!hasCallObj()); +} + inline void JSStackFrame::initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flagsArg) { @@ -269,6 +308,13 @@ JSStackFrame::forEachFormalArg(Op op) op(i, p); } +JS_ALWAYS_INLINE void +JSStackFrame::clearMissingArgs() +{ + if (flags_ & JSFRAME_UNDERFLOW_ARGS) + SetValueRangeToUndefined(formalArgs() + numActualArgs(), formalArgsEnd()); +} + inline JSObject * JSStackFrame::computeThisObject(JSContext *cx) { @@ -394,6 +440,37 @@ JSStackFrame::maybeCallObj() const namespace js { +class AutoPreserveEnumerators { + JSContext *cx; + JSObject *enumerators; + + public: + AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators) + { + } + + ~AutoPreserveEnumerators() + { + cx->enumerators = enumerators; + } +}; + +struct AutoInterpPreparer { + JSContext *cx; + JSScript *script; + + AutoInterpPreparer(JSContext *cx, JSScript *script) + : cx(cx), script(script) + { + cx->interpLevel++; + } + + ~AutoInterpPreparer() + { + --cx->interpLevel; + } +}; + inline void PutActivationObjects(JSContext *cx, JSStackFrame *fp) { @@ -407,6 +484,85 @@ PutActivationObjects(JSContext *cx, JSStackFrame *fp) } } +class InvokeSessionGuard +{ + InvokeArgsGuard args_; + InvokeFrameGuard frame_; + Value savedCallee_; + Value *formals_, *actuals_; + unsigned nformals_; + JSScript *script_; + void *code_; + Value *stackLimit_; + jsbytecode *stop_; + + bool optimized() const { return frame_.pushed(); } + + public: + InvokeSessionGuard() : args_(), frame_() {} + ~InvokeSessionGuard() {} + + bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc); + bool invoke(JSContext *cx) const; + + bool started() const { + return args_.pushed(); + } + + Value &operator[](unsigned i) const { + JS_ASSERT(i < argc()); + Value &arg = i < nformals_ ? formals_[i] : actuals_[i]; + JS_ASSERT_IF(optimized(), &arg == &frame_.fp()->canonicalActualArg(i)); + JS_ASSERT_IF(!optimized(), &arg == &args_[i]); + return arg; + } + + uintN argc() const { + return args_.argc(); + } + + const Value &rval() const { + return optimized() ? frame_.fp()->returnValue() : args_.rval(); + } +}; + +inline bool +InvokeSessionGuard::invoke(JSContext *cx) const +{ + /* N.B. Must be kept in sync with Invoke */ + + if (!optimized()) { + args_.callee() = savedCallee_; + return Invoke(cx, args_, 0); + } + + /* Clear any garbage left from the last Invoke. */ + JSStackFrame *fp = frame_.fp(); + fp->clearMissingArgs(); + fp->resetInvokeCallFrame(); + SetValueRangeToUndefined(fp->slots(), script_->nfixed); + + JSBool ok; + { + AutoPreserveEnumerators preserve(cx); + Probes::enterJSFun(cx, fp->fun()); +#ifdef JS_METHODJIT + AutoInterpPreparer prepareInterp(cx, script_); + ok = mjit::EnterMethodJIT(cx, fp, code_, stackLimit_); + cx->regs->pc = stop_; +#else + cx->regs->pc = script_->code; + ok = Interpret(cx, cx->fp()); +#endif + Probes::exitJSFun(cx, fp->fun()); + } + + PutActivationObjects(cx, fp); + + /* Don't clobber callee with rval; rval gets read from fp->rval. */ + return ok; +} + } #endif /* jsinterpinlines_h__ */ diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index a79cbbeaa95b..8006387fdee4 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -128,6 +128,7 @@ class AutoStringRooter; class ExecuteArgsGuard; class InvokeFrameGuard; class InvokeArgsGuard; +class InvokeSessionGuard; class TraceRecorder; struct TraceMonitor; class StackSpace; diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 4cec144ee1f4..2e6112af5b95 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -77,6 +77,7 @@ #include "jsversion.h" #include "jscntxtinlines.h" +#include "jsinterpinlines.h" #include "jsobjinlines.h" #include "jsstrinlines.h" #include "jsregexpinlines.h" @@ -1977,18 +1978,19 @@ struct ReplaceData : g(cx), cb(cx) {} - JSString *str; /* 'this' parameter object as a string */ - RegExpGuard g; /* regexp parameter object and private data */ - JSObject *lambda; /* replacement function object or null */ - JSString *repstr; /* replacement string */ - jschar *dollar; /* null or pointer to first $ in repstr */ - jschar *dollarEnd; /* limit pointer for js_strchr_limit */ - jsint index; /* index in result of next replacement */ - jsint leftIndex; /* left context index in str->chars */ - JSSubString dollarStr; /* for "$$" InterpretDollar result */ - bool calledBack; /* record whether callback has been called */ - InvokeArgsGuard args; /* arguments for lambda's js_Invoke call */ - JSCharBuffer cb; /* buffer built during DoMatch */ + JSString *str; /* 'this' parameter object as a string */ + RegExpGuard g; /* regexp parameter object and private data */ + JSObject *lambda; /* replacement function object or null */ + JSString *repstr; /* replacement string */ + jschar *dollar; /* null or pointer to first $ in repstr */ + jschar *dollarEnd; /* limit pointer for js_strchr_limit */ + jsint index; /* index in result of next replacement */ + jsint leftIndex; /* left context index in str->chars */ + JSSubString dollarStr; /* for "$$" InterpretDollar result */ + bool calledBack; /* record whether callback has been called */ + InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */ + InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */ + JSCharBuffer cb; /* buffer built during DoMatch */ }; static bool @@ -2076,8 +2078,6 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t { JSObject *lambda = rdata.lambda; if (lambda) { - LeaveTrace(cx); - /* * In the lambda case, not only do we find the replacement string's * length, we compute repstr and return it via rdata for use within @@ -2089,32 +2089,33 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t uintN p = res->getParenCount(); uintN argc = 1 + p + 2; - if (!rdata.args.pushed() && !cx->stack().pushInvokeArgs(cx, argc, &rdata.args)) - return false; + if (!rdata.session.started()) { + Value lambdav = ObjectValue(*lambda); + if (!rdata.session.start(cx, lambdav, NullValue(), argc)) + return false; + } PreserveRegExpStatics save(res); /* Push lambda and its 'this' parameter. */ - CallArgs &args = rdata.args; - args.callee().setObject(*lambda); - args.thisv().setNull(); + InvokeSessionGuard &session = rdata.session; - Value *sp = args.argv(); + uintN argi = 0; /* Push $&, $1, $2, ... */ - if (!res->createLastMatch(cx, sp++)) + if (!res->createLastMatch(cx, &session[argi++])) return false; for (size_t i = 0; i < res->getParenCount(); ++i) { - if (!res->createParen(cx, i, sp++)) + if (!res->createParen(cx, i, &session[argi++])) return false; } /* Push match index and input string. */ - sp[0].setInt32(res->get(0, 0)); - sp[1].setString(rdata.str); + session[argi++].setInt32(res->get(0, 0)); + session[argi].setString(rdata.str); - if (!Invoke(cx, rdata.args, 0)) + if (!session.invoke(cx)) return false; /* @@ -2122,7 +2123,7 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t * created by this js_ValueToString that would otherwise be GC- * able, until we use rdata.repstr in DoReplace. */ - JSString *repstr = js_ValueToString(cx, args.rval()); + JSString *repstr = js_ValueToString(cx, session.rval()); if (!repstr) return false; @@ -2402,10 +2403,10 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata /* lambda(matchStr, matchStart, textstr) */ static const uint32 lambdaArgc = 3; - if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.args)) + if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot)) return false; - CallArgs &args = rdata.args; + CallArgs &args = rdata.singleShot; args.callee().setObject(*rdata.lambda); args.thisv().setNull(); @@ -2414,7 +2415,7 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata sp[1].setInt32(fm.match()); sp[2].setString(rdata.str); - if (!Invoke(cx, rdata.args, 0)) + if (!Invoke(cx, rdata.singleShot, 0)) return false; JSString *repstr = js_ValueToString(cx, args.rval()); diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index e82193df99e6..566d5cc1095d 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -720,15 +720,12 @@ ThreadData::Finish() #endif } -extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code, - Value *stackLimit); +extern "C" JSBool +JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit); -static inline JSBool -EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) +JSBool +mjit::EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit) { - JS_ASSERT(cx->regs); - JS_CHECK_RECURSION(cx, return JS_FALSE;); - #ifdef JS_METHODJIT_SPEW Profiler prof; JSScript *script = fp->script(); @@ -738,10 +735,7 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) prof.start(); #endif - Value *stackLimit = cx->stack().getStackLimit(cx); - if (!stackLimit) - return false; - + JS_ASSERT(cx->regs->fp == fp); JSFrameRegs *oldRegs = cx->regs; JSAutoResolveFlags rf(cx, JSRESOLVE_INFER); @@ -761,6 +755,18 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) return ok; } +static inline JSBool +CheckStackAndEnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code) +{ + JS_CHECK_RECURSION(cx, return JS_FALSE;); + + Value *stackLimit = cx->stack().getStackLimit(cx); + if (!stackLimit) + return false; + + return EnterMethodJIT(cx, fp, code, stackLimit); +} + JSBool mjit::JaegerShot(JSContext *cx) { @@ -775,7 +781,7 @@ mjit::JaegerShot(JSContext *cx) JS_ASSERT(cx->regs->pc == script->code); - return EnterMethodJIT(cx, cx->fp(), jit->invokeEntry); + return CheckStackAndEnterMethodJIT(cx, cx->fp(), jit->invokeEntry); } JSBool @@ -785,7 +791,7 @@ js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint) JS_ASSERT(!TRACE_RECORDER(cx)); #endif - return EnterMethodJIT(cx, cx->fp(), safePoint); + return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint); } template diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index a3618f9281d9..31b01cdc18a9 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -216,6 +216,12 @@ struct JITScript { void release(); }; +/* + * Execute the given mjit code. This is a low-level call and callers must + * provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT. + */ +JSBool EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit); + /* Execute a method that has been JIT compiled. */ JSBool JaegerShot(JSContext *cx); From b0292aaab12839947e3ea2cb73b3070059566bb5 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 4 Oct 2010 11:45:34 -0700 Subject: [PATCH 101/284] Bug 601296, part 1 - speedup FindReplaceLength - inline js_ValueToString (r=cdleary) --- js/src/jsstr.cpp | 14 ++++---------- js/src/jsstr.h | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 2e6112af5b95..e1b0b4b0eee4 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2118,18 +2118,12 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t if (!session.invoke(cx)) return false; - /* - * NB: we count on the newborn string root to hold any string - * created by this js_ValueToString that would otherwise be GC- - * able, until we use rdata.repstr in DoReplace. - */ - JSString *repstr = js_ValueToString(cx, session.rval()); - if (!repstr) + /* root repstr: rdata is on the stack, so scanned by conservative gc. */ + rdata.repstr = ValueToString_TestForStringInline(cx, session.rval()); + if (!rdata.repstr) return false; - rdata.repstr = repstr; - *sizep = repstr->length(); - + *sizep = rdata.repstr->length(); return true; } diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 6288cd9e5314..6ea99d91d8d9 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -973,6 +973,23 @@ js_ValueToPrintable(JSContext *cx, const js::Value &, JSValueToStringFun v2sfun) extern JSString * js_ValueToString(JSContext *cx, const js::Value &v); +namespace js { + +/* + * Most code that calls js_ValueToString knows the value is (probably) not a + * string, so it does not make sense to put this inline fast path into + * js_ValueToString. + */ +static JS_ALWAYS_INLINE JSString * +ValueToString_TestForStringInline(JSContext *cx, const Value &v) +{ + if (v.isString()) + return v.toString(); + return js_ValueToString(cx, v); +} + +} + /* * This function implements E-262-3 section 9.8, toString. Convert the given * value to a string of jschars appended to the given buffer. On error, the From 8cd1ffc9c83c2238cd57a460813f13ffbf3e19a2 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 4 Oct 2010 11:45:34 -0700 Subject: [PATCH 102/284] Bug 601296, part 2 - speedup FindReplaceLength - don't copy RegExpStatics (r=cdleary) --- js/src/jsregexp.h | 52 +++++++++++++++++-- js/src/jsregexpinlines.h | 20 ++----- js/src/jsstr.cpp | 31 +++++------ js/src/jsvector.h | 12 +++++ .../tests/basic/testStaticsInRegExp.js | 9 ++++ 5 files changed, 89 insertions(+), 35 deletions(-) create mode 100644 js/src/trace-test/tests/basic/testStaticsInRegExp.js diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index 1043056da0b1..84edd681c571 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -59,9 +59,11 @@ namespace js { class RegExpStatics { typedef Vector MatchPairs; - MatchPairs matchPairs; - JSString *input; - uintN flags; + MatchPairs matchPairs; + JSString *input; + uintN flags; + RegExpStatics *bufferLink; + bool copied; bool createDependent(JSContext *cx, size_t start, size_t end, Value *out) const; @@ -69,6 +71,22 @@ class RegExpStatics JS_ASSERT(matchPairs.length() % 2 == 0); return matchPairs.length() / 2; } + + void copyTo(RegExpStatics &dst) { + dst.matchPairs.clear(); + /* 'save' has already reserved space in matchPairs */ + JS_ALWAYS_TRUE(dst.matchPairs.append(matchPairs)); + dst.input = input; + dst.flags = flags; + } + + void aboutToWrite() { + if (bufferLink && !bufferLink->copied) { + copyTo(*bufferLink); + bufferLink->copied = true; + } + } + /* * Check whether the index at |checkValidIndex| is valid (>= 0). * If so, construct a string for it and place it in |*out|. @@ -79,13 +97,34 @@ class RegExpStatics friend class RegExp; public: - explicit RegExpStatics() { clear(); } - void clone(const RegExpStatics &other); + RegExpStatics() : bufferLink(NULL), copied(false) { clear(); } + + struct InitBuffer {}; + explicit RegExpStatics(InitBuffer) : bufferLink(NULL), copied(false) {} + static RegExpStatics *extractFrom(JSObject *global); /* Mutators. */ + bool save(JSContext *cx, RegExpStatics *buffer) { + JS_ASSERT(!buffer->copied && !buffer->bufferLink); + buffer->bufferLink = bufferLink; + bufferLink = buffer; + if (!buffer->matchPairs.reserve(matchPairs.length())) { + js_ReportOutOfMemory(cx); + return false; + } + return true; + } + + void restore() { + if (bufferLink->copied) + bufferLink->copyTo(*this); + bufferLink = bufferLink->bufferLink; + } + void setMultiline(bool enabled) { + aboutToWrite(); if (enabled) flags = flags | JSREG_MULTILINE; else @@ -93,6 +132,7 @@ class RegExpStatics } void clear() { + aboutToWrite(); input = 0; flags = 0; matchPairs.clear(); @@ -107,6 +147,7 @@ class RegExpStatics } void reset(JSString *newInput, bool newMultiline) { + aboutToWrite(); clear(); input = newInput; setMultiline(newMultiline); @@ -114,6 +155,7 @@ class RegExpStatics } void setInput(JSString *newInput) { + aboutToWrite(); input = newInput; } diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index 2c434db49543..30b3f930e5e8 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -321,12 +321,14 @@ RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, checkMatchPairs(buf, matchItemCount); if (res) { + res->aboutToWrite(); res->input = input; - res->matchPairs.clear(); - if (!res->matchPairs.reserve(matchItemCount)) + if (!res->matchPairs.resizeUninitialized(matchItemCount)) { + js_ReportOutOfMemory(cx); return false; + } for (size_t i = 0; i < matchItemCount; ++i) - JS_ALWAYS_TRUE(res->matchPairs.append(buf[i] + inputOffset)); + res->matchPairs[i] = buf[i] + inputOffset; } *lastIndex = buf[1] + inputOffset; @@ -494,7 +496,6 @@ RegExp::clone(JSContext *cx, const RegExp &other) /* RegExpStatics inlines. */ - inline RegExpStatics * RegExpStatics::extractFrom(JSObject *global) { @@ -503,17 +504,6 @@ RegExpStatics::extractFrom(JSObject *global) return res; } -inline void -RegExpStatics::clone(const RegExpStatics &other) -{ - JS_ASSERT(this != &other); - clear(); - input = other.input; - flags = other.flags; - JS_ASSERT((flags & allFlags) == flags); - matchPairs.append(other.matchPairs); -} - inline bool RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, Value *out) const { diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index e1b0b4b0eee4..4b3639b9a386 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2057,19 +2057,21 @@ InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, Repla class PreserveRegExpStatics { - js::RegExpStatics * const saved; - js::RegExpStatics container; + js::RegExpStatics *const original; + js::RegExpStatics buffer; public: - explicit PreserveRegExpStatics(RegExpStatics *toSave) : saved(toSave) { - container.clone(*toSave); - container.checkInvariants(); - saved->checkInvariants(); + explicit PreserveRegExpStatics(RegExpStatics *original) + : original(original), + buffer(RegExpStatics::InitBuffer()) + {} + + bool init(JSContext *cx) { + return original->save(cx, &buffer); } ~PreserveRegExpStatics() { - saved->clone(container); - saved->checkInvariants(); + original->restore(); } }; @@ -2089,20 +2091,19 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t uintN p = res->getParenCount(); uintN argc = 1 + p + 2; - if (!rdata.session.started()) { + InvokeSessionGuard &session = rdata.session; + if (!session.started()) { Value lambdav = ObjectValue(*lambda); if (!rdata.session.start(cx, lambdav, NullValue(), argc)) return false; } - PreserveRegExpStatics save(res); - - /* Push lambda and its 'this' parameter. */ - InvokeSessionGuard &session = rdata.session; - - uintN argi = 0; + PreserveRegExpStatics staticsGuard(res); + if (!staticsGuard.init(cx)) + return false; /* Push $&, $1, $2, ... */ + uintN argi = 0; if (!res->createLastMatch(cx, &session[argi++])) return false; diff --git a/js/src/jsvector.h b/js/src/jsvector.h index 2450e1236c59..90917f8a5826 100644 --- a/js/src/jsvector.h +++ b/js/src/jsvector.h @@ -382,6 +382,7 @@ class Vector : AllocPolicy /* Leave new elements as uninitialized memory. */ bool growByUninitialized(size_t incr); + bool resizeUninitialized(size_t newLength); void clear(); @@ -634,6 +635,17 @@ Vector::resize(size_t newLength) return true; } +template +JS_ALWAYS_INLINE bool +Vector::resizeUninitialized(size_t newLength) +{ + size_t curLength = length(); + if (newLength > curLength) + return growByUninitialized(newLength - curLength); + shrinkBy(curLength - newLength); + return true; +} + template inline void Vector::clear() diff --git a/js/src/trace-test/tests/basic/testStaticsInRegExp.js b/js/src/trace-test/tests/basic/testStaticsInRegExp.js new file mode 100644 index 000000000000..cef69cde09ee --- /dev/null +++ b/js/src/trace-test/tests/basic/testStaticsInRegExp.js @@ -0,0 +1,9 @@ +'abcdef'.replace(/a(\w+)c/, function() { + assertEq(RegExp.lastMatch, 'abc'); + '123456'.replace(/1(\d+)3/, function() { + assertEq(RegExp.lastMatch, '123'); + }); + assertEq(RegExp.lastMatch, '123'); +}); +assertEq(RegExp.lastMatch, 'abc'); + From c52aa9fd36bbbb01b45e7b445dc1c51d600766c7 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 4 Oct 2010 11:45:56 -0700 Subject: [PATCH 103/284] Bug 601296, part 3 - speedup FindReplaceLength - inline JS_GetEmptyStringValue (r=cdleary) --- js/src/jsregexpinlines.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index 30b3f930e5e8..6f8f90bf393c 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -519,7 +519,7 @@ RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, Value *o inline bool RegExpStatics::createInput(JSContext *cx, Value *out) const { - *out = input ? StringValue(input) : Valueify(JS_GetEmptyStringValue(cx)); + out->setString(input ? input : cx->runtime->emptyString); return true; } @@ -527,7 +527,7 @@ inline bool RegExpStatics::makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out) const { if (checkValidIndex / 2 >= pairCount() || matchPairs[checkValidIndex] < 0) { - *out = Valueify(JS_GetEmptyStringValue(cx)); + out->setString(cx->runtime->emptyString); return true; } return createDependent(cx, get(pairNum, 0), get(pairNum, 1), out); @@ -537,7 +537,7 @@ inline bool RegExpStatics::createLastParen(JSContext *cx, Value *out) const { if (pairCount() <= 1) { - *out = Valueify(JS_GetEmptyStringValue(cx)); + out->setString(cx->runtime->emptyString); return true; } size_t num = pairCount() - 1; @@ -545,7 +545,7 @@ RegExpStatics::createLastParen(JSContext *cx, Value *out) const int end = get(num, 1); if (start == -1) { JS_ASSERT(end == -1); - *out = Valueify(JS_GetEmptyStringValue(cx)); + out->setString(cx->runtime->emptyString); return true; } JS_ASSERT(start >= 0 && end >= 0); @@ -556,7 +556,7 @@ inline bool RegExpStatics::createLeftContext(JSContext *cx, Value *out) const { if (!pairCount()) { - *out = Valueify(JS_GetEmptyStringValue(cx)); + out->setString(cx->runtime->emptyString); return true; } if (matchPairs[0] < 0) { @@ -570,7 +570,7 @@ inline bool RegExpStatics::createRightContext(JSContext *cx, Value *out) const { if (!pairCount()) { - *out = Valueify(JS_GetEmptyStringValue(cx)); + out->setString(cx->runtime->emptyString); return true; } if (matchPairs[1] < 0) { From 5697a880c9afd7be399fa508a9c976d41b73a2ab Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Wed, 6 Oct 2010 12:13:20 -0700 Subject: [PATCH 104/284] Bug 578916 - Cache results of Math.sin() (r=waldo) --- js/src/jsbuiltins.h | 1 + js/src/jscntxt.cpp | 11 ++++ js/src/jscntxt.h | 11 ++++ js/src/jscompartment.h | 1 + js/src/jsmath.cpp | 124 +++++++++++++++++++++++++---------------- js/src/jsmath.h | 39 +++++++++++++ js/src/jstracer.cpp | 5 ++ 7 files changed, 143 insertions(+), 49 deletions(-) diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index e884098dcd78..b959acbb8e3e 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -184,6 +184,7 @@ struct ClosureVarInfo; #define _JS_CTYPE_CONTEXT _JS_CTYPE(JSContext *, _JS_PTR,"C", "", INFALLIBLE) #define _JS_CTYPE_RUNTIME _JS_CTYPE(JSRuntime *, _JS_PTR,"R", "", INFALLIBLE) +#define _JS_CTYPE_MATHCACHE _JS_CTYPE(js::MathCache *, _JS_PTR,"M", "", INFALLIBLE) #define _JS_CTYPE_THIS _JS_CTYPE(JSObject *, _JS_PTR,"T", "", INFALLIBLE) #define _JS_CTYPE_THIS_DOUBLE _JS_CTYPE(jsdouble, _JS_F64,"D", "", INFALLIBLE) #define _JS_CTYPE_THIS_STRING _JS_CTYPE(JSString *, _JS_PTR,"S", "", INFALLIBLE) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 1b2ac3d535e8..18802d71d472 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -510,6 +510,16 @@ JSThreadData::init() return true; } +MathCache * +JSThreadData::allocMathCache(JSContext *cx) +{ + JS_ASSERT(!mathCache); + mathCache = new MathCache; + if (!mathCache) + js_ReportOutOfMemory(cx); + return mathCache; +} + void JSThreadData::finish() { @@ -530,6 +540,7 @@ JSThreadData::finish() jmData.Finish(); #endif stackSpace.finish(); + delete mathCache; } void diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index e6c9d5bcb170..4ae5aa44c5ab 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -64,6 +64,7 @@ #include "jsgcchunk.h" #include "jshashtable.h" #include "jsinterp.h" +#include "jsmath.h" #include "jsobj.h" #include "jspropertycache.h" #include "jspropertytree.h" @@ -1188,6 +1189,16 @@ struct JSThreadData { js::ConservativeGCThreadData conservativeGC; + private: + js::MathCache *mathCache; + + js::MathCache *allocMathCache(JSContext *cx); + public: + + js::MathCache *getMathCache(JSContext *cx) { + return mathCache ? mathCache : allocMathCache(cx); + } + bool init(); void finish(); void mark(JSTracer *trc); diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 6e8b24c6dcec..c5782306d92b 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -42,6 +42,7 @@ #include "jscntxt.h" #include "jsgc.h" +#include "jsmath.h" #include "jsobj.h" #include "jsfun.h" #include "jsgcstats.h" diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index e430ff63bfef..9f9ce647351c 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -94,6 +94,15 @@ static JSConstDoubleSpec math_constants[] = { {0,0,0,{0,0,0}} }; +MathCache::MathCache() { + memset(table, 0, sizeof(table)); + + /* See comments in lookup(). */ + JS_ASSERT(JSDOUBLE_IS_NEGZERO(-0.0)); + JS_ASSERT(!JSDOUBLE_IS_NEGZERO(+0.0)); + JS_ASSERT(hash(-0.0) != hash(+0.0)); +} + Class js_MathClass = { js_Math_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Math), @@ -139,7 +148,10 @@ math_acos(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } #endif - z = acos(x); + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return JS_FALSE; + z = mathCache->lookup(acos, x); vp->setDouble(z); return JS_TRUE; } @@ -161,7 +173,10 @@ math_asin(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } #endif - z = asin(x); + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return JS_FALSE; + z = mathCache->lookup(asin, x); vp->setDouble(z); return JS_TRUE; } @@ -177,7 +192,10 @@ math_atan(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - z = atan(x); + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return JS_FALSE; + z = mathCache->lookup(atan, x); vp->setDouble(z); return JS_TRUE; } @@ -267,11 +285,28 @@ math_cos(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - z = cos(x); + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return JS_FALSE; + z = mathCache->lookup(cos, x); vp->setDouble(z); return JS_TRUE; } +static double +math_exp_body(double d) +{ +#ifdef _WIN32 + if (!JSDOUBLE_IS_NaN(d)) { + if (d == js_PositiveInfinity) + return js_PositiveInfinity; + if (d == js_NegativeInfinity) + return 0.0; + } +#endif + return exp(d); +} + static JSBool math_exp(JSContext *cx, uintN argc, Value *vp) { @@ -283,19 +318,10 @@ math_exp(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; -#ifdef _WIN32 - if (!JSDOUBLE_IS_NaN(x)) { - if (x == js_PositiveInfinity) { - vp->setDouble(js_PositiveInfinity); - return JS_TRUE; - } - if (x == js_NegativeInfinity) { - vp->setInt32(0); - return JS_TRUE; - } - } -#endif - z = exp(x); + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return JS_FALSE; + z = mathCache->lookup(math_exp_body, x); vp->setNumber(z); return JS_TRUE; } @@ -333,7 +359,10 @@ math_log(JSContext *cx, uintN argc, Value *vp) return JS_TRUE; } #endif - z = log(x); + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return JS_FALSE; + z = mathCache->lookup(log, x); vp->setNumber(z); return JS_TRUE; } @@ -571,7 +600,10 @@ math_sin(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - z = sin(x); + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return JS_FALSE; + z = mathCache->lookup(sin, x); vp->setDouble(z); return JS_TRUE; } @@ -587,7 +619,10 @@ math_sqrt(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - z = sqrt(x); + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return JS_FALSE; + z = mathCache->lookup(sqrt, x); vp->setDouble(z); return JS_TRUE; } @@ -603,7 +638,10 @@ math_tan(JSContext *cx, uintN argc, Value *vp) } if (!ValueToNumber(cx, vp[2], &x)) return JS_FALSE; - z = tan(x); + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return JS_FALSE; + z = mathCache->lookup(tan, x); vp->setDouble(z); return JS_TRUE; } @@ -621,9 +659,11 @@ math_toSource(JSContext *cx, uintN argc, Value *vp) #define MATH_BUILTIN_1(name) MATH_BUILTIN_CFUN_1(name, name) #define MATH_BUILTIN_CFUN_1(name, cfun) \ - static jsdouble FASTCALL math_##name##_tn(jsdouble d) { return cfun(d); } \ + static jsdouble FASTCALL math_##name##_tn(MathCache *cache, jsdouble d) { \ + return cache->lookup(cfun, d); \ + } \ JS_DEFINE_TRCINFO_1(math_##name, \ - (1, (static, DOUBLE, math_##name##_tn, DOUBLE, 1, nanojit::ACCSET_NONE))) + (2, (static, DOUBLE, math_##name##_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) MATH_BUILTIN_CFUN_1(abs, fabs) MATH_BUILTIN_1(atan) @@ -633,58 +673,44 @@ MATH_BUILTIN_1(sqrt) MATH_BUILTIN_1(tan) static jsdouble FASTCALL -math_acos_tn(jsdouble d) +math_acos_tn(MathCache *cache, jsdouble d) { #if defined(SOLARIS) && defined(__GNUC__) if (d < -1 || 1 < d) { return js_NaN; } #endif - return acos(d); + return cache->lookup(acos, d); } static jsdouble FASTCALL -math_asin_tn(jsdouble d) +math_asin_tn(MathCache *cache, jsdouble d) { #if defined(SOLARIS) && defined(__GNUC__) if (d < -1 || 1 < d) { return js_NaN; } #endif - return asin(d); + return cache->lookup(asin, d); } -#ifdef _WIN32 - static jsdouble FASTCALL -math_exp_tn(JSContext *cx, jsdouble d) +math_exp_tn(MathCache *cache, jsdouble d) { - if (!JSDOUBLE_IS_NaN(d)) { - if (d == js_PositiveInfinity) - return js_PositiveInfinity; - if (d == js_NegativeInfinity) - return 0.0; - } - return exp(d); + return cache->lookup(math_exp_body, d); } JS_DEFINE_TRCINFO_1(math_exp, - (2, (static, DOUBLE, math_exp_tn, CONTEXT, DOUBLE, 1, nanojit::ACCSET_NONE))) - -#else - -MATH_BUILTIN_1(exp) - -#endif + (2, (static, DOUBLE, math_exp_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) static jsdouble FASTCALL -math_log_tn(jsdouble d) +math_log_tn(MathCache *cache, jsdouble d) { #if defined(SOLARIS) && defined(__GNUC__) if (d < 0) return js_NaN; #endif - return log(d); + return cache->lookup(log, d); } static jsdouble FASTCALL @@ -767,15 +793,15 @@ math_floor_tn(jsdouble x) } JS_DEFINE_TRCINFO_1(math_acos, - (1, (static, DOUBLE, math_acos_tn, DOUBLE, 1, nanojit::ACCSET_NONE))) + (2, (static, DOUBLE, math_acos_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) JS_DEFINE_TRCINFO_1(math_asin, - (1, (static, DOUBLE, math_asin_tn, DOUBLE, 1, nanojit::ACCSET_NONE))) + (2, (static, DOUBLE, math_asin_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) JS_DEFINE_TRCINFO_1(math_atan2, (2, (static, DOUBLE, math_atan2_kernel, DOUBLE, DOUBLE, 1, nanojit::ACCSET_NONE))) JS_DEFINE_TRCINFO_1(js_math_floor, (1, (static, DOUBLE, math_floor_tn, DOUBLE, 1, nanojit::ACCSET_NONE))) JS_DEFINE_TRCINFO_1(math_log, - (1, (static, DOUBLE, math_log_tn, DOUBLE, 1, nanojit::ACCSET_NONE))) + (2, (static, DOUBLE, math_log_tn, MATHCACHE, DOUBLE, 1, nanojit::ACCSET_NONE))) JS_DEFINE_TRCINFO_1(js_math_max, (2, (static, DOUBLE, math_max_tn, DOUBLE, DOUBLE, 1, nanojit::ACCSET_NONE))) JS_DEFINE_TRCINFO_1(js_math_min, diff --git a/js/src/jsmath.h b/js/src/jsmath.h index ec5a5386bdda..12cf25f4e0ac 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -39,6 +39,45 @@ #ifndef jsmath_h___ #define jsmath_h___ + +namespace js { + +typedef double (*UnaryFunType)(double); + +class MathCache +{ + static const unsigned SizeLog2 = 12; + static const unsigned Size = 1 << SizeLog2; + struct Entry { double in; UnaryFunType f; double out; }; + Entry table[Size]; + + public: + MathCache(); + + uintN hash(double x) { + union { double d; struct { uint32 one, two; } s; } u = { x }; + uint32 hash32 = u.s.one ^ u.s.two; + uint16 hash16 = (uint16)(hash32 ^ (hash32 >> 16)); + return (hash16 & (Size - 1)) ^ (hash16 >> (16 - SizeLog2)); + } + + /* + * N.B. lookup uses double-equality. This is only safe if hash() maps +0 + * and -0 to different table entries, which is asserted in MathCache(). + */ + double lookup(UnaryFunType f, double x) { + uintN index = hash(x); + Entry &e = table[index]; + if (e.in == x && e.f == f) + return e.out; + e.in = x; + e.f = f; + return (e.out = f(x)); + } +}; + +} /* namespace js */ + /* * JS math functions. */ diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index c325302efe6a..fd2151f8e3c0 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -11228,6 +11228,11 @@ TraceRecorder::callSpecializedNative(JSNativeTraceInfo *trcinfo, uintN argc, if (!tval.isNumber()) goto next_specialization; *argp = this_ins; + } else if (argtype == 'M') { + MathCache *mathCache = JS_THREAD_DATA(cx)->getMathCache(cx); + if (!mathCache) + return RECORD_ERROR; + *argp = INS_CONSTPTR(mathCache); } else { JS_NOT_REACHED("unknown prefix arg type"); } From 9e4f70ad52b99877ac05c5352dc44c4dd3baedc9 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 8 Oct 2010 22:58:33 -0700 Subject: [PATCH 105/284] Remove unnecessary padding from JSStackFrame --- js/src/jsinterp.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 453485677cd6..d819616f363d 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -129,10 +129,6 @@ struct JSStackFrame void *hookData_; /* closure returned by call hook */ void *annotation_; /* perhaps remove with bug 546848 */ -#if JS_BITS_PER_WORD == 32 - void *padding; -#endif - friend class js::StackSpace; friend class js::FrameRegsIter; friend struct JSContext; From c5cde776e49d66c252fda572bc54e010f48c780b Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:39 -0700 Subject: [PATCH 106/284] Fix hiding warnings. r=peterv --- content/html/content/src/nsHTMLOptionElement.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/html/content/src/nsHTMLOptionElement.h b/content/html/content/src/nsHTMLOptionElement.h index 8c1ad52549b1..dd1bdbae9d07 100644 --- a/content/html/content/src/nsHTMLOptionElement.h +++ b/content/html/content/src/nsHTMLOptionElement.h @@ -74,6 +74,8 @@ public: NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::) // nsIDOMHTMLOptionElement + using nsGenericElement::SetText; + using nsGenericElement::GetText; NS_DECL_NSIDOMHTMLOPTIONELEMENT // nsIJSNativeInitializer From 234cbcb3a17701189d5c6337ad401fca0db22a1d Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 107/284] Bug 580128 - Give objects a way to inform XPConnect that they implement their own JS object in a way that does not involve wrapped natives. r=peterv --- dom/base/nsWrapperCache.h | 18 ++- js/src/xpconnect/src/xpcconvert.cpp | 132 +++++++++++++++------- js/src/xpconnect/src/xpcwrappednative.cpp | 7 +- 3 files changed, 110 insertions(+), 47 deletions(-) diff --git a/dom/base/nsWrapperCache.h b/dom/base/nsWrapperCache.h index d7697cbbaffc..555955fec311 100644 --- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -78,7 +78,8 @@ public: void SetWrapper(JSObject* aWrapper) { NS_ASSERTION(!PreservingWrapper(), "Clearing a preserved wrapper!"); - mWrapperPtrBits = reinterpret_cast(aWrapper); + mWrapperPtrBits = reinterpret_cast(aWrapper) | + (mWrapperPtrBits & WRAPPER_IS_PROXY); } void ClearWrapper() @@ -99,12 +100,23 @@ public: PRBool PreservingWrapper() { - return mWrapperPtrBits & WRAPPER_BIT_PRESERVED; + return (mWrapperPtrBits & WRAPPER_BIT_PRESERVED) != 0; + } + + void SetIsProxy() + { + mWrapperPtrBits |= WRAPPER_IS_PROXY; + } + + PRBool IsProxy() + { + return (mWrapperPtrBits & WRAPPER_IS_PROXY) != 0; } private: enum { WRAPPER_BIT_PRESERVED = 1 << 0 }; - enum { kWrapperBitMask = 0x1 }; + enum { WRAPPER_IS_PROXY = 1 << 1 }; + enum { kWrapperBitMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_PROXY) }; PtrBits mWrapperPtrBits; }; diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index f5088ac6fc9e..ce99ee239359 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -1070,7 +1070,7 @@ CreateHolderIfNeeded(XPCCallContext& ccx, JSObject* obj, jsval* d, XPCJSObjectHolder* objHolder = XPCJSObjectHolder::newHolder(ccx, obj); if(!objHolder) return JS_FALSE; - + NS_ADDREF(*dest = objHolder); } @@ -1079,6 +1079,48 @@ CreateHolderIfNeeded(XPCCallContext& ccx, JSObject* obj, jsval* d, return JS_TRUE; } +static void +ComputeWrapperInfo(const XPCCallContext &ccx, + JSObject **callee, + JSScript **script) +{ + *callee = nsnull; + *script = nsnull; + + if(ccx.GetXPCContext()->CallerTypeIsJavaScript()) + { + // Called from JS. We're going to hand the resulting + // JSObject to said JS, so look for the script we want on + // the stack. + JSStackFrame* fp = JS_GetScriptedCaller(ccx, NULL); + if(fp) + { + *script = fp->maybeScript(); + *callee = fp->isFunctionFrame() + ? &fp->callee() + : &fp->scopeChain(); + } + } + else if(ccx.GetXPCContext()->CallerTypeIsNative()) + { + *callee = ccx.GetCallee(); + if(*callee && JS_ObjectIsFunction(ccx, *callee)) + { + // Called from c++, and calling out to |callee|, which is a JS + // function object. Look for the script for this function. + JSFunction* fun = (JSFunction*) xpc_GetJSPrivate(*callee); + NS_ASSERTION(fun, "Must have JSFunction for a Function object"); + *script = JS_GetFunctionScript(ccx, fun); + } + else + { + // Else we don't know whom we're calling, so don't + // create XPCNativeWrappers. + *callee = nsnull; + } + } +} + /***************************************************************************/ // static JSBool @@ -1151,11 +1193,38 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, nsWrapperCache *cache = aHelper.GetWrapperCache(); + JSObject *callee = nsnull; + JSScript *script = nsnull; + PRBool tryConstructSlimWrapper = PR_FALSE; JSObject *flat; if(cache) { flat = cache->GetWrapper(); + if(cache->IsProxy()) + { + if(flat) + { + XPCCallContext &ccx = lccx.GetXPCCallContext(); + if(!ccx.IsValid()) + return JS_FALSE; + + ComputeWrapperInfo(ccx, &callee, &script); + if(!callee) + callee = xpcscope->GetGlobalJSObject(); + + JSAutoCrossCompartmentCall accc; + if(!accc.enter(ccx, callee) || !JS_WrapObject(ccx, &flat)) + return JS_FALSE; + + return CreateHolderIfNeeded(ccx, flat, d, dest); + } + else + { + tryConstructSlimWrapper = PR_TRUE; + } + } + if(!dest) { if(!flat) @@ -1196,6 +1265,22 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, // fall through because we either have a slim wrapper that needs to be // morphed or we have an XPCWrappedNative. flat = cache->GetWrapper(); + if(cache->IsProxy()) + { + XPCCallContext &ccx = lccx.GetXPCCallContext(); + if(!ccx.IsValid()) + return JS_FALSE; + + ComputeWrapperInfo(ccx, &callee, &script); + if(!callee) + callee = xpcscope->GetGlobalJSObject(); + + JSAutoCrossCompartmentCall accc; + if(!accc.enter(ccx, callee) || !JS_WrapObject(ccx, &flat)) + return JS_FALSE; + + return CreateHolderIfNeeded(ccx, flat, d, dest); + } } AutoMarkingNativeInterfacePtr iface; @@ -1221,7 +1306,8 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, } } - NS_ASSERTION(!flat || IS_WRAPPER_CLASS(flat->getClass()), + NS_ASSERTION(!flat || IS_WRAPPER_CLASS(flat->getClass()) || + cache->IsProxy(), "What kind of wrapper is this?"); nsresult rv; @@ -1257,7 +1343,7 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, else rv = NS_OK; } - else + else if(!cache->IsProxy()) { NS_ASSERTION(IS_SLIM_WRAPPER(flat), "What kind of wrapper is this?"); @@ -1299,45 +1385,7 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, // printf("Wrapped native accessed across scope boundary\n"); - JSScript* script = nsnull; - JSObject* callee = nsnull; - if(ccx.GetXPCContext()->CallerTypeIsJavaScript()) - { - // Called from JS. We're going to hand the resulting - // JSObject to said JS, so look for the script we want on - // the stack. - JSContext* cx = ccx; - JSStackFrame* fp = JS_GetScriptedCaller(cx, NULL); - if(fp) - { - script = JS_GetFrameScript(cx, fp); - callee = JS_GetFrameCalleeObject(cx, fp); - } - } - else if(ccx.GetXPCContext()->CallerTypeIsNative()) - { - callee = ccx.GetCallee(); - if(callee && JS_ObjectIsFunction(ccx, callee)) - { - // Called from c++, and calling out to |callee|, which - // is a JS function object. Look for the script for - // this function. - JSFunction* fun = - (JSFunction*) xpc_GetJSPrivate(callee); - NS_ASSERTION(fun, - "Must have JSFunction for a Function " - "object"); - script = JS_GetFunctionScript(ccx, fun); - } - else - { - // Else we don't know whom we're calling, so don't - // create XPCNativeWrappers. - callee = nsnull; - } - } - // else don't create XPCNativeWrappers, since we have - // no idea what's calling what here. + ComputeWrapperInfo(ccx, &callee, &script); flags = script ? JS_GetScriptFilenameFlags(script) : 0; NS_ASSERTION(flags != JSFILENAME_NULL, "null script filename"); diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 9a25fe508add..c492d33132aa 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -3879,11 +3879,15 @@ ConstructSlimWrapper(XPCCallContext &ccx, return JS_FALSE; } + nsWrapperCache *cache = aHelper.GetWrapperCache(); JSObject* plannedParent = parent; rv = classInfoHelper->PreCreate(identityObj, ccx, parent, &parent); if(rv != NS_SUCCESS_ALLOW_SLIM_WRAPPERS) { - SLIM_LOG_NOT_CREATED(ccx, identityObj, "PreCreate hook refused"); + if(cache->IsProxy()) + NS_ASSERTION(cache->GetWrapper(), "out of memory?"); + else + SLIM_LOG_NOT_CREATED(ccx, identityObj, "PreCreate hook refused"); return JS_FALSE; } @@ -3902,7 +3906,6 @@ ConstructSlimWrapper(XPCCallContext &ccx, // The PreCreate hook could have forced the creation of a wrapper, need // to check for that here and return early. - nsWrapperCache *cache = aHelper.GetWrapperCache(); JSObject* wrapper = cache->GetWrapper(); if(wrapper) { From 6400e2ea1d8ed7af3a7732e3fe419706329af3ad Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 108/284] bug 580128 - Give API consumers a way to perform "brain transplants" on an object. r=gal --- js/src/jsapi.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++-- js/src/jsapi.h | 5 +++- js/src/jscntxt.h | 8 +++++- 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 0527dc85185d..6c128c7b9739 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1148,9 +1148,8 @@ JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback) } JS_PUBLIC_API(JSWrapObjectCallback) -JS_SetWrapObjectCallback(JSContext *cx, JSWrapObjectCallback callback) +JS_SetWrapObjectCallback(JSRuntime *rt, JSWrapObjectCallback callback) { - JSRuntime *rt = cx->runtime; JSWrapObjectCallback old = rt->wrapObjectCallback; rt->wrapObjectCallback = callback; return old; @@ -1227,6 +1226,69 @@ JS_WrapValue(JSContext *cx, jsval *vp) return cx->compartment->wrap(cx, Valueify(vp)); } +JS_PUBLIC_API(JSObject *) +JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) +{ + JS_ASSERT(wrapper->isWrapper()); + + /* + * This function is called when a window is navigating. In that case, we + * need to "move" the window from wrapper's compartment to target's + * compartment. + */ + JSCompartment *destination = target->getCompartment(cx); + + JSObject *obj; + WrapperMap &map = destination->crossCompartmentWrappers; + Value wrapperv = ObjectValue(*wrapper); + + // There might already be a wrapper for the window in the new compartment. + if (WrapperMap::Ptr p = map.lookup(wrapperv)) { + // If there is, make it the primary outer window proxy around the + // inner (accomplished by swapping target's innards with the old, + // possibly security wrapper, innards). + obj = &p->value.toObject(); + map.remove(p); + obj->swap(target); + } else { + // Otherwise, this is going to be our outer window proxy in the new + // compartment. + obj = target; + } + + // Now, iterate through other scopes looking for references to the old + // outer window. They need to be updated to point at the new outer window. + // They also might transition between different types of security wrappers + // based on whether the new compartment is same origin with them. + Value targetv = ObjectValue(*target); + WrapperVector &vector = cx->runtime->compartments; + for (JSCompartment **p = vector.begin(), **end = vector.end(); p != end; ++p) { + WrapperMap &pmap = (*p)->crossCompartmentWrappers; + if (WrapperMap::Ptr wp = pmap.lookup(wrapperv)) { + // We found a wrapper around the outer window! + JSObject *wobj = &wp->value.toObject(); + + // First, we wrap it in the new compartment. This will return a + // new wrapper. + JSAutoEnterCompartment ec; + JSObject *tobj = obj; + if (!ec.enter(cx, wobj) || !(*p)->wrap(cx, &tobj)) + return NULL; + + // Now, because we need to maintain object identity, we do a brain + // transplant on the old object. At the same time, we update the + // entry in the compartment's wrapper map to point to the old + // wrapper, and remove the old outer window from the wrapper map, + // since it is now an obsolete reference. + wobj->swap(tobj); + pmap.put(targetv, ObjectValue(*wobj)); + pmap.remove(wp); + } + } + + return obj; +} + JS_PUBLIC_API(JSObject *) JS_GetGlobalObject(JSContext *cx) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 93886a105e0f..a4f914947eb5 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -937,7 +937,7 @@ extern JS_PUBLIC_API(JSCompartmentCallback) JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback); extern JS_PUBLIC_API(JSWrapObjectCallback) -JS_SetWrapObjectCallback(JSContext *cx, JSWrapObjectCallback callback); +JS_SetWrapObjectCallback(JSRuntime *rt, JSWrapObjectCallback callback); extern JS_PUBLIC_API(JSCrossCompartmentCall *) JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target); @@ -957,6 +957,9 @@ JS_WrapObject(JSContext *cx, JSObject **objp); extern JS_PUBLIC_API(JSBool) JS_WrapValue(JSContext *cx, jsval *vp); +extern JS_PUBLIC_API(JSObject *) +JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target); + #ifdef __cplusplus JS_END_EXTERN_C diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 4ae5aa44c5ab..3072311db842 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1295,6 +1295,12 @@ typedef struct JSPropertyTreeEntry { typedef void (* JSActivityCallback)(void *arg, JSBool active); +namespace js { + +typedef js::Vector WrapperVector; + +} + struct JSRuntime { /* Default compartment. */ JSCompartment *defaultCompartment; @@ -1303,7 +1309,7 @@ struct JSRuntime { #endif /* List of compartments (protected by the GC lock). */ - js::Vector compartments; + js::WrapperVector compartments; /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ JSRuntimeState state; From 4e10badc68e21f415bd281efa05f6ce8a026fb5d Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 109/284] Bug 580128 - Make the system principal consistent over the strings it gives out. r=peterv --- caps/src/nsSystemPrincipal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caps/src/nsSystemPrincipal.cpp b/caps/src/nsSystemPrincipal.cpp index 7b02804c3d26..fb48d4feaa0d 100644 --- a/caps/src/nsSystemPrincipal.cpp +++ b/caps/src/nsSystemPrincipal.cpp @@ -189,7 +189,7 @@ nsSystemPrincipal::GetURI(nsIURI** aURI) NS_IMETHODIMP nsSystemPrincipal::GetOrigin(char** aOrigin) { - *aOrigin = ToNewCString(NS_LITERAL_CSTRING("[System]")); + *aOrigin = ToNewCString(NS_LITERAL_CSTRING("[System Principal]")); return *aOrigin ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } From 2b8eff64dffc7de16d077b57095f72a240769c2b Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 110/284] bug 580128 - Don't assume that we have a wrapped native object here. r=gal --- js/src/xpconnect/wrappers/AccessCheck.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index db7a1631c080..bbe2a608be29 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -233,7 +233,9 @@ AccessCheck::isSystemOnlyAccessPermitted(JSContext *cx) bool AccessCheck::needsSystemOnlyWrapper(JSObject *obj) { - NS_ASSERTION(IS_WN_WRAPPER_OBJECT(obj), "expected a wrapped native here"); + if (!IS_WN_WRAPPER(obj)) + return false; + XPCWrappedNative *wn = static_cast(obj->getPrivate()); return wn->NeedsSOW(); } From b548e78f8af4d316691982ed2f9e1e1ba5779a49 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 111/284] bug 580128 - Create a proxy for the outer window and use it. Note: this turns on new wrappers for all objects. r=peterv --- dom/base/nsDOMClassInfo.cpp | 61 +- dom/base/nsGlobalWindow.cpp | 103 ++-- dom/base/nsGlobalWindow.h | 24 + dom/base/nsIScriptContext.h | 11 +- dom/base/nsJSEnvironment.cpp | 64 +- dom/base/nsJSEnvironment.h | 1 + js/src/xpconnect/src/XPCWrapper.cpp | 9 + js/src/xpconnect/src/xpcconvert.cpp | 564 +++++++----------- js/src/xpconnect/src/xpcjsruntime.cpp | 2 + js/src/xpconnect/src/xpcprivate.h | 4 + js/src/xpconnect/src/xpcwrappednative.cpp | 47 +- .../xpconnect/src/xpcwrappednativejsops.cpp | 87 +-- 12 files changed, 394 insertions(+), 583 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index ca5947064cbb..65be19d0bf04 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -5317,17 +5317,6 @@ nsCommonWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, rv = WrapNative(cx, frameWin->GetGlobalJSObject(), frame, &NS_GET_IID(nsIDOMWindow), PR_TRUE, vp, getter_AddRefs(holder)); - - if (NS_SUCCEEDED(rv) && !win->IsChromeWindow()) { - JSObject *scopeobj = JS_GetScopeChain(cx); - if (!scopeobj) { - *_retval = JS_FALSE; - return NS_ERROR_FAILURE; - } - - rv = sXPConnect->GetXOWForObject(cx, scopeobj, JSVAL_TO_OBJECT(*vp), - vp); - } } return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; @@ -5532,21 +5521,6 @@ nsCommonWindowSH::DelProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } } - // Notify any XOWs on our outer window. - - nsGlobalWindow *outerWin = win->GetOuterWindowInternal(); - if (outerWin) { - nsCOMPtr wn; - nsIXPConnect *xpc = nsContentUtils::XPConnect(); - nsresult rv = - xpc->GetWrappedNativeOfJSObject(cx, outerWin->GetGlobalJSObject(), - getter_AddRefs(wn)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = xpc->UpdateXOWs(cx, wn, nsIXPConnect::XPC_XOW_CLEARSCOPE); - NS_ENSURE_SUCCESS(rv, rv); - } - return NS_OK; } @@ -6950,17 +6924,6 @@ nsCommonWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); -#ifdef DEBUG - if (!win->IsChromeWindow()) { - NS_ASSERTION(JSVAL_IS_OBJECT(v) && - !strcmp(JSVAL_TO_OBJECT(v)->getClass()->name, - "XPCCrossOriginWrapper"), - "Didn't wrap a location object!"); - } -#endif - - JSAutoRequest ar(cx); - JSBool ok = ::JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, JSPROP_PERMANENT | JSPROP_ENUMERATE); @@ -7060,29 +7023,15 @@ nsCommonWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, if (id == sWindow_id) { // window should *always* be the outer window object. - nsGlobalWindow *oldWin = win; win = win->GetOuterWindowInternal(); NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE); - JSAutoRequest ar(cx); + jsval winVal; + nsCOMPtr holder; + rv = WrapNative(cx, obj, nsGlobalWindow::ToSupports(win), PR_TRUE, + &winVal, getter_AddRefs(holder)); + NS_ENSURE_SUCCESS(rv, rv); - jsval winVal = OBJECT_TO_JSVAL(win->GetGlobalJSObject()); - if (!win->IsChromeWindow()) { - JSObject *scope; - nsGlobalWindow *innerWin; - if (oldWin->IsInnerWindow()) { - scope = oldWin->GetGlobalJSObject(); - } else if ((innerWin = oldWin->GetCurrentInnerWindowInternal())) { - scope = innerWin->GetGlobalJSObject(); - } else { - NS_ERROR("I don't know what scope to use!"); - scope = oldWin->GetGlobalJSObject(); - } - - rv = sXPConnect->GetXOWForObject(cx, scope, JSVAL_TO_OBJECT(winVal), - &winVal); - NS_ENSURE_SUCCESS(rv, rv); - } PRBool ok = ::JS_DefinePropertyById(cx, obj, id, winVal, JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_ENUMERATE); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 149787d7f75c..ef50a4ecd7fd 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -656,6 +656,30 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow) nsPIDOMWindow::~nsPIDOMWindow() {} +//***************************************************************************** +// nsOuterWindowProxy: Outer Window Proxy +//***************************************************************************** + +JSString * +nsOuterWindowProxy::obj_toString(JSContext *cx, JSObject *proxy) +{ + JS_ASSERT(proxy->isProxy()); + + return JS_NewStringCopyZ(cx, "[object WindowProxy]"); +} + +nsOuterWindowProxy +nsOuterWindowProxy::singleton; + +JSObject * +NS_NewOuterWindowProxy(JSContext *cx, JSObject *parent) +{ + JSObject *obj = JSWrapper::New(cx, parent, parent->getProto(), parent, + &nsOuterWindowProxy::singleton); + NS_ASSERTION(obj->getClass()->ext.innerObject, "bad class"); + return obj; +} + //***************************************************************************** //*** nsGlobalWindow: Object Management //***************************************************************************** @@ -740,6 +764,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) Freeze(); mObserver = nsnull; + SetIsProxy(); } // We could have failed the first time through trying @@ -1156,6 +1181,13 @@ nsGlobalWindow::FreeInnerObjects(PRBool aClearScope) } \ } else +#define OUTER_WINDOW_ONLY \ + if (IsOuterWindow()) { + +#define END_OUTER_WINDOW_ONLY \ + foundInterface = 0; \ + } else + NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow) DOMCI_DATA(Window, nsGlobalWindow) @@ -1182,6 +1214,9 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) WINDOW_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window) + OUTER_WINDOW_ONLY + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + END_OUTER_WINDOW_ONLY NS_INTERFACE_MAP_END @@ -1898,39 +1933,41 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, if (!mJSObject) { mContext->CreateOuterObject(this, newInnerWindow); mContext->DidInitializeContext(); - mJSObject = (JSObject *)mContext->GetNativeGlobal(); - } else { - // XXX New global object and brain transplant! - rv = xpc->GetWrappedNativeOfJSObject(cx, mJSObject, - getter_AddRefs(wrapper)); - NS_ENSURE_SUCCESS(rv, rv); - // Restore our object's prototype to its original value so we're sure to - // update it under ReparentWrappedNativeIfFound. - JSObject *proto; - wrapper->GetJSObjectPrototype(&proto); - if (!JS_SetPrototype(cx, mJSObject, proto)) { - NS_ERROR("Can't set prototype"); - return NS_ERROR_UNEXPECTED; + mJSObject = (JSObject *)mContext->GetNativeGlobal(); + SetWrapper(mJSObject); + } else { + JSObject *outerObject = + NS_NewOuterWindowProxy(cx, newInnerWindow->mJSObject); + if (!outerObject) { + NS_ERROR("out of memory"); + return NS_ERROR_FAILURE; } - nsCOMPtr holder; - xpc->ReparentWrappedNativeIfFound(cx, currentInner->mJSObject, - newInnerWindow->mJSObject, - ToSupports(this), - getter_AddRefs(holder)); + outerObject = JS_TransplantWrapper(cx, mJSObject, outerObject); + if (!outerObject) { + NS_ERROR("unable to transplant wrappers, probably OOM"); + return NS_ERROR_FAILURE; + } - if (aState) { - if (nsIXPConnectJSObjectHolder *holder = wsh->GetOuterRealProto()) { - holder->GetJSObject(&proto); - } else { - proto = nsnull; - } + mJSObject = outerObject; + SetWrapper(mJSObject); - if (!JS_SetPrototype(cx, mJSObject, proto)) { - NS_ERROR("can't set prototype"); - return NS_ERROR_FAILURE; - } + mContext->SetOuterObject(mJSObject); + } + + // XXX Not sure if this is needed. + if (aState) { + JSObject *proto; + if (nsIXPConnectJSObjectHolder *holder = wsh->GetOuterRealProto()) { + holder->GetJSObject(&proto); + } else { + proto = nsnull; + } + + if (!JS_SetPrototype(cx, mJSObject, proto)) { + NS_ERROR("can't set prototype"); + return NS_ERROR_FAILURE; } } } @@ -2050,16 +2087,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, mContext->GC(); mContext->DidInitializeContext(); - if (!wrapper) { - rv = xpc->GetWrappedNativeOfJSObject(cx, mJSObject, - getter_AddRefs(wrapper)); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = xpc->UpdateXOWs((JSContext *)GetContextInternal()->GetNativeContext(), - wrapper, nsIXPConnect::XPC_XOW_NAVIGATED); - NS_ENSURE_SUCCESS(rv, rv); - if (!aState && !reUseInnerWindow) { nsContentUtils::AddScriptRunner( NS_NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated)); diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index c5ef09447f74..37432e1f7550 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -110,6 +110,10 @@ #include "nsFrameMessageManager.h" #include "mozilla/TimeStamp.h" +// JS includes +#include "jsapi.h" +#include "jswrapper.h" + #define DEFAULT_HOME_PAGE "www.mozilla.org" #define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage" @@ -226,6 +230,25 @@ private: nsAutoRefCnt mRefCnt; }; +//***************************************************************************** +// nsOuterWindow: Outer Window Proxy +//***************************************************************************** + +class nsOuterWindowProxy : public JSWrapper +{ +public: + nsOuterWindowProxy() : JSWrapper(0) {} + + virtual bool isOuterWindow() { + return true; + } + JSString *obj_toString(JSContext *cx, JSObject *wrapper); + + static nsOuterWindowProxy singleton; +}; + +JSObject *NS_NewOuterWindowProxy(JSContext *cx, JSObject *parent); + //***************************************************************************** // nsGlobalWindow: Global Object for Scripting //***************************************************************************** @@ -257,6 +280,7 @@ class nsGlobalWindow : public nsPIDOMWindow, public nsIDOMStorageWindow, public nsSupportsWeakReference, public nsIInterfaceRequestor, + public nsWrapperCache, public PRCListStr { public: diff --git a/dom/base/nsIScriptContext.h b/dom/base/nsIScriptContext.h index 314172ca1510..1a32a1e64f42 100644 --- a/dom/base/nsIScriptContext.h +++ b/dom/base/nsIScriptContext.h @@ -72,10 +72,10 @@ public: NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptContextPrincipal, NS_ISCRIPTCONTEXTPRINCIPAL_IID) -// 5b6d04a3-f095-4924-ad84-4f44f9b3fae0 +// a7139c0e-962c-44b6-bec3-e4166bfe84eb #define NS_ISCRIPTCONTEXT_IID \ -{ 0x5b6d04a3, 0xf095, 0x4924, \ - { 0xad, 0x84, 0x4f, 0x44, 0xf9, 0xb3, 0xfa, 0xe0 } } +{ 0xa7139c0e, 0x962c, 0x44b6, \ + { 0xbe, 0xc3, 0xe4, 0x16, 0x6b, 0xfe, 0x84, 0xeb } } /* This MUST match JSVERSION_DEFAULT. This version stuff if we don't know what language we have is a little silly... */ @@ -346,6 +346,11 @@ public: virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, nsIScriptGlobalObject *aCurrentInner) = 0; + /** + * Given an outer object, updates this context with that outer object. + */ + virtual nsresult SetOuterObject(void *aOuterObject) = 0; + /** * Prepares this context for use with the current inner window for the * context's global object. This must be called after CreateOuterObject. diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index d621df2a597c..566de3d90efe 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -102,10 +102,7 @@ #include "nsITimelineService.h" #include "nsDOMScriptObjectHolder.h" #include "prmem.h" - -#ifdef NS_DEBUG #include "nsGlobalWindow.h" -#endif #ifdef MOZ_JSDEBUGGER #include "jsdIDebuggerService.h" @@ -2407,7 +2404,11 @@ nsJSContext::GetGlobalObject() JSObject *global = ::JS_GetGlobalObject(mContext); if (!global) { - NS_WARNING("Context has no global."); + return nsnull; + } + + OBJ_TO_INNER_OBJECT(mContext, global); + if (!global) { return nsnull; } @@ -2436,7 +2437,11 @@ nsJSContext::GetGlobalObject() // This'll return a pointer to something we're about to release, but // that's ok, the JS object will hold it alive long enough. - return sgo; + nsCOMPtr pwin(do_QueryInterface(sgo)); + if (!pwin) + return sgo; + + return static_cast(pwin->GetOuterWindow()); } void * @@ -2564,17 +2569,28 @@ nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, JS_SetOptions(mContext, JS_GetOptions(mContext) | JSOPTION_XML); } + JSObject *outer = + NS_NewOuterWindowProxy(mContext, aCurrentInner->GetGlobalJSObject()); + if (!outer) { + return NS_ERROR_FAILURE; + } + + return SetOuterObject(outer); +} + +nsresult +nsJSContext::SetOuterObject(void *aOuterObject) +{ + JSObject *outer = static_cast(aOuterObject); + nsIXPConnect *xpc = nsContentUtils::XPConnect(); nsCOMPtr holder; - nsresult rv = xpc->WrapNative(mContext, aCurrentInner->GetGlobalJSObject(), - aGlobalObject, NS_GET_IID(nsISupports), - getter_AddRefs(holder)); + + nsresult rv = xpc->HoldObject(mContext, outer, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); // Force our context's global object to be the outer. - JSObject *globalObj; - holder->GetJSObject(&globalObj); - JS_SetGlobalObject(mContext, globalObj); + JS_SetGlobalObject(mContext, outer); // Hold a strong reference to the wrapper for the global to avoid // rooting and unrooting the global object every time its AddRef() @@ -2587,32 +2603,8 @@ nsresult nsJSContext::InitOuterWindow() { JSObject *global = JS_GetGlobalObject(mContext); - nsIScriptGlobalObject *sgo = GetGlobalObject(); - // Call ClearScope to nuke any properties (e.g. Function and Object) on the - // outer object. From now on, anybody asking the outer object for these - // properties will be forwarded to the inner window. - JS_ClearScope(mContext, global); - - nsresult rv = NS_OK; - - nsCOMPtr ci(do_QueryInterface(sgo)); - if (ci) { - jsval v; - - nsCOMPtr holder; - rv = nsContentUtils::WrapNative(mContext, global, sgo, &v, - getter_AddRefs(holder)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr wrapper(do_QueryInterface(holder)); - NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE); - - rv = wrapper->RefreshPrototype(); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = InitClasses(global); // this will complete global object initialization + nsresult rv = InitClasses(global); // this will complete global object initialization NS_ENSURE_SUCCESS(rv, rv); return NS_OK; diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index baa28e02763a..520d06e5ad97 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -143,6 +143,7 @@ public: virtual nsresult InitContext(); virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, nsIScriptGlobalObject *aCurrentInner); + virtual nsresult SetOuterObject(void *aOuterObject); virtual nsresult InitOuterWindow(); virtual PRBool IsContextInitialized(); virtual void FinalizeContext(); diff --git a/js/src/xpconnect/src/XPCWrapper.cpp b/js/src/xpconnect/src/XPCWrapper.cpp index b84ccd669f46..cf150b48b04b 100644 --- a/js/src/xpconnect/src/XPCWrapper.cpp +++ b/js/src/xpconnect/src/XPCWrapper.cpp @@ -43,6 +43,7 @@ #include "XPCWrapper.h" #include "XPCNativeWrapper.h" #include "nsPIDOMWindow.h" +#include "jswrapper.h" namespace XPCWrapper { @@ -61,6 +62,14 @@ const PRUint32 sSecMgrGetProp = nsIXPCSecurityManager::ACCESS_GET_PROPERTY; JSObject * Unwrap(JSContext *cx, JSObject *wrapper) { + if (wrapper->isProxy()) { + if (wrapper->getProxyHandler() != &JSCrossCompartmentWrapper::singleton) { + // XXX Security check! + } + + return wrapper->unwrap(); + } + js::Class *clasp = wrapper->getClass(); if (clasp == &XPCCrossOriginWrapper::XOWClass) { return UnwrapXOW(cx, wrapper); diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index ce99ee239359..04f8f1d2786f 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -1148,376 +1148,244 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, if(pErr) *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; -// #define this if we want to 'double wrap' of JSObjects. -// This is for the case where we have a JSObject wrapped for native use -// which needs to be converted to a JSObject. Originally, we were unwrapping -// and just exposing the underlying JSObject. This causes anomolies when -// JSComponents are accessed from other JS code - they don't act like -// other xpconnect wrapped components. Eventually we want to build a new -// kind of wrapper especially for JS <-> JS. For now we are building a wrapper -// around a wrapper. This is not optimal, but good enough for now. -#define XPC_DO_DOUBLE_WRAP 1 + // We used to have code here that unwrapped and simply exposed the + // underlying JSObject. That caused anomolies when JSComponents were + // accessed from other JS code - they didn't act like other xpconnect + // wrapped components. So, instead, we create "double wrapped" objects + // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't + // optimal -- we could detect this and roll the functionality into a + // single wrapper, but the current solution is good enough for now. + JSContext* cx = lccx.GetJSContext(); -#ifndef XPC_DO_DOUBLE_WRAP - // is this a wrapped JS object? - if(nsXPCWrappedJSClass::IsWrappedJS(src)) + XPCWrappedNativeScope* xpcscope = + XPCWrappedNativeScope::FindInJSObjectScope(cx, scope); + if(!xpcscope) + return JS_FALSE; + + // First, see if this object supports the wrapper cache. + // Note: If |cache->IsProxy()| is true, then it means that the object + // implementing it doesn't want a wrapped native as its JS Object, but + // instead it provides its own proxy object. In that case, the object + // to use is found as cache->GetWrapper(). If that is null, then the + // object will create (and fill the cache) from its PreCreate call. + nsWrapperCache *cache = aHelper.GetWrapperCache(); + + JSObject *callee; + JSScript *script; + + PRBool tryConstructSlimWrapper = PR_FALSE; + JSObject *flat; + if(cache) { - NS_ASSERTION(!isGlobal, "The global object must be native"); + flat = cache->GetWrapper(); + if(cache->IsProxy()) + { + XPCCallContext &ccx = lccx.GetXPCCallContext(); + if(!ccx.IsValid()) + return JS_FALSE; - // verify that this wrapper is for the right interface - nsCOMPtr wrapper; - if(iid) - src->QueryInterface(*iid, (void**)getter_AddRefs(wrapper)); - else - wrapper = do_QueryInterface(src); - nsCOMPtr holder = - do_QueryInterface(wrapper); - JSObject* flat; - if(!holder || !(flat = holder->GetFlatJSObject())) - return JS_FALSE; + if(!flat) + flat = ConstructProxyObject(ccx, aHelper, xpcscope); - *d = OBJECT_TO_JSVAL(flat); - if(dest) - holder.swap(*dest); - return JS_TRUE; + ComputeWrapperInfo(ccx, &callee, &script); + if(!callee) + { + callee = xpcscope->GetGlobalJSObject(); + OBJ_TO_INNER_OBJECT(ccx, callee); + if(!callee) + return JS_FALSE; + } + + JSAutoEnterCompartment ac; + if(!ac.enter(ccx, callee) || !JS_WrapObject(ccx, &flat)) + return JS_FALSE; + + return CreateHolderIfNeeded(ccx, flat, d, dest); + } + + if(!dest) + { + if(!flat) + { + tryConstructSlimWrapper = PR_TRUE; + } + else if(IS_SLIM_WRAPPER_OBJECT(flat)) + { + JSObject* global = JS_GetGlobalForObject(cx, flat); + if(global == xpcscope->GetGlobalJSObject()) + { + *d = OBJECT_TO_JSVAL(flat); + return JS_TRUE; + } + } + } } else -#endif /* XPC_DO_DOUBLE_WRAP */ { - JSContext* cx = lccx.GetJSContext(); + flat = nsnull; + } - XPCWrappedNativeScope* xpcscope = - XPCWrappedNativeScope::FindInJSObjectScope(cx, scope); - if(!xpcscope) + // If we're not handing this wrapper to an nsIXPConnectJSObjectHolder, and + // the object supports slim wrappers, try to create one here. + if(tryConstructSlimWrapper) + { + XPCCallContext &ccx = lccx.GetXPCCallContext(); + if(!ccx.IsValid()) return JS_FALSE; - nsWrapperCache *cache = aHelper.GetWrapperCache(); - - JSObject *callee = nsnull; - JSScript *script = nsnull; - - PRBool tryConstructSlimWrapper = PR_FALSE; - JSObject *flat; - if(cache) + jsval slim; + if(ConstructSlimWrapper(ccx, aHelper, xpcscope, &slim)) { - flat = cache->GetWrapper(); - if(cache->IsProxy()) - { - if(flat) - { - XPCCallContext &ccx = lccx.GetXPCCallContext(); - if(!ccx.IsValid()) - return JS_FALSE; + *d = slim; + return JS_TRUE; + } - ComputeWrapperInfo(ccx, &callee, &script); - if(!callee) - callee = xpcscope->GetGlobalJSObject(); + // Even if ConstructSlimWrapper returns JS_FALSE it might have created a + // wrapper (while calling the PreCreate hook). In that case we need to + // fall through because we either have a slim wrapper that needs to be + // morphed or an XPCWrappedNative. + flat = cache->GetWrapper(); + } - JSAutoCrossCompartmentCall accc; - if(!accc.enter(ccx, callee) || !JS_WrapObject(ccx, &flat)) - return JS_FALSE; + // We can't simply construct a slim wrapper. Go ahead and create an + // XPCWrappedNative for this object. At this point, |flat| could be + // non-null, meaning that either we already have a wrapped native from + // the cache (which might need to be QI'd to the new interface) or that + // we found a slim wrapper that we'll have to morph. + AutoMarkingNativeInterfacePtr iface; + if(iid) + { + XPCCallContext &ccx = lccx.GetXPCCallContext(); + if(!ccx.IsValid()) + return JS_FALSE; - return CreateHolderIfNeeded(ccx, flat, d, dest); - } - else - { - tryConstructSlimWrapper = PR_TRUE; - } - } + iface.Init(ccx); - if(!dest) - { - if(!flat) - { - tryConstructSlimWrapper = PR_TRUE; - } - else if(IS_SLIM_WRAPPER_OBJECT(flat)) - { - JSObject* global = JS_GetGlobalForObject(cx, flat); - if(global == xpcscope->GetGlobalJSObject()) - { - *d = OBJECT_TO_JSVAL(flat); - return JS_TRUE; - } - } - } + if(Interface) + iface = *Interface; + + if(!iface) + { + iface = XPCNativeInterface::GetNewOrUsed(ccx, iid); + if(!iface) + return JS_FALSE; + + if(Interface) + *Interface = iface; + } + } + + NS_ASSERTION(!flat || IS_WRAPPER_CLASS(flat->getClass()), + "What kind of wrapper is this?"); + + nsresult rv; + XPCWrappedNative* wrapper; + nsRefPtr strongWrapper; + if(!flat) + { + XPCCallContext &ccx = lccx.GetXPCCallContext(); + if(!ccx.IsValid()) + return JS_FALSE; + + rv = XPCWrappedNative::GetNewOrUsed(ccx, aHelper, xpcscope, iface, + isGlobal, + getter_AddRefs(strongWrapper)); + + wrapper = strongWrapper; + } + else if(IS_WN_WRAPPER_OBJECT(flat)) + { + wrapper = static_cast(xpc_GetJSPrivate(flat)); + + // If asked to return the wrapper we'll return a strong reference, + // otherwise we'll just return its JSObject in d (which should be + // rooted in that case). + if(dest) + strongWrapper = wrapper; + // If iface is not null we know lccx.GetXPCCallContext() returns + // a valid XPCCallContext because we checked when calling Init on + // iface. + if(iface) + wrapper->FindTearOff(lccx.GetXPCCallContext(), iface, JS_FALSE, + &rv); + else + rv = NS_OK; + } + else + { + NS_ASSERTION(IS_SLIM_WRAPPER(flat), + "What kind of wrapper is this?"); + + XPCCallContext &ccx = lccx.GetXPCCallContext(); + if(!ccx.IsValid()) + return JS_FALSE; + + SLIM_LOG(("***** morphing from XPCConvert::NativeInterface2JSObject" + "(%p)\n", + static_cast(xpc_GetJSPrivate(flat)))); + + rv = XPCWrappedNative::Morph(ccx, flat, iface, cache, + getter_AddRefs(strongWrapper)); + wrapper = strongWrapper; + } + + if(pErr) + *pErr = rv; + + // If creating the wrapped native failed, then return early. + if(NS_FAILED(rv) || !wrapper) + return JS_FALSE; + + // If we're not creating security wrappers, we can return the + // XPCWrappedNative as-is here. + flat = wrapper->GetFlatJSObject(); + jsval v = OBJECT_TO_JSVAL(flat); + if(!XPCPerThreadData::IsMainThread(lccx.GetJSContext()) || + !allowNativeWrapper) + { + *d = v; + if(dest) + *dest = strongWrapper.forget().get(); + return JS_TRUE; + } + + XPCCallContext &ccx = lccx.GetXPCCallContext(); + if(!ccx.IsValid()) + return JS_FALSE; + + ComputeWrapperInfo(ccx, &callee, &script); + if(!callee) + { + callee = xpcscope->GetGlobalJSObject(); + OBJ_TO_INNER_OBJECT(cx, callee); + if(!callee) + return JS_FALSE; + } + + JSAutoEnterCompartment ac; + if(!ac.enter(ccx, callee) || !JS_WrapObject(ccx, &flat)) + return JS_FALSE; + + *d = OBJECT_TO_JSVAL(flat); + if(dest) + { + // The strongWrapper still holds the original flat object. + if(OBJECT_TO_JSVAL(flat) == *d) + { + *dest = strongWrapper.forget().get(); } else { - flat = nsnull; - } - - if(tryConstructSlimWrapper) - { - XPCCallContext &ccx = lccx.GetXPCCallContext(); - if(!ccx.IsValid()) + nsRefPtr objHolder = + XPCJSObjectHolder::newHolder(ccx, JSVAL_TO_OBJECT(*d)); + if(!objHolder) return JS_FALSE; - jsval slim; - if(ConstructSlimWrapper(ccx, aHelper, xpcscope, &slim)) - { - *d = slim; - return JS_TRUE; - } - - // Even if ConstructSlimWrapper returns JS_FALSE it might have created a - // wrapper (while calling the PreCreate hook). In that case we need to - // fall through because we either have a slim wrapper that needs to be - // morphed or we have an XPCWrappedNative. - flat = cache->GetWrapper(); - if(cache->IsProxy()) - { - XPCCallContext &ccx = lccx.GetXPCCallContext(); - if(!ccx.IsValid()) - return JS_FALSE; - - ComputeWrapperInfo(ccx, &callee, &script); - if(!callee) - callee = xpcscope->GetGlobalJSObject(); - - JSAutoCrossCompartmentCall accc; - if(!accc.enter(ccx, callee) || !JS_WrapObject(ccx, &flat)) - return JS_FALSE; - - return CreateHolderIfNeeded(ccx, flat, d, dest); - } - } - - AutoMarkingNativeInterfacePtr iface; - if(iid) - { - XPCCallContext &ccx = lccx.GetXPCCallContext(); - if(!ccx.IsValid()) - return JS_FALSE; - - iface.Init(ccx); - - if(Interface) - iface = *Interface; - - if(!iface) - { - iface = XPCNativeInterface::GetNewOrUsed(ccx, iid); - if(!iface) - return JS_FALSE; - - if(Interface) - *Interface = iface; - } - } - - NS_ASSERTION(!flat || IS_WRAPPER_CLASS(flat->getClass()) || - cache->IsProxy(), - "What kind of wrapper is this?"); - - nsresult rv; - XPCWrappedNative* wrapper; - nsRefPtr strongWrapper; - if(!flat) - { - XPCCallContext &ccx = lccx.GetXPCCallContext(); - if(!ccx.IsValid()) - return JS_FALSE; - - rv = XPCWrappedNative::GetNewOrUsed(ccx, aHelper, xpcscope, iface, - isGlobal, - getter_AddRefs(strongWrapper)); - - wrapper = strongWrapper; - } - else if(IS_WN_WRAPPER_OBJECT(flat)) - { - wrapper = static_cast(xpc_GetJSPrivate(flat)); - - // If asked to return the wrapper we'll return a strong reference, - // otherwise we'll just return its JSObject in d (which should be - // rooted in that case). - if(dest) - strongWrapper = wrapper; - // If iface is not null we know lccx.GetXPCCallContext() returns - // a valid XPCCallContext because we checked when calling Init on - // iface. - if(iface) - wrapper->FindTearOff(lccx.GetXPCCallContext(), iface, JS_FALSE, - &rv); - else - rv = NS_OK; - } - else if(!cache->IsProxy()) - { - NS_ASSERTION(IS_SLIM_WRAPPER(flat), - "What kind of wrapper is this?"); - - XPCCallContext &ccx = lccx.GetXPCCallContext(); - if(!ccx.IsValid()) - return JS_FALSE; - - SLIM_LOG(("***** morphing from XPCConvert::NativeInterface2JSObject" - "(%p)\n", - static_cast(xpc_GetJSPrivate(flat)))); - - rv = XPCWrappedNative::Morph(ccx, flat, iface, cache, - getter_AddRefs(strongWrapper)); - wrapper = strongWrapper; - } - - if(pErr) - *pErr = rv; - if(NS_SUCCEEDED(rv) && wrapper) - { - XPCCallContext &ccx = lccx.GetXPCCallContext(); - if(!ccx.IsValid()) - return JS_FALSE; - - uint32 flags = 0; - flat = wrapper->GetFlatJSObject(); - jsval v = OBJECT_TO_JSVAL(flat); - - JSBool sameOrigin; - if (allowNativeWrapper && - !xpc_SameScope(wrapper->GetScope(), xpcscope, &sameOrigin)) - { - // Cross scope access detected. Check if chrome code - // is accessing non-chrome objects, and if so, wrap - // the XPCWrappedNative with an XPCNativeWrapper to - // prevent user-defined properties from shadowing DOM - // properties from chrome code. - - // printf("Wrapped native accessed across scope boundary\n"); - - ComputeWrapperInfo(ccx, &callee, &script); - - flags = script ? JS_GetScriptFilenameFlags(script) : 0; - NS_ASSERTION(flags != JSFILENAME_NULL, "null script filename"); - - if(!JS_IsSystemObject(ccx, flat)) - { - // From here on we might create new JSObjects, so we need to - // make sure that wrapper stays alive. - if(!strongWrapper) - strongWrapper = wrapper; - - JSObject *destObj = nsnull; - JSBool triedWrapping = JS_FALSE; - if(flags & JSFILENAME_PROTECTED) - { -#ifdef DEBUG_XPCNativeWrapper - { - char *s = wrapper->ToString(ccx); - printf("Content accessed from chrome, wrapping " - "wrapper (%s) in XPCNativeWrapper\n", s); - if (s) - JS_smprintf_free(s); - } -#endif - nsIScriptSecurityManager *ssm = - XPCWrapper::GetSecurityManager(); - nsCOMPtr objPrincipal; - if(callee) - { - // Prefer getting the object princpal here. - nsresult rv = - ssm->GetObjectPrincipal(ccx, callee, - getter_AddRefs(objPrincipal)); - if(NS_FAILED(rv)) - return JS_FALSE; - } - else - { - JSPrincipals *scriptPrincipal = - JS_GetScriptPrincipals(ccx, script); - if(scriptPrincipal) - { - nsJSPrincipals *nsjsp = - static_cast(scriptPrincipal); - objPrincipal = nsjsp->nsIPrincipalPtr; - } - } - - destObj = - XPCNativeWrapper::GetNewOrUsed(ccx, wrapper, - scope, objPrincipal); - triedWrapping = JS_TRUE; - } - else if (flags & JSFILENAME_SYSTEM) - { -#ifdef DEBUG_mrbkap - printf("Content accessed from chrome, wrapping in an " - "XPCSafeJSObjectWrapper\n"); -#endif - - if(XPCSafeJSObjectWrapper::WrapObject(ccx, scope, v, &v)) - destObj = JSVAL_TO_OBJECT(v); - triedWrapping = JS_TRUE; - } - else if (!sameOrigin) - { - // Reaching across scopes from content code. Wrap - // the new object in a XOW. - if (XPCCrossOriginWrapper::WrapObject(ccx, scope, &v)) - destObj = JSVAL_TO_OBJECT(v); - triedWrapping = JS_TRUE; - } - - if(triedWrapping) - { - if(!destObj) - return JS_FALSE; - - jsval wrappedObjVal = OBJECT_TO_JSVAL(destObj); - AUTO_MARK_JSVAL(ccx, &wrappedObjVal); - if(wrapper->NeedsSOW()) - { - using SystemOnlyWrapper::WrapObject; - if(!WrapObject(ccx, xpcscope->GetGlobalJSObject(), - OBJECT_TO_JSVAL(destObj), - &wrappedObjVal)) - return JS_FALSE; - } - - return CreateHolderIfNeeded(ccx, JSVAL_TO_OBJECT(wrappedObjVal), - d, dest); - } - } - } - - const char *name = flat->getClass()->name; - if(allowNativeWrapper && - !(flags & JSFILENAME_SYSTEM) && - !JS_IsSystemObject(ccx, flat) && - XPCCrossOriginWrapper::ClassNeedsXOW(name)) - { - // From here on we might create new JSObjects, so we need to - // make sure that wrapper stays alive. - if(!strongWrapper) - strongWrapper = wrapper; - - AUTO_MARK_JSVAL(ccx, &v); - return XPCCrossOriginWrapper::WrapObject(ccx, scope, &v) && - (!wrapper->NeedsSOW() || - SystemOnlyWrapper::WrapObject(ccx, xpcscope->GetGlobalJSObject(), - v, &v)) && - CreateHolderIfNeeded(ccx, JSVAL_TO_OBJECT(v), d, dest); - } - - *d = v; - if(allowNativeWrapper) - { - if(wrapper->NeedsSOW()) - if(!SystemOnlyWrapper::WrapObject(ccx, - xpcscope->GetGlobalJSObject(), - v, d)) - return JS_FALSE; - if(wrapper->NeedsCOW()) - if(!ChromeObjectWrapper::WrapObject(ccx, xpcscope->GetGlobalJSObject(), v, d)) - return JS_FALSE; - } - if(dest) - *dest = strongWrapper.forget().get(); - return JS_TRUE; + *dest = objHolder.forget().get(); } } - return JS_FALSE; + + return JS_TRUE; } /***************************************************************************/ diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 0cacee417dd3..584fb7ab1cfb 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -41,6 +41,7 @@ /* Per JSRuntime object */ #include "xpcprivate.h" +#include "WrapperFactory.h" #include "dom_quickstubs.h" #include "jsgcchunk.h" @@ -1144,6 +1145,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback); JS_SetGCCallbackRT(mJSRuntime, GCCallback); JS_SetExtraGCRoots(mJSRuntime, TraceJS, this); + JS_SetWrapObjectCallback(mJSRuntime, xpc::WrapperFactory::Rewrap); mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock); mJSRuntime->setActivityCallback(ActivityCallback, this); diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index bc77e6c6d213..70613379b7bd 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -2282,6 +2282,10 @@ private: }; class xpcObjectHelper; +JSObject * +ConstructProxyObject(XPCCallContext &ccx, + xpcObjectHelper &aHelper, + XPCWrappedNativeScope *xpcscope); extern JSBool ConstructSlimWrapper(XPCCallContext &ccx, xpcObjectHelper &aHelper, XPCWrappedNativeScope* xpcScope, diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index c492d33132aa..26cdf2b106d9 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -1778,17 +1778,6 @@ return_tearoff: pTearOff); } - // If we didn't find a wrapper using the given funobj and obj, try - // again with obj's outer object, if it's got one. - - if(JSObjectOp op = obj->getClass()->ext.outerObject) - { - JSObject *outer = op(cx, obj); - if(outer && outer != obj) - return GetWrappedNativeOfJSObject(cx, outer, funobj, pobj2, - pTearOff); - } - if(pobj2) *pobj2 = nsnull; return nsnull; @@ -3852,6 +3841,35 @@ MorphSlimWrapper(JSContext *cx, JSObject *obj) static PRUint32 sSlimWrappers; #endif +JSObject * +ConstructProxyObject(XPCCallContext &ccx, + xpcObjectHelper &aHelper, + XPCWrappedNativeScope *xpcscope) +{ + nsISupports *identityObj = aHelper.GetCanonical(); + nsXPCClassInfo *classInfoHelper = aHelper.GetXPCClassInfo(); + +#ifdef DEBUG + { + JSUint32 flagsInt; + nsresult debug_rv = classInfoHelper->GetScriptableFlags(&flagsInt); + XPCNativeScriptableFlags flags(flagsInt); + NS_ASSERTION(NS_SUCCEEDED(debug_rv) && flags.WantPreCreate(), + "bad flags, cache->IsProxy() implies WantPreCreate()"); + } +#endif + + // We re-use the PreCreate hook to create the actual proxy object. + JSObject* parent = xpcscope->GetGlobalJSObject(); + nsresult rv = classInfoHelper->PreCreate(identityObj, ccx, parent, &parent); + NS_ENSURE_SUCCESS(rv, nsnull); + + nsWrapperCache *cache = aHelper.GetWrapperCache(); + JSObject *flat = cache->GetWrapper(); + NS_ASSERTION(flat, "PreCreate is supposed to create the wrapper"); + return flat; +} + JSBool ConstructSlimWrapper(XPCCallContext &ccx, xpcObjectHelper &aHelper, @@ -3879,15 +3897,11 @@ ConstructSlimWrapper(XPCCallContext &ccx, return JS_FALSE; } - nsWrapperCache *cache = aHelper.GetWrapperCache(); JSObject* plannedParent = parent; rv = classInfoHelper->PreCreate(identityObj, ccx, parent, &parent); if(rv != NS_SUCCESS_ALLOW_SLIM_WRAPPERS) { - if(cache->IsProxy()) - NS_ASSERTION(cache->GetWrapper(), "out of memory?"); - else - SLIM_LOG_NOT_CREATED(ccx, identityObj, "PreCreate hook refused"); + SLIM_LOG_NOT_CREATED(ccx, identityObj, "PreCreate hook refused"); return JS_FALSE; } @@ -3906,6 +3920,7 @@ ConstructSlimWrapper(XPCCallContext &ccx, // The PreCreate hook could have forced the creation of a wrapper, need // to check for that here and return early. + nsWrapperCache *cache = aHelper.GetWrapperCache(); JSObject* wrapper = cache->GetWrapper(); if(wrapper) { diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index 8aa2aa4a9406..49489f1f574f 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -1468,92 +1468,7 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JSObject *obj) return obj; OBJ_TO_OUTER_OBJECT(cx, obj); - if(!obj) - return nsnull; - - JSObject *scope = JS_GetGlobalForScopeChain(cx); - if(!scope) - { - XPCThrower::Throw(NS_ERROR_FAILURE, cx); - return nsnull; - } - - // Note that by innerizing the incoming object instead of outerizing the - // scope, we are doing an implicit security check: if the window has - // already navigated, then we don't want to use our cache. - JSObject* innerobj = obj; - OBJ_TO_INNER_OBJECT(cx, innerobj); - if(!innerobj) - return nsnull; - - if(innerobj == scope) - { - // Fast-path for the common case: a window being wrapped in its own - // scope. Check to see if the object actually needs a XOW, and then - // give it one in its own scope. - - XPCWrappedNative *wn = - static_cast(xpc_GetJSPrivate(obj)); - - if(!wn->NeedsXOW()) - return obj; - - XPCWrappedNativeWithXOW *wnxow = - static_cast(wn); - JSObject *wrapper = wnxow->GetXOW(); - if(wrapper) - return wrapper; - - // Otherwise, this is our first time through, - // XPCCrossOriginWrapper::WrapObject will fill the cache. - } - - XPCPerThreadData *threadData = XPCPerThreadData::GetData(cx); - if(!threadData) - { - XPCThrower::Throw(NS_ERROR_FAILURE, cx); - return nsnull; - } - - AutoPopJSContext popper(threadData->GetJSContextStack()); - popper.PushIfNotTop(cx); - - nsIScriptSecurityManager* secMan = XPCWrapper::GetSecurityManager(); - if(!secMan) - { - XPCThrower::Throw(NS_ERROR_FAILURE, cx); - return nsnull; - } - - JSStackFrame *fp; - nsIPrincipal *principal = secMan->GetCxSubjectPrincipalAndFrame(cx, &fp); - - js::AutoValueRooter retval(cx, js::ObjectValue(*obj)); - - if(principal && fp) - { - JSScript* script = JS_GetFrameScript(cx, fp); - - PRUint32 flags = script ? JS_GetScriptFilenameFlags(script) : 0; - NS_ASSERTION(flags != JSFILENAME_NULL, "Null filename!"); - - nsXPConnect *xpc = nsXPConnect::GetXPConnect(); - if(!xpc) - { - XPCThrower::Throw(NS_ERROR_FAILURE, cx); - return nsnull; - } - - nsresult rv = xpc->GetWrapperForObject(cx, obj, scope, principal, flags, - retval.jsval_addr()); - if(NS_FAILED(rv)) - { - XPCThrower::Throw(rv, cx); - return nsnull; - } - } - - return JSVAL_TO_OBJECT(retval.jsval_value()); + return obj; } /***************************************************************************/ From 6f88d76769650a5cd93fd24b1c0130103b453ebe Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 112/284] bug 580128 - Now that we use a proxy for the outer window, nsOuterWindowSH is unused (except for PreCreate). Nuke it. r=peterv --- dom/base/nsDOMClassInfo.cpp | 420 ++++--------------------------- dom/base/nsDOMClassInfo.h | 66 +---- dom/base/nsDOMClassInfoClasses.h | 3 - dom/base/nsGlobalWindow.cpp | 29 +-- dom/base/nsJSEnvironment.cpp | 2 +- 5 files changed, 69 insertions(+), 451 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 65be19d0bf04..432c20127cc9 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -503,7 +503,7 @@ static const char kDOMStringBundleURL[] = // NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS // are defined in nsIDOMClassInfo.h. -#define COMMON_WINDOW_SCRIPTABLE_FLAGS \ +#define WINDOW_SCRIPTABLE_FLAGS \ (nsIXPCScriptable::WANT_GETPROPERTY | \ nsIXPCScriptable::WANT_SETPROPERTY | \ nsIXPCScriptable::WANT_PRECREATE | \ @@ -511,16 +511,8 @@ static const char kDOMStringBundleURL[] = nsIXPCScriptable::WANT_DELPROPERTY | \ nsIXPCScriptable::WANT_FINALIZE | \ nsIXPCScriptable::WANT_EQUALITY | \ - nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE) - -#define INNER_WINDOW_SCRIPTABLE_FLAGS \ - (COMMON_WINDOW_SCRIPTABLE_FLAGS | \ - nsIXPCScriptable::WANT_OUTER_OBJECT) \ - -#define OUTER_WINDOW_SCRIPTABLE_FLAGS \ - (COMMON_WINDOW_SCRIPTABLE_FLAGS | \ - nsIXPCScriptable::WANT_INNER_OBJECT | \ - nsIXPCScriptable::WANT_NEWENUMERATE) + nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \ + nsIXPCScriptable::WANT_OUTER_OBJECT) #define NODE_SCRIPTABLE_FLAGS \ ((DOM_DEFAULT_SCRIPTABLE_FLAGS | \ @@ -645,13 +637,9 @@ static nsDOMClassInfoData sClassInfoData[] = { // to JS. - NS_DEFINE_CLASSINFO_DATA(Window, nsOuterWindowSH, + NS_DEFINE_CLASSINFO_DATA(Window, nsWindowSH, DEFAULT_SCRIPTABLE_FLAGS | - OUTER_WINDOW_SCRIPTABLE_FLAGS) - - NS_DEFINE_CLASSINFO_DATA(InnerWindow, nsInnerWindowSH, - DEFAULT_SCRIPTABLE_FLAGS | - INNER_WINDOW_SCRIPTABLE_FLAGS) + WINDOW_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(Location, nsLocationSH, (DOM_DEFAULT_SCRIPTABLE_FLAGS & @@ -944,13 +932,9 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) // DOM Chrome Window class. - NS_DEFINE_CLASSINFO_DATA(ChromeWindow, nsOuterWindowSH, + NS_DEFINE_CLASSINFO_DATA(ChromeWindow, nsWindowSH, DEFAULT_SCRIPTABLE_FLAGS | - OUTER_WINDOW_SCRIPTABLE_FLAGS) - - NS_DEFINE_CLASSINFO_DATA(InnerChromeWindow, nsInnerWindowSH, - DEFAULT_SCRIPTABLE_FLAGS | - INNER_WINDOW_SCRIPTABLE_FLAGS) + WINDOW_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(CSSRGBColor, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -1331,13 +1315,9 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(FileReader, nsEventTargetSH, EVENTTARGET_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(ModalContentWindow, nsOuterWindowSH, + NS_DEFINE_CLASSINFO_DATA(ModalContentWindow, nsWindowSH, DEFAULT_SCRIPTABLE_FLAGS | - OUTER_WINDOW_SCRIPTABLE_FLAGS) - - NS_DEFINE_CLASSINFO_DATA(InnerModalContentWindow, nsInnerWindowSH, - DEFAULT_SCRIPTABLE_FLAGS | - INNER_WINDOW_SCRIPTABLE_FLAGS) + WINDOW_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(DataContainerEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -2260,17 +2240,6 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(InnerWindow, nsIDOMWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMViewCSS) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMAbstractView) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow) - DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(WindowUtils, nsIDOMWindowUtils) DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowUtils) DOM_CLASSINFO_MAP_END @@ -2968,18 +2937,6 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMAbstractView) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(InnerChromeWindow, nsIDOMWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMChromeWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMViewCSS) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMAbstractView) - DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(RangeException, nsIDOMRangeException) DOM_CLASSINFO_MAP_ENTRY(nsIDOMRangeException) DOM_CLASSINFO_MAP_ENTRY(nsIException) @@ -3859,18 +3816,6 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMModalContentWindow) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(InnerModalContentWindow, nsIDOMWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMViewCSS) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMAbstractView) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMModalContentWindow) - DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(DataContainerEvent, nsIDOMDataContainerEvent) DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataContainerEvent) DOM_CLASSINFO_EVENT_MAP_ENTRIES @@ -4708,15 +4653,7 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto) if (if_info) { nsXPIDLCString name; if_info->GetName(getter_Copies(name)); - - // Allow for inner/non-inner mismatch. - static const char inner[] = "Inner"; - const char *dataname = mData->mName; - if (!strncmp(dataname, "Inner", sizeof(inner) - 1)) { - dataname += sizeof(inner) - 1; - } - - NS_ASSERTION(nsCRT::strcmp(CutPrefix(name), dataname) == 0, + NS_ASSERTION(nsCRT::strcmp(CutPrefix(name), mData->mName) == 0, "Class name and proto chain interface name mismatch!"); } } @@ -4953,8 +4890,8 @@ nsDOMClassInfo::ShutDown() // Window helper NS_IMETHODIMP -nsInnerWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, - JSObject *globalObj, JSObject **parentObj) +nsWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj) { // Normally ::PreCreate() is used to give XPConnect the parent // object for the object that's being wrapped, this parent object is @@ -4972,6 +4909,15 @@ nsInnerWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, NS_ASSERTION(sgo, "nativeObj not a global object!"); nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj); + if (win->IsOuterWindow()) { + if (!win->EnsureInnerWindow()) { + return NS_ERROR_FAILURE; + } + + *parentObj = win->GetCurrentInnerWindowInternal()->FastGetGlobalJSObject(); + return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW; + } + JSObject *winObj = win->FastGetGlobalJSObject(); if (!winObj) { NS_ASSERTION(win->GetOuterWindowInternal()->IsCreatingInnerWindow(), @@ -4983,34 +4929,17 @@ nsInnerWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, return NS_OK; } -NS_IMETHODIMP -nsOuterWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, - JSObject *globalObj, JSObject **parentObj) -{ - nsCOMPtr sgo(do_QueryInterface(nativeObj)); - NS_ASSERTION(sgo, "nativeObj not a global object!"); - - nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj); - if (!win->EnsureInnerWindow()) { - return NS_ERROR_FAILURE; - } - - *parentObj = win->GetCurrentInnerWindowInternal()->FastGetGlobalJSObject(); - return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW; -} - - // This JS class piggybacks on nsHTMLDocumentSH::ReleaseDocument()... static JSClass sGlobalScopePolluterClass = { "Global Scope Polluter", JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_NEW_RESOLVE, - nsCommonWindowSH::SecurityCheckOnSetProp, - nsCommonWindowSH::SecurityCheckOnSetProp, - nsCommonWindowSH::GlobalScopePolluterGetProperty, - nsCommonWindowSH::SecurityCheckOnSetProp, + nsWindowSH::SecurityCheckOnSetProp, + nsWindowSH::SecurityCheckOnSetProp, + nsWindowSH::GlobalScopePolluterGetProperty, + nsWindowSH::SecurityCheckOnSetProp, JS_EnumerateStub, - (JSResolveOp)nsCommonWindowSH::GlobalScopePolluterNewResolve, + (JSResolveOp)nsWindowSH::GlobalScopePolluterNewResolve, JS_ConvertStub, nsHTMLDocumentSH::ReleaseDocument }; @@ -5018,8 +4947,8 @@ static JSClass sGlobalScopePolluterClass = { // static JSBool -nsCommonWindowSH::GlobalScopePolluterGetProperty(JSContext *cx, JSObject *obj, - jsid id, jsval *vp) +nsWindowSH::GlobalScopePolluterGetProperty(JSContext *cx, JSObject *obj, + jsid id, jsval *vp) { // Someone is accessing a element by referencing its name/id in the // global scope, do a security check to make sure that's ok. @@ -5045,8 +4974,8 @@ nsCommonWindowSH::GlobalScopePolluterGetProperty(JSContext *cx, JSObject *obj, // static JSBool -nsCommonWindowSH::SecurityCheckOnSetProp(JSContext *cx, JSObject *obj, jsid id, - jsval *vp) +nsWindowSH::SecurityCheckOnSetProp(JSContext *cx, JSObject *obj, jsid id, + jsval *vp) { // Someone is accessing a element by referencing its name/id in the // global scope, do a security check to make sure that's ok. @@ -5070,8 +4999,8 @@ GetDocument(JSContext *cx, JSObject *obj) // static JSBool -nsCommonWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, - jsid id, uintN flags, +nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, + jsid id, uintN flags, JSObject **objp) { if (flags & (JSRESOLVE_ASSIGNING | JSRESOLVE_DECLARING | @@ -5140,7 +5069,7 @@ nsCommonWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, // static void -nsCommonWindowSH::InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj) +nsWindowSH::InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj) { JSObject *proto; @@ -5167,8 +5096,8 @@ nsCommonWindowSH::InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj) // static nsresult -nsCommonWindowSH::InstallGlobalScopePolluter(JSContext *cx, JSObject *obj, - nsIHTMLDocument *doc) +nsWindowSH::InstallGlobalScopePolluter(JSContext *cx, JSObject *obj, + nsIHTMLDocument *doc) { // If global scope pollution is disabled, or if our document is not // a HTML document, do nothing @@ -5235,8 +5164,8 @@ GetChildFrame(nsGlobalWindow *win, jsid id) } NS_IMETHODIMP -nsCommonWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, PRBool *_retval) +nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, jsid id, jsval *vp, PRBool *_retval) { nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper); @@ -5325,7 +5254,7 @@ nsCommonWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, if (JSID_IS_STRING(id) && !JSVAL_IS_PRIMITIVE(*vp) && ::JS_TypeOfValue(cx, *vp) != JSTYPE_FUNCTION) { // A named property accessed which could have been resolved to a - // child frame in nsCommonWindowSH::NewResolve() (*vp will tell us if + // child frame in nsWindowSH::NewResolve() (*vp will tell us if // that's the case). If *vp is a window object (i.e. a child // frame), return without doing a security check. // @@ -5363,8 +5292,8 @@ nsCommonWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } NS_IMETHODIMP -nsCommonWindowSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, PRBool *_retval) +nsWindowSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, jsid id, jsval *vp, PRBool *_retval) { nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper); @@ -5440,49 +5369,8 @@ nsCommonWindowSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } NS_IMETHODIMP -nsOuterWindowSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, - PRBool *_retval) -{ - nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper); - - JSObject *realObj; - wrapper->GetJSObject(&realObj); - if (obj == realObj) { - nsGlobalWindow *innerWin = win->GetCurrentInnerWindowInternal(); - - JSObject *innerObj; - if (innerWin && (innerObj = innerWin->GetGlobalJSObject())) { - if (sResolving) { - return NS_OK; - } - -#ifdef DEBUG_SH_FORWARDING - printf(" --- Forwarding add to inner window %p\n", (void *)innerWin); -#endif - - JSPropertyDescriptor desc; - if (!JS_GetPropertyDescriptorById(cx, obj, id, - JSRESOLVE_QUALIFIED, &desc)) { - *_retval = JS_FALSE; - return NS_OK; - } - - // Forward the add to the inner object - *_retval = JS_DefinePropertyById(cx, innerObj, id, *vp, - desc.getter, desc.setter, - desc.attrs | JSPROP_ENUMERATE); - return NS_OK; - } - } - - return nsEventReceiverSH::AddProperty(wrapper, cx, obj, id, vp, _retval); -} - -NS_IMETHODIMP -nsCommonWindowSH::DelProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, - PRBool *_retval) +nsWindowSH::DelProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, jsid id, jsval *vp, PRBool *_retval) { nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper); @@ -6309,8 +6197,8 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx, // static nsresult -nsCommonWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, - JSObject *obj, JSString *str, PRBool *did_resolve) +nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, + JSObject *obj, JSString *str, PRBool *did_resolve) { *did_resolve = PR_FALSE; @@ -6552,111 +6440,10 @@ ContentWindowGetter(JSContext *cx, uintN argc, jsval *vp) return ::JS_GetProperty(cx, obj, "content", vp); } -PRBool -nsOuterWindowSH::sResolving = PR_FALSE; - NS_IMETHODIMP -nsOuterWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, PRUint32 flags, - JSObject **objp, PRBool *_retval) -{ - nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper); - - // Note, we won't forward resolve of the location property to the - // inner window, we need to deal with that one for the outer too - // since we've got special security protection code for that - // property. Also note that we want to enter this block even for - // native wrappers, so that we'll ensure an inner window to wrap - // against for the result of whatever we're getting. - if (id != sLocation_id) { - // XXXjst: Do security checks here when we remove the security - // checks on the inner window. - - nsGlobalWindow *innerWin = win->GetCurrentInnerWindowInternal(); - - if ((!innerWin || !innerWin->GetExtantDocument()) && - !win->IsCreatingInnerWindow()) { - // We're resolving a property on an outer window for which there - // is no inner window yet, and we're not in the midst of - // creating the inner window or in the middle of initializing - // XPConnect classes on it. If the context is already - // initialized, force creation of a new inner window. This will - // create a synthetic about:blank document, and an inner window - // which may be reused by the actual document being loaded into - // this outer window. This way properties defined on the window - // before the document load started will be visible to the - // document once it's loaded, assuming same origin etc. - nsIScriptContext *scx = win->GetContextInternal(); - - if (scx && scx->IsContextInitialized()) { - // Grab the new inner window. - innerWin = win->EnsureInnerWindowInternal(); - - if (!innerWin) { - return NS_ERROR_OUT_OF_MEMORY; - } - } - } - - JSObject *innerObj; - JSObject *realObj; - wrapper->GetJSObject(&realObj); - if (realObj == obj && - innerWin && (innerObj = innerWin->GetGlobalJSObject())) { -#ifdef DEBUG_SH_FORWARDING - printf(" --- Forwarding resolve to inner window %p\n", (void *)innerWin); -#endif - - JSPropertyDescriptor desc; - - *_retval = JS_GetPropertyDescriptorById(cx, innerObj, id, flags, &desc); - - if (*_retval && desc.obj) { -#ifdef DEBUG_SH_FORWARDING - printf(" --- Resolve on inner window found property.\n"); -#endif - - // The JS engine assumes that the object that we return in objp is on - // our prototype chain. As a result, for an assignment, it wants to - // shadow the property by defining one on our object (unless the - // property has a setter). This confuses our code and, for fast - // expandos, we end up overriding the fast expando with a slow one. So - // detect when we're about to get a new property from the JS engine - // that would shadow a fast expando and define it ourselves, sending - // ourselves a signal via sResolving that we are doing this. Note that - // we only care about fast expandos on the innerObj itself, things - // found further up the prototype chain need to fend for themselves. - if ((flags & JSRESOLVE_ASSIGNING) && - !(desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) && - desc.obj == innerObj) { - PRBool oldResolving = sResolving; - sResolving = PR_TRUE; - - *_retval = JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, - nsnull, nsnull, - desc.attrs & JSPROP_ENUMERATE); - - sResolving = oldResolving; - if (!*_retval) { - return NS_OK; - } - } - - *objp = desc.obj; - } - - return NS_OK; - } - } - - return nsCommonWindowSH::NewResolve(wrapper, cx, obj, id, flags, objp, - _retval); -} - -NS_IMETHODIMP -nsCommonWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, PRUint32 flags, - JSObject **objp, PRBool *_retval) +nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, jsid id, PRUint32 flags, + JSObject **objp, PRBool *_retval) { nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper); @@ -6807,15 +6594,6 @@ nsCommonWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); -#ifdef DEBUG - if (!win->IsChromeWindow()) { - NS_ASSERTION(JSVAL_IS_OBJECT(v) && - !strcmp(JSVAL_TO_OBJECT(v)->getClass()->name, - "XPCCrossOriginWrapper"), - "Didn't wrap a window!"); - } -#endif - JSAutoRequest ar(cx); PRBool ok = ::JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, 0); @@ -7159,70 +6937,8 @@ nsCommonWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } NS_IMETHODIMP -nsOuterWindowSH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, PRUint32 enum_op, jsval *statep, - jsid *idp, PRBool *_retval) -{ - switch ((JSIterateOp)enum_op) { - /* FIXME bug 576449: non-enumerable property support */ - case JSENUMERATE_INIT_ALL: - case JSENUMERATE_INIT: - { -#ifdef DEBUG - // First, do the security check that nsDOMClassInfo does to see - // if we need to do any work at all. - nsDOMClassInfo::Enumerate(wrapper, cx, obj, _retval); - if (!*_retval) { - NS_ERROR("security wrappers failed us"); - return NS_OK; - } -#endif - - // The security check passed, let's see if we need to get the inner - // window's JS object or if we can just start enumerating. - nsGlobalWindow *win = - nsGlobalWindow::FromWrapper(wrapper)->GetCurrentInnerWindowInternal(); - JSObject *enumobj = win->FastGetGlobalJSObject(); - - // Great, we have the js object, now let's enumerate it. - JSObject *iterator = JS_NewPropertyIterator(cx, enumobj); - if (!iterator) { - return NS_ERROR_OUT_OF_MEMORY; - } - - *statep = OBJECT_TO_JSVAL(iterator); - if (idp) { - // Note: With these property iterators, we can't tell ahead of time how - // many properties we're going to be iterating over. - *idp = INT_TO_JSID(0); - } - break; - } - case JSENUMERATE_NEXT: - { - JSObject *iterator = (JSObject*)JSVAL_TO_OBJECT(*statep); - if (!JS_NextProperty(cx, iterator, idp)) { - return NS_ERROR_UNEXPECTED; - } - - if (*idp != JSID_VOID) { - break; - } - - // Fall through. - } - case JSENUMERATE_DESTROY: - // Let GC at our iterator object. - *statep = JSVAL_NULL; - break; - } - - return NS_OK; -} - -NS_IMETHODIMP -nsCommonWindowSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj) +nsWindowSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) { nsCOMPtr sgo(do_QueryWrappedNative(wrapper)); NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED); @@ -7233,8 +6949,8 @@ nsCommonWindowSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } NS_IMETHODIMP -nsCommonWindowSH::Equality(nsIXPConnectWrappedNative *wrapper, JSContext * cx, - JSObject * obj, const jsval &val, PRBool *bp) +nsWindowSH::Equality(nsIXPConnectWrappedNative *wrapper, JSContext * cx, + JSObject * obj, const jsval &val, PRBool *bp) { *bp = PR_FALSE; @@ -7270,8 +6986,8 @@ nsCommonWindowSH::Equality(nsIXPConnectWrappedNative *wrapper, JSContext * cx, } NS_IMETHODIMP -nsInnerWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, - JSObject * obj, JSObject * *_retval) +nsWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, + JSObject * obj, JSObject * *_retval) { nsGlobalWindow *origWin = nsGlobalWindow::FromWrapper(wrapper); nsGlobalWindow *win = origWin->GetOuterWindowInternal(); @@ -7297,36 +7013,6 @@ nsInnerWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, return NS_OK; } -NS_IMETHODIMP -nsOuterWindowSH::InnerObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, - JSObject * obj, JSObject * *_retval) -{ - nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper); - - if (win->IsFrozen()) { - // Return the inner window, or the outer if we're dealing with a - // frozen outer. - - *_retval = obj; - } else { - // Try to find the current inner window. - - nsGlobalWindow *inner = win->GetCurrentInnerWindowInternal(); - if (!inner) { - // Yikes! No inner window! Instead of leaking the outer window into the - // scope chain, let's return an error. - NS_ERROR("using an outer that doesn't have an inner?"); - *_retval = nsnull; - - return NS_ERROR_UNEXPECTED; - } - - *_retval = inner->FastGetGlobalJSObject(); - } - - return NS_OK; -} - // DOM Location helper NS_IMETHODIMP diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 094fa43fad55..22c67d12b57b 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -501,14 +501,14 @@ public: // Window scriptable helper -class nsCommonWindowSH : public nsEventReceiverSH +class nsWindowSH : public nsEventReceiverSH { protected: - nsCommonWindowSH(nsDOMClassInfoData *aData) : nsEventReceiverSH(aData) + nsWindowSH(nsDOMClassInfoData *aData) : nsEventReceiverSH(aData) { } - virtual ~nsCommonWindowSH() + virtual ~nsWindowSH() { } @@ -517,6 +517,8 @@ protected: PRBool *did_resolve); public: + NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj); #ifdef DEBUG NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj) @@ -552,6 +554,8 @@ public: JSObject *obj); NS_IMETHOD Equality(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, const jsval &val, PRBool *bp); + NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, + JSObject * obj, JSObject * *_retval); static JSBool GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, @@ -563,66 +567,12 @@ public: static void InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj); static nsresult InstallGlobalScopePolluter(JSContext *cx, JSObject *obj, nsIHTMLDocument *doc); -}; - -class nsOuterWindowSH : public nsCommonWindowSH -{ -protected: - nsOuterWindowSH(nsDOMClassInfoData* aData) : nsCommonWindowSH(aData) - { - } - - virtual ~nsOuterWindowSH() - { - } - - static PRBool sResolving; - -public: - NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, - JSObject *globalObj, JSObject **parentObj); - NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, PRBool *_retval); - NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, PRUint32 flags, - JSObject **objp, PRBool *_retval); - NS_IMETHOD NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, PRUint32 enum_op, jsval *statep, - jsid *idp, PRBool *_retval); - NS_IMETHOD InnerObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, - JSObject * obj, JSObject * *_retval); - static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) { - return new nsOuterWindowSH(aData); + return new nsWindowSH(aData); } }; -class nsInnerWindowSH : public nsCommonWindowSH -{ -protected: - nsInnerWindowSH(nsDOMClassInfoData* aData) : nsCommonWindowSH(aData) - { - } - - virtual ~nsInnerWindowSH() - { - } - -public: - NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, - JSObject *globalObj, JSObject **parentObj); - // We WANT_ADDPROPERTY, but are content to inherit it from nsEventReceiverSH. - NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, - JSObject * obj, JSObject * *_retval); - - static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) - { - return new nsInnerWindowSH(aData); - } -}; - - // Location scriptable helper class nsLocationSH : public nsDOMGenericSH diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index ce14c1a8bc60..ec07cc6da52e 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -37,7 +37,6 @@ * ***** END LICENSE BLOCK ***** */ DOMCI_CLASS(Window) -DOMCI_CLASS(InnerWindow) DOMCI_CLASS(Location) DOMCI_CLASS(Navigator) DOMCI_CLASS(Plugin) @@ -189,7 +188,6 @@ DOMCI_CLASS(CSSRect) // DOM Chrome Window class, almost identical to Window DOMCI_CLASS(ChromeWindow) -DOMCI_CLASS(InnerChromeWindow) // RGBColor object used by getComputedStyle DOMCI_CLASS(CSSRGBColor) @@ -409,7 +407,6 @@ DOMCI_CLASS(FileReader) // DOM modal content window class, almost identical to Window DOMCI_CLASS(ModalContentWindow) -DOMCI_CLASS(InnerModalContentWindow) // Data Events DOMCI_CLASS(DataContainerEvent) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index ef50a4ecd7fd..73fce3787187 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1169,18 +1169,6 @@ nsGlobalWindow::FreeInnerObjects(PRBool aClearScope) // nsGlobalWindow::nsISupports //***************************************************************************** -#define WINDOW_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(_class) \ - if (aIID.Equals(NS_GET_IID(nsIClassInfo)) || \ - aIID.Equals(NS_GET_IID(nsXPCClassInfo))) { \ - foundInterface = NS_GetDOMClassInfoInstance(IsInnerWindow() \ - ? eDOMClassInfo_Inner##_class##_id \ - : eDOMClassInfo_##_class##_id);\ - if (!foundInterface) { \ - *aInstancePtr = nsnull; \ - return NS_ERROR_OUT_OF_MEMORY; \ - } \ - } else - #define OUTER_WINDOW_ONLY \ if (IsOuterWindow()) { @@ -1191,7 +1179,6 @@ nsGlobalWindow::FreeInnerObjects(PRBool aClearScope) NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow) DOMCI_DATA(Window, nsGlobalWindow) -DOMCI_DATA(InnerWindow, nsGlobalWindow) // QueryInterface implementation for nsGlobalWindow NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow) @@ -1213,7 +1200,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow) NS_INTERFACE_MAP_ENTRY(nsIDOMStorageWindow) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) - WINDOW_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window) OUTER_WINDOW_ONLY NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY END_OUTER_WINDOW_ONLY @@ -1806,7 +1793,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, newInnerWindow = currentInner; if (aDocument != oldDoc) { - nsCommonWindowSH::InvalidateGlobalScopePolluter(cx, currentInner->mJSObject); + nsWindowSH::InvalidateGlobalScopePolluter(cx, currentInner->mJSObject); } } else { if (aState) { @@ -2018,8 +2005,8 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, if ((!reUseInnerWindow || aDocument != oldDoc) && !aState) { nsCOMPtr html_doc(do_QueryInterface(mDocument)); - nsCommonWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject, - html_doc); + nsWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject, + html_doc); } if (aDocument) { @@ -2627,7 +2614,7 @@ nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments) if (mIsModalContentWindow) { // Modal content windows don't have an "arguments" property, they // have a "dialogArguments" property which is handled - // separately. See nsCommonWindowSH::NewResolve(). + // separately. See nsWindowSH::NewResolve(). return NS_OK; } @@ -9710,12 +9697,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow, NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END DOMCI_DATA(ChromeWindow, nsGlobalChromeWindow) -DOMCI_DATA(InnerChromeWindow, nsGlobalChromeWindow) // QueryInterface implementation for nsGlobalChromeWindow NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalChromeWindow) NS_INTERFACE_MAP_ENTRY(nsIDOMChromeWindow) - WINDOW_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ChromeWindow) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ChromeWindow) NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow) NS_IMPL_ADDREF_INHERITED(nsGlobalChromeWindow, nsGlobalWindow) @@ -9994,11 +9980,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalModalWindow, NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END DOMCI_DATA(ModalContentWindow, nsGlobalModalWindow) -DOMCI_DATA(InnerModalContentWindow, nsGlobalModalWindow) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalModalWindow) NS_INTERFACE_MAP_ENTRY(nsIDOMModalContentWindow) - WINDOW_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow) NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow) NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 566de3d90efe..5307b63dfef5 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3355,7 +3355,7 @@ nsJSContext::ClearScope(void *aGlobalObj, PRBool aClearFromProtoChain) // chain when we're clearing an outer window whose current inner we // still want. if (aClearFromProtoChain) { - nsCommonWindowSH::InvalidateGlobalScopePolluter(mContext, obj); + nsWindowSH::InvalidateGlobalScopePolluter(mContext, obj); // Clear up obj's prototype chain, but not Object.prototype. for (JSObject *o = ::JS_GetPrototype(mContext, obj), *next; From f021fe41baab9966999addd8d8861073dee64a40 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 113/284] bug 580128 - Fix code that expects XPConnect to hand outer windows to C++ from JS. r=peterv --- dom/base/nsDOMClassInfo.cpp | 6 ------ dom/base/nsFocusManager.cpp | 3 ++- dom/base/nsGlobalWindow.cpp | 10 +--------- security/manager/boot/src/nsSecureBrowserUIImpl.cpp | 7 ++++++- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 432c20127cc9..9d23d130f9b8 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -6970,15 +6970,9 @@ nsWindowSH::Equality(nsIXPConnectWrappedNative *wrapper, JSContext * cx, nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper); - NS_ASSERTION(win->IsOuterWindow(), - "Inner window detected in Equality hook!"); - nsCOMPtr other = do_QueryWrappedNative(other_wrapper); if (other) { - NS_ASSERTION(other->IsOuterWindow(), - "Inner window detected in Equality hook!"); - *bp = win->GetOuterWindow() == other->GetOuterWindow(); } diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 7cdc4cd6a083..4162e61abe3c 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -328,7 +328,8 @@ nsFocusManager::SetActiveWindow(nsIDOMWindow* aWindow) { // only top-level windows can be made active nsCOMPtr piWindow = do_QueryInterface(aWindow); - NS_ASSERTION(!piWindow || piWindow->IsOuterWindow(), "outer window expected"); + if (piWindow) + piWindow = piWindow->GetOuterWindow(); NS_ENSURE_TRUE(piWindow && (piWindow == piWindow->GetPrivateRoot()), NS_ERROR_INVALID_ARG); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 73fce3787187..03d5bd5371fc 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -5717,15 +5717,7 @@ PostMessageEvent::Run() NS_IMETHODIMP nsGlobalWindow::PostMessageMoz(const nsAString& aMessage, const nsAString& aOrigin) { - // NB: Since much of what this method does must happen at event dispatch time, - // this method does not forward to the inner window, unlike most other - // methods. We do this because the only time we need to refer to this - // window, we need a reference to the outer window (the PostMessageEvent - // ctor call), and we don't want to pay the price of forwarding to the - // inner window for no actual benefit. Furthermore, this function must - // only be called from script anyway, which should only have references to - // outer windows (and if script has an inner window we've already lost). - NS_ABORT_IF_FALSE(IsOuterWindow(), "only call this method on outer windows"); + FORWARD_TO_OUTER(PostMessageMoz, (aMessage, aOrigin), NS_ERROR_NOT_INITIALIZED); // // Window.postMessage is an intentional subversion of the same-origin policy. diff --git a/security/manager/boot/src/nsSecureBrowserUIImpl.cpp b/security/manager/boot/src/nsSecureBrowserUIImpl.cpp index 01d478fa3c71..8fd55b8ad34c 100644 --- a/security/manager/boot/src/nsSecureBrowserUIImpl.cpp +++ b/security/manager/boot/src/nsSecureBrowserUIImpl.cpp @@ -223,8 +223,13 @@ nsSecureBrowserUIImpl::Init(nsIDOMWindow *aWindow) return NS_ERROR_ALREADY_INITIALIZED; } + nsCOMPtr pwin(do_QueryInterface(aWindow)); + if (pwin->IsInnerWindow()) { + pwin = pwin->GetOuterWindow(); + } + nsresult rv; - mWindow = do_GetWeakReference(aWindow, &rv); + mWindow = do_GetWeakReference(pwin, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr service(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv)); From b1c7604058ba56e4a008b4014011450d763c519e Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 114/284] bug 580128 - The rewrap hook needs to know what parent to use when creating wrappers. r=gal --- js/src/jscompartment.cpp | 36 +++++++++++++------- js/src/jspubtd.h | 3 +- js/src/jswrapper.cpp | 3 +- js/src/jswrapper.h | 3 +- js/src/xpconnect/wrappers/WrapperFactory.cpp | 3 +- js/src/xpconnect/wrappers/WrapperFactory.h | 1 + 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index b6ee679d48c2..364b052d65b9 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -189,19 +189,6 @@ JSCompartment::wrap(JSContext *cx, Value *vp) if (!wrap(cx, &proto)) return false; - /* - * We hand in the original wrapped object into the wrap hook to allow - * the wrap hook to reason over what wrappers are currently applied - * to the object. - */ - JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, flags); - if (!wrapper) - return false; - wrapper->setProto(proto); - vp->setObject(*wrapper); - if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp)) - return false; - /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL @@ -219,6 +206,29 @@ JSCompartment::wrap(JSContext *cx, Value *vp) return false; } + /* + * We hand in the original wrapped object into the wrap hook to allow + * the wrap hook to reason over what wrappers are currently applied + * to the object. + */ + JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags); + if (!wrapper) + return false; + + vp->setObject(*wrapper); + + /* + * If the returned "wrapper" is not a proxy, then we were attempting to + * wrap an XPConnect "holder" object and the actual wrapped object was + * in our compartment. + */ + if (!wrapper->isProxy()) + return true; + + wrapper->setProto(proto); + if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp)) + return false; + wrapper->setParent(global); return true; } diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index e0184fcdf6ac..03ed062ba966 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -559,7 +559,8 @@ typedef JSBool * destination compartment. */ typedef JSObject * -(* JSWrapObjectCallback)(JSContext *cx, JSObject *obj, JSObject *proto, uintN flags); +(* JSWrapObjectCallback)(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, + uintN flags); typedef enum { JSCOMPARTMENT_NEW, /* XXX Does it make sense to have a NEW? */ diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 0abc58346584..78d23eee8047 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -287,7 +287,8 @@ JSWrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, namespace js { extern JSObject * -TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, uintN flags) +TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, + uintN flags) { // Allow wrapping outer window proxies. JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index b14daa8f3ee3..5087eaa208a4 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -173,7 +173,8 @@ class AutoCompartment }; extern JSObject * -TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, uintN flags); +TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, + uintN flags); } diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 0c40ecfe369b..284da91f440c 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -65,7 +65,8 @@ JSWrapper WaiveXrayWrapperWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); JSCrossCompartmentWrapper XrayWrapperWaivedWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); JSObject * -WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, uintN flags) +WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, + uintN flags) { NS_ASSERTION(!obj->isWrapper() || obj->getClass()->ext.innerObject, "wrapped object passed to rewrap"); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.h b/js/src/xpconnect/wrappers/WrapperFactory.h index 5d6abf82910b..16cf4ae69ed3 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.h +++ b/js/src/xpconnect/wrappers/WrapperFactory.h @@ -57,6 +57,7 @@ class WrapperFactory { static JSObject *Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, + JSObject *parent, uintN flags); }; From a89000be64b5371391e32b5bbfc9d17ea2e7d551 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 115/284] bug 580128 - Make AccessCheck work (fixing bad calls/missing assumptions). r=gal/peterv --- js/src/xpconnect/wrappers/AccessCheck.cpp | 43 +++++++++++-------- .../xpconnect/wrappers/FilteringWrapper.cpp | 10 ++++- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index bbe2a608be29..e225b5ce67c8 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -42,8 +42,10 @@ #include "nsJSPrincipals.h" #include "nsIDOMWindow.h" #include "nsIDOMWindowCollection.h" +#include "nsContentUtils.h" #include "XPCWrapper.h" +#include "XrayWrapper.h" namespace xpc { @@ -91,11 +93,15 @@ IsPermitted(const char *name, const char* prop, bool set) PROP('c', RW("code")) PROP('m', RW("message")) PROP('n', RW("name")) - PROP('r', RW("result"))) + PROP('r', RW("result")) + PROP('t', R("toString"))) NAME('H', "History", PROP('b', R("back")) PROP('f', R("forward")) PROP('g', R("go"))) + NAME('L', "Location", + PROP('h', W("hash") W("href")) + PROP('r', R("replace"))) NAME('N', "Navigator", PROP('p', RW("preference"))) NAME('W', "Window", @@ -109,10 +115,6 @@ IsPermitted(const char *name, const char* prop, bool set) PROP('s', R("self")) PROP('t', R("top")) PROP('w', R("window"))) - NAME('X', "XMLHttpRequest", - PROP('o', RW("open-uri"))) - NAME('S', "SOAPCall", - PROP('i', RW("invokeVerifySourceHeader"))) } return false; } @@ -123,9 +125,12 @@ IsPermitted(const char *name, const char* prop, bool set) #undef W static bool -IsFrameId(JSObject *obj, jsid id) +IsFrameId(JSContext *cx, JSObject *obj, jsid id) { - XPCWrappedNative *wn = static_cast(obj->getPrivate()); + XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); + if (!wn) { + return false; + } nsCOMPtr domwin(do_QueryWrappedNative(wn)); if (!domwin) { @@ -160,14 +165,17 @@ IsWindow(const char *name) bool AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid id, bool set) { + if (!XPCWrapper::GetSecurityManager()) + return true; + JSObject *obj = JSWrapper::wrappedObject(wrapper); - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if (!ssm) { - return true; - } - - const char *name = obj->getClass()->name; + const char *name; + js::Class *clasp = obj->getClass(); + if (clasp->ext.innerObject) + name = "Window"; + else + name = clasp->name; if (JSID_IS_ATOM(id)) { JSString *str = ATOM_TO_STRING(JSID_TO_ATOM(id)); @@ -176,11 +184,12 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid return true; } - if (IsWindow(name) && IsFrameId(obj, id)) + if (IsWindow(name) && IsFrameId(cx, obj, id)) return true; - PRBool privileged; - return NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) && privileged; + return set + ? nsContentUtils::IsCallerTrustedForWrite() + : nsContentUtils::IsCallerTrustedForRead(); } bool @@ -252,7 +261,7 @@ AccessCheck::deny(JSContext *cx, jsid id) JSString *str = JS_ValueToString(cx, idval); if (!str) return; - JS_ReportError(cx, "Permission denied to access property '%hs'", str); + JS_ReportError(cx, "Permission denied to access property '%hs'", JS_GetStringChars(str)); } } diff --git a/js/src/xpconnect/wrappers/FilteringWrapper.cpp b/js/src/xpconnect/wrappers/FilteringWrapper.cpp index 96f071129d1b..c71d813e1671 100644 --- a/js/src/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/src/xpconnect/wrappers/FilteringWrapper.cpp @@ -90,6 +90,14 @@ CheckAndReport(JSContext *cx, JSObject *wrapper, jsid id, bool set, Permission & return false; } if (perm == DenyAccess) { + // Reporting an error here indicates a problem entering the + // compartment. Therefore, any errors that we throw should be + // thrown in our *caller's* compartment, so they can inspect + // the error object. + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wrapper)) + return false; + AccessCheck::deny(cx, id); return false; } @@ -136,7 +144,7 @@ bool FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, bool set) { Permission perm; - return CheckAndReport(cx, wrapper, JSID_VOID, set, perm) && + return CheckAndReport(cx, wrapper, id, set, perm) && Base::enter(cx, wrapper, id, set); } From c2f27f016a03b7efe0f569971be31b28b9cc38b7 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 2 Sep 2010 16:02:51 -0700 Subject: [PATCH 116/284] bug 580128 - Temporary fixes to XrayWrapper - note: these were mostly overridden by later patches. r=gal --- js/src/xpconnect/src/XPCWrapper.cpp | 9 +- js/src/xpconnect/wrappers/AccessCheck.cpp | 2 + .../xpconnect/wrappers/FilteringWrapper.cpp | 3 + js/src/xpconnect/wrappers/WrapperFactory.cpp | 23 +++- js/src/xpconnect/wrappers/XrayWrapper.cpp | 107 ++++++++++++++---- js/src/xpconnect/wrappers/XrayWrapper.h | 7 ++ 6 files changed, 121 insertions(+), 30 deletions(-) diff --git a/js/src/xpconnect/src/XPCWrapper.cpp b/js/src/xpconnect/src/XPCWrapper.cpp index cf150b48b04b..adfe9404306a 100644 --- a/js/src/xpconnect/src/XPCWrapper.cpp +++ b/js/src/xpconnect/src/XPCWrapper.cpp @@ -44,6 +44,7 @@ #include "XPCNativeWrapper.h" #include "nsPIDOMWindow.h" #include "jswrapper.h" +#include "XrayWrapper.h" namespace XPCWrapper { @@ -67,7 +68,13 @@ Unwrap(JSContext *cx, JSObject *wrapper) // XXX Security check! } - return wrapper->unwrap(); + JSObject *wrappedObj = wrapper->unwrap(); + if (wrappedObj->getJSClass() == &xpc::HolderClass) { + typedef xpc::XrayWrapper Xray; + wrappedObj = Xray::unwrapHolder(cx, wrappedObj); + } + + return wrappedObj; } js::Class *clasp = wrapper->getClass(); diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index e225b5ce67c8..9fdf5c7a288e 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -174,6 +174,8 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid js::Class *clasp = obj->getClass(); if (clasp->ext.innerObject) name = "Window"; + else if (Jsvalify(clasp) == &HolderClass) + name = XrayWrapper::unwrapHolder(cx, obj)->getClass()->name; else name = clasp->name; diff --git a/js/src/xpconnect/wrappers/FilteringWrapper.cpp b/js/src/xpconnect/wrappers/FilteringWrapper.cpp index c71d813e1671..76f397594821 100644 --- a/js/src/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/src/xpconnect/wrappers/FilteringWrapper.cpp @@ -151,13 +151,16 @@ FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, #define SOW FilteringWrapper #define COW FilteringWrapper #define XOW FilteringWrapper, CrossOriginAccessiblePropertiesOnly> +#define NNXOW FilteringWrapper template<> SOW SOW::singleton(0); template<> COW COW::singleton(0); template<> XOW XOW::singleton(0); +template<> NNXOW NNXOW::singleton(0); template class SOW; template class COW; template class XOW; +template class NNXOW; } diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 284da91f440c..1e7dc9eede1f 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -84,9 +84,15 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO wrapper = &XrayWrapperWaivedWrapper; } else { // Native objects must be wrapped into an X-ray wrapper. - wrapper = IS_WN_WRAPPER_OBJECT(obj) - ? &XrayWrapper::singleton - : &JSCrossCompartmentWrapper::singleton; + if (!obj->getGlobal()->isSystem() && + (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject)) { + typedef XrayWrapper Xray; + + wrapper = &Xray::singleton; + obj = Xray::createHolder(cx, parent, obj); + } else { + wrapper = &JSCrossCompartmentWrapper::singleton; + } } } else if (AccessCheck::isChrome(origin)) { // If an object that needs a system only wrapper crosses into content @@ -108,8 +114,15 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO // a predefined set of properties. XrayWrapper adds a property // (.wrappedJSObject) which allows bypassing the XrayWrapper, but // we filter out access to that property. - wrapper = &FilteringWrapper, - CrossOriginAccessiblePropertiesOnly>::singleton; + if (!IS_WN_WRAPPER(obj)) { + wrapper = &FilteringWrapper::singleton; + } else { + typedef XrayWrapper Xray; + wrapper = &FilteringWrapper, + CrossOriginAccessiblePropertiesOnly>::singleton; + obj = Xray::createHolder(cx, parent, obj); + } } return JSWrapper::New(cx, obj, wrappedProto, NULL, wrapper); } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 17743713999f..460a2aa1db10 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -90,10 +90,12 @@ GetWrappedNative(JSObject *obj) } static JSObject * -GetWrappedNativeObjectFromHolder(JSObject *holder) +GetWrappedNativeObjectFromHolder(JSContext *cx, JSObject *holder) { NS_ASSERTION(holder->getJSClass() == &HolderClass, "expected a native property holder object"); - return holder->getSlot(JSSLOT_WN_OBJ).toObjectOrNull(); + JSObject *wrappedObj = &holder->getSlot(JSSLOT_WN_OBJ).toObject(); + OBJ_TO_INNER_OBJECT(cx, wrappedObj); + return wrappedObj; } // Some DOM objects have shared properties that don't have an explicit @@ -102,7 +104,23 @@ GetWrappedNativeObjectFromHolder(JSObject *holder) static JSBool holder_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { - JSObject *wnObject = GetWrappedNativeObjectFromHolder(obj); + if (obj->isWrapper()) { +#ifdef DEBUG + { + typedef FilteringWrapper, + CrossOriginAccessiblePropertiesOnly> + FilteringXRay; + JSProxyHandler *handler = obj->getProxyHandler(); + NS_ASSERTION(handler == &XrayWrapper::singleton || + handler == &XrayWrapper::singleton || + handler == &FilteringXRay::singleton, + "bad object"); + } +#endif + obj = obj->unwrap(); + } + + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, obj); XPCWrappedNative *wn = GetWrappedNative(wnObject); if (NATIVE_HAS_FLAG(wn, WantGetProperty)) { JSBool retval = true; @@ -120,7 +138,7 @@ holder_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) static JSBool holder_set(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { - JSObject *wnObject = GetWrappedNativeObjectFromHolder(obj); + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, obj); XPCWrappedNative *wn = GetWrappedNative(wnObject); if (NATIVE_HAS_FLAG(wn, WantSetProperty)) { JSBool retval = true; @@ -141,27 +159,29 @@ ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, JSProp desc->obj = NULL; NS_ASSERTION(holder->getJSClass() == &HolderClass, "expected a native property holder object"); - JSObject *wnObject = GetWrappedNativeObjectFromHolder(holder); + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); XPCWrappedNative *wn = GetWrappedNative(wnObject); // This will do verification and the method lookup for us. - XPCCallContext ccx(JS_CALLER, cx, holder, nsnull, id); + XPCCallContext ccx(JS_CALLER, cx, wnObject, nsnull, id); // Run the resolve hook of the wrapped native. - JSBool retval = true; - JSObject *pobj = NULL; - uintN flags = cx->resolveFlags | (set ? JSRESOLVE_ASSIGNING : 0); - nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, holder, id, flags, - &pobj, &retval); - if (NS_FAILED(rv)) { - if (retval) { - XPCThrower::Throw(rv, cx); + if (NATIVE_HAS_FLAG(wn, WantNewResolve)) { + JSBool retval = true; + JSObject *pobj = NULL; + uintN flags = cx->resolveFlags | (set ? JSRESOLVE_ASSIGNING : 0); + nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, holder, id, flags, + &pobj, &retval); + if (NS_FAILED(rv)) { + if (retval) { + XPCThrower::Throw(rv, cx); + } + return false; } - return false; - } - if (pobj) { - return JS_GetPropertyDescriptorById(cx, pobj, id, cx->resolveFlags, desc); + if (pobj) { + return JS_GetPropertyDescriptorById(cx, pobj, id, cx->resolveFlags, desc); + } } // There are no native numeric properties, so we can shortcut here. We will not @@ -174,9 +194,9 @@ ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, JSProp XPCNativeInterface *iface; XPCNativeMember *member; if (ccx.GetWrapper() != wn || - !wn->IsValid() || - !(iface = ccx.GetInterface()) || - !(member = ccx.GetMember())) { + !wn->IsValid() || + !(iface = ccx.GetInterface()) || + !(member = ccx.GetMember())) { /* Not found */ return true; } @@ -233,12 +253,13 @@ static JSBool holder_enumerate(JSContext *cx, JSObject *holder) { // Ask the native wrapper for all its ids - JSIdArray *ida = JS_Enumerate(cx, GetWrappedNativeObjectFromHolder(holder)); + JSIdArray *ida = JS_Enumerate(cx, GetWrappedNativeObjectFromHolder(cx, holder)); if (!ida) return false; + + // Resolve the underlying native properties onto the holder object jsid *idp = ida->vector; size_t length = ida->length; - // Resolve the underlyign native properties onto the holder object while (length-- > 0) { JSPropertyDescriptor dummy; if (!ResolveNativeProperty(cx, holder, *idp++, false, &dummy)) @@ -259,7 +280,7 @@ wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) // If the caller intentionally waives the X-ray wrapper we usually // apply for wrapped natives, use a special wrapper to make sure the // membrane will not automatically apply an X-ray wrapper. - JSObject *wn = GetWrappedNativeObjectFromHolder(holder); + JSObject *wn = GetWrappedNativeObjectFromHolder(cx, holder); JSObject *obj = JSWrapper::New(cx, wn, NULL, wn->getParent(), &WaiveXrayWrapperWrapper); if (!obj) return false; @@ -267,6 +288,22 @@ wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) return true; } +template +bool +XrayWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp) +{ + return JSProxyHandler::get(cx, wrapper, receiver, id, vp); +} + +template +bool +XrayWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp) +{ + return JSProxyHandler::set(cx, wrapper, receiver, id, vp); +} + template bool XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc_in) @@ -313,6 +350,28 @@ XrayWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) return JSProxyHandler::hasOwn(cx, wrapper, id, bp); } +template +JSObject * +XrayWrapper::createHolder(JSContext *cx, + JSObject *parent, + JSObject *wrappedNative) +{ + JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, nsnull, parent); + if (!holder) + return nsnull; + + holder->setSlot(JSSLOT_WN_OBJ, ObjectValue(*wrappedNative)); + return holder; +} + +template +JSObject * +XrayWrapper::unwrapHolder(JSContext *cx, JSObject *holder) +{ + NS_ASSERTION(holder->getJSClass() == &HolderClass, "bad holder"); + return GetWrappedNativeObjectFromHolder(cx, holder); +} + #define SJOW XrayWrapper #define XOSJOW XrayWrapper diff --git a/js/src/xpconnect/wrappers/XrayWrapper.h b/js/src/xpconnect/wrappers/XrayWrapper.h index 8f4645a2e201..5ca6c9016856 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.h +++ b/js/src/xpconnect/wrappers/XrayWrapper.h @@ -53,6 +53,10 @@ class XrayWrapper : public Base { XrayWrapper(uintN flags); virtual ~XrayWrapper(); + virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp); + virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp); virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc); virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, @@ -60,6 +64,9 @@ class XrayWrapper : public Base { virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); + static JSObject *createHolder(JSContext *cx, JSObject *parent, JSObject *wrappedNative); + static JSObject *unwrapHolder(JSContext *cx, JSObject *holder); + static XrayWrapper singleton; }; From 8e08990a45b671ced9cd9fc326f93442d5089d08 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 3 Sep 2010 14:15:50 -0700 Subject: [PATCH 117/284] Bug 580128 - Fix a leak caused by holding a non-cycle collectable object instead of a cycle-collectable one. r=peterv --- dom/base/nsDOMClassInfo.cpp | 26 -------------------------- dom/base/nsDOMClassInfo.h | 1 - dom/base/nsGlobalWindow.cpp | 8 ++++++++ dom/base/nsJSEnvironment.cpp | 19 +++++-------------- dom/base/nsJSEnvironment.h | 9 +++------ 5 files changed, 16 insertions(+), 47 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 9d23d130f9b8..bedc1b86578e 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1581,7 +1581,6 @@ jsid nsDOMClassInfo::sNamedItem_id = JSID_VOID; jsid nsDOMClassInfo::sEnumerate_id = JSID_VOID; jsid nsDOMClassInfo::sNavigator_id = JSID_VOID; jsid nsDOMClassInfo::sDocument_id = JSID_VOID; -jsid nsDOMClassInfo::sWindow_id = JSID_VOID; jsid nsDOMClassInfo::sFrames_id = JSID_VOID; jsid nsDOMClassInfo::sSelf_id = JSID_VOID; jsid nsDOMClassInfo::sOpener_id = JSID_VOID; @@ -1806,7 +1805,6 @@ nsDOMClassInfo::DefineStaticJSVals(JSContext *cx) SET_JSID_TO_STRING(sEnumerate_id, cx, "enumerateProperties"); SET_JSID_TO_STRING(sNavigator_id, cx, "navigator"); SET_JSID_TO_STRING(sDocument_id, cx, "document"); - SET_JSID_TO_STRING(sWindow_id, cx, "window"); SET_JSID_TO_STRING(sFrames_id, cx, "frames"); SET_JSID_TO_STRING(sSelf_id, cx, "self"); SET_JSID_TO_STRING(sOpener_id, cx, "opener"); @@ -4843,7 +4841,6 @@ nsDOMClassInfo::ShutDown() sEnumerate_id = JSID_VOID; sNavigator_id = JSID_VOID; sDocument_id = JSID_VOID; - sWindow_id = JSID_VOID; sFrames_id = JSID_VOID; sSelf_id = JSID_VOID; sOpener_id = JSID_VOID; @@ -6799,29 +6796,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, return NS_OK; } - if (id == sWindow_id) { - // window should *always* be the outer window object. - win = win->GetOuterWindowInternal(); - NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE); - - jsval winVal; - nsCOMPtr holder; - rv = WrapNative(cx, obj, nsGlobalWindow::ToSupports(win), PR_TRUE, - &winVal, getter_AddRefs(holder)); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool ok = - ::JS_DefinePropertyById(cx, obj, id, winVal, JS_PropertyStub, JS_PropertyStub, - JSPROP_READONLY | JSPROP_ENUMERATE); - - if (!ok) { - return NS_ERROR_FAILURE; - } - *objp = obj; - - return NS_OK; - } - if (id == sJava_id || id == sPackages_id) { static PRBool isResolvingJavaProperties; diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 22c67d12b57b..02987fc417db 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -345,7 +345,6 @@ protected: static jsid sEnumerate_id; static jsid sNavigator_id; static jsid sDocument_id; - static jsid sWindow_id; static jsid sFrames_id; static jsid sSelf_id; static jsid sOpener_id; diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 03d5bd5371fc..79762bedb98f 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1956,6 +1956,14 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, NS_ERROR("can't set prototype"); return NS_ERROR_FAILURE; } + } else { + if (!JS_DefineProperty(cx, newInnerWindow->mJSObject, "window", + OBJECT_TO_JSVAL(mJSObject), + JS_PropertyStub, JS_PropertyStub, + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { + NS_ERROR("can't create the 'window' property"); + return NS_ERROR_FAILURE; + } } } diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 5307b63dfef5..cf64087ccc5b 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -1314,7 +1314,7 @@ nsJSContext::~nsJSContext() #endif NS_PRECONDITION(!mTerminations, "Shouldn't have termination funcs by now"); - mGlobalWrapperRef = nsnull; + mGlobalObjectRef = nsnull; DestroyJSContext(); @@ -1372,11 +1372,11 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobalWrapperRef) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobalObjectRef) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext) NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt()) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobalWrapperRef) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobalObjectRef) NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContext"); nsContentUtils::XPConnect()->NoteJSContext(tmp->mContext, cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -2553,6 +2553,8 @@ nsresult nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, nsIScriptGlobalObject *aCurrentInner) { + mGlobalObjectRef = aGlobalObject; + nsCOMPtr chromeWindow(do_QueryInterface(aGlobalObject)); PRUint32 flags = 0; @@ -2583,19 +2585,8 @@ nsJSContext::SetOuterObject(void *aOuterObject) { JSObject *outer = static_cast(aOuterObject); - nsIXPConnect *xpc = nsContentUtils::XPConnect(); - nsCOMPtr holder; - - nsresult rv = xpc->HoldObject(mContext, outer, getter_AddRefs(holder)); - NS_ENSURE_SUCCESS(rv, rv); - // Force our context's global object to be the outer. JS_SetGlobalObject(mContext, outer); - - // Hold a strong reference to the wrapper for the global to avoid - // rooting and unrooting the global object every time its AddRef() - // or Release() methods are called - mGlobalWrapperRef = holder; return NS_OK; } diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 520d06e5ad97..57c5c239fe76 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -312,12 +312,9 @@ private: PRTime mModalStateTime; PRUint32 mModalStateDepth; - // mGlobalWrapperRef is used only to hold a strong reference to the - // global object wrapper while the nsJSContext is alive. This cuts - // down on the number of rooting and unrooting calls XPConnect has - // to make when the global object is touched in JS. - - nsCOMPtr mGlobalWrapperRef; + // mGlobalObjectRef ensures that the outer window stays alive as long as the + // context does. It is eventually collected by the cycle collector. + nsCOMPtr mGlobalObjectRef; static int JSOptionChangedCallback(const char *pref, void *data); From 58c04eaa66f121a73a01a45728f319ec4521fea6 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 3 Sep 2010 19:24:23 -0700 Subject: [PATCH 118/284] Bug 580128 - Preliminary fixes to make .wrappedJSObject on XrayWrappers work. Overridden by later patches. r=gal --- js/src/xpconnect/wrappers/XrayWrapper.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 460a2aa1db10..1e1a64b47f71 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -272,16 +272,34 @@ struct NativePropertiesOnly : public Policy { static bool check(JSContext *cx, JSObject *obj, jsid id, bool set, Permission &perm); }; -extern JSWrapper WaiveXrayWrapperWrapper; +extern JSCrossCompartmentWrapper XrayWrapperWaivedWrapper; static JSBool wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) { + if (holder->isWrapper()) { +#ifdef DEBUG + { + JSProxyHandler *handler = holder->getProxyHandler(); + NS_ASSERTION(handler == &XrayWrapper::singleton || + handler == &XrayWrapper::singleton, + "bad object"); + } +#endif + holder = holder->unwrap(); + } + // If the caller intentionally waives the X-ray wrapper we usually // apply for wrapped natives, use a special wrapper to make sure the // membrane will not automatically apply an X-ray wrapper. JSObject *wn = GetWrappedNativeObjectFromHolder(cx, holder); - JSObject *obj = JSWrapper::New(cx, wn, NULL, wn->getParent(), &WaiveXrayWrapperWrapper); + + // We have to make sure that if we're wrapping an outer window, that + // the .wrappedJSObject also wraps the outer window. + OBJ_TO_OUTER_OBJECT(cx, wn); + if (!wn) + return false; + JSObject *obj = JSWrapper::New(cx, wn, NULL, holder->getParent(), &XrayWrapperWaivedWrapper); if (!obj) return false; *vp = OBJECT_TO_JSVAL(obj); From 697c0a497c5e81174535cb790cb0ef6c3c263e3b Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 119/284] bug 580128 - Give windows a .wrappedJSObject property to help out old consumers of XPCNativeWrappers. r=peterv --- dom/base/nsDOMClassInfo.cpp | 8 ++++++++ dom/base/nsDOMClassInfo.h | 1 + 2 files changed, 9 insertions(+) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index bedc1b86578e..970aa8bdbd26 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1618,6 +1618,7 @@ jsid nsDOMClassInfo::sOnvolumechange_id = JSID_VOID; jsid nsDOMClassInfo::sOnmessage_id = JSID_VOID; jsid nsDOMClassInfo::sOnbeforescriptexecute_id = JSID_VOID; jsid nsDOMClassInfo::sOnafterscriptexecute_id = JSID_VOID; +jsid nsDOMClassInfo::sWrappedJSObject_id = JSID_VOID; static const JSClass *sObjectClass = nsnull; JSPropertyOp nsDOMClassInfo::sXPCNativeWrapperGetPropertyOp = nsnull; @@ -1844,6 +1845,7 @@ nsDOMClassInfo::DefineStaticJSVals(JSContext *cx) SET_JSID_TO_STRING(sOnbeforescriptexecute_id, cx, "onbeforescriptexecute"); SET_JSID_TO_STRING(sOnafterscriptexecute_id, cx, "onafterscriptexecute"); #endif // MOZ_MEDIA + SET_JSID_TO_STRING(sWrappedJSObject_id, cx, "wrappedJSObject"); return NS_OK; } @@ -4878,6 +4880,7 @@ nsDOMClassInfo::ShutDown() sOnmessage_id = JSID_VOID; sOnbeforescriptexecute_id = JSID_VOID; sOnafterscriptexecute_id = JSID_VOID; + sWrappedJSObject_id = JSID_VOID; NS_IF_RELEASE(sXPConnect); NS_IF_RELEASE(sSecMan); @@ -5285,6 +5288,11 @@ nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } } + if (id == sWrappedJSObject_id) { + *vp = OBJECT_TO_JSVAL(obj); + return NS_SUCCESS_I_DID_SOMETHING; + } + return NS_OK; } diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 02987fc417db..21b2832409fe 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -382,6 +382,7 @@ protected: static jsid sOnmessage_id; static jsid sOnbeforescriptexecute_id; static jsid sOnafterscriptexecute_id; + static jsid sWrappedJSObject_id; static JSPropertyOp sXPCNativeWrapperGetPropertyOp; static JSPropertyOp sXrayWrapperPropertyHolderGetPropertyOp; From fb85f65bb85b881178203848d9b5c165bb85fb23 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 120/284] bug 580128 - Make the Window constructor be defined on new windows. r=peterv --- dom/base/nsJSEnvironment.cpp | 13 +++++++++++++ js/src/xpconnect/src/nsXPConnect.cpp | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index cf64087ccc5b..77947c057835 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2571,6 +2571,19 @@ nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, JS_SetOptions(mContext, JS_GetOptions(mContext) | JSOPTION_XML); } + nsIXPConnect *xpc = nsContentUtils::XPConnect(); + nsCOMPtr holder; + + nsresult rv = xpc->WrapNative(mContext, aCurrentInner->GetGlobalJSObject(), + aCurrentInner, NS_GET_IID(nsISupports), + getter_AddRefs(holder)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr wrapper(do_QueryInterface(holder)); + NS_ABORT_IF_FALSE(wrapper, "bad wrapper"); + + wrapper->RefreshPrototype(); + JSObject *outer = NS_NewOuterWindowProxy(mContext, aCurrentInner->GetGlobalJSObject()); if (!outer) { diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index b0de4108dc13..20682c1735ff 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -1124,6 +1124,12 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext, if(!scope) return UnexpectedFailure(NS_ERROR_FAILURE); + // Note: This call cooperates with a call to wrapper->RefreshPrototype() + // in nsJSEnvironment::CreateOuterObject in order to ensure that the + // prototype defines its constructor on the right global object. + if(wrapper->GetProto()->GetScriptableInfo()) + scope->RemoveWrappedNativeProtos(); + NS_ASSERTION(scope->GetGlobalJSObject() == tempGlobal, "stealing scope!"); scope->SetGlobal(ccx, globalJSObj); From 7f6ca841c5b7c18655eb5c30441619f2418dd978 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:40 -0700 Subject: [PATCH 121/284] bug 580128 - Allow calling functions cross origin. r=gal --- js/src/jswrapper.cpp | 16 ++++++++-------- js/src/jswrapper.h | 3 ++- js/src/xpconnect/wrappers/AccessCheck.cpp | 17 +++++++++++------ js/src/xpconnect/wrappers/AccessCheck.h | 17 +++++++++++------ .../xpconnect/wrappers/CrossOriginWrapper.cpp | 2 +- js/src/xpconnect/wrappers/CrossOriginWrapper.h | 2 +- js/src/xpconnect/wrappers/FilteringWrapper.cpp | 13 +++++++------ js/src/xpconnect/wrappers/FilteringWrapper.h | 2 +- 8 files changed, 42 insertions(+), 30 deletions(-) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 78d23eee8047..173512a01bcc 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -87,17 +87,17 @@ JSWrapper::~JSWrapper() { } -#define CHECKED(op, set) \ +#define CHECKED(op, act) \ JS_BEGIN_MACRO \ - if (!enter(cx, wrapper, id, set)) \ + if (!enter(cx, wrapper, id, act)) \ return false; \ bool ok = (op); \ leave(cx, wrapper); \ return ok; \ JS_END_MACRO -#define SET(action) CHECKED(action, true) -#define GET(action) CHECKED(action, false) +#define SET(action) CHECKED(action, SET) +#define GET(action) CHECKED(action, GET) bool JSWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, @@ -223,7 +223,7 @@ bool JSWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp) { const jsid id = JSID_VOID; - GET(JSProxyHandler::call(cx, wrapper, argc, vp)); + CHECKED(JSProxyHandler::call(cx, wrapper, argc, vp), CALL); } bool @@ -237,7 +237,7 @@ JSString * JSWrapper::obj_toString(JSContext *cx, JSObject *wrapper) { JSString *str; - if (!enter(cx, wrapper, JSID_VOID, false)) + if (!enter(cx, wrapper, JSID_VOID, GET)) return NULL; str = JSProxyHandler::obj_toString(cx, wrapper); leave(cx, wrapper); @@ -248,7 +248,7 @@ JSString * JSWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) { JSString *str; - if (!enter(cx, wrapper, JSID_VOID, false)) + if (!enter(cx, wrapper, JSID_VOID, GET)) return NULL; str = JSProxyHandler::fun_toString(cx, wrapper, indent); leave(cx, wrapper); @@ -262,7 +262,7 @@ JSWrapper::trace(JSTracer *trc, JSObject *wrapper) } bool -JSWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, bool set) +JSWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act) { return true; } diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 5087eaa208a4..9c58357da14e 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -92,7 +92,8 @@ class JSWrapper : public js::JSProxyHandler { virtual JS_FRIEND_API(void) trace(JSTracer *trc, JSObject *wrapper); /* Policy enforcement traps. */ - virtual JS_FRIEND_API(bool) enter(JSContext *cx, JSObject *wrapper, jsid id, bool set); + enum Action { GET, SET, CALL }; + virtual JS_FRIEND_API(bool) enter(JSContext *cx, JSObject *wrapper, jsid id, Action act); virtual JS_FRIEND_API(void) leave(JSContext *cx, JSObject *wrapper); static JS_FRIEND_API(JSWrapper) singleton; diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 9fdf5c7a288e..d7b9047cdb97 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -163,11 +163,15 @@ IsWindow(const char *name) } bool -AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid id, bool set) +AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid id, + JSWrapper::Action act) { if (!XPCWrapper::GetSecurityManager()) return true; + if (act == JSWrapper::CALL) + return true; + JSObject *obj = JSWrapper::wrappedObject(wrapper); const char *name; @@ -182,14 +186,14 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid if (JSID_IS_ATOM(id)) { JSString *str = ATOM_TO_STRING(JSID_TO_ATOM(id)); const char *prop = JS_GetStringBytes(str); - if (IsPermitted(name, prop, set)) + if (IsPermitted(name, prop, act == JSWrapper::SET)) return true; } if (IsWindow(name) && IsFrameId(cx, obj, id)) return true; - return set + return (act == JSWrapper::SET) ? nsContentUtils::IsCallerTrustedForWrite() : nsContentUtils::IsCallerTrustedForRead(); } @@ -270,7 +274,8 @@ AccessCheck::deny(JSContext *cx, jsid id) typedef enum { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 } Access; bool -ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, bool set, Permission &perm) +ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, JSWrapper::Action act, + Permission &perm) { JSObject *holder = JSWrapper::wrappedObject(wrapper); @@ -351,8 +356,8 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, bool set return false; } - if ((set && !(access & WRITE)) || - (!set && !(access & READ))) { + if ((act == JSWrapper::SET && !(access & WRITE)) || + (act != JSWrapper::SET && !(access & READ))) { return true; // Deny } diff --git a/js/src/xpconnect/wrappers/AccessCheck.h b/js/src/xpconnect/wrappers/AccessCheck.h index fc08128b26a6..5a4b17a65d7c 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.h +++ b/js/src/xpconnect/wrappers/AccessCheck.h @@ -46,7 +46,8 @@ class AccessCheck { public: static bool isSameOrigin(JSCompartment *a, JSCompartment *b); static bool isChrome(JSCompartment *compartment); - static bool isCrossOriginAccessPermitted(JSContext *cx, JSObject *obj, jsid id, bool set); + static bool isCrossOriginAccessPermitted(JSContext *cx, JSObject *obj, jsid id, + JSWrapper::Action act); static bool isSystemOnlyAccessPermitted(JSContext *cx); static bool needsSystemOnlyWrapper(JSObject *obj); @@ -64,7 +65,8 @@ struct Policy { // This policy permits access to all properties. struct Permissive : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, bool set, Permission &perm) { + static bool check(JSContext *cx, JSObject *wrapper, jsid id, JSWrapper::Action act, + Permission &perm) { perm = PermitObjectAccess; return true; } @@ -73,7 +75,8 @@ struct Permissive : public Policy { // This policy only permits access to the object if the subject can touch // system objects. struct OnlyIfSubjectIsSystem : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, bool set, Permission &perm) { + static bool check(JSContext *cx, JSObject *wrapper, jsid id, JSWrapper::Action act, + Permission &perm) { perm = DenyAccess; if (AccessCheck::isSystemOnlyAccessPermitted(cx)) perm = PermitObjectAccess; @@ -84,9 +87,10 @@ struct OnlyIfSubjectIsSystem : public Policy { // This policy only permits access to properties that are safe to be used // across origins. struct CrossOriginAccessiblePropertiesOnly : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, bool set, Permission &perm) { + static bool check(JSContext *cx, JSObject *wrapper, jsid id, JSWrapper::Action act, + Permission &perm) { perm = DenyAccess; - if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, set)) + if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act)) perm = PermitPropertyAccess; return true; } @@ -95,7 +99,8 @@ struct CrossOriginAccessiblePropertiesOnly : public Policy { // This policy only permits access to properties if they appear in the // objects exposed properties list. struct ExposedPropertiesOnly : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, bool set, Permission &perm); + static bool check(JSContext *cx, JSObject *wrapper, jsid id, JSWrapper::Action act, + Permission &perm); }; } diff --git a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp index cf86f8caeb36..19433b6d811a 100644 --- a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp +++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp @@ -60,7 +60,7 @@ GetCompartmentPrincipal(JSCompartment *compartment) } bool -CrossOriginWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, bool set) +CrossOriginWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act) { nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) { diff --git a/js/src/xpconnect/wrappers/CrossOriginWrapper.h b/js/src/xpconnect/wrappers/CrossOriginWrapper.h index 3cd163fa6a04..d61476f4a33a 100644 --- a/js/src/xpconnect/wrappers/CrossOriginWrapper.h +++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.h @@ -50,7 +50,7 @@ class CrossOriginWrapper : public JSCrossCompartmentWrapper { CrossOriginWrapper(uintN flags); virtual ~CrossOriginWrapper(); - virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, bool set); + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act); virtual void leave(JSContext *cx, JSObject *wrapper); static CrossOriginWrapper singleton; diff --git a/js/src/xpconnect/wrappers/FilteringWrapper.cpp b/js/src/xpconnect/wrappers/FilteringWrapper.cpp index 76f397594821..d809c92614a0 100644 --- a/js/src/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/src/xpconnect/wrappers/FilteringWrapper.cpp @@ -72,7 +72,7 @@ Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props) for (size_t n = 0; n < props.length(); ++n) { jsid id = props[n]; Permission perm; - if (perm != PermitObjectAccess && !Policy::check(cx, wrapper, id, false, perm)) + if (perm != PermitObjectAccess && !Policy::check(cx, wrapper, id, JSWrapper::GET, perm)) return false; // Error if (perm != DenyAccess) { props[w++] = id; @@ -84,9 +84,9 @@ Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props) template static bool -CheckAndReport(JSContext *cx, JSObject *wrapper, jsid id, bool set, Permission &perm) +CheckAndReport(JSContext *cx, JSObject *wrapper, jsid id, JSWrapper::Action act, Permission &perm) { - if (!Policy::check(cx, wrapper, id, set, perm)) { + if (!Policy::check(cx, wrapper, id, act, perm)) { return false; } if (perm == DenyAccess) { @@ -141,11 +141,12 @@ FilteringWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN template bool -FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, bool set) +FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, + JSWrapper::Action act) { Permission perm; - return CheckAndReport(cx, wrapper, id, set, perm) && - Base::enter(cx, wrapper, id, set); + return CheckAndReport(cx, wrapper, id, act, perm) && + Base::enter(cx, wrapper, id, act); } #define SOW FilteringWrapper diff --git a/js/src/xpconnect/wrappers/FilteringWrapper.h b/js/src/xpconnect/wrappers/FilteringWrapper.h index 11ecc9222a8c..28f7bbe5d5b4 100644 --- a/js/src/xpconnect/wrappers/FilteringWrapper.h +++ b/js/src/xpconnect/wrappers/FilteringWrapper.h @@ -53,7 +53,7 @@ class FilteringWrapper : public Base { virtual bool enumerateOwn(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, js::Value *vp); - virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, bool set); + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, JSWrapper::Action act); static FilteringWrapper singleton; }; From 1f7e1fc1bfe75a9f6b80c797daae888478cf7f2c Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 122/284] bug 580128 - Prepare objects for wrapping. r=peterv --- js/src/xpconnect/wrappers/WrapperFactory.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 1e7dc9eede1f..bd0dc4a226a0 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -71,6 +71,13 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO NS_ASSERTION(!obj->isWrapper() || obj->getClass()->ext.innerObject, "wrapped object passed to rewrap"); + if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj)) + return nsnull; + + OBJ_TO_OUTER_OBJECT(cx, obj); + if (!obj) + return nsnull; + JSCompartment *origin = obj->getCompartment(cx); JSCompartment *target = cx->compartment; From 8ab910d842ed7e698b4e494690c0534b6345316a Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 123/284] bug 580128 - Fix a bug in early XrayWrappers that allowed Holders to get in here. r=gal --- js/src/xpconnect/wrappers/WrapperFactory.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index bd0dc4a226a0..c515276afd53 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -78,6 +78,17 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO if (!obj) return nsnull; + // Ugly hack to avoid wrapping holder objects instead of the actual + // underlying wrapped native JS object. + if (JS_GET_CLASS(cx, obj) == &HolderClass) { + obj = XrayWrapper::unwrapHolder(cx, obj); + OBJ_TO_OUTER_OBJECT(cx, obj); + if (!JS_WrapObject(cx, &obj)) + return nsnull; + + return obj; + } + JSCompartment *origin = obj->getCompartment(cx); JSCompartment *target = cx->compartment; From ac4351596939787121c5013dd66f1555a97a517a Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 124/284] bug 580128 - Make evalInSandbox work with the new wrappers. r=peterv --- js/src/xpconnect/src/XPCWrapper.cpp | 10 ++++ js/src/xpconnect/src/xpccomponents.cpp | 76 ++++++++++++++++---------- 2 files changed, 57 insertions(+), 29 deletions(-) diff --git a/js/src/xpconnect/src/XPCWrapper.cpp b/js/src/xpconnect/src/XPCWrapper.cpp index adfe9404306a..58514deca667 100644 --- a/js/src/xpconnect/src/XPCWrapper.cpp +++ b/js/src/xpconnect/src/XPCWrapper.cpp @@ -263,6 +263,16 @@ RewrapObject(JSContext *cx, JSObject *scope, JSObject *obj, WrapperType hint, JSObject * UnsafeUnwrapSecurityWrapper(JSContext *cx, JSObject *obj) { + if (obj->isProxy()) { + JSObject *wrappedObj = obj->unwrap(); + if (wrappedObj->getJSClass() == &xpc::HolderClass) { + typedef xpc::XrayWrapper Xray; + wrappedObj = Xray::unwrapHolder(cx, wrappedObj); + } + + return wrappedObj; + } + if (IsSecurityWrapper(obj)) { jsval v; JS_GetReservedSlot(cx, obj, sWrappedObjSlot, &v); diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index 04de378489b6..f4eaa0ec3478 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -50,6 +50,7 @@ #include "xpcJSWeakReference.h" #include "XPCNativeWrapper.h" #include "XPCWrapper.h" +#include "jsproxy.h" #ifdef MOZ_JSLOADER #include "mozJSComponentLoader.h" @@ -3070,16 +3071,25 @@ SandboxImport(JSContext *cx, uintN argc, jsval *vp) if (!thisobj) return JS_FALSE; - if (argc < 1) { + jsval *argv = JS_ARGV(cx, vp); + if (argc < 1 || JSVAL_IS_PRIMITIVE(argv[0])) { XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); return JS_FALSE; } - jsval *argv = JS_ARGV(cx, vp); - JSFunction *fun = JS_ValueToFunction(cx, argv[0]); - if (!fun) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return JS_FALSE; + JSFunction *fun; + { + // NB: funobj must only be used to get the JSFunction out. + JSObject *funobj = JSVAL_TO_OBJECT(argv[0]); + if (funobj->isProxy()) { + funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(cx, funobj); + } + + fun = JS_ValueToFunction(cx, OBJECT_TO_JSVAL(funobj)); + if (!fun) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return JS_FALSE; + } } JSString *funname; @@ -3290,10 +3300,8 @@ xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop) if (vp) { *vp = OBJECT_TO_JSVAL(sandbox); - JSObject *scope; - if (!(scope = JS_GetScopeChain(cx)) || - !XPCWrapper::RewrapObject(cx, scope, sandbox, XPCWrapper::NONE, vp)) { - return NS_ERROR_FAILURE; + if (!JS_WrapValue(cx, vp)) { + return NS_ERROR_UNEXPECTED; } } @@ -3654,17 +3662,24 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, { JSAutoRequest req(sandcx->GetJSContext()); + JSAutoEnterCompartment ac; + jsval v; JSString *str = nsnull; + if (!ac.enter(sandcx->GetJSContext(), sandbox)) { + // XXX HELP! + NS_ABORT(); + } + JSBool ok = JS_EvaluateUCScriptForPrincipals(sandcx->GetJSContext(), sandbox, jsPrincipals, reinterpret_cast (PromiseFlatString(source).get()), source.Length(), filename, lineNo, - rval); - if (ok && returnStringOnly && !(JSVAL_IS_VOID(*rval))) { - ok = !!(str = JS_ValueToString(sandcx->GetJSContext(), *rval)); + &v); + if (ok && returnStringOnly && !(JSVAL_IS_VOID(v))) { + ok = !!(str = JS_ValueToString(sandcx->GetJSContext(), v)); } if (!ok) { @@ -3673,10 +3688,8 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, jsval exn; if (JS_GetPendingException(sandcx->GetJSContext(), &exn)) { - // Root the exception temporarily so we can execute code on - // sandcx without a pending exception. - js::AutoValueRooter exnroot(sandcx->GetJSContext(), exn); JS_ClearPendingException(sandcx->GetJSContext()); + if (returnStringOnly) { // The caller asked for strings only, convert the // exception into a string. @@ -3686,7 +3699,13 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, if (str) { // We converted the exception to a string. Use that // as the value exception. - JS_SetPendingException(cx, STRING_TO_JSVAL(str)); + exn = STRING_TO_JSVAL(str); + if (JS_WrapValue(cx, &exn)) { + JS_SetPendingException(cx, STRING_TO_JSVAL(str)); + } else { + JS_ClearPendingException(cx); + rv = NS_ERROR_FAILURE; + } } else { JS_ClearPendingException(cx); rv = NS_ERROR_FAILURE; @@ -3694,10 +3713,7 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, } else { JSAutoRequest req(cx); - if (!JSVAL_IS_PRIMITIVE(exn) && - XPCWrapper::RewrapObject(cx, callingScope, - JSVAL_TO_OBJECT(exn), - XPCWrapper::SJOW, &exn)) { + if (JS_WrapValue(cx, &exn)) { JS_SetPendingException(cx, exn); } } @@ -3710,15 +3726,17 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, } } else { // Convert the result into something safe for our caller. + JSAutoRequest req(cx); if (str) { - *rval = STRING_TO_JSVAL(str); - } else if (!JSVAL_IS_PRIMITIVE(*rval)) { - if (!XPCWrapper::RewrapObject(sandcx->GetJSContext(), - callingScope, - JSVAL_TO_OBJECT(*rval), - XPCWrapper::SJOW, rval)) { - rv = NS_ERROR_FAILURE; - } + v = STRING_TO_JSVAL(str); + } + + if (!JS_WrapValue(cx, &v)) { + rv = NS_ERROR_FAILURE; + } + + if (NS_SUCCEEDED(rv)) { + *rval = v; } } } From 86276c7e44d20b0c361d833bdda70b2e0d1b80d2 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 125/284] bug 580128 - Chrome sandboxes cause this assertion to box: use plain cross compartment wrappers when we see them. r=jst --- js/src/xpconnect/wrappers/WrapperFactory.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index c515276afd53..cce3408c7d71 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -94,11 +94,11 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO JSWrapper *wrapper; if (AccessCheck::isChrome(target)) { - NS_ASSERTION(!AccessCheck::isChrome(origin), "we shouldn't rewrap from chrome into chrome"); - - // If we waived the X-ray wrapper for this object, wrap it into a - // special wrapper to transitively maintain the X-ray waiver. - if (flags & WAIVE_XRAY_WRAPPER_FLAG) { + if (AccessCheck::isChrome(origin)) { + wrapper = &JSCrossCompartmentWrapper::singleton; + } else if (flags & WAIVE_XRAY_WRAPPER_FLAG) { + // If we waived the X-ray wrapper for this object, wrap it into a + // special wrapper to transitively maintain the X-ray waiver. wrapper = &XrayWrapperWaivedWrapper; } else { // Native objects must be wrapped into an X-ray wrapper. From 5cee296d309ea848c86ceb43eb64ef1c40a349d8 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 126/284] bug 580128 - Propagate whether we're getting or setting to getPropertyDescriptor. r=gal --- js/src/jsapi.cpp | 4 +- js/src/jsobj.cpp | 2 +- js/src/jsproxy.cpp | 41 ++++++++++--------- js/src/jsproxy.h | 13 +++--- js/src/jswrapper.cpp | 26 ++++++------ js/src/jswrapper.h | 8 ++-- .../xpconnect/wrappers/FilteringWrapper.cpp | 5 +-- js/src/xpconnect/wrappers/XrayWrapper.cpp | 10 +++-- js/src/xpconnect/wrappers/XrayWrapper.h | 4 +- 9 files changed, 59 insertions(+), 54 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 6c128c7b9739..13c406bda455 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3593,8 +3593,8 @@ GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (obj2->isProxy()) { JSAutoResolveFlags rf(cx, flags); return own - ? JSProxy::getOwnPropertyDescriptor(cx, obj2, id, desc) - : JSProxy::getPropertyDescriptor(cx, obj2, id, desc); + ? JSProxy::getOwnPropertyDescriptor(cx, obj2, id, false, desc) + : JSProxy::getPropertyDescriptor(cx, obj2, id, false, desc); } if (!obj2->getAttributes(cx, id, &desc->attrs)) return false; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 58e3d128c9a7..4e3949a0a35d 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1676,7 +1676,7 @@ JSBool js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp) { if (obj->isProxy()) { - if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, vp)) + if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, false, vp)) return false; } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 937b07515e7b..251cc1020d59 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -93,7 +93,7 @@ JSProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { JS_ASSERT(OperationInProgress(cx, proxy)); AutoPropertyDescriptorRooter desc(cx); - if (!getPropertyDescriptor(cx, proxy, id, &desc)) + if (!getPropertyDescriptor(cx, proxy, id, false, &desc)) return false; *bp = !!desc.obj; return true; @@ -104,7 +104,7 @@ JSProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { JS_ASSERT(OperationInProgress(cx, proxy)); AutoPropertyDescriptorRooter desc(cx); - if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) + if (!getOwnPropertyDescriptor(cx, proxy, id, false, &desc)) return false; *bp = !!desc.obj; return true; @@ -115,7 +115,7 @@ JSProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, { JS_ASSERT(OperationInProgress(cx, proxy)); AutoPropertyDescriptorRooter desc(cx); - if (!getPropertyDescriptor(cx, proxy, id, &desc)) + if (!getPropertyDescriptor(cx, proxy, id, false, &desc)) return false; if (!desc.obj) { vp->setUndefined(); @@ -142,7 +142,7 @@ JSProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, { JS_ASSERT(OperationInProgress(cx, proxy)); AutoPropertyDescriptorRooter desc(cx); - if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) + if (!getOwnPropertyDescriptor(cx, proxy, id, true, &desc)) return false; /* The control-flow here differs from ::get() because of the fall-through case below. */ if (desc.obj) { @@ -160,7 +160,7 @@ JSProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, desc.value = *vp; return defineProperty(cx, proxy, id, &desc); } - if (!getPropertyDescriptor(cx, proxy, id, &desc)) + if (!getPropertyDescriptor(cx, proxy, id, true, &desc)) return false; if (desc.obj) { if (desc.setter && ((desc.attrs & JSPROP_SETTER) || desc.setter != PropertyStub)) { @@ -200,7 +200,7 @@ JSProxyHandler::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props for (size_t j = 0, len = props.length(); j < len; j++) { JS_ASSERT(i <= j); jsid id = props[j]; - if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) + if (!getOwnPropertyDescriptor(cx, proxy, id, false, &desc)) return false; if (desc.obj && (desc.attrs & JSPROP_ENUMERATE)) props[i++] = id; @@ -435,9 +435,9 @@ class JSScriptedProxyHandler : public JSProxyHandler { virtual ~JSScriptedProxyHandler(); /* ES5 Harmony fundamental proxy traps. */ - virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, + virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc); - virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, + virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc); virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc); @@ -487,7 +487,7 @@ GetProxyHandlerObject(JSContext *cx, JSObject *proxy) } bool -JSScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, +JSScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc) { JSObject *handler = GetProxyHandlerObject(cx, proxy); @@ -499,7 +499,7 @@ JSScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, js } bool -JSScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, +JSScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc) { JSObject *handler = GetProxyHandlerObject(cx, proxy); @@ -665,35 +665,36 @@ class AutoPendingProxyOperation { }; bool -JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc) +JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, + PropertyDescriptor *desc) { AutoPendingProxyOperation pending(cx, proxy); - return proxy->getProxyHandler()->getPropertyDescriptor(cx, proxy, id, desc); + return proxy->getProxyHandler()->getPropertyDescriptor(cx, proxy, id, set, desc); } bool -JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, Value *vp) +JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp) { AutoPendingProxyOperation pending(cx, proxy); AutoPropertyDescriptorRooter desc(cx); - return JSProxy::getPropertyDescriptor(cx, proxy, id, &desc) && + return JSProxy::getPropertyDescriptor(cx, proxy, id, set, &desc) && MakePropertyDescriptorObject(cx, id, &desc, vp); } bool -JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, +JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc) { AutoPendingProxyOperation pending(cx, proxy); - return proxy->getProxyHandler()->getOwnPropertyDescriptor(cx, proxy, id, desc); + return proxy->getProxyHandler()->getOwnPropertyDescriptor(cx, proxy, id, set, desc); } bool -JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, Value *vp) +JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp) { AutoPendingProxyOperation pending(cx, proxy); AutoPropertyDescriptorRooter desc(cx); - return JSProxy::getOwnPropertyDescriptor(cx, proxy, id, &desc) && + return JSProxy::getOwnPropertyDescriptor(cx, proxy, id, set, &desc) && MakePropertyDescriptorObject(cx, id, &desc, vp); } @@ -866,7 +867,7 @@ static JSBool proxy_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) { AutoPropertyDescriptorRooter desc(cx); - if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, &desc)) + if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, false, &desc)) return false; *attrsp = desc.attrs; return true; @@ -877,7 +878,7 @@ proxy_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp) { /* Lookup the current property descriptor so we have setter/getter/value. */ AutoPropertyDescriptorRooter desc(cx); - if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, &desc)) + if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, true, &desc)) return false; desc.attrs = (*attrsp & (~JSPROP_SHORTID)); return JSProxy::defineProperty(cx, obj, id, &desc); diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 00568de77d47..58490ed26150 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -56,9 +56,9 @@ class JSProxyHandler { virtual ~JSProxyHandler(); /* ES5 Harmony fundamental proxy traps. */ - virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, + virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc) = 0; - virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, + virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc) = 0; virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc) = 0; @@ -97,12 +97,13 @@ class JSProxyHandler { class JSProxy { public: /* ES5 Harmony fundamental proxy traps. */ - static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, + static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc); - static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, Value *vp); - static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, + static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp); + static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, PropertyDescriptor *desc); - static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, Value *vp); + static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, + Value *vp); static bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc); static bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v); static bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, js::AutoIdVector &props); diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 173512a01bcc..c4db0f4b3259 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -101,10 +101,10 @@ JSWrapper::~JSWrapper() bool JSWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - PropertyDescriptor *desc) + bool set, PropertyDescriptor *desc) { - GET(JS_GetPropertyDescriptorById(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, - Jsvalify(desc))); + CHECKED(JS_GetPropertyDescriptorById(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, + Jsvalify(desc)), set ? SET : GET); } static bool @@ -118,11 +118,11 @@ GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSP } bool -JSWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, +JSWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, PropertyDescriptor *desc) { - GET(GetOwnPropertyDescriptor(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, - Jsvalify(desc))); + CHECKED(GetOwnPropertyDescriptor(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, + Jsvalify(desc)), set ? SET : GET); } bool @@ -373,20 +373,22 @@ JSCrossCompartmentWrapper::isCrossCompartmentWrapper(JSObject *obj) #define NOTHING (true) bool -JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc) +JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, PropertyDescriptor *desc) { - PIERCE(cx, wrapper, GET, + PIERCE(cx, wrapper, set ? SET : GET, call.destination->wrapId(cx, &id), - JSWrapper::getPropertyDescriptor(cx, wrapper, id, desc), + JSWrapper::getPropertyDescriptor(cx, wrapper, id, set, desc), call.origin->wrap(cx, desc)); } bool -JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc) +JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, PropertyDescriptor *desc) { - PIERCE(cx, wrapper, GET, + PIERCE(cx, wrapper, set ? SET : GET, call.destination->wrapId(cx, &id), - JSWrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc), + JSWrapper::getOwnPropertyDescriptor(cx, wrapper, id, set, desc), call.origin->wrap(cx, desc)); } diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 9c58357da14e..e9ca50f8dc1e 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -61,9 +61,9 @@ class JSWrapper : public js::JSProxyHandler { /* ES5 Harmony fundamental wrapper traps. */ virtual JS_FRIEND_API(bool) getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - js::PropertyDescriptor *desc); + bool set, js::PropertyDescriptor *desc); virtual JS_FRIEND_API(bool) getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - js::PropertyDescriptor *desc); + bool set, js::PropertyDescriptor *desc); virtual JS_FRIEND_API(bool) defineProperty(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc); virtual JS_FRIEND_API(bool) getOwnPropertyNames(JSContext *cx, JSObject *wrapper, @@ -115,9 +115,9 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper { virtual ~JSCrossCompartmentWrapper(); /* ES5 Harmony fundamental wrapper traps. */ - virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, js::PropertyDescriptor *desc); - virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, js::PropertyDescriptor *desc); virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc); diff --git a/js/src/xpconnect/wrappers/FilteringWrapper.cpp b/js/src/xpconnect/wrappers/FilteringWrapper.cpp index d809c92614a0..67bc02761579 100644 --- a/js/src/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/src/xpconnect/wrappers/FilteringWrapper.cpp @@ -72,11 +72,10 @@ Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props) for (size_t n = 0; n < props.length(); ++n) { jsid id = props[n]; Permission perm; - if (perm != PermitObjectAccess && !Policy::check(cx, wrapper, id, JSWrapper::GET, perm)) + if (!Policy::check(cx, wrapper, id, JSWrapper::GET, perm)) return false; // Error - if (perm != DenyAccess) { + if (perm != DenyAccess) props[w++] = id; - } } props.resize(w); return true; diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 1e1a64b47f71..542b947e7f00 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -324,7 +324,8 @@ XrayWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsi template bool -XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc_in) +XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, PropertyDescriptor *desc_in) { JSPropertyDescriptor *desc = Jsvalify(desc_in); @@ -337,7 +338,7 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid desc->value = JSVAL_VOID; return true; } - if (!Base::getPropertyDescriptor(cx, wrapper, id, desc_in)) { + if (!Base::getPropertyDescriptor(cx, wrapper, id, set, desc_in)) { return false; } if (desc->obj) @@ -347,9 +348,10 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid template bool -XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc) +XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, PropertyDescriptor *desc) { - return getPropertyDescriptor(cx, wrapper, id, desc); + return getPropertyDescriptor(cx, wrapper, id, set, desc); } template diff --git a/js/src/xpconnect/wrappers/XrayWrapper.h b/js/src/xpconnect/wrappers/XrayWrapper.h index 5ca6c9016856..ec58ae47e93b 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.h +++ b/js/src/xpconnect/wrappers/XrayWrapper.h @@ -58,9 +58,9 @@ class XrayWrapper : public Base { virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp); virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - js::PropertyDescriptor *desc); + bool set, js::PropertyDescriptor *desc); virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - js::PropertyDescriptor *desc); + bool set, js::PropertyDescriptor *desc); virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); From 667e474ffe5c2942901222b8602e29beb96966cb Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 127/284] bug 603152 - Inner windows principals must remain constant. If we want to change them, change the inner window instead. r=bz --- docshell/base/nsDocShell.cpp | 7 +++++++ docshell/base/nsDocShell.h | 4 +++- docshell/base/nsIDocShell.idl | 9 +++++++++ dom/base/nsGlobalWindow.cpp | 12 +++++------- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index fda34a671723..f79cfa0f073f 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -852,6 +852,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell) NS_INTERFACE_MAP_ENTRY(nsIWebShellServices) NS_INTERFACE_MAP_ENTRY(nsILinkHandler) NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands) + NS_INTERFACE_MAP_ENTRY(nsIDocShell_MOZILLA_2_0_BRANCH) NS_INTERFACE_MAP_END_INHERITING(nsDocLoader) ///***************************************************************************** @@ -6463,6 +6464,12 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal, return rv; } +NS_IMETHODIMP +nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal *aPrincipal) +{ + return CreateAboutBlankContentViewer(aPrincipal, nsnull); +} + PRBool nsDocShell::CanSavePresentation(PRUint32 aLoadType, nsIRequest *aNewRequest, diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 2e95717263ac..a6c33f149c16 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -186,7 +186,8 @@ class nsDocShell : public nsDocLoader, public nsILoadContext, public nsIWebShellServices, public nsILinkHandler, - public nsIClipboardCommands + public nsIClipboardCommands, + public nsIDocShell_MOZILLA_2_0_BRANCH { friend class nsDSURIContentListener; @@ -201,6 +202,7 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOCSHELL + NS_DECL_NSIDOCSHELL_MOZILLA_2_0_BRANCH NS_DECL_NSIDOCSHELLTREEITEM NS_DECL_NSIDOCSHELLTREENODE NS_DECL_NSIDOCSHELLHISTORY diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index 1cb5ff4e7d91..788be5c5ef2a 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -535,3 +535,12 @@ interface nsIDocShell : nsISupports */ readonly attribute unsigned long long historyID; }; + +[uuid(5f7a2184-31b6-4d67-9c75-0c17477766e2)] +interface nsIDocShell_MOZILLA_2_0_BRANCH : nsISupports { + /** + * Create a new about:blank document and content viewer. + * @param aPrincipal the principal to use for the new document. + */ + void createAboutBlankContentViewer(in nsIPrincipal aPrincipal); +}; diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 79762bedb98f..1aade5d2b5ba 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1469,7 +1469,7 @@ nsGlobalWindow::SetOpenerScriptPrincipal(nsIPrincipal* aPrincipal) // Do NOT set mOpenerScriptPrincipal in this case, just to be safe. return; } - + #ifdef DEBUG // We better have an about:blank document loaded at this point. Otherwise, // something is really weird. @@ -1479,13 +1479,11 @@ nsGlobalWindow::SetOpenerScriptPrincipal(nsIPrincipal* aPrincipal) IsAboutBlank(mDoc->GetDocumentURI()), "Unexpected original document"); #endif - - // Set the opener principal on our document; given the above check, this - // is safe. - mDoc->SetPrincipal(aPrincipal); + + nsCOMPtr ds(do_QueryInterface(GetDocShell())); + ds->CreateAboutBlankContentViewer(aPrincipal); + mDoc->SetIsInitialDocument(PR_TRUE); } - - mOpenerScriptPrincipal = aPrincipal; } nsIPrincipal* From e9e8be93703ab750666dee83bf17997ea6c192fa Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 128/284] bug 580128 - Don't allow fast getting of nsGlobalWindow's wrapper cache. r=peterv --- js/src/xpconnect/src/xpcquickstubs.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/js/src/xpconnect/src/xpcquickstubs.h b/js/src/xpconnect/src/xpcquickstubs.h index 2d76f85da15d..55dc4ff353dd 100644 --- a/js/src/xpconnect/src/xpcquickstubs.h +++ b/js/src/xpconnect/src/xpcquickstubs.h @@ -627,6 +627,12 @@ xpc_qsGetWrapperCache(nsWrapperCache *cache) return cache; } +// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't +// try to use it without fixing that first. +class nsGlobalWindow; +inline nsWrapperCache* +xpc_qsGetWrapperCache(nsGlobalWindow *not_allowed); + inline nsWrapperCache* xpc_qsGetWrapperCache(void *p) { From da04c919987bcb22dc09ec045721153d19a39228 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 129/284] bug 580128 - Set nsGlobalChromeWindows' principals to chrome even before they know it. r=jst --- dom/base/nsJSEnvironment.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 77947c057835..95376c9fe4b5 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2460,11 +2460,18 @@ nsJSContext::CreateNativeGlobalForInner( nsIXPConnect *xpc = nsContentUtils::XPConnect(); PRUint32 flags = aIsChrome? nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT : 0; nsCOMPtr jsholder; + + nsCOMPtr systemPrincipal; + if (aIsChrome) { + nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager(); + ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal)); + } + nsresult rv = xpc-> InitClassesWithNewWrappedGlobal(mContext, aNewInner, NS_GET_IID(nsISupports), - aPrincipal, EmptyCString(), - flags, + aIsChrome ? systemPrincipal.get() : aPrincipal, + EmptyCString(), flags, getter_AddRefs(jsholder)); if (NS_FAILED(rv)) return rv; From 3dab00be72af33764c0e67390c6f0e9f023473ac Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 130/284] bug 580128 - Make compartment warnings real. r=jst --- js/src/jscntxtinlines.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index c4daa0fcd33a..4b5a20d4d104 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -508,10 +508,8 @@ class CompartmentChecker * compartment mismatches. */ static void fail(JSCompartment *c1, JSCompartment *c2) { -#ifdef DEBUG_jorendorff printf("*** Compartment mismatch %p vs. %p\n", (void *) c1, (void *) c2); - // JS_NOT_REACHED("compartment mismatch"); -#endif + JS_NOT_REACHED("compartment mismatched"); } void check(JSCompartment *c) { From 2549d7dc99fd3bbead79772f05b864b01b51f8fb Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 17 Sep 2010 14:54:41 -0700 Subject: [PATCH 131/284] bug 580128 - Make sure nsDOMClassInfo uses the right compartment for objects that it defines on wrappers (both security and not). r=jst --- dom/base/nsDOMClassInfo.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 970aa8bdbd26..184e970f0657 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -5055,9 +5055,8 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, JS_FALSE); - if (!::JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, 0)) { - nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_UNEXPECTED); - + if (!JS_WrapValue(cx, &v) || + !JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, 0)) { return JS_FALSE; } @@ -6601,7 +6600,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSAutoRequest ar(cx); - PRBool ok = ::JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, 0); + PRBool ok = JS_WrapValue(cx, &v) && + JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, 0); if (!ok) { return NS_ERROR_FAILURE; } @@ -6707,8 +6707,9 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); - JSBool ok = ::JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, - JSPROP_PERMANENT | JSPROP_ENUMERATE); + JSBool ok = JS_WrapValue(cx, &v) && + JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, + JSPROP_PERMANENT | JSPROP_ENUMERATE); if (!ok) { return NS_ERROR_FAILURE; @@ -6761,8 +6762,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); - JSAutoRequest ar(cx); - if (!::JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE)) { From c48532f70519e942d3b93a5fbcce3d91780739b6 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Mon, 20 Sep 2010 14:47:15 -0700 Subject: [PATCH 132/284] bug 580128 - .wrappedJSObject returns a wrapper that pushes principals. r=gal --- js/src/xpconnect/wrappers/WrapperFactory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index cce3408c7d71..44fd79f7a7a7 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -62,7 +62,7 @@ JSWrapper WaiveXrayWrapperWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); // chrome, we wrap them into a special cross-compartment wrapper // that transitively extends the waiver to all properties we get // off it. -JSCrossCompartmentWrapper XrayWrapperWaivedWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); +CrossOriginWrapper XrayWrapperWaivedWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); JSObject * WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, @@ -137,7 +137,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO CrossOriginAccessiblePropertiesOnly>::singleton; } else { typedef XrayWrapper Xray; - wrapper = &FilteringWrapper, + wrapper = &FilteringWrapper::singleton; obj = Xray::createHolder(cx, parent, obj); } From f120eb571337eb58eebd77f8920b6df13593e5ec Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Mon, 20 Sep 2010 14:48:01 -0700 Subject: [PATCH 133/284] bug 580128 - Rewrite XrayWrapper so the wrapped object is in the wrapped object slot and the holder goes in an extra slot. r=gal --- js/src/jsobj.h | 2 + js/src/jsproxy.cpp | 7 +- js/src/jsproxy.h | 41 ++- js/src/jswrapper.cpp | 14 +- js/src/jswrapper.h | 7 + js/src/xpconnect/src/XPCWrapper.cpp | 16 +- js/src/xpconnect/src/xpcwrappednative.cpp | 1 + js/src/xpconnect/wrappers/AccessCheck.cpp | 3 +- .../xpconnect/wrappers/FilteringWrapper.cpp | 3 +- js/src/xpconnect/wrappers/WrapperFactory.cpp | 34 +-- js/src/xpconnect/wrappers/XrayWrapper.cpp | 237 +++++++++++------- js/src/xpconnect/wrappers/XrayWrapper.h | 47 +++- 12 files changed, 264 insertions(+), 148 deletions(-) diff --git a/js/src/jsobj.h b/js/src/jsobj.h index c76af07c3ed3..4155e411c599 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -954,6 +954,8 @@ struct JSObject : js::gc::Cell { inline js::JSProxyHandler *getProxyHandler() const; inline const js::Value &getProxyPrivate() const; inline void setProxyPrivate(const js::Value &priv); + inline const js::Value &getProxyExtra() const; + inline void setProxyExtra(const js::Value &extra); /* * With object-specific getters and setters. diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 251cc1020d59..2daa9587acb7 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -905,6 +905,7 @@ proxy_TraceObject(JSTracer *trc, JSObject *obj) obj->getProxyHandler()->trace(trc, obj); MarkValue(trc, obj->getProxyPrivate(), "private"); + MarkValue(trc, obj->getProxyExtra(), "extra"); if (obj->isFunctionProxy()) { MarkValue(trc, GetCall(obj), "call"); MarkValue(trc, GetConstruct(obj), "construct"); @@ -921,7 +922,7 @@ proxy_Finalize(JSContext *cx, JSObject *obj) JS_FRIEND_API(Class) ObjectProxyClass = { "Proxy", - Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(2), + Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(3), PropertyStub, /* addProperty */ PropertyStub, /* delProperty */ PropertyStub, /* getProperty */ @@ -957,7 +958,7 @@ JS_FRIEND_API(Class) ObjectProxyClass = { JS_FRIEND_API(Class) OuterWindowProxyClass = { "Proxy", - Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(2), + Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(3), PropertyStub, /* addProperty */ PropertyStub, /* delProperty */ PropertyStub, /* getProperty */ @@ -1025,7 +1026,7 @@ proxy_TypeOf_fun(JSContext *cx, JSObject *obj) JS_FRIEND_API(Class) FunctionProxyClass = { "Proxy", - Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(4), + Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(5), PropertyStub, /* addProperty */ PropertyStub, /* delProperty */ PropertyStub, /* getProperty */ diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 58490ed26150..53f447576e34 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -49,7 +49,7 @@ namespace js { /* Base class for all C++ proxy handlers. */ -class JSProxyHandler { +class JS_FRIEND_API(JSProxyHandler) { void *mFamily; public: explicit JSProxyHandler(void *family); @@ -68,21 +68,21 @@ class JSProxyHandler { virtual bool fix(JSContext *cx, JSObject *proxy, Value *vp) = 0; /* ES5 Harmony derived proxy traps. */ - virtual JS_FRIEND_API(bool) has(JSContext *cx, JSObject *proxy, jsid id, bool *bp); - virtual JS_FRIEND_API(bool) hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp); - virtual JS_FRIEND_API(bool) get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, js::Value *vp); - virtual JS_FRIEND_API(bool) set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, js::Value *vp); - virtual JS_FRIEND_API(bool) enumerateOwn(JSContext *cx, JSObject *proxy, js::AutoIdVector &props); - virtual JS_FRIEND_API(bool) iterate(JSContext *cx, JSObject *proxy, uintN flags, js::Value *vp); + virtual bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp); + virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp); + virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, js::Value *vp); + virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, js::Value *vp); + virtual bool enumerateOwn(JSContext *cx, JSObject *proxy, js::AutoIdVector &props); + virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, js::Value *vp); /* Spidermonkey extensions. */ - virtual JS_FRIEND_API(bool) call(JSContext *cx, JSObject *proxy, uintN argc, js::Value *vp); - virtual JS_FRIEND_API(bool) construct(JSContext *cx, JSObject *proxy, + virtual bool call(JSContext *cx, JSObject *proxy, uintN argc, js::Value *vp); + virtual bool construct(JSContext *cx, JSObject *proxy, uintN argc, js::Value *argv, js::Value *rval); - virtual JS_FRIEND_API(JSString *) obj_toString(JSContext *cx, JSObject *proxy); - virtual JS_FRIEND_API(JSString *) fun_toString(JSContext *cx, JSObject *proxy, uintN indent); - virtual JS_FRIEND_API(void) finalize(JSContext *cx, JSObject *proxy); - virtual JS_FRIEND_API(void) trace(JSTracer *trc, JSObject *proxy); + virtual JSString *obj_toString(JSContext *cx, JSObject *proxy); + virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent); + virtual void finalize(JSContext *cx, JSObject *proxy); + virtual void trace(JSTracer *trc, JSObject *proxy); virtual bool isOuterWindow() { return false; @@ -129,6 +129,7 @@ class JSProxy { /* Shared between object and function proxies. */ const uint32 JSSLOT_PROXY_HANDLER = JSSLOT_PRIVATE + 0; const uint32 JSSLOT_PROXY_PRIVATE = JSSLOT_PRIVATE + 1; +const uint32 JSSLOT_PROXY_EXTRA = JSSLOT_PRIVATE + 2; /* Function proxies only. */ const uint32 JSSLOT_PROXY_CALL = JSSLOT_PRIVATE + 2; const uint32 JSSLOT_PROXY_CONSTRUCT = JSSLOT_PRIVATE + 3; @@ -180,6 +181,20 @@ JSObject::setProxyPrivate(const js::Value &priv) setSlot(js::JSSLOT_PROXY_PRIVATE, priv); } +inline const js::Value & +JSObject::getProxyExtra() const +{ + JS_ASSERT(isProxy()); + return getSlot(js::JSSLOT_PROXY_EXTRA); +} + +inline void +JSObject::setProxyExtra(const js::Value &extra) +{ + JS_ASSERT(isProxy()); + setSlot(js::JSSLOT_PROXY_EXTRA, extra); +} + namespace js { JS_FRIEND_API(JSObject *) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index c4db0f4b3259..67d4c35d414f 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -57,7 +57,13 @@ using namespace js; using namespace js::gc; -static int sWrapperFamily = 0; +static int sWrapperFamily; + +void * +JSWrapper::getWrapperFamily() +{ + return &sWrapperFamily; +} bool JSObject::isWrapper() const @@ -346,6 +352,10 @@ AutoCompartment::leave() /* Cross compartment wrappers. */ +JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(void *family) : JSWrapper(0) +{ +} + JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags) : JSWrapper(flags) { } @@ -624,4 +634,4 @@ JSCrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN return str; } -JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton(0); +JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton(0u); diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index e9ca50f8dc1e..6e1d75d8d0f0 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -105,10 +105,17 @@ class JSWrapper : public js::JSProxyHandler { static inline JSObject *wrappedObject(JSObject *wrapper) { return wrapper->getProxyPrivate().toObjectOrNull(); } + + static JS_FRIEND_API(void *) getWrapperFamily(); }; /* Base class for all cross compartment wrapper handlers. */ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper { + protected: + // XXX Hack to let Xray wrappers derive from either cross compartment + // wrappers or JSProxyHandlers. + JSCrossCompartmentWrapper(void *family); + public: JSCrossCompartmentWrapper(uintN flags); diff --git a/js/src/xpconnect/src/XPCWrapper.cpp b/js/src/xpconnect/src/XPCWrapper.cpp index 58514deca667..a359916e1821 100644 --- a/js/src/xpconnect/src/XPCWrapper.cpp +++ b/js/src/xpconnect/src/XPCWrapper.cpp @@ -68,13 +68,7 @@ Unwrap(JSContext *cx, JSObject *wrapper) // XXX Security check! } - JSObject *wrappedObj = wrapper->unwrap(); - if (wrappedObj->getJSClass() == &xpc::HolderClass) { - typedef xpc::XrayWrapper Xray; - wrappedObj = Xray::unwrapHolder(cx, wrappedObj); - } - - return wrappedObj; + return wrapper->unwrap(); } js::Class *clasp = wrapper->getClass(); @@ -264,13 +258,7 @@ JSObject * UnsafeUnwrapSecurityWrapper(JSContext *cx, JSObject *obj) { if (obj->isProxy()) { - JSObject *wrappedObj = obj->unwrap(); - if (wrappedObj->getJSClass() == &xpc::HolderClass) { - typedef xpc::XrayWrapper Xray; - wrappedObj = Xray::unwrapHolder(cx, wrappedObj); - } - - return wrappedObj; + return obj->unwrap(); } if (IsSecurityWrapper(obj)) { diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 26cdf2b106d9..010a6b207fae 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -50,6 +50,7 @@ #include "jstl.h" #include "nsINode.h" #include "xpcquickstubs.h" +#include "jsproxy.h" /***************************************************************************/ diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index d7b9047cdb97..26bb137b5fcb 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -176,10 +176,9 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid const char *name; js::Class *clasp = obj->getClass(); + NS_ASSERTION(Jsvalify(clasp) != &HolderClass, "shouldn't have a holder here"); if (clasp->ext.innerObject) name = "Window"; - else if (Jsvalify(clasp) == &HolderClass) - name = XrayWrapper::unwrapHolder(cx, obj)->getClass()->name; else name = clasp->name; diff --git a/js/src/xpconnect/wrappers/FilteringWrapper.cpp b/js/src/xpconnect/wrappers/FilteringWrapper.cpp index 67bc02761579..ec977bee2f2a 100644 --- a/js/src/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/src/xpconnect/wrappers/FilteringWrapper.cpp @@ -150,7 +150,8 @@ FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, #define SOW FilteringWrapper #define COW FilteringWrapper -#define XOW FilteringWrapper, CrossOriginAccessiblePropertiesOnly> +#define XOW FilteringWrapper, \ + CrossOriginAccessiblePropertiesOnly> #define NNXOW FilteringWrapper template<> SOW SOW::singleton(0); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 44fd79f7a7a7..1beb628ec813 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -38,6 +38,7 @@ * ***** END LICENSE BLOCK ***** */ #include "jsobj.h" +#include "jsvalue.h" #include "WrapperFactory.h" #include "CrossOriginWrapper.h" @@ -70,6 +71,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO { NS_ASSERTION(!obj->isWrapper() || obj->getClass()->ext.innerObject, "wrapped object passed to rewrap"); + NS_ASSERTION(JS_GET_CLASS(cx, obj) != &HolderClass, "trying to wrap a holder"); if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj)) return nsnull; @@ -78,19 +80,9 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO if (!obj) return nsnull; - // Ugly hack to avoid wrapping holder objects instead of the actual - // underlying wrapped native JS object. - if (JS_GET_CLASS(cx, obj) == &HolderClass) { - obj = XrayWrapper::unwrapHolder(cx, obj); - OBJ_TO_OUTER_OBJECT(cx, obj); - if (!JS_WrapObject(cx, &obj)) - return nsnull; - - return obj; - } - JSCompartment *origin = obj->getCompartment(cx); JSCompartment *target = cx->compartment; + JSObject *xrayHolder = nsnull; JSWrapper *wrapper; if (AccessCheck::isChrome(target)) { @@ -104,10 +96,11 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO // Native objects must be wrapped into an X-ray wrapper. if (!obj->getGlobal()->isSystem() && (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject)) { - typedef XrayWrapper Xray; - + typedef XrayWrapper Xray; wrapper = &Xray::singleton; - obj = Xray::createHolder(cx, parent, obj); + xrayHolder = Xray::createHolder(cx, obj, parent); + if (!xrayHolder) + return nsnull; } else { wrapper = &JSCrossCompartmentWrapper::singleton; } @@ -136,13 +129,20 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO wrapper = &FilteringWrapper::singleton; } else { - typedef XrayWrapper Xray; + typedef XrayWrapper Xray; wrapper = &FilteringWrapper::singleton; - obj = Xray::createHolder(cx, parent, obj); + xrayHolder = Xray::createHolder(cx, obj, parent); + if (!xrayHolder) + return nsnull; } } - return JSWrapper::New(cx, obj, wrappedProto, NULL, wrapper); + + JSObject *wrapperObj = JSWrapper::New(cx, obj, wrappedProto, NULL, wrapper); + if (!wrapperObj || !xrayHolder) + return wrapperObj; + wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder)); + return wrapperObj; } } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 542b947e7f00..4836f51954e5 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -72,16 +72,6 @@ JSClass HolderClass = { NULL, NULL, NULL, NULL }; -template -XrayWrapper::XrayWrapper(uintN flags) : Base(flags) -{ -} - -template -XrayWrapper::~XrayWrapper() -{ -} - static XPCWrappedNative * GetWrappedNative(JSObject *obj) { @@ -104,21 +94,8 @@ GetWrappedNativeObjectFromHolder(JSContext *cx, JSObject *holder) static JSBool holder_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { - if (obj->isWrapper()) { -#ifdef DEBUG - { - typedef FilteringWrapper, - CrossOriginAccessiblePropertiesOnly> - FilteringXRay; - JSProxyHandler *handler = obj->getProxyHandler(); - NS_ASSERTION(handler == &XrayWrapper::singleton || - handler == &XrayWrapper::singleton || - handler == &FilteringXRay::singleton, - "bad object"); - } -#endif - obj = obj->unwrap(); - } + if (obj->isWrapper()) + obj = &obj->getProxyExtra().toObject(); JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, obj); XPCWrappedNative *wn = GetWrappedNative(wnObject); @@ -154,7 +131,8 @@ holder_set(JSContext *cx, JSObject *obj, jsid id, jsval *vp) } static bool -ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, JSPropertyDescriptor *desc) +ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, + JSPropertyDescriptor *desc) { desc->obj = NULL; @@ -208,6 +186,7 @@ ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, JSProp desc->shortid = NULL; desc->value = JSVAL_VOID; + jsval fval = JSVAL_VOID; if (member->IsConstant()) { if (!member->GetConstantValue(ccx, iface, &desc->value)) { JS_ReportError(cx, "Failed to convert constant native property to JS value"); @@ -215,17 +194,14 @@ ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, JSProp } } else if (member->IsAttribute()) { // This is a getter/setter. Clone a function for it. - jsval fval; if (!member->NewFunctionObject(ccx, iface, wnObject, &fval)) { JS_ReportError(cx, "Failed to clone function object for native getter/setter"); return false; } - desc->getter = CastAsJSPropertyOp(JSVAL_TO_OBJECT(fval)); + desc->attrs |= JSPROP_GETTER; - if (member->IsWritableAttribute()) { - desc->setter = desc->getter;; + if (member->IsWritableAttribute()) desc->attrs |= JSPROP_SETTER; - } // Make the property shared on the holder so no slot is allocated // for it. This avoids keeping garbage alive through that slot. @@ -244,6 +220,18 @@ ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, JSProp desc->getter = desc->setter = JS_PropertyStub; } + JSAutoEnterCompartment ac; + if (!ac.enter(cx, holder)) + return false; + + if (!JS_WrapValue(cx, &desc->value) || !JS_WrapValue(cx, &fval)) + return false; + + if (desc->attrs & JSPROP_GETTER) + desc->getter = CastAsJSPropertyOp(JSVAL_TO_OBJECT(fval)); + if (desc->attrs & JSPROP_SETTER) + desc->setter = desc->getter; + // Define the property. return JS_DefinePropertyById(cx, holder, id, desc->value, desc->getter, desc->setter, desc->attrs); @@ -268,26 +256,13 @@ holder_enumerate(JSContext *cx, JSObject *holder) return true; } -struct NativePropertiesOnly : public Policy { - static bool check(JSContext *cx, JSObject *obj, jsid id, bool set, Permission &perm); -}; - extern JSCrossCompartmentWrapper XrayWrapperWaivedWrapper; static JSBool wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) { - if (holder->isWrapper()) { -#ifdef DEBUG - { - JSProxyHandler *handler = holder->getProxyHandler(); - NS_ASSERTION(handler == &XrayWrapper::singleton || - handler == &XrayWrapper::singleton, - "bad object"); - } -#endif - holder = holder->unwrap(); - } + if (holder->isWrapper()) + holder = &holder->getProxyExtra().toObject(); // If the caller intentionally waives the X-ray wrapper we usually // apply for wrapped natives, use a special wrapper to make sure the @@ -306,26 +281,20 @@ wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) return true; } -template -bool -XrayWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, - js::Value *vp) +template +XrayWrapper::XrayWrapper(int flags) : Base(JSWrapper::getWrapperFamily()) { - return JSProxyHandler::get(cx, wrapper, receiver, id, vp); } -template -bool -XrayWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, - js::Value *vp) +template +XrayWrapper::~XrayWrapper() { - return JSProxyHandler::set(cx, wrapper, receiver, id, vp); } -template +template bool -XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - bool set, PropertyDescriptor *desc_in) +XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, PropertyDescriptor *desc_in) { JSPropertyDescriptor *desc = Jsvalify(desc_in); @@ -338,43 +307,123 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid desc->value = JSVAL_VOID; return true; } - if (!Base::getPropertyDescriptor(cx, wrapper, id, set, desc_in)) { + + void *priv; + if (!Policy::enter(cx, wrapper, &id, set ? JSWrapper::SET : JSWrapper::GET, &priv)) return false; - } - if (desc->obj) - return true; - return ResolveNativeProperty(cx, Base::wrappedObject(wrapper), id, false, desc); + + JSObject *holder = &wrapper->getProxyExtra().toObject(); + bool ok = ResolveNativeProperty(cx, holder, id, false, desc); + + // TODO expandos + + Policy::leave(cx, wrapper, priv); + return ok; } -template +template bool -XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - bool set, PropertyDescriptor *desc) +XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, PropertyDescriptor *desc) { return getPropertyDescriptor(cx, wrapper, id, set, desc); } -template +template bool -XrayWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, + js::PropertyDescriptor *desc) { - // Use the default implementation, which forwards to getPropertyDescriptor. + // XXX When am I called? Implement me! + return true; +} + +template +bool +XrayWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, + js::AutoIdVector &props) +{ + // XXX implement me. + return true; +} + +template +bool +XrayWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +{ + // XXX implement me. + return true; +} + +template +bool +XrayWrapper::enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props) +{ + // XXX implement me. + return true; +} + +template +bool +XrayWrapper::fix(JSContext *cx, JSObject *proxy, js::Value *vp) +{ + vp->setUndefined(); + return true; +} + +template +bool +XrayWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp) +{ + // Skip our Base if it isn't already JSProxyHandler. + return JSProxyHandler::get(cx, wrapper, receiver, id, vp); +} + +template +bool +XrayWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp) +{ + // Skip our Base if it isn't already JSProxyHandler. + return JSProxyHandler::set(cx, wrapper, receiver, id, vp); +} + +template +bool +XrayWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +{ + // Skip our Base if it isn't already JSProxyHandler. return JSProxyHandler::has(cx, wrapper, id, bp); } -template +template bool -XrayWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) +XrayWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { - // Use the default implementation, which forwards to getOwnPropertyDescriptor. + // Skip our Base if it isn't already JSProxyHandler. return JSProxyHandler::hasOwn(cx, wrapper, id, bp); } -template +template +bool +XrayWrapper::enumerateOwn(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props) +{ + // Skip our Base if it isn't already JSProxyHandler. + return JSProxyHandler::enumerateOwn(cx, wrapper, props); +} + +template +bool +XrayWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, js::Value *vp) +{ + // Skip our Base if it isn't already JSProxyHandler. + return JSProxyHandler::iterate(cx, wrapper, flags, vp); +} + +template JSObject * -XrayWrapper::createHolder(JSContext *cx, - JSObject *parent, - JSObject *wrappedNative) +XrayWrapper::createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent) { JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, nsnull, parent); if (!holder) @@ -384,21 +433,33 @@ XrayWrapper::createHolder(JSContext *cx, return holder; } -template -JSObject * -XrayWrapper::unwrapHolder(JSContext *cx, JSObject *holder) +bool +CrossCompartmentXray::enter(JSContext *cx, JSObject *wrapper, jsid *idp, + JSWrapper::Action act, void **priv) { - NS_ASSERTION(holder->getJSClass() == &HolderClass, "bad holder"); - return GetWrappedNativeObjectFromHolder(cx, holder); + JSObject *target = wrapper->unwrap(); + JSCrossCompartmentCall *call = JS_EnterCrossCompartmentCall(cx, target); + if (!call) + return false; + + *priv = call; + // XXX wrap id + return true; } -#define SJOW XrayWrapper -#define XOSJOW XrayWrapper +void +CrossCompartmentXray::leave(JSContext *cx, JSObject *wrapper, void *priv) +{ + JS_LeaveCrossCompartmentCall(static_cast(priv)); +} -template <> SJOW SJOW::singleton(0); -template <> XOSJOW XOSJOW::singleton(0); +#define XPCNW XrayWrapper +#define SCNW XrayWrapper -template class SJOW; -template class XOSJOW; +template <> XPCNW XPCNW::singleton(0); +template <> SCNW SCNW::singleton(0); + +template class XPCNW; +template class SCNW; } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.h b/js/src/xpconnect/wrappers/XrayWrapper.h index ec58ae47e93b..dfc7d3084804 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.h +++ b/js/src/xpconnect/wrappers/XrayWrapper.h @@ -42,32 +42,63 @@ // Xray wrappers re-resolve the original native properties on the native // object and always directly access to those properties. +// Because they work so differently from the rest of the wrapper hierarchy, +// we pull them out of the JSWrapper inheritance hierarchy and create a +// little world around them. namespace xpc { extern JSClass HolderClass; -template +// NB: Base *must* derive from JSProxyHandler +template class XrayWrapper : public Base { public: - XrayWrapper(uintN flags); + XrayWrapper(int flags); virtual ~XrayWrapper(); - virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, - js::Value *vp); - virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, - js::Value *vp); + /* Fundamental proxy traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, js::PropertyDescriptor *desc); virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, js::PropertyDescriptor *desc); + virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, + js::PropertyDescriptor *desc); + virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, + js::AutoIdVector &props); + virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); + virtual bool enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); + virtual bool fix(JSContext *cx, JSObject *proxy, js::Value *vp); + + /* Derived proxy traps. */ + virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp); + virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp); virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); + virtual bool enumerateOwn(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); + virtual bool iterate(JSContext *cx, JSObject *wrapper, uintN flags, js::Value *vp); - static JSObject *createHolder(JSContext *cx, JSObject *parent, JSObject *wrappedNative); - static JSObject *unwrapHolder(JSContext *cx, JSObject *holder); + static JSObject *createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent); static XrayWrapper singleton; }; +class CrossCompartmentXray { + public: + static bool enter(JSContext *cx, JSObject *wrapper, jsid *idp, + JSWrapper::Action act, void **priv); + static void leave(JSContext *cx, JSObject *wrapper, void *priv); +}; + +class SameCompartmentXray { + public: + static bool enter(JSContext *, JSObject *, jsid *, JSWrapper::Action, void **) { + return true; + } + static void leave(JSContext *cx, JSObject *wrapper, void *priv) { + } +}; + } From 05101cc6dc2e5669156cee1f2efa9e7e6097d06d Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Wed, 22 Sep 2010 16:35:10 -0700 Subject: [PATCH 134/284] bug 580128 - Make xpcconvert trust cx->compartment when called from JS. r=peterv --- js/src/xpconnect/src/xpcconvert.cpp | 82 +++++++++++------------------ 1 file changed, 31 insertions(+), 51 deletions(-) diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 04f8f1d2786f..530495c1ffc4 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -1079,46 +1079,32 @@ CreateHolderIfNeeded(XPCCallContext& ccx, JSObject* obj, jsval* d, return JS_TRUE; } -static void -ComputeWrapperInfo(const XPCCallContext &ccx, - JSObject **callee, - JSScript **script) +static PRBool +ComputeWrapperInfo(const XPCCallContext &ccx, XPCWrappedNativeScope *scope, + JSObject **callee) { - *callee = nsnull; - *script = nsnull; - if(ccx.GetXPCContext()->CallerTypeIsJavaScript()) { - // Called from JS. We're going to hand the resulting - // JSObject to said JS, so look for the script we want on - // the stack. - JSStackFrame* fp = JS_GetScriptedCaller(ccx, NULL); - if(fp) - { - *script = fp->maybeScript(); - *callee = fp->isFunctionFrame() - ? &fp->callee() - : &fp->scopeChain(); - } + // Called from JS. We're going to hand the resulting JSObject + // to said JS. The correct compartment must already be pushed + // on cx, so return true and don't give back a callee. + *callee = nsnull; + return PR_TRUE; } - else if(ccx.GetXPCContext()->CallerTypeIsNative()) + + if(ccx.GetXPCContext()->CallerTypeIsNative()) { + // Calling from JS -> C++, return false to indicate that we need + // to push a compartment and return the callee. *callee = ccx.GetCallee(); - if(*callee && JS_ObjectIsFunction(ccx, *callee)) + if(!*callee || !JS_ObjectIsFunction(ccx, *callee)) { - // Called from c++, and calling out to |callee|, which is a JS - // function object. Look for the script for this function. - JSFunction* fun = (JSFunction*) xpc_GetJSPrivate(*callee); - NS_ASSERTION(fun, "Must have JSFunction for a Function object"); - *script = JS_GetFunctionScript(ccx, fun); - } - else - { - // Else we don't know whom we're calling, so don't - // create XPCNativeWrappers. - *callee = nsnull; + *callee = scope->GetGlobalJSObject(); + OBJ_TO_INNER_OBJECT(ccx, *callee); } } + + return PR_FALSE; } /***************************************************************************/ @@ -1171,7 +1157,6 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, nsWrapperCache *cache = aHelper.GetWrapperCache(); JSObject *callee; - JSScript *script; PRBool tryConstructSlimWrapper = PR_FALSE; JSObject *flat; @@ -1187,17 +1172,14 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, if(!flat) flat = ConstructProxyObject(ccx, aHelper, xpcscope); - ComputeWrapperInfo(ccx, &callee, &script); - if(!callee) + JSAutoEnterCompartment ac; + if(!ComputeWrapperInfo(ccx, xpcscope, &callee) && + !ac.enter(ccx, callee)) { - callee = xpcscope->GetGlobalJSObject(); - OBJ_TO_INNER_OBJECT(ccx, callee); - if(!callee) - return JS_FALSE; + return JS_FALSE; } - JSAutoEnterCompartment ac; - if(!ac.enter(ccx, callee) || !JS_WrapObject(ccx, &flat)) + if(!JS_WrapObject(ccx, &flat)) return JS_FALSE; return CreateHolderIfNeeded(ccx, flat, d, dest); @@ -1353,31 +1335,29 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, if(!ccx.IsValid()) return JS_FALSE; - ComputeWrapperInfo(ccx, &callee, &script); - if(!callee) + JSAutoEnterCompartment ac; + if(!ComputeWrapperInfo(ccx, xpcscope, &callee) && + !ac.enter(ccx, callee)) { - callee = xpcscope->GetGlobalJSObject(); - OBJ_TO_INNER_OBJECT(cx, callee); - if(!callee) - return JS_FALSE; + return JS_FALSE; } - JSAutoEnterCompartment ac; - if(!ac.enter(ccx, callee) || !JS_WrapObject(ccx, &flat)) + JSObject *original = flat; + if(!JS_WrapObject(ccx, &flat)) return JS_FALSE; - *d = OBJECT_TO_JSVAL(flat); + if(dest) { // The strongWrapper still holds the original flat object. - if(OBJECT_TO_JSVAL(flat) == *d) + if(flat == original) { *dest = strongWrapper.forget().get(); } else { nsRefPtr objHolder = - XPCJSObjectHolder::newHolder(ccx, JSVAL_TO_OBJECT(*d)); + XPCJSObjectHolder::newHolder(ccx, flat); if(!objHolder) return JS_FALSE; From 275fe3585d4509bc07771085274f4c2588470d19 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Wed, 22 Sep 2010 17:34:20 -0700 Subject: [PATCH 135/284] Bug 580128 - Fix compartment warnings by entering. r=gal/peterv/jst/mrbkap/jorendorff (and written by all of us too). --- content/xbl/src/nsXBLBinding.cpp | 10 ++++++ content/xbl/src/nsXBLProtoImplMethod.cpp | 6 ++++ dom/base/nsDOMClassInfo.cpp | 28 +++++++++++---- dom/base/nsGlobalWindow.cpp | 5 +++ dom/base/nsJSEnvironment.cpp | 35 +++++++++++++++++-- dom/src/json/nsJSON.cpp | 7 ++++ js/src/jsobj.h | 2 +- js/src/xpconnect/src/nsXPConnect.cpp | 14 ++++---- js/src/xpconnect/src/xpccomponents.cpp | 32 +++++++++-------- js/src/xpconnect/src/xpcconvert.cpp | 9 +++++ js/src/xpconnect/src/xpcdebug.cpp | 3 ++ js/src/xpconnect/src/xpcwrappedjsclass.cpp | 8 +++-- js/src/xpconnect/src/xpcwrappednative.cpp | 25 +++++++++---- .../xpconnect/src/xpcwrappednativescope.cpp | 3 ++ js/src/xpconnect/wrappers/AccessCheck.cpp | 3 +- js/src/xpconnect/wrappers/XrayWrapper.cpp | 4 +++ modules/plugin/base/src/nsJSNPRuntime.cpp | 12 ++++++- 17 files changed, 164 insertions(+), 42 deletions(-) diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index 01f7910d9f4b..f248727f9cfe 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -1114,6 +1114,11 @@ nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocumen JSObject* base = scriptObject; JSObject* proto; JSAutoRequest ar(cx); + JSAutoEnterCompartment ac; + if (!ac.enter(cx, scriptObject)) { + return; + } + for ( ; true; base = proto) { // Will break out on null proto proto = ::JS_GetPrototype(cx, base); if (!proto) { @@ -1246,6 +1251,11 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj, nsCAutoString className(aClassName); JSObject* parent_proto = nsnull; // If we have an "obj" we can set this JSAutoRequest ar(cx); + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, global)) + return NS_ERROR_FAILURE; + if (obj) { // Retrieve the current prototype of obj. parent_proto = ::JS_GetPrototype(cx, obj); diff --git a/content/xbl/src/nsXBLProtoImplMethod.cpp b/content/xbl/src/nsXBLProtoImplMethod.cpp index 2169a8a1edf8..f26a6ec38080 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.cpp +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -149,6 +149,12 @@ nsXBLProtoImplMethod::InstallMember(nsIScriptContext* aContext, if (mJSMethodObject && targetClassObject) { nsDependentString name(mName); JSAutoRequest ar(cx); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, mJSMethodObject)) { + return NS_ERROR_UNEXPECTED; + } + JSObject * method = ::JS_CloneFunctionObject(cx, mJSMethodObject, globalObject); if (!method) { return NS_ERROR_OUT_OF_MEMORY; diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 184e970f0657..bcea0f8c0165 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1880,6 +1880,14 @@ CreateExceptionFromResult(JSContext *cx, nsresult aResult) return NS_ERROR_FAILURE; } + JSAutoEnterCompartment ac; + + if (JSVAL_IS_OBJECT(jv)) { + if (!ac.enter(cx, JSVAL_TO_OBJECT(jv))) { + return NS_ERROR_UNEXPECTED; + } + } + JS_SetPendingException(cx, jv); return NS_OK; } @@ -6490,15 +6498,23 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSBool did_resolve = JS_FALSE; JSContext *my_cx; - if (!my_context) { - my_cx = cx; - } else { - my_cx = (JSContext *)my_context->GetNativeContext(); - } - JSBool ok = JS_TRUE; jsval exn = JSVAL_VOID; if (win->IsInnerWindow()) { + JSAutoEnterCompartment ac; + + if (!my_context) { + my_cx = cx; + } else { + my_cx = (JSContext *)my_context->GetNativeContext(); + + if (my_cx != cx) { + if (!ac.enter(my_cx, obj)) { + return NS_ERROR_UNEXPECTED; + } + } + } + JSAutoRequest transfer(my_cx); JSObject *realObj; diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 1aade5d2b5ba..9acc236557f7 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -674,6 +674,11 @@ nsOuterWindowProxy::singleton; JSObject * NS_NewOuterWindowProxy(JSContext *cx, JSObject *parent) { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, parent)) { + return nsnull; + } + JSObject *obj = JSWrapper::New(cx, parent, parent->getProto(), parent, &nsOuterWindowProxy::singleton); NS_ASSERTION(obj->getClass()->ext.innerObject, "bad class"); diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 95376c9fe4b5..ec13f0ee99bf 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2189,11 +2189,25 @@ nsJSContext::BindCompiledEventHandler(nsISupports* aTarget, void *aScope, NS_ENSURE_SUCCESS(rv, rv); JSObject *funobj = (JSObject*) aHandler; - JSAutoRequest ar(mContext); - NS_ASSERTION(JS_TypeOfValue(mContext, OBJECT_TO_JSVAL(funobj)) == JSTYPE_FUNCTION, - "Event handler object not a function"); +#ifdef DEBUG + { + JSAutoEnterCompartment ac; + if (!ac.enter(mContext, funobj)) { + return NS_ERROR_FAILURE; + } + + NS_ASSERTION(JS_TypeOfValue(mContext, + OBJECT_TO_JSVAL(funobj)) == JSTYPE_FUNCTION, + "Event handler object not a function"); + } +#endif + + JSAutoEnterCompartment ac; + if (!ac.enter(mContext, target)) { + return NS_ERROR_FAILURE; + } // Push our JSContext on our thread's context stack, in case native code // called from JS calls back into JS via XPConnect. @@ -2244,6 +2258,11 @@ nsJSContext::GetBoundEventHandler(nsISupports* aTarget, void *aScope, rv = JSObjectFromInterface(aTarget, aScope, &obj); NS_ENSURE_SUCCESS(rv, rv); + JSAutoEnterCompartment ac; + if (!ac.enter(mContext, obj)) { + return NS_ERROR_FAILURE; + } + jsval funval; if (!JS_LookupProperty(mContext, obj, nsAtomCString(aName).get(), &funval)) @@ -2419,6 +2438,12 @@ nsJSContext::GetGlobalObject() return nsnull; } + JSAutoEnterCompartment ac; + + // NB: This AutoCrossCompartmentCall is only here to silence a warning. If + // it fails, nothing bad will happen. + ac.enterAndIgnoreErrors(mContext, global); + nsCOMPtr sgo; nsISupports *priv = (nsISupports *)::JS_GetPrivate(mContext, global); @@ -3344,6 +3369,10 @@ nsJSContext::ClearScope(void *aGlobalObj, PRBool aClearFromProtoChain) if (aGlobalObj) { JSObject *obj = (JSObject *)aGlobalObj; JSAutoRequest ar(mContext); + + JSAutoEnterCompartment ac; + ac.enterAndIgnoreErrors(mContext, obj); + JS_ClearScope(mContext, obj); if (!obj->getParent()) { JS_ClearRegExpStatics(mContext, obj); diff --git a/dom/src/json/nsJSON.cpp b/dom/src/json/nsJSON.cpp index 3e8d582a55bc..1005d2e4be06 100644 --- a/dom/src/json/nsJSON.cpp +++ b/dom/src/json/nsJSON.cpp @@ -202,6 +202,13 @@ nsJSON::EncodeFromJSVal(jsval *value, JSContext *cx, nsAString &result) // Begin a new request JSAutoRequest ar(cx); + JSAutoEnterCompartment ac; + JSObject *obj; + if (JSVAL_IS_OBJECT(*value) && (obj = JSVAL_TO_OBJECT(*value)) && + !ac.enter(cx, obj)) { + return NS_ERROR_FAILURE; + } + nsJSONWriter writer; JSBool ok = JS_Stringify(cx, value, NULL, JSVAL_NULL, WriteCallback, &writer); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 4155e411c599..541f970a5c73 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -652,7 +652,7 @@ struct JSObject : js::gc::Cell { parent = newParent; } - JSObject *getGlobal() const; + JS_FRIEND_API(JSObject *) getGlobal() const; bool isGlobal() const { return !!(getClass()->flags & JSCLASS_IS_GLOBAL); diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 20682c1735ff..a29cce812661 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -946,10 +946,9 @@ nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj) XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); - SaveFrame sf(aJSContext); JSAutoEnterCompartment ac; - if (!ac.enter(ccx, aGlobalJSObj)) + if(!ac.enter(ccx, aGlobalJSObj)) return UnexpectedFailure(NS_ERROR_FAILURE); xpc_InitJSxIDClassObjects(); @@ -1059,8 +1058,6 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext, else origin = aOrigin; - SaveFrame sf(ccx); - JSCompartment* compartment; JSObject* tempGlobal; @@ -1069,7 +1066,7 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext, NS_ENSURE_SUCCESS(rv, rv); JSAutoEnterCompartment ac; - if (!ac.enter(ccx, tempGlobal)) + if(!ac.enter(ccx, tempGlobal)) return UnexpectedFailure(NS_ERROR_FAILURE); PRBool system = (aFlags & nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT) != 0; @@ -1253,8 +1250,11 @@ nsXPConnect::WrapJS(JSContext * aJSContext, if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); - nsresult rv; - if(!XPCConvert::JSObject2NativeInterface(ccx, result, aJSObj, + JSAutoEnterCompartment aec; + + nsresult rv = NS_ERROR_UNEXPECTED; + if(!aec.enter(ccx, aJSObj) || + !XPCConvert::JSObject2NativeInterface(ccx, result, aJSObj, &aIID, nsnull, &rv)) return rv; return NS_OK; diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index f4eaa0ec3478..628d16bc8e21 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -3077,21 +3077,6 @@ SandboxImport(JSContext *cx, uintN argc, jsval *vp) return JS_FALSE; } - JSFunction *fun; - { - // NB: funobj must only be used to get the JSFunction out. - JSObject *funobj = JSVAL_TO_OBJECT(argv[0]); - if (funobj->isProxy()) { - funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(cx, funobj); - } - - fun = JS_ValueToFunction(cx, OBJECT_TO_JSVAL(funobj)); - if (!fun) { - XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); - return JS_FALSE; - } - } - JSString *funname; if (argc > 1) { // Use the second parameter as the function name. @@ -3100,6 +3085,23 @@ SandboxImport(JSContext *cx, uintN argc, jsval *vp) return JS_FALSE; argv[1] = STRING_TO_JSVAL(funname); } else { + // NB: funobj must only be used to get the JSFunction out. + JSObject *funobj = JSVAL_TO_OBJECT(argv[0]); + if (funobj->isProxy()) { + funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(cx, funobj); + } + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, funobj)) { + return JS_FALSE; + } + + JSFunction *fun = JS_ValueToFunction(cx, OBJECT_TO_JSVAL(funobj)); + if (!fun) { + XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); + return JS_FALSE; + } + // Use the actual function name as the name. funname = JS_GetFunctionId(fun); if (!funname) { diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 530495c1ffc4..c881d769ccd6 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -1384,6 +1384,15 @@ XPCConvert::JSObject2NativeInterface(XPCCallContext& ccx, JSContext* cx = ccx.GetJSContext(); + JSAutoEnterCompartment ac; + + if(!ac.enter(cx, src)) + { + if(pErr) + *pErr = NS_ERROR_UNEXPECTED; + return PR_FALSE; + } + *dest = nsnull; if(pErr) *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; diff --git a/js/src/xpconnect/src/xpcdebug.cpp b/js/src/xpconnect/src/xpcdebug.cpp index 15a9ac30a9d7..51fbcf624241 100644 --- a/js/src/xpconnect/src/xpcdebug.cpp +++ b/js/src/xpconnect/src/xpcdebug.cpp @@ -88,6 +88,9 @@ static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp, jsbytecode* pc = JS_GetFramePC(cx, fp); JSAutoRequest ar(cx); + JSAutoEnterCompartment ac; + if(!ac.enter(cx, JS_GetFrameScopeChain(cx, fp))) + return buf; if(script && pc) { diff --git a/js/src/xpconnect/src/xpcwrappedjsclass.cpp b/js/src/xpconnect/src/xpcwrappedjsclass.cpp index c1e04bffdf40..059b887eab93 100644 --- a/js/src/xpconnect/src/xpcwrappedjsclass.cpp +++ b/js/src/xpconnect/src/xpcwrappedjsclass.cpp @@ -247,8 +247,8 @@ nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(XPCCallContext& ccx, JSBool success = JS_FALSE; jsid funid; jsval fun; - JSAutoEnterCompartment ac; + if(!ac.enter(cx, jsobj)) return nsnull; @@ -567,6 +567,10 @@ GetContextFromObject(JSObject *obj) XPCCallContext ccx(NATIVE_CALLER); if(!ccx.IsValid()) return nsnull; + + JSAutoEnterCompartment ac; + if(!ac.enter(ccx, obj)) + return nsnull; XPCWrappedNativeScope* scope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, obj); XPCContext *xpcc = scope->GetContext(); @@ -1312,7 +1316,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, obj = thisObj = wrapper->GetJSObject(); JSAutoEnterCompartment ac; - if(!ac.enter(ccx, obj)) + if (!ac.enter(ccx, obj)) goto pre_call_clean_up; // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this. diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 010a6b207fae..bb3e056ceaa6 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -447,6 +447,9 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(parent), "Parent should never be an XPCNativeWrapper here"); + if(!ac.enter(ccx, parent)) + return NS_ERROR_FAILURE; + if(parent != plannedParent) { XPCWrappedNativeScope* betterScope = @@ -458,9 +461,6 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, newParentVal = OBJECT_TO_JSVAL(parent); } - if(!ac.enter(ccx, parent)) - return NS_ERROR_FAILURE; - // Take the performance hit of checking the hashtable again in case // the preCreate call caused the wrapper to get created through some // interesting path (the DOM code tends to make this happen sometimes). @@ -760,8 +760,9 @@ XPCWrappedNative::Morph(XPCCallContext& ccx, NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(existingJSObject ->getParent()), "XPCNativeWrapper being used to parent XPCWrappedNative?"); - - if(!wrapper->Init(ccx, existingJSObject)) + + JSAutoEnterCompartment ac; + if(!ac.enter(ccx, existingJSObject) || !wrapper->Init(ccx, existingJSObject)) { NS_RELEASE(wrapper); return NS_ERROR_FAILURE; @@ -3181,9 +3182,13 @@ NS_IMETHODIMP XPCWrappedNative::RefreshPrototype() if(!GetFlatJSObject()) return UnexpectedFailure(NS_ERROR_FAILURE); + JSAutoEnterCompartment ac; + if(!ac.enter(ccx, GetFlatJSObject())) + return UnexpectedFailure(NS_ERROR_FAILURE); + AutoMarkingWrappedNativeProtoPtr oldProto(ccx); AutoMarkingWrappedNativeProtoPtr newProto(ccx); - + oldProto = GetProto(); XPCNativeScriptableInfo *info = oldProto->GetScriptableInfo(); @@ -3907,6 +3912,14 @@ ConstructSlimWrapper(XPCCallContext &ccx, return JS_FALSE; } + JSAutoEnterCompartment ac; + if(!ac.enter(ccx, parent)) + { + SLIM_LOG_NOT_CREATED(ccx, identityObj, "unable to enter compartment"); + + return JS_FALSE; + } + if(parent != plannedParent) { XPCWrappedNativeScope *newXpcScope = diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp index f82bf4e49102..76c25b3b4e32 100644 --- a/js/src/xpconnect/src/xpcwrappednativescope.cpp +++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp @@ -819,6 +819,9 @@ XPCWrappedNativeScope::FindInJSObjectScope(JSContext* cx, JSObject* obj, // Else we'll have to look up the parent chain to get the scope + JSAutoEnterCompartment ac; + ac.enterAndIgnoreErrors(cx, obj); + obj = JS_GetGlobalForObject(cx, obj); if(!runtime) diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 26bb137b5fcb..6c22f7a3cb19 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -283,7 +283,8 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, JSWrappe jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS); JSBool found = JS_FALSE; - if (!JS_HasPropertyById(cx, holder, exposedPropsId, &found)) + JSAutoEnterCompartment ac; + if (!ac.enter(cx, holder) || !JS_HasPropertyById(cx, holder, exposedPropsId, &found)) return false; if (!found) { perm = PermitObjectAccess; diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 4836f51954e5..a574cbaefb24 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -145,6 +145,10 @@ ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, // Run the resolve hook of the wrapped native. if (NATIVE_HAS_FLAG(wn, WantNewResolve)) { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, holder)) + return false; + JSBool retval = true; JSObject *pobj = NULL; uintN flags = cx->resolveFlags | (set ? JSRESOLVE_ASSIGNING : 0); diff --git a/modules/plugin/base/src/nsJSNPRuntime.cpp b/modules/plugin/base/src/nsJSNPRuntime.cpp index 68c25acd5366..81aade70ca45 100644 --- a/modules/plugin/base/src/nsJSNPRuntime.cpp +++ b/modules/plugin/base/src/nsJSNPRuntime.cpp @@ -1983,7 +1983,10 @@ NPObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, PR_Free(npobj); } - ::JS_SetPrivate(nppcx->cx, entry->mJSObj, nsnull); + JSAutoEnterCompartment ac; + if (ac.enter(nppcx->cx, entry->mJSObj)) { + ::JS_SetPrivate(nppcx->cx, entry->mJSObj, nsnull); + } table->ops = ops; @@ -2083,6 +2086,13 @@ nsJSNPRuntime::OnPluginDestroy(NPP npp) JSObject *proto; + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, obj)) { + // Failure to enter compartment, nothing more we can do then. + return; + } + // Loop over the DOM element's JS object prototype chain and remove // all JS objects of the class sNPObjectJSWrapperClass (there should // be only one, but remove all instances found in case the page put From ce180ccef37f8b47d3747b2c15346f3b213039c6 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 23 Sep 2010 15:56:28 -0700 Subject: [PATCH 136/284] bug 580128 - Pass the XrayWrapper itself to scriptable helpers (and related cleanup) since the holder doesn't have enough smarts to do lookups, etc. r=gal --- dom/base/nsDOMClassInfo.cpp | 3 + js/src/xpconnect/src/nsXPConnect.cpp | 2 +- js/src/xpconnect/wrappers/AccessCheck.cpp | 2 +- js/src/xpconnect/wrappers/WrapperFactory.cpp | 3 +- js/src/xpconnect/wrappers/XrayWrapper.cpp | 141 +++++++++++++++---- js/src/xpconnect/wrappers/XrayWrapper.h | 5 + 6 files changed, 125 insertions(+), 31 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index bcea0f8c0165..7bec08148e4e 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1928,6 +1928,9 @@ nsDOMClassInfo::ObjectIsNativeWrapper(JSContext* cx, JSObject* obj) } #endif + if (obj->isWrapper()) + return PR_TRUE; + JSPropertyOp op = obj->getJSClass()->getProperty; return !!op && (op == sXPCNativeWrapperGetPropertyOp || op == sXrayWrapperPropertyHolderGetPropertyOp); diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index a29cce812661..7b72937836f5 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -2712,7 +2712,7 @@ nsXPConnect::GetPrincipal(JSObject* obj, PRBool allowShortCircuit) const NS_IMETHODIMP_(void) nsXPConnect::GetXrayWrapperPropertyHolderGetPropertyOp(JSPropertyOp *getPropertyPtr) { - *getPropertyPtr = xpc::HolderClass.getProperty; + *getPropertyPtr = xpc::XrayUtils::HolderClass.getProperty; } NS_IMETHODIMP_(void) diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 6c22f7a3cb19..9a3b5d899772 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -176,7 +176,7 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid const char *name; js::Class *clasp = obj->getClass(); - NS_ASSERTION(Jsvalify(clasp) != &HolderClass, "shouldn't have a holder here"); + NS_ASSERTION(Jsvalify(clasp) != &XrayUtils::HolderClass, "shouldn't have a holder here"); if (clasp->ext.innerObject) name = "Window"; else diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 1beb628ec813..637a3367e16e 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -71,7 +71,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO { NS_ASSERTION(!obj->isWrapper() || obj->getClass()->ext.innerObject, "wrapped object passed to rewrap"); - NS_ASSERTION(JS_GET_CLASS(cx, obj) != &HolderClass, "trying to wrap a holder"); + NS_ASSERTION(JS_GET_CLASS(cx, obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj)) return nsnull; @@ -142,6 +142,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO if (!wrapperObj || !xrayHolder) return wrapperObj; wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder)); + xrayHolder->setSlot(XrayUtils::JSSLOT_PROXY_OBJ, js::ObjectValue(*wrapperObj)); return wrapperObj; } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index a574cbaefb24..c1b15133df4e 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -53,6 +53,51 @@ namespace xpc { using namespace js; static const uint32 JSSLOT_WN_OBJ = JSSLOT_PRIVATE; +static const uint32 JSSLOT_RESOLVING = JSSLOT_PRIVATE + 1; + +namespace XrayUtils { + +const uint32 JSSLOT_PROXY_OBJ = JSSLOT_PRIVATE + 2; + +} + +class ResolvingId +{ + public: + ResolvingId(JSObject *holder, jsid id) + : mId(id), + mPrev(getResolvingId(holder)), + mHolder(holder) + { + holder->setSlot(JSSLOT_RESOLVING, PrivateValue(this)); + } + + ~ResolvingId() { + NS_ASSERTION(getResolvingId(mHolder) == this, "unbalanced ResolvingIds"); + mHolder->setSlot(JSSLOT_RESOLVING, PrivateValue(mPrev)); + } + + static ResolvingId *getResolvingId(JSObject *holder) { + return (ResolvingId *)holder->getSlot(JSSLOT_RESOLVING).toPrivate(); + } + + jsid mId; + ResolvingId *mPrev; + + private: + JSObject *mHolder; +}; + +static bool +IsResolving(JSObject *holder, jsid id) +{ + for (ResolvingId *cur = ResolvingId::getResolvingId(holder); cur; cur = cur->mPrev) { + if (cur->mId == id) + return true; + } + + return false; +} static JSBool holder_get(JSContext *cx, JSObject *holder, jsid id, jsval *vp); @@ -63,15 +108,27 @@ holder_set(JSContext *cx, JSObject *holder, jsid id, jsval *vp); static JSBool holder_enumerate(JSContext *cx, JSObject *holder); +namespace XrayUtils { + JSClass HolderClass = { "NativePropertyHolder", - JSCLASS_HAS_RESERVED_SLOTS(1), + JSCLASS_HAS_RESERVED_SLOTS(3), JS_PropertyStub, JS_PropertyStub, holder_get, holder_set, holder_enumerate, JS_ResolveStub, JS_ConvertStub, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; +} + +using namespace XrayUtils; + +static JSObject * +GetHolder(JSObject *obj) +{ + return &obj->getProxyExtra().toObject(); +} + static XPCWrappedNative * GetWrappedNative(JSObject *obj) { @@ -92,20 +149,19 @@ GetWrappedNativeObjectFromHolder(JSContext *cx, JSObject *holder) // getter/setter and rely on the class getter/setter. We install a // class getter/setter on the holder object to trigger them. static JSBool -holder_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +holder_get(JSContext *cx, JSObject *wrapper, jsid id, jsval *vp) { - if (obj->isWrapper()) - obj = &obj->getProxyExtra().toObject(); + NS_ASSERTION(wrapper->isProxy(), "bad this object in get"); + JSObject *holder = GetHolder(wrapper); - JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, obj); + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); XPCWrappedNative *wn = GetWrappedNative(wnObject); if (NATIVE_HAS_FLAG(wn, WantGetProperty)) { JSBool retval = true; - nsresult rv = wn->GetScriptableCallback()->GetProperty(wn, cx, obj, id, vp, &retval); + nsresult rv = wn->GetScriptableCallback()->GetProperty(wn, cx, wrapper, id, vp, &retval); if (NS_FAILED(rv)) { - if (retval) { + if (retval) XPCThrower::Throw(rv, cx); - } return false; } } @@ -113,17 +169,19 @@ holder_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) } static JSBool -holder_set(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +holder_set(JSContext *cx, JSObject *wrapper, jsid id, jsval *vp) { - JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, obj); + NS_ASSERTION(wrapper->isProxy(), "bad this object in set"); + JSObject *holder = GetHolder(wrapper); + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); + XPCWrappedNative *wn = GetWrappedNative(wnObject); if (NATIVE_HAS_FLAG(wn, WantSetProperty)) { JSBool retval = true; - nsresult rv = wn->GetScriptableCallback()->SetProperty(wn, cx, obj, id, vp, &retval); + nsresult rv = wn->GetScriptableCallback()->SetProperty(wn, cx, wrapper, id, vp, &retval); if (NS_FAILED(rv)) { - if (retval) { + if (retval) XPCThrower::Throw(rv, cx); - } return false; } } @@ -131,7 +189,7 @@ holder_set(JSContext *cx, JSObject *obj, jsid id, jsval *vp) } static bool -ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, +ResolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid id, bool set, JSPropertyDescriptor *desc) { desc->obj = NULL; @@ -152,8 +210,8 @@ ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, JSBool retval = true; JSObject *pobj = NULL; uintN flags = cx->resolveFlags | (set ? JSRESOLVE_ASSIGNING : 0); - nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, holder, id, flags, - &pobj, &retval); + nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, wrapper, id, + flags, &pobj, &retval); if (NS_FAILED(rv)) { if (retval) { XPCThrower::Throw(rv, cx); @@ -161,9 +219,8 @@ ResolveNativeProperty(JSContext *cx, JSObject *holder, jsid id, bool set, return false; } - if (pobj) { + if (pobj) return JS_GetPropertyDescriptorById(cx, pobj, id, cx->resolveFlags, desc); - } } // There are no native numeric properties, so we can shortcut here. We will not @@ -249,12 +306,14 @@ holder_enumerate(JSContext *cx, JSObject *holder) if (!ida) return false; + JSObject *wrapper = &holder->getSlot(JSSLOT_PROXY_OBJ).toObject(); + // Resolve the underlying native properties onto the holder object jsid *idp = ida->vector; size_t length = ida->length; while (length-- > 0) { JSPropertyDescriptor dummy; - if (!ResolveNativeProperty(cx, holder, *idp++, false, &dummy)) + if (!ResolveNativeProperty(cx, wrapper, holder, *idp++, false, &dummy)) return false; } return true; @@ -266,7 +325,7 @@ static JSBool wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) { if (holder->isWrapper()) - holder = &holder->getProxyExtra().toObject(); + holder = GetHolder(holder); // If the caller intentionally waives the X-ray wrapper we usually // apply for wrapped natives, use a special wrapper to make sure the @@ -312,17 +371,26 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe return true; } + JSObject *holder = GetHolder(wrapper); + if (IsResolving(holder, id)) { + desc->obj = NULL; + return true; + } + + ResolvingId resolving(holder, id); + void *priv; if (!Policy::enter(cx, wrapper, &id, set ? JSWrapper::SET : JSWrapper::GET, &priv)) return false; - JSObject *holder = &wrapper->getProxyExtra().toObject(); - bool ok = ResolveNativeProperty(cx, holder, id, false, desc); - - // TODO expandos - + bool ok = ResolveNativeProperty(cx, wrapper, holder, id, false, desc); Policy::leave(cx, wrapper, priv); - return ok; + if (!ok || desc->obj) + return ok; + + return JS_GetPropertyDescriptorById(cx, holder, id, + (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED, + desc); } template @@ -338,8 +406,24 @@ bool XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc) { - // XXX When am I called? Implement me! - return true; + JSObject *holder = GetHolder(wrapper); + PropertyDescriptor existing_desc; + if (!getOwnPropertyDescriptor(cx, wrapper, id, true, &existing_desc)) + return false; + + if (existing_desc.attrs & JSPROP_PERMANENT) + return true; // XXX throw? + + JSPropertyDescriptor *jsdesc = Jsvalify(desc); + if (!(jsdesc->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { + if (!desc->getter) + jsdesc->getter = holder_get; + if (!desc->setter) + jsdesc->setter = holder_set; + } + + return JS_DefinePropertyById(cx, holder, id, jsdesc->value, jsdesc->getter, jsdesc->setter, + jsdesc->attrs); } template @@ -434,6 +518,7 @@ XrayWrapper::createHolder(JSContext *cx, JSObject *wrappedNative, return nsnull; holder->setSlot(JSSLOT_WN_OBJ, ObjectValue(*wrappedNative)); + holder->setSlot(JSSLOT_RESOLVING, PrivateValue(NULL)); return holder; } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.h b/js/src/xpconnect/wrappers/XrayWrapper.h index dfc7d3084804..37f6da88a769 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.h +++ b/js/src/xpconnect/wrappers/XrayWrapper.h @@ -48,7 +48,12 @@ namespace xpc { +namespace XrayUtils { + extern JSClass HolderClass; +extern const uint32 JSSLOT_PROXY_OBJ; + +} // NB: Base *must* derive from JSProxyHandler template From 64aaf55c41a49cdd5924a2339d7fbfe898f64c25 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 24 Sep 2010 15:30:00 -0700 Subject: [PATCH 137/284] bug 580128 - Pass the right resolve flags to ResolveNativeHandler. r=peterv/jst --- dom/base/nsDOMClassInfo.cpp | 3 ++- js/src/xpconnect/wrappers/XrayWrapper.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 7bec08148e4e..3cd60bee6d37 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -6812,7 +6812,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, // Unless our object is a native wrapper, in which case we have to // define it ourselves. - *_retval = JS_DefineProperty(cx, obj, "document", v, NULL, NULL, + *_retval = JS_WrapValue(cx, &v) && + JS_DefineProperty(cx, obj, "document", v, NULL, NULL, JSPROP_READONLY | JSPROP_ENUMERATE); if (!*_retval) { return NS_ERROR_UNEXPECTED; diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index c1b15133df4e..e235fc8b8539 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -209,7 +209,7 @@ ResolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid i JSBool retval = true; JSObject *pobj = NULL; - uintN flags = cx->resolveFlags | (set ? JSRESOLVE_ASSIGNING : 0); + uintN flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED; nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, wrapper, id, flags, &pobj, &retval); if (NS_FAILED(rv)) { @@ -220,7 +220,7 @@ ResolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid i } if (pobj) - return JS_GetPropertyDescriptorById(cx, pobj, id, cx->resolveFlags, desc); + return JS_GetPropertyDescriptorById(cx, pobj, id, flags, desc); } // There are no native numeric properties, so we can shortcut here. We will not @@ -411,7 +411,7 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid if (!getOwnPropertyDescriptor(cx, wrapper, id, true, &existing_desc)) return false; - if (existing_desc.attrs & JSPROP_PERMANENT) + if (existing_desc.obj && (existing_desc.attrs & JSPROP_PERMANENT)) return true; // XXX throw? JSPropertyDescriptor *jsdesc = Jsvalify(desc); From bdb04e3dccfabc52de497fd9c1003eeaf2115443 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 24 Sep 2010 18:00:58 -0700 Subject: [PATCH 138/284] bug 580128 - Avoid using the parent chain of proxies for anything because it's often wrong. r=jst --- caps/src/nsSecurityManagerFactory.cpp | 2 + dom/base/nsDOMClassInfo.cpp | 64 ++++++++++++-------- dom/base/nsGlobalWindow.cpp | 28 +++++---- dom/base/nsJSEnvironment.cpp | 12 ++-- js/src/xpconnect/src/xpcwrappedjsclass.cpp | 36 +++++------ js/src/xpconnect/src/xpcwrappednative.cpp | 6 +- js/src/xpconnect/wrappers/AccessCheck.cpp | 6 ++ js/src/xpconnect/wrappers/AccessCheck.h | 3 + js/src/xpconnect/wrappers/WrapperFactory.cpp | 2 +- 9 files changed, 94 insertions(+), 65 deletions(-) diff --git a/caps/src/nsSecurityManagerFactory.cpp b/caps/src/nsSecurityManagerFactory.cpp index 73eacda82cee..838f6e64a3b0 100644 --- a/caps/src/nsSecurityManagerFactory.cpp +++ b/caps/src/nsSecurityManagerFactory.cpp @@ -52,6 +52,7 @@ #include "nsString.h" #include "nsNetCID.h" #include "nsIClassInfoImpl.h" +#include "jsobj.h" /////////////////////// // nsSecurityNameSet // @@ -294,6 +295,7 @@ nsSecurityNameSet::InitializeNameSet(nsIScriptContext* aScriptContext) { JSContext *cx = (JSContext *) aScriptContext->GetNativeContext(); JSObject *global = JS_GetGlobalObject(cx); + OBJ_TO_INNER_OBJECT(cx, global); /* * Find Object.prototype's class by walking up the global object's diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 3cd60bee6d37..2b8aa89024e1 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -5253,9 +5253,17 @@ nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, frameWin->EnsureInnerWindow(); nsCOMPtr holder; + jsval v; rv = WrapNative(cx, frameWin->GetGlobalJSObject(), frame, - &NS_GET_IID(nsIDOMWindow), PR_TRUE, vp, + &NS_GET_IID(nsIDOMWindow), PR_TRUE, &v, getter_AddRefs(holder)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!JS_WrapValue(cx, &v)) { + return NS_ERROR_FAILURE; + } + + *vp = v; } return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; @@ -6800,8 +6808,9 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, // from it. jsval v; nsCOMPtr holder; - rv = WrapNative(cx, obj, document, &NS_GET_IID(nsIDOMDocument), PR_FALSE, - &v, getter_AddRefs(holder)); + rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), document, + &NS_GET_IID(nsIDOMDocument), PR_FALSE, &v, + getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); // The PostCreate hook for the document will handle defining the @@ -7318,7 +7327,8 @@ nsNodeSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } nsCOMPtr holder; - nsresult rv = WrapNative(cx, obj, uri, &NS_GET_IID(nsIURI), PR_TRUE, vp, + nsresult rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), uri, + &NS_GET_IID(nsIURI), PR_TRUE, vp, getter_AddRefs(holder)); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } @@ -7328,9 +7338,9 @@ nsNodeSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED); nsCOMPtr holder; - nsresult rv = WrapNative(cx, obj, node->NodePrincipal(), - &NS_GET_IID(nsIPrincipal), PR_TRUE, vp, - getter_AddRefs(holder)); + nsresult rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), + node->NodePrincipal(), &NS_GET_IID(nsIPrincipal), + PR_TRUE, vp, getter_AddRefs(holder)); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } @@ -7948,7 +7958,8 @@ nsArraySH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_SUCCESS(rv, rv); if (array_item) { - rv = WrapNative(cx, obj, array_item, cache, PR_TRUE, vp); + rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), array_item, cache, + PR_TRUE, vp); NS_ENSURE_SUCCESS(rv, rv); rv = NS_SUCCESS_I_DID_SOMETHING; @@ -8068,7 +8079,8 @@ nsNamedArraySH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_SUCCESS(rv, rv); if (item) { - rv = WrapNative(cx, obj, item, cache, PR_TRUE, vp); + rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), item, cache, + PR_TRUE, vp); NS_ENSURE_SUCCESS(rv, rv); rv = NS_SUCCESS_I_DID_SOMETHING; @@ -8243,8 +8255,9 @@ nsDocumentSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, jsval v; nsCOMPtr holder; - rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), PR_TRUE, - &v, getter_AddRefs(holder)); + rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), location, + &NS_GET_IID(nsIDOMLocation), PR_TRUE, &v, + getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); JSAutoRequest ar(cx); @@ -8280,7 +8293,8 @@ nsDocumentSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_TRUE(uri, NS_ERROR_NOT_AVAILABLE); nsCOMPtr holder; - nsresult rv = WrapNative(cx, obj, uri, &NS_GET_IID(nsIURI), PR_TRUE, vp, + nsresult rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), uri, + &NS_GET_IID(nsIURI), PR_TRUE, vp, getter_AddRefs(holder)); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; @@ -8312,8 +8326,9 @@ nsDocumentSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr holder; - rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), PR_TRUE, - vp, getter_AddRefs(holder)); + rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), location, + &NS_GET_IID(nsIDOMLocation), PR_TRUE, vp, + getter_AddRefs(holder)); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } } @@ -8466,11 +8481,7 @@ nsHTMLDocumentSH::DocumentOpen(JSContext *cx, uintN argc, jsval *vp) return JS_FALSE; } - nsCOMPtr holder; - rv = WrapNative(cx, obj, retval, PR_FALSE, vp, - getter_AddRefs(holder)); - NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to wrap native!"); - + *vp = OBJECT_TO_JSVAL(obj); return NS_SUCCEEDED(rv); } @@ -8546,7 +8557,8 @@ nsHTMLDocumentSH::GetDocumentAllNodeList(JSContext *cx, JSObject *obj, } nsCOMPtr holder; - rv |= nsDOMClassInfo::WrapNative(cx, obj, static_cast(list), + rv |= nsDOMClassInfo::WrapNative(cx, JS_GetGlobalForScopeChain(cx), + static_cast(list), list, PR_FALSE, &collection, getter_AddRefs(holder)); @@ -8559,7 +8571,7 @@ nsHTMLDocumentSH::GetDocumentAllNodeList(JSContext *cx, JSObject *obj, } if (NS_FAILED(rv)) { - nsDOMClassInfo::ThrowJSException(cx, rv); + nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_FAILURE); return JS_FALSE; } @@ -8649,7 +8661,7 @@ nsHTMLDocumentSH::DocumentAllGetProperty(JSContext *cx, JSObject *obj, } if (result) { - rv = WrapNative(cx, obj, result, cache, PR_TRUE, vp); + rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), result, cache, PR_TRUE, vp); if (NS_FAILED(rv)) { nsDOMClassInfo::ThrowJSException(cx, rv); @@ -8922,7 +8934,7 @@ nsHTMLDocumentSH::DocumentAllTagsNewResolve(JSContext *cx, JSObject *obj, if (tags) { jsval v; nsCOMPtr holder; - nsresult rv = nsDOMClassInfo::WrapNative(cx, obj, + nsresult rv = nsDOMClassInfo::WrapNative(cx, JS_GetGlobalForScopeChain(cx), static_cast(tags), tags, PR_TRUE, &v, getter_AddRefs(holder)); @@ -9180,7 +9192,8 @@ nsHTMLFormElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper, if (control) { Element *element = static_cast(form->GetElementAt(n)); - nsresult rv = WrapNative(cx, obj, element, element, PR_TRUE, vp); + nsresult rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), element, + element, PR_TRUE, vp); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } } @@ -9285,7 +9298,8 @@ nsHTMLSelectElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper, if (options) { nsISupports *node = options->GetNodeAt(n, &rv); - rv = WrapNative(cx, obj, node, &NS_GET_IID(nsIDOMNode), PR_TRUE, vp); + rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), node, + &NS_GET_IID(nsIDOMNode), PR_TRUE, vp); if (NS_SUCCEEDED(rv)) { rv = NS_SUCCESS_I_DID_SOMETHING; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 9acc236557f7..4dfcaa5b41a0 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1943,9 +1943,25 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, mJSObject = outerObject; SetWrapper(mJSObject); + { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, mJSObject)) { + NS_ERROR("unable to enter a compartment"); + return NS_ERROR_FAILURE; + } + + JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject); + } + mContext->SetOuterObject(mJSObject); } + JSAutoEnterCompartment ac; + if (!ac.enter(cx, mJSObject)) { + NS_ERROR("unable to enter a compartment"); + return NS_ERROR_FAILURE; + } + // XXX Not sure if this is needed. if (aState) { JSObject *proto; @@ -1974,16 +1990,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, // Loading a new page and creating a new inner window, *not* // restoring from session history. - // InitClassesWithNewWrappedGlobal() (via CreateNativeGlobalForInner) - // for the new inner window - // sets the global object in cx to be the new wrapped global. We - // don't want that, but re-initializing the outer window will - // fix that for us. And perhaps more importantly, this will - // ensure that the outer window gets a new prototype so we don't - // leak prototype properties from the old inner window to the - // new one. - mContext->InitOuterWindow(); - // Now that both the the inner and outer windows are initialized // let the script context do its magic to hook them together. mContext->ConnectToInner(newInnerWindow, mJSObject); @@ -2042,7 +2048,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, NS_ENSURE_SUCCESS(rv, rv); // Initialize DOM classes etc on the inner window. - rv = mContext->InitClasses(mJSObject); + rv = mContext->InitClasses(newInnerWindow->mJSObject); NS_ENSURE_SUCCESS(rv, rv); if (navigatorHolder) { diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index ec13f0ee99bf..4775b040507b 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2525,6 +2525,12 @@ nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal // outer (through the JSExtendedClass hook outerObject), so this // prototype sharing works. + // Now that we're connecting the outer global to the inner one, + // we must have transplanted it. The JS engine tries to maintain + // the global object's compartment as its default compartment, + // so update that now since it might have changed. + JS_SetGlobalObject(mContext, outerGlobal); + // We do *not* want to use anything else out of the outer // object's prototype chain than the first prototype, which is // the XPConnect prototype. The rest we want from the inner @@ -2539,11 +2545,6 @@ nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal JS_SetPrototype(mContext, newInnerJSObject, proto); JS_SetPrototype(mContext, proto, innerProtoProto); - // Now that we're connecting the outer global to the inner one, - // we must have transplanted it. The JS engine tries to maintain - // the global object's compartment as its default compartment, - // so update that now since it might have changed. - JS_SetGlobalObject(mContext, outerGlobal); return NS_OK; } @@ -2639,6 +2640,7 @@ nsresult nsJSContext::InitOuterWindow() { JSObject *global = JS_GetGlobalObject(mContext); + OBJ_TO_INNER_OBJECT(mContext, global); nsresult rv = InitClasses(global); // this will complete global object initialization NS_ENSURE_SUCCESS(rv, rv); diff --git a/js/src/xpconnect/src/xpcwrappedjsclass.cpp b/js/src/xpconnect/src/xpcwrappedjsclass.cpp index 059b887eab93..dd303a981f3a 100644 --- a/js/src/xpconnect/src/xpcwrappedjsclass.cpp +++ b/js/src/xpconnect/src/xpcwrappedjsclass.cpp @@ -45,6 +45,7 @@ #include "nsArrayEnumerator.h" #include "nsWrapperCache.h" #include "XPCWrapper.h" +#include "AccessCheck.h" NS_IMPL_THREADSAFE_ISUPPORTS1(nsXPCWrappedJSClass, nsIXPCWrappedJSClass) @@ -257,22 +258,9 @@ nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(XPCCallContext& ccx, // interface (i.e. whether the interface is scriptable) and most content // objects don't have QI implementations anyway. Also see bug 503926. if(XPCPerThreadData::IsMainThread(ccx) && - !JS_GetGlobalForObject(ccx, jsobj)->isSystem()) + !xpc::AccessCheck::isChrome(jsobj->getCompartment(ccx))) { - nsCOMPtr objprin; - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if(ssm) - { - nsresult rv = ssm->GetObjectPrincipal(ccx, jsobj, getter_AddRefs(objprin)); - NS_ENSURE_SUCCESS(rv, nsnull); - - PRBool isSystem; - rv = ssm->IsSystemPrincipal(objprin, &isSystem); - NS_ENSURE_SUCCESS(rv, nsnull); - - if(!isSystem) - return nsnull; - } + return nsnull; } // check upfront for the existence of the function property @@ -1335,11 +1323,12 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, if(XPCPerThreadData::IsMainThread(ccx)) { + // TODO Remove me in favor of security wrappers. nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if(ssm) { - nsCOMPtr objPrincipal; - ssm->GetObjectPrincipal(ccx, obj, getter_AddRefs(objPrincipal)); + nsIPrincipal *objPrincipal = + xpc::AccessCheck::getPrincipal(obj->getCompartment(ccx)); if(objPrincipal) { JSStackFrame* fp = nsnull; @@ -1515,6 +1504,13 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, } // build the args + // NB: This assignment *looks* wrong because we haven't yet called our + // function. However, we *have* already entered the compartmen that we're + // about to call, and that's the global that we want here. In other words: + // we're trusting the JS engine to come up with a good global to use for + // our object (whatever it was). + JSObject *scopeobj; + scopeobj = JS_GetGlobalForScopeChain(ccx); for(i = 0; i < argc; i++) { const nsXPTParamInfo& param = info->params[i]; @@ -1575,7 +1571,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, if(!XPCConvert::NativeArray2JS(lccx, &val, (const void**)&pv->val, datum_type, ¶m_iid, - array_count, obj, nsnull)) + array_count, scopeobj, nsnull)) goto pre_call_clean_up; } else if(isSizedString) @@ -1589,7 +1585,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, else { if(!XPCConvert::NativeData2JS(ccx, &val, &pv->val, type, - ¶m_iid, obj, nsnull)) + ¶m_iid, scopeobj, nsnull)) goto pre_call_clean_up; } } @@ -1597,7 +1593,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, if(param.IsOut() || param.IsDipper()) { // create an 'out' object - JSObject* out_obj = NewOutObject(cx, obj); + JSObject* out_obj = NewOutObject(cx, scopeobj); if(!out_obj) { retval = NS_ERROR_OUT_OF_MEMORY; diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index bb3e056ceaa6..4828b06078e6 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -51,6 +51,7 @@ #include "nsINode.h" #include "xpcquickstubs.h" #include "jsproxy.h" +#include "AccessCheck.h" /***************************************************************************/ @@ -518,9 +519,8 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, nsCOMPtr wrappedjs(do_QueryInterface(Object)); JSObject *obj; wrappedjs->GetJSObject(&obj); - if((obj->isSystem() || - JS_GetGlobalForObject(ccx, obj)->isSystem()) && - !Scope->GetGlobalJSObject()->isSystem()) + if(xpc::AccessCheck::isChrome(obj->getCompartment(ccx)) && + !xpc::AccessCheck::isChrome(Scope->GetGlobalJSObject()->getCompartment(ccx))) { needsCOW = JS_TRUE; } diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 9a3b5d899772..549173514205 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -76,6 +76,12 @@ AccessCheck::isChrome(JSCompartment *compartment) return NS_SUCCEEDED(ssm->IsSystemPrincipal(principal, &privileged)) && privileged; } +nsIPrincipal * +AccessCheck::getPrincipal(JSCompartment *compartment) +{ + return GetCompartmentPrincipal(compartment); +} + #define NAME(ch, str, cases) case ch: if (!strcmp(name, str)) switch (prop[0]) { cases }; break; #define PROP(ch, actions) case ch: { actions }; break; #define RW(str) if (!strcmp(prop, str)) return true; diff --git a/js/src/xpconnect/wrappers/AccessCheck.h b/js/src/xpconnect/wrappers/AccessCheck.h index 5a4b17a65d7c..74aacfdc3967 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.h +++ b/js/src/xpconnect/wrappers/AccessCheck.h @@ -40,12 +40,15 @@ #include "jsapi.h" #include "jswrapper.h" +class nsIPrincipal; + namespace xpc { class AccessCheck { public: static bool isSameOrigin(JSCompartment *a, JSCompartment *b); static bool isChrome(JSCompartment *compartment); + static nsIPrincipal *getPrincipal(JSCompartment *compartment); static bool isCrossOriginAccessPermitted(JSContext *cx, JSObject *obj, jsid id, JSWrapper::Action act); static bool isSystemOnlyAccessPermitted(JSContext *cx); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 637a3367e16e..6269ae01c856 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -138,7 +138,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO } } - JSObject *wrapperObj = JSWrapper::New(cx, obj, wrappedProto, NULL, wrapper); + JSObject *wrapperObj = JSWrapper::New(cx, obj, wrappedProto, parent, wrapper); if (!wrapperObj || !xrayHolder) return wrapperObj; wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder)); From 948fa12cc47e2774eeb0d32b28aebde143ca5577 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 24 Sep 2010 23:49:58 -0700 Subject: [PATCH 139/284] Bug 580128 - Use compartments instead of global objects to see if we should use slim wrappers. r=peterv --- js/src/xpconnect/src/xpcconvert.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index c881d769ccd6..a07a8b6d0529 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -1193,8 +1193,8 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, } else if(IS_SLIM_WRAPPER_OBJECT(flat)) { - JSObject* global = JS_GetGlobalForObject(cx, flat); - if(global == xpcscope->GetGlobalJSObject()) + if(flat->getCompartment(cx) == + xpcscope->GetGlobalJSObject()->getCompartment(cx)) { *d = OBJECT_TO_JSVAL(flat); return JS_TRUE; From 6fd1e21fc8f11dea9edfeca8b8adf481e6bea651 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 27 Sep 2010 13:49:09 -0700 Subject: [PATCH 140/284] bug 599503 - Return true compartment info from JSObject::getCompartment(). r=jorendorff/gregor/gal --- js/src/jsapi.cpp | 13 +++---- js/src/jsapi.h | 5 +-- js/src/jscntxtinlines.h | 3 +- js/src/jscompartment.cpp | 2 +- js/src/jscompartment.h | 2 +- js/src/jsfun.cpp | 2 +- js/src/jsgc.h | 6 +++ js/src/jsobj.cpp | 37 +------------------ js/src/jsobj.h | 2 +- js/src/jswrapper.cpp | 2 +- js/src/xpconnect/src/nsXPConnect.cpp | 2 +- js/src/xpconnect/src/xpcconvert.cpp | 4 +- js/src/xpconnect/src/xpcwrappedjsclass.cpp | 4 +- js/src/xpconnect/src/xpcwrappednative.cpp | 4 +- .../xpconnect/wrappers/CrossOriginWrapper.cpp | 2 +- js/src/xpconnect/wrappers/WrapperFactory.cpp | 2 +- 16 files changed, 30 insertions(+), 62 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 13c406bda455..54f1b5fd9de7 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1184,7 +1184,7 @@ bool JSAutoEnterCompartment::enter(JSContext *cx, JSObject *target) { JS_ASSERT(!call); - if (cx->compartment == target->getCompartment(cx)) + if (cx->compartment == target->getCompartment()) return true; call = JS_EnterCrossCompartmentCall(cx, target); return call != NULL; @@ -1236,7 +1236,7 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) * need to "move" the window from wrapper's compartment to target's * compartment. */ - JSCompartment *destination = target->getCompartment(cx); + JSCompartment *destination = target->getCompartment(); JSObject *obj; WrapperMap &map = destination->crossCompartmentWrappers; @@ -1302,7 +1302,7 @@ JS_SetGlobalObject(JSContext *cx, JSObject *obj) cx->globalObject = obj; if (!cx->maybefp()) - cx->compartment = obj ? obj->getCompartment(cx) : cx->runtime->defaultCompartment; + cx->compartment = obj ? obj->getCompartment() : cx->runtime->defaultCompartment; } class AutoResolvingEntry { @@ -2976,13 +2976,10 @@ JS_NewGlobalObject(JSContext *cx, JSClass *clasp) CHECK_REQUEST(cx); JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL); JSObject *obj = NewNonFunction(cx, Valueify(clasp), NULL, NULL); - if (!obj || - !js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_COMPARTMENT, - PrivateValue(cx->compartment))) { + if (!obj) return NULL; - } - /* FIXME: comment. */ + /* Construct a regexp statics object for this global object. */ JSObject *res = regexp_statics_construct(cx); if (!res || !js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_REGEXP_STATICS, diff --git a/js/src/jsapi.h b/js/src/jsapi.h index a4f914947eb5..0909dc726162 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1721,9 +1721,8 @@ struct JSClass { #define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4)) /* Additional global reserved slots, beyond those for standard prototypes. */ -#define JSRESERVED_GLOBAL_SLOTS_COUNT 4 -#define JSRESERVED_GLOBAL_COMPARTMENT (JSProto_LIMIT * 3) -#define JSRESERVED_GLOBAL_THIS (JSRESERVED_GLOBAL_COMPARTMENT + 1) +#define JSRESERVED_GLOBAL_SLOTS_COUNT 3 +#define JSRESERVED_GLOBAL_THIS (JSProto_LIMIT * 3) #define JSRESERVED_GLOBAL_THROWTYPEERROR (JSRESERVED_GLOBAL_THIS + 1) #define JSRESERVED_GLOBAL_REGEXP_STATICS (JSRESERVED_GLOBAL_THROWTYPEERROR + 1) diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 4b5a20d4d104..eea500249050 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -46,6 +46,7 @@ #include "jsstaticcheck.h" #include "jsxml.h" #include "jsregexp.h" +#include "jsgc.h" inline js::RegExpStatics * JSContext::regExpStatics() @@ -525,7 +526,7 @@ class CompartmentChecker void check(JSObject *obj) { if (obj) - check(obj->getCompartment(context)); + check(obj->getCompartment()); } void check(const js::Value &v) { diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 364b052d65b9..6b9eeece2eee 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -153,7 +153,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp) } /* If the wrapped object is already in this compartment, we are done. */ - if (obj->getCompartment(cx) == this) + if (obj->compartment() == this) return true; } diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index c5782306d92b..944b2dcd24b2 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -123,7 +123,7 @@ class SwitchToCompartment : public PreserveCompartment { } SwitchToCompartment(JSContext *cx, JSObject *target) : PreserveCompartment(cx) { - cx->compartment = target->getCompartment(cx); + cx->compartment = target->getCompartment(); } }; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index c009148681d5..2b56b50ab46c 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1636,7 +1636,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) JSObject &caller = vp->toObject(); /* Censor the caller if it is from another compartment. */ - if (caller.getCompartment(cx) != cx->compartment) { + if (caller.getCompartment() != cx->compartment) { vp->setNull(); } else if (caller.isFunction() && caller.getFunctionPrivate()->inStrictMode()) { JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, diff --git a/js/src/jsgc.h b/js/src/jsgc.h index dc01d92aed18..d000cdb7d015 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1037,4 +1037,10 @@ NewCompartment(JSContext *cx, JSPrincipals *principals); } /* namespace js */ } /* namespace gc */ +inline JSCompartment * +JSObject::getCompartment() const +{ + return ((Cell *)this)->compartment(); +} + #endif /* jsgc_h___ */ diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 4e3949a0a35d..894388d53510 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -6207,7 +6207,7 @@ js_TraceObject(JSTracer *trc, JSObject *obj) (void) clasp->mark(cx, obj, trc); } if (clasp->flags & JSCLASS_IS_GLOBAL) { - JSCompartment *compartment = obj->getCompartment(cx); + JSCompartment *compartment = obj->getCompartment(); compartment->marked = true; } @@ -6326,41 +6326,6 @@ js_ReportGetterOnlyAssignment(JSContext *cx) JSMSG_GETTER_ONLY); } -JSCompartment * -JSObject::getCompartment(JSContext *cx) -{ - JSObject *obj = getGlobal(); - - Class *clasp = obj->getClass(); - if (!(clasp->flags & JSCLASS_IS_GLOBAL)) { -#if JS_HAS_XML_SUPPORT - // The magic AnyName object is runtime-wide. - if (clasp == &js_AnyNameClass) - return cx->runtime->defaultCompartment; - - // The magic function namespace object is runtime-wide. - if (clasp == &js_NamespaceClass && - obj->getNameURI() == ATOM_TO_JSVAL(cx->runtime-> - atomState.functionNamespaceURIAtom)) { - return cx->runtime->defaultCompartment; - } -#endif - - /* - * Script objects and compile-time Function, Block, RegExp objects - * are not parented. - */ - if (clasp == &js_FunctionClass || clasp == &js_BlockClass || clasp == &js_RegExpClass || - clasp == &js_ScriptClass) { - // This is a bogus answer, but it'll do for now. - return cx->runtime->defaultCompartment; - } - JS_NOT_REACHED("non-global object at end of scope chain"); - } - const Value &v = obj->getReservedSlot(JSRESERVED_GLOBAL_COMPARTMENT); - return (JSCompartment *)v.toPrivate(); -} - JS_FRIEND_API(JSBool) js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 541f970a5c73..d3f85a3ae2ce 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1117,7 +1117,7 @@ struct JSObject : js::gc::Cell { inline void dropProperty(JSContext *cx, JSProperty *prop); - JS_FRIEND_API(JSCompartment *) getCompartment(JSContext *cx); + inline JSCompartment *getCompartment() const; inline JSObject *getThrowTypeError() const; diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 67d4c35d414f..7f3e8fd21927 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -307,7 +307,7 @@ AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target) : context(cx), origin(cx->compartment), target(target), - destination(target->getCompartment(cx)), + destination(target->getCompartment()), input(cx), entered(false) { diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 7b72937836f5..7e534cb3d5d9 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -1010,7 +1010,7 @@ xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, return UnexpectedFailure(NS_ERROR_FAILURE); *global = tempGlobal; - *compartment = tempGlobal->getCompartment(cx); + *compartment = tempGlobal->getCompartment(); js::SwitchToCompartment sc(cx, *compartment); diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index a07a8b6d0529..9f840be8cc12 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -1193,8 +1193,8 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, } else if(IS_SLIM_WRAPPER_OBJECT(flat)) { - if(flat->getCompartment(cx) == - xpcscope->GetGlobalJSObject()->getCompartment(cx)) + if(flat->getCompartment() == + xpcscope->GetGlobalJSObject()->getCompartment()) { *d = OBJECT_TO_JSVAL(flat); return JS_TRUE; diff --git a/js/src/xpconnect/src/xpcwrappedjsclass.cpp b/js/src/xpconnect/src/xpcwrappedjsclass.cpp index dd303a981f3a..6b2d5386dc7f 100644 --- a/js/src/xpconnect/src/xpcwrappedjsclass.cpp +++ b/js/src/xpconnect/src/xpcwrappedjsclass.cpp @@ -258,7 +258,7 @@ nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(XPCCallContext& ccx, // interface (i.e. whether the interface is scriptable) and most content // objects don't have QI implementations anyway. Also see bug 503926. if(XPCPerThreadData::IsMainThread(ccx) && - !xpc::AccessCheck::isChrome(jsobj->getCompartment(ccx))) + !xpc::AccessCheck::isChrome(jsobj->getCompartment())) { return nsnull; } @@ -1328,7 +1328,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, if(ssm) { nsIPrincipal *objPrincipal = - xpc::AccessCheck::getPrincipal(obj->getCompartment(ccx)); + xpc::AccessCheck::getPrincipal(obj->getCompartment()); if(objPrincipal) { JSStackFrame* fp = nsnull; diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 4828b06078e6..fc79cbaba2e4 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -519,8 +519,8 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, nsCOMPtr wrappedjs(do_QueryInterface(Object)); JSObject *obj; wrappedjs->GetJSObject(&obj); - if(xpc::AccessCheck::isChrome(obj->getCompartment(ccx)) && - !xpc::AccessCheck::isChrome(Scope->GetGlobalJSObject()->getCompartment(ccx))) + if(xpc::AccessCheck::isChrome(obj->getCompartment()) && + !xpc::AccessCheck::isChrome(Scope->GetGlobalJSObject()->getCompartment())) { needsCOW = JS_TRUE; } diff --git a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp index 19433b6d811a..9e949945bf16 100644 --- a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp +++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp @@ -67,7 +67,7 @@ CrossOriginWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act) return true; } JSStackFrame *fp = NULL; - nsIPrincipal *principal = GetCompartmentPrincipal(wrappedObject(wrapper)->getCompartment(cx)); + nsIPrincipal *principal = GetCompartmentPrincipal(wrappedObject(wrapper)->getCompartment()); nsresult rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp), principal); if (NS_FAILED(rv)) { NS_WARNING("Not allowing call because we're out of memory"); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 6269ae01c856..7eac89ddfa8d 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -80,7 +80,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO if (!obj) return nsnull; - JSCompartment *origin = obj->getCompartment(cx); + JSCompartment *origin = obj->getCompartment(); JSCompartment *target = cx->compartment; JSObject *xrayHolder = nsnull; From be323545f1d2c9eddc5eaec853e120d15c4f7950 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Mon, 27 Sep 2010 18:24:24 -0700 Subject: [PATCH 141/284] bug 580033 - Make JS_Save/RestoreFrameChain set cx->compartment. r=mrbkap --- js/src/jsapi.cpp | 5 +++-- js/src/jscntxt.cpp | 31 +++++++++++++++++++++++++++++++ js/src/jscntxt.h | 5 +++++ js/src/jswrapper.cpp | 12 ++++++++++-- js/src/jswrapper.h | 3 +++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 54f1b5fd9de7..0540fa2e2d84 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1301,8 +1301,8 @@ JS_SetGlobalObject(JSContext *cx, JSObject *obj) CHECK_REQUEST(cx); cx->globalObject = obj; - if (!cx->maybefp()) - cx->compartment = obj ? obj->getCompartment() : cx->runtime->defaultCompartment; + if (!cx->hasfp()) + cx->resetCompartment(); } class AutoResolvingEntry { @@ -5032,6 +5032,7 @@ JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp) if (!fp) return; cx->restoreSegment(); + cx->resetCompartment(); } /************************************************************************/ diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 18802d71d472..16ce63a9ead2 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -2049,6 +2049,37 @@ JSContext::JSContext(JSRuntime *rt) busyArrays(this) {} +void +JSContext::resetCompartment() +{ + JSObject *scopeobj; + if (hasfp()) { + scopeobj = &fp()->scopeChain(); + } else { + scopeobj = globalObject; + if (!scopeobj) { + compartment = runtime->defaultCompartment; + return; + } + + /* + * Innerize. Assert, but check anyway, that this succeeds. (It + * can only fail due to bugs in the engine or embedding.) + */ + OBJ_TO_INNER_OBJECT(this, scopeobj); + if (!scopeobj) { + /* + * Bug. Return NULL, not defaultCompartment, to crash rather + * than open a security hole. + */ + JS_ASSERT(0); + compartment = NULL; + return; + } + } + compartment = scopeobj->getCompartment(); +} + void JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs) { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 3072311db842..e7569cef64a0 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -2021,9 +2021,14 @@ struct JSContext friend class js::StackSpace; friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, uintN); + void resetCompartment(); + /* 'regs' must only be changed by calling this function. */ void setCurrentRegs(JSFrameRegs *regs) { + JS_ASSERT_IF(regs, regs->fp); this->regs = regs; + if (!regs) + resetCompartment(); } /* Temporary arena pool used while compiling and decompiling. */ diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 7f3e8fd21927..def4a95088d6 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -325,6 +325,13 @@ AutoCompartment::enter() JS_ASSERT(!entered); if (origin != destination) { LeaveTrace(context); + +#ifdef DEBUG + JSCompartment *oldCompartment = context->compartment; + context->resetCompartment(); + wasSane = (context->compartment == oldCompartment); +#endif + context->compartment = destination; JSObject *scopeChain = target->getGlobal(); frame.construct(); @@ -344,8 +351,9 @@ AutoCompartment::leave() JS_ASSERT(entered); if (origin != destination) { frame.destroy(); - context->compartment = origin; - origin->wrapException(context); + context->resetCompartment(); + JS_ASSERT_IF(wasSane && context->hasfp(), context->compartment == origin); + context->compartment->wrapException(context); } entered = false; } diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 6e1d75d8d0f0..22b0d0e8f55c 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -166,6 +166,9 @@ class AutoCompartment JSFrameRegs regs; AutoStringRooter input; bool entered; +#ifdef DEBUG + bool wasSane; +#endif public: AutoCompartment(JSContext *cx, JSObject *target); From 51fd47c31c3a2d43b8cf91846c9736ab40e14581 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Mon, 27 Sep 2010 19:52:15 -0700 Subject: [PATCH 142/284] bug 580128 - Make js_DumpStackFrame deal with dummy frames. r=jst --- js/src/jsobj.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 894388d53510..7e740bad3841 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -6656,9 +6656,13 @@ js_DumpStackFrame(JSContext *cx, JSStackFrame *start) } MaybeDumpObject("callobj", fp->maybeCallObj()); MaybeDumpObject("argsobj", fp->maybeArgsObj()); - MaybeDumpValue("this", fp->thisValue()); - fprintf(stderr, " rval: "); - dumpValue(fp->returnValue()); + if (!fp->isDummyFrame()) { + MaybeDumpValue("this", fp->thisValue()); + fprintf(stderr, " rval: "); + dumpValue(fp->returnValue()); + } else { + fprintf(stderr, "dummy frame"); + } fputc('\n', stderr); fprintf(stderr, " flags:"); From b72500f6bcdee67e493d2655a677a38c7823dc56 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Mon, 27 Sep 2010 20:40:27 -0700 Subject: [PATCH 143/284] bug 580128 - Wrap values in the array. r=jst --- dom/base/nsJSEnvironment.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 4775b040507b..f9d16ff653ac 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2682,6 +2682,12 @@ nsJSContext::SetProperty(void *aTarget, const char *aPropName, nsISupports *aArg if (strcmp(aPropName, "dialogArguments") == 0 && argc <= 1) { vargs = argc ? argv[0] : JSVAL_VOID; } else { + for (PRUint32 i = 0; i < argc; ++i) { + if (!JS_WrapValue(mContext, &argv[i])) { + return NS_ERROR_FAILURE; + } + } + JSObject *args = ::JS_NewArrayObject(mContext, argc, argv); vargs = OBJECT_TO_JSVAL(args); } From 7fa4640e8eeb9a380108095e6e827c7e0b387ea6 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Tue, 28 Sep 2010 17:02:43 -0700 Subject: [PATCH 144/284] bug 580128 - Fix bugs dealing with transplanting when navigating. r=gal --- js/src/jsapi.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 0540fa2e2d84..54cf07249419 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1237,6 +1237,13 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) * compartment. */ JSCompartment *destination = target->getCompartment(); + if (wrapper->getCompartment() == destination) { + // If the wrapper is in the same compartment as the destination, then + // we know that we won't find wrapper in the destination's cross + // compartment map and that the same object will continue to work. + wrapper->swap(target); + return wrapper; + } JSObject *obj; WrapperMap &map = destination->crossCompartmentWrappers; @@ -1260,7 +1267,7 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) // outer window. They need to be updated to point at the new outer window. // They also might transition between different types of security wrappers // based on whether the new compartment is same origin with them. - Value targetv = ObjectValue(*target); + Value targetv = ObjectValue(*obj); WrapperVector &vector = cx->runtime->compartments; for (JSCompartment **p = vector.begin(), **end = vector.end(); p != end; ++p) { WrapperMap &pmap = (*p)->crossCompartmentWrappers; @@ -1268,6 +1275,11 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) // We found a wrapper around the outer window! JSObject *wobj = &wp->value.toObject(); + // NB: Can't use wp after this point. We remove wp from the map in + // order to ensure that we create a wrapper with the proper guts + // for the brain transplant. + pmap.remove(wp); + // First, we wrap it in the new compartment. This will return a // new wrapper. JSAutoEnterCompartment ec; @@ -1278,14 +1290,23 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) // Now, because we need to maintain object identity, we do a brain // transplant on the old object. At the same time, we update the // entry in the compartment's wrapper map to point to the old - // wrapper, and remove the old outer window from the wrapper map, - // since it is now an obsolete reference. + // wrapper. + JS_ASSERT(tobj != wobj); wobj->swap(tobj); pmap.put(targetv, ObjectValue(*wobj)); - pmap.remove(wp); } } + // Lastly, update the old outer window proxy to point to the new one. + { + JSAutoEnterCompartment ac; + JSObject *tobj = obj; + if (!ac.enter(cx, wrapper) || !JS_WrapObject(cx, &tobj)) + return NULL; + wrapper->swap(tobj); + wrapper->getCompartment()->crossCompartmentWrappers.put(targetv, wrapperv); + } + return obj; } From e8317e009fef09c3e1edc8e8dc083361b82df300 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Tue, 28 Sep 2010 17:02:43 -0700 Subject: [PATCH 145/284] bug 580128 - Outerize in JS instead of in the rewrap hook so the identity in the wrapper map stays constant. r=gal --- js/src/jscompartment.cpp | 27 ++++++++++---------- js/src/xpconnect/wrappers/WrapperFactory.cpp | 4 --- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 6b9eeece2eee..17a563018bbf 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -145,16 +145,23 @@ JSCompartment::wrap(JSContext *cx, Value *vp) /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { obj = vp->toObject().unwrap(&flags); - OBJ_TO_OUTER_OBJECT(cx, obj); - if (!obj) - return false; vp->setObject(*obj); + + /* If the wrapped object is already in this compartment, we are done. */ + if (obj->getCompartment() == this) + return true; + } else { + JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); + vp->setObject(*obj); } - /* If the wrapped object is already in this compartment, we are done. */ - if (obj->compartment() == this) - return true; + OBJ_TO_OUTER_OBJECT(cx, obj); + if (!obj) + return false; + + JS_ASSERT(obj->getCompartment() == vp->toObject().getCompartment()); + vp->setObject(*obj); } /* If we already have a wrapper for this value, use it. */ @@ -217,14 +224,6 @@ JSCompartment::wrap(JSContext *cx, Value *vp) vp->setObject(*wrapper); - /* - * If the returned "wrapper" is not a proxy, then we were attempting to - * wrap an XPConnect "holder" object and the actual wrapped object was - * in our compartment. - */ - if (!wrapper->isProxy()) - return true; - wrapper->setProto(proto); if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp)) return false; diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 7eac89ddfa8d..5005ce4f8c68 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -76,10 +76,6 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj)) return nsnull; - OBJ_TO_OUTER_OBJECT(cx, obj); - if (!obj) - return nsnull; - JSCompartment *origin = obj->getCompartment(); JSCompartment *target = cx->compartment; JSObject *xrayHolder = nsnull; From cba6015395e6ee05b58da838a53d85955e9da65d Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Tue, 28 Sep 2010 17:02:43 -0700 Subject: [PATCH 146/284] bug 580128 - Forward to outer in AreDialogsBlocked. r=mrbkap --- dom/base/nsGlobalWindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 4dfcaa5b41a0..a50bb96a9a8c 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2438,6 +2438,8 @@ nsGlobalWindow::AreDialogsBlocked() bool nsGlobalWindow::ConfirmDialogAllowed() { + FORWARD_TO_OUTER(ConfirmDialogAllowed, (), NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_TRUE(mDocShell, false); nsCOMPtr promptSvc = do_GetService("@mozilla.org/embedcomp/prompt-service;1"); From aea5afc9a8e776d02feff813858bb355ca25e3e6 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 28 Sep 2010 17:02:43 -0700 Subject: [PATCH 147/284] Fix dom/tests/mochitest/general/file_frameElementWrapping.html to deal with proxies. r=mrbkap --- dom/tests/mochitest/general/file_frameElementWrapping.html | 4 ++-- js/src/xpconnect/tests/mochitest/test_frameWrapping.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dom/tests/mochitest/general/file_frameElementWrapping.html b/dom/tests/mochitest/general/file_frameElementWrapping.html index 7d30a5e117df..4e91efe4ab61 100644 --- a/dom/tests/mochitest/general/file_frameElementWrapping.html +++ b/dom/tests/mochitest/general/file_frameElementWrapping.html @@ -1,10 +1,10 @@ + + + + + Mozilla Bug 448587 + + + + + diff --git a/js/src/xpconnect/tests/mochitest/Makefile.in b/js/src/xpconnect/tests/mochitest/Makefile.in index 6e692208f480..75e99a6d611b 100644 --- a/js/src/xpconnect/tests/mochitest/Makefile.in +++ b/js/src/xpconnect/tests/mochitest/Makefile.in @@ -57,7 +57,6 @@ _TEST_FILES = bug500931_helper.html \ test_bug393269.html \ test_bug396851.html \ test_bug428021.html \ - test_bug448587.html \ test_wrappers.html \ test_bug446584.html \ test_bug462428.html \ diff --git a/js/src/xpconnect/tests/mochitest/test_bug448587.html b/js/src/xpconnect/tests/mochitest/test_bug448587.html deleted file mode 100644 index 112be86c84a4..000000000000 --- a/js/src/xpconnect/tests/mochitest/test_bug448587.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Test for Bug 448587 - - - - - -Mozilla Bug 448587 -

- -
-
-
- - - diff --git a/js/src/xpconnect/tests/mochitest/test_bug505915.html b/js/src/xpconnect/tests/mochitest/test_bug505915.html index cfba3d001c16..b510ffde060a 100644 --- a/js/src/xpconnect/tests/mochitest/test_bug505915.html +++ b/js/src/xpconnect/tests/mochitest/test_bug505915.html @@ -39,9 +39,11 @@ function go() { } var xhr = new XMLHttpRequest(); +/* XXX var orsc = new XPCSafeJSObjectWrapper(function(){}); xhr.onreadystatechange = orsc; // we only can test that the above didn't throw (which it shouldn't). +*/ try { xhr.onreadystatechange = ifr.contentWindow; diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index f2b1d0b3a6a1..8e4a36d69583 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -146,6 +146,9 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO JSObject *wrapperObj = JSWrapper::New(cx, obj, wrappedProto, parent, wrapper); if (!wrapperObj || !xrayHolder) return wrapperObj; + + // NB: The fact that the only wrappers to use ProxyExtra are XrayWrappers + // is relied on by XPCNativeWrapper.unwrap. wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder)); xrayHolder->setSlot(XrayUtils::JSSLOT_PROXY_OBJ, js::ObjectValue(*wrapperObj)); return wrapperObj; From c00723aef747dccfce89b0c80d8506dfdeb575e6 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:36:01 -0700 Subject: [PATCH 157/284] bug 580128 - Find the true caller through the wrappers. r=jst --- dom/base/nsGlobalWindow.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index a50bb96a9a8c..4eb5006069fe 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -5573,8 +5573,24 @@ nsGlobalWindow::CallerInnerWindow() return nsnull; } - JSObject *scope = ::JS_GetScopeChain(cx); + JSObject *scope = nsnull; + JSStackFrame *fp = nsnull; + JS_FrameIterator(cx, &fp); + if (fp) { + while (fp->isDummyFrame()) { + if (!JS_FrameIterator(cx, &fp)) + break; + } + + if (fp) + scope = &fp->scopeChain(); + } + if (!scope) + scope = JS_GetScopeChain(cx); + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, scope)) return nsnull; nsCOMPtr wrapper; From 3ae5caff40cc0001d07d103000837d328fad090b Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:36:04 -0700 Subject: [PATCH 158/284] bug 580128 - Give XrayWrappers a flag they can query for. r=mrbkap --- js/src/jswrapper.cpp | 8 --- js/src/jswrapper.h | 75 +++++++++------------- js/src/xpconnect/wrappers/WrapperFactory.h | 9 ++- js/src/xpconnect/wrappers/XrayWrapper.cpp | 3 +- js/src/xpconnect/wrappers/XrayWrapper.h | 2 +- 5 files changed, 41 insertions(+), 56 deletions(-) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index eba224b87840..003d138d40a1 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -85,10 +85,6 @@ JSObject::unwrap(uintN *flagsp) return wrapped; } -JSWrapper::JSWrapper(void *family) : JSProxyHandler(&sWrapperFamily), mFlags(0) -{ -} - JSWrapper::JSWrapper(uintN flags) : JSProxyHandler(&sWrapperFamily), mFlags(flags) { } @@ -364,10 +360,6 @@ AutoCompartment::leave() /* Cross compartment wrappers. */ -JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(void *family) : JSWrapper((uintN)0) -{ -} - JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags) : JSWrapper(flags) { } diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 57fc96e5efad..a7ff77102462 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -48,78 +48,65 @@ JS_BEGIN_EXTERN_C /* No-op wrapper handler base class. */ -class JSWrapper : public js::JSProxyHandler { +class JS_FRIEND_API(JSWrapper) : public js::JSProxyHandler { uintN mFlags; - protected: - // XXX Hack to let wrappers derive from either cross compartment - // wrappers or JSProxyHandlers. - JS_FRIEND_API(JSWrapper(void *family)); public: uintN flags() const { return mFlags; } - explicit JS_FRIEND_API(JSWrapper(uintN flags)); + explicit JSWrapper(uintN flags); typedef enum { PermitObjectAccess, PermitPropertyAccess, DenyAccess } Permission; - JS_FRIEND_API(virtual ~JSWrapper()); + virtual ~JSWrapper(); /* ES5 Harmony fundamental wrapper traps. */ - virtual JS_FRIEND_API(bool) getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - bool set, js::PropertyDescriptor *desc); - virtual JS_FRIEND_API(bool) getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - bool set, js::PropertyDescriptor *desc); - virtual JS_FRIEND_API(bool) defineProperty(JSContext *cx, JSObject *wrapper, jsid id, - js::PropertyDescriptor *desc); - virtual JS_FRIEND_API(bool) getOwnPropertyNames(JSContext *cx, JSObject *wrapper, - js::AutoIdVector &props); - virtual JS_FRIEND_API(bool) delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); - virtual JS_FRIEND_API(bool) enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); - virtual JS_FRIEND_API(bool) fix(JSContext *cx, JSObject *wrapper, js::Value *vp); + virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, + js::PropertyDescriptor *desc); + virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, + js::PropertyDescriptor *desc); + virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, + js::PropertyDescriptor *desc); + virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); + virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); + virtual bool enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); + virtual bool fix(JSContext *cx, JSObject *wrapper, js::Value *vp); /* ES5 Harmony derived wrapper traps. */ - virtual JS_FRIEND_API(bool) has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); - virtual JS_FRIEND_API(bool) hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); - virtual JS_FRIEND_API(bool) get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, - js::Value *vp); - virtual JS_FRIEND_API(bool) set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, - js::Value *vp); - virtual JS_FRIEND_API(bool) enumerateOwn(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); - virtual JS_FRIEND_API(bool) iterate(JSContext *cx, JSObject *wrapper, uintN flags, js::Value *vp); + virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); + virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp); + virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp); + virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp); + virtual bool enumerateOwn(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props); + virtual bool iterate(JSContext *cx, JSObject *wrapper, uintN flags, js::Value *vp); /* Spidermonkey extensions. */ - virtual JS_FRIEND_API(bool) call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp); - virtual JS_FRIEND_API(bool) construct(JSContext *cx, JSObject *wrapper, - uintN argc, js::Value *argv, js::Value *rval); - virtual JS_FRIEND_API(JSString *) obj_toString(JSContext *cx, JSObject *wrapper); - virtual JS_FRIEND_API(JSString *) fun_toString(JSContext *cx, JSObject *wrapper, uintN indent); + virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp); + virtual bool construct(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *argv, + js::Value *rval); + virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper); + virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent); - virtual JS_FRIEND_API(void) trace(JSTracer *trc, JSObject *wrapper); + virtual void trace(JSTracer *trc, JSObject *wrapper); /* Policy enforcement traps. */ enum Action { GET, SET, CALL }; - virtual JS_FRIEND_API(bool) enter(JSContext *cx, JSObject *wrapper, jsid id, Action act); - virtual JS_FRIEND_API(void) leave(JSContext *cx, JSObject *wrapper); + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act); + virtual void leave(JSContext *cx, JSObject *wrapper); - static JS_FRIEND_API(JSWrapper) singleton; + static JSWrapper singleton; - static JS_FRIEND_API(JSObject *) New(JSContext *cx, JSObject *obj, - JSObject *proto, JSObject *parent, - JSWrapper *handler); + static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, + JSWrapper *handler); static inline JSObject *wrappedObject(JSObject *wrapper) { return wrapper->getProxyPrivate().toObjectOrNull(); } - static JS_FRIEND_API(void *) getWrapperFamily(); + static void *getWrapperFamily(); }; /* Base class for all cross compartment wrapper handlers. */ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper { - protected: - // XXX Hack to let Xray wrappers derive from either cross compartment - // wrappers or JSProxyHandlers. - JSCrossCompartmentWrapper(void *family); - public: JSCrossCompartmentWrapper(uintN flags); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.h b/js/src/xpconnect/wrappers/WrapperFactory.h index 5c08c82af43a..dc14bdf83057 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.h +++ b/js/src/xpconnect/wrappers/WrapperFactory.h @@ -44,15 +44,20 @@ namespace xpc { class WrapperFactory { public: - enum { WAIVE_XRAY_WRAPPER_FLAG = (1<<0) }; + enum { WAIVE_XRAY_WRAPPER_FLAG = (1<<0), + IS_XRAY_WRAPPER_FLAG = (1<<1) }; // Return true if any of any of the nested wrappers have the flag set. - bool HasWrapperFlag(JSObject *wrapper, uintN flag) { + static bool HasWrapperFlag(JSObject *wrapper, uintN flag) { uintN flags = 0; wrapper->unwrap(&flags); return !!(flags & flag); } + static bool IsXrayWrapper(JSObject *wrapper) { + return HasWrapperFlag(wrapper, IS_XRAY_WRAPPER_FLAG); + } + // Rewrap an object that is about to cross compartment boundaries. static JSObject *Rewrap(JSContext *cx, JSObject *obj, diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index cfb1134125cb..7336ce086703 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -345,7 +345,8 @@ wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) } template -XrayWrapper::XrayWrapper(int flags) : Base(JSWrapper::getWrapperFamily()) +XrayWrapper::XrayWrapper(uintN flags) + : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG) { } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.h b/js/src/xpconnect/wrappers/XrayWrapper.h index 37f6da88a769..074cd6e90742 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.h +++ b/js/src/xpconnect/wrappers/XrayWrapper.h @@ -59,7 +59,7 @@ extern const uint32 JSSLOT_PROXY_OBJ; template class XrayWrapper : public Base { public: - XrayWrapper(int flags); + XrayWrapper(uintN flags); virtual ~XrayWrapper(); /* Fundamental proxy traps. */ From f55282e9f1c3b05aac8c8688785751e90547c1cc Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:36:20 -0700 Subject: [PATCH 159/284] bug 580128 - Use the shiny new API for finding XrayWrappers. r=jst --- js/src/xpconnect/src/XPCNativeWrapper.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/src/xpconnect/src/XPCNativeWrapper.cpp b/js/src/xpconnect/src/XPCNativeWrapper.cpp index 66b931a02fac..c43571b4c532 100644 --- a/js/src/xpconnect/src/XPCNativeWrapper.cpp +++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp @@ -888,9 +888,7 @@ UnwrapNW(JSContext *cx, uintN argc, jsval *vp) return JS_TRUE; } - // NB: This relies on the fact that the only wrappers to use ProxyExtra are - // Xray wrappers. - if (!obj->getProxyExtra().isUndefined()) { + if (xpc::WrapperFactory::IsXrayWrapper(obj)) { return JS_GetProperty(cx, obj, "wrappedJSObject", vp); } From 58483cc6853fdbb1ff0cf5f75f5d2da1c5599e9d Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:36:32 -0700 Subject: [PATCH 160/284] bug 580128 - Implement XrayWrapper::toString. r=jst --- js/src/xpconnect/wrappers/XrayWrapper.cpp | 49 ++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 7336ce086703..4818f9881bfa 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -344,6 +344,39 @@ wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) return true; } +static JSBool +XrayToString(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *wrapper = JS_THIS_OBJECT(cx, vp); + if (!wrapper->isWrapper() || !WrapperFactory::IsXrayWrapper(wrapper)) { + JS_ReportError(cx, "XrayToString called on an incompatible object"); + return false; + } + JSObject *holder = GetHolder(wrapper); + JSObject *wrappednative = GetWrappedNativeObjectFromHolder(cx, holder); + XPCWrappedNative *wn = GetWrappedNative(wrappednative); + + XPCCallContext ccx(JS_CALLER, cx, wrappednative); + char *wrapperStr = wn->ToString(ccx); + if (!wrapperStr) { + JS_ReportOutOfMemory(cx); + return false; + } + + nsAutoString result(NS_LITERAL_STRING("[object XrayWrapper ")); + result.AppendASCII(wrapperStr); + JS_smprintf_free(wrapperStr); + result.Append(']'); + + JSString *str = JS_NewUCStringCopyN(cx, reinterpret_cast(result.get()), + result.Length()); + if (!str) + return false; + + *vp = STRING_TO_JSVAL(str); + return true; +} + template XrayWrapper::XrayWrapper(uintN flags) : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG) @@ -391,7 +424,7 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe desc->attrs = JSPROP_ENUMERATE|JSPROP_SHARED; desc->getter = wrappedJSObject_getter; desc->setter = NULL; - desc->shortid = NULL; + desc->shortid = 0; desc->value = JSVAL_VOID; return true; } @@ -413,6 +446,20 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe if (!ok || desc->obj) return ok; + if (id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING)) { + desc->obj = wrapper; + desc->attrs = 0; + desc->getter = NULL; + desc->setter = NULL; + desc->shortid = 0; + + JSObject *toString = JS_NewFunction(cx, XrayToString, 0, 0, holder, "toString"); + if (!toString) + return false; + desc->value = OBJECT_TO_JSVAL(toString); + return true; + } + return JS_GetPropertyDescriptorById(cx, holder, id, (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED, desc); From 0e8ed043cbf6c498408fb6c6fbbb040cac8a099f Mon Sep 17 00:00:00 2001 From: Mounir Lamouri Date: Thu, 30 Sep 2010 00:14:30 -0700 Subject: [PATCH 161/284] Bug 580128. Make tests that now need to be chrome tests be chrome tests. r=jst@mozilla.org --HG-- rename : js/src/xpconnect/tests/mochitest/test_bug484459.html => js/src/xpconnect/tests/chrome/test_bug484459.xul rename : js/src/xpconnect/tests/mochitest/test_cows.html => js/src/xpconnect/tests/chrome/test_cows.xul rename : js/src/xpconnect/tests/mochitest/test_wrappers.html => js/src/xpconnect/tests/chrome/test_wrappers-2.xul --- js/src/xpconnect/tests/chrome/Makefile.in | 4 + .../tests/chrome/file_wrappers-2.html | 13 ++ .../xpconnect/tests/chrome/test_bug484459.xul | 37 +++++ .../test_cows.html => chrome/test_cows.xul} | 56 ++++--- .../test_wrappers-2.xul} | 137 ++++++++++-------- js/src/xpconnect/tests/mochitest/Makefile.in | 3 - .../tests/mochitest/test_bug484459.html | 36 ----- .../tests/mochitest/test_bug505915.html | 5 - 8 files changed, 165 insertions(+), 126 deletions(-) create mode 100644 js/src/xpconnect/tests/chrome/file_wrappers-2.html create mode 100644 js/src/xpconnect/tests/chrome/test_bug484459.xul rename js/src/xpconnect/tests/{mochitest/test_cows.html => chrome/test_cows.xul} (83%) rename js/src/xpconnect/tests/{mochitest/test_wrappers.html => chrome/test_wrappers-2.xul} (54%) delete mode 100644 js/src/xpconnect/tests/mochitest/test_bug484459.html diff --git a/js/src/xpconnect/tests/chrome/Makefile.in b/js/src/xpconnect/tests/chrome/Makefile.in index 823466b6bf3a..5f4ee1a4b15b 100644 --- a/js/src/xpconnect/tests/chrome/Makefile.in +++ b/js/src/xpconnect/tests/chrome/Makefile.in @@ -52,6 +52,10 @@ _CHROME_FILES = \ test_evalInSandbox.xul \ test_sandboxImport.xul \ test_wrappers.xul \ + test_wrappers-2.xul \ + test_bug484459.xul \ + file_wrappers-2.html \ + test_cows.xul \ $(NULL) libs:: $(_CHROME_FILES) diff --git a/js/src/xpconnect/tests/chrome/file_wrappers-2.html b/js/src/xpconnect/tests/chrome/file_wrappers-2.html new file mode 100644 index 000000000000..e27b07ed6a89 --- /dev/null +++ b/js/src/xpconnect/tests/chrome/file_wrappers-2.html @@ -0,0 +1,13 @@ + + + + + + diff --git a/js/src/xpconnect/tests/chrome/test_bug484459.xul b/js/src/xpconnect/tests/chrome/test_bug484459.xul new file mode 100644 index 000000000000..deaa260868a0 --- /dev/null +++ b/js/src/xpconnect/tests/chrome/test_bug484459.xul @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/js/src/xpconnect/tests/mochitest/test_cows.html b/js/src/xpconnect/tests/chrome/test_cows.xul similarity index 83% rename from js/src/xpconnect/tests/mochitest/test_cows.html rename to js/src/xpconnect/tests/chrome/test_cows.xul index 71f2ca92c64d..6f9a442d7c4d 100644 --- a/js/src/xpconnect/tests/mochitest/test_cows.html +++ b/js/src/xpconnect/tests/chrome/test_cows.xul @@ -1,20 +1,25 @@ - - - - Test for ChromeObjectWrappers - - - - - -

- -
-
+  
 
+  
+  
+  Mozilla Bug 522764 
+  
+
+  
+  
-
- - + ]]> + diff --git a/js/src/xpconnect/tests/mochitest/test_wrappers.html b/js/src/xpconnect/tests/chrome/test_wrappers-2.xul similarity index 54% rename from js/src/xpconnect/tests/mochitest/test_wrappers.html rename to js/src/xpconnect/tests/chrome/test_wrappers-2.xul index a97bfbfd5ed5..3a802f7d19cc 100644 --- a/js/src/xpconnect/tests/mochitest/test_wrappers.html +++ b/js/src/xpconnect/tests/chrome/test_wrappers-2.xul @@ -1,12 +1,34 @@ - - - Tests XPConnect Wrappers - - - - - - + + + + + Mozilla Bug 403005 + Mozilla Bug 409298 + + + + - - + + SimpleTest.finish(); + } + ]]> + + diff --git a/js/src/xpconnect/tests/mochitest/Makefile.in b/js/src/xpconnect/tests/mochitest/Makefile.in index 75e99a6d611b..589ffa694258 100644 --- a/js/src/xpconnect/tests/mochitest/Makefile.in +++ b/js/src/xpconnect/tests/mochitest/Makefile.in @@ -57,11 +57,9 @@ _TEST_FILES = bug500931_helper.html \ test_bug393269.html \ test_bug396851.html \ test_bug428021.html \ - test_wrappers.html \ test_bug446584.html \ test_bug462428.html \ test_bug478438.html \ - test_bug484459.html \ test_bug500691.html \ test_bug502959.html \ test_bug503926.html \ @@ -71,7 +69,6 @@ _TEST_FILES = bug500931_helper.html \ test_bug553407.html \ test_bug560351.html \ test_bug564330.html \ - test_cows.html \ test_frameWrapping.html \ test_bug589028.html \ bug589028_helper.html \ diff --git a/js/src/xpconnect/tests/mochitest/test_bug484459.html b/js/src/xpconnect/tests/mochitest/test_bug484459.html deleted file mode 100644 index 492af0dc9492..000000000000 --- a/js/src/xpconnect/tests/mochitest/test_bug484459.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - Test for Bug 484459 - - - - - -Mozilla Bug 484459 -

- -
-
-
- - diff --git a/js/src/xpconnect/tests/mochitest/test_bug505915.html b/js/src/xpconnect/tests/mochitest/test_bug505915.html index b510ffde060a..5ad9b947a7b2 100644 --- a/js/src/xpconnect/tests/mochitest/test_bug505915.html +++ b/js/src/xpconnect/tests/mochitest/test_bug505915.html @@ -39,11 +39,6 @@ function go() { } var xhr = new XMLHttpRequest(); -/* XXX - var orsc = new XPCSafeJSObjectWrapper(function(){}); - xhr.onreadystatechange = orsc; - // we only can test that the above didn't throw (which it shouldn't). -*/ try { xhr.onreadystatechange = ifr.contentWindow; From a6e9a41e46fdaf1c9a1c1ca011a3f7428f3ac967 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:36:38 -0700 Subject: [PATCH 162/284] bug 580128 - Allow API consumers to pass in a new object altogether before trying to wrap in a security wrapper. r=gal/peterv --- js/src/jsapi.cpp | 5 +- js/src/jsapi.h | 4 +- js/src/jscntxt.h | 1 + js/src/jscompartment.cpp | 58 +++++++++------- js/src/jspubtd.h | 8 +++ js/src/xpconnect/src/xpcjsruntime.cpp | 4 +- js/src/xpconnect/wrappers/WrapperFactory.cpp | 69 +++++++++++++++++++- js/src/xpconnect/wrappers/WrapperFactory.h | 6 ++ 8 files changed, 124 insertions(+), 31 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 54cf07249419..f35666fdde5c 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1148,10 +1148,13 @@ JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback) } JS_PUBLIC_API(JSWrapObjectCallback) -JS_SetWrapObjectCallback(JSRuntime *rt, JSWrapObjectCallback callback) +JS_SetWrapObjectCallbacks(JSRuntime *rt, + JSWrapObjectCallback callback, + JSPreWrapCallback precallback) { JSWrapObjectCallback old = rt->wrapObjectCallback; rt->wrapObjectCallback = callback; + rt->preWrapObjectCallback = precallback; return old; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 0909dc726162..14379c86d293 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -937,7 +937,9 @@ extern JS_PUBLIC_API(JSCompartmentCallback) JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback); extern JS_PUBLIC_API(JSWrapObjectCallback) -JS_SetWrapObjectCallback(JSRuntime *rt, JSWrapObjectCallback callback); +JS_SetWrapObjectCallbacks(JSRuntime *rt, + JSWrapObjectCallback callback, + JSPreWrapCallback precallback); extern JS_PUBLIC_API(JSCrossCompartmentCall *) JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index e7569cef64a0..ea9cde121893 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1691,6 +1691,7 @@ struct JSRuntime { #endif JSWrapObjectCallback wrapObjectCallback; + JSPreWrapCallback preWrapObjectCallback; JSC::ExecutableAllocator *regExpAllocator; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 17a563018bbf..c53d77a42f4e 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -134,6 +134,24 @@ JSCompartment::wrap(JSContext *cx, Value *vp) } } + /* + * Wrappers should really be parented to the wrapped parent of the wrapped + * object, but in that case a wrapped global object would have a NULL + * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead +, + * we parent all wrappers to the global object in their home compartment. + * This loses us some transparency, and is generally very cheesy. + */ + JSObject *global; + if (cx->hasfp()) { + global = cx->fp()->scopeChain().getGlobal(); + } else { + global = cx->globalObject; + OBJ_TO_INNER_OBJECT(cx, global); + if (!global) + return false; + } + /* Unwrap incoming objects. */ if (vp->isObject()) { JSObject *obj = &vp->toObject(); @@ -145,10 +163,16 @@ JSCompartment::wrap(JSContext *cx, Value *vp) /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { obj = vp->toObject().unwrap(&flags); + vp->setObject(*obj); + if (obj->getCompartment() == this) + return true; + + if (cx->runtime->preWrapObjectCallback) + obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); + if (!obj) + return false; vp->setObject(*obj); - - /* If the wrapped object is already in this compartment, we are done. */ if (obj->getCompartment() == this) return true; } else { @@ -156,12 +180,13 @@ JSCompartment::wrap(JSContext *cx, Value *vp) vp->setObject(*obj); } - OBJ_TO_OUTER_OBJECT(cx, obj); - if (!obj) - return false; - - JS_ASSERT(obj->getCompartment() == vp->toObject().getCompartment()); - vp->setObject(*obj); +#ifdef DEBUG + { + JSObject *outer = obj; + OBJ_TO_OUTER_OBJECT(cx, outer); + JS_ASSERT(outer && outer == obj); + } +#endif } /* If we already have a wrapper for this value, use it. */ @@ -196,23 +221,6 @@ JSCompartment::wrap(JSContext *cx, Value *vp) if (!wrap(cx, &proto)) return false; - /* - * Wrappers should really be parented to the wrapped parent of the wrapped - * object, but in that case a wrapped global object would have a NULL - * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, - * we parent all wrappers to the global object in their home compartment. - * This loses us some transparency, and is generally very cheesy. - */ - JSObject *global; - if (cx->hasfp()) { - global = cx->fp()->scopeChain().getGlobal(); - } else { - global = cx->globalObject; - OBJ_TO_INNER_OBJECT(cx, global); - if (!global) - return false; - } - /* * We hand in the original wrapped object into the wrap hook to allow * the wrap hook to reason over what wrappers are currently applied diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 03ed062ba966..33ea9976501e 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -562,6 +562,14 @@ typedef JSObject * (* JSWrapObjectCallback)(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, uintN flags); +/* + * Callback used by the wrap hook to ask the embedding to prepare an object + * for wrapping in a context. This might include unwrapping other wrappers + * or even finding a more suitable object for the new compartment. + */ +typedef JSObject * +(* JSPreWrapCallback)(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags); + typedef enum { JSCOMPARTMENT_NEW, /* XXX Does it make sense to have a NEW? */ JSCOMPARTMENT_DESTROY diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 584fb7ab1cfb..ebf95f3c4c42 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -1145,7 +1145,9 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback); JS_SetGCCallbackRT(mJSRuntime, GCCallback); JS_SetExtraGCRoots(mJSRuntime, TraceJS, this); - JS_SetWrapObjectCallback(mJSRuntime, xpc::WrapperFactory::Rewrap); + JS_SetWrapObjectCallbacks(mJSRuntime, + xpc::WrapperFactory::Rewrap, + xpc::WrapperFactory::PrepareForWrapping); mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock); mJSRuntime->setActivityCallback(ActivityCallback, this); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 8e4a36d69583..71210c35886e 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -45,6 +45,7 @@ #include "FilteringWrapper.h" #include "XrayWrapper.h" #include "AccessCheck.h" +#include "XPCWrapper.h" #include "xpcprivate.h" @@ -65,6 +66,71 @@ JSWrapper WaiveXrayWrapperWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); // off it. CrossOriginWrapper XrayWrapperWaivedWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); +JSObject * +WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags) +{ + // Here are the rules for wrapping: + // We should never get a proxy here (the JS engine unwraps those for us). + JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); + + // As soon as an object is wrapped in a security wrapper, it morphs to be + // a fat wrapper. (see also: bug XXX). + if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj)) + return nsnull; + + // We only hand out outer objects to script. + OBJ_TO_OUTER_OBJECT(cx, obj); + + // Now, our object is ready to be wrapped, but several objects (notably + // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of + // those objects in a security wrapper, then we need to hand back the + // wrapper for the new scope instead. So... + if (!IS_WN_WRAPPER(obj)) + return obj; + + XPCWrappedNative *wn = static_cast(xpc_GetJSPrivate(obj)); + + // We know that DOM objects only allow one object, we can return early. + if (wn->GetProto()->ClassIsDOMObject()) + return obj; + + XPCCallContext ccx(JS_CALLER, cx, obj); + if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) { + // We have a precreate hook. This object might enforce that we only + // ever create JS object for it. + JSObject *originalScope = scope; + nsresult rv = wn->GetScriptableInfo()->GetCallback()-> + PreCreate(wn->Native(), cx, scope, &scope); + NS_ENSURE_SUCCESS(rv, obj); + + // If the handed back scope differs from the passed-in scope and is in + // a separate compartment, then this object is explicitly requesting + // that we don't create a second JS object for it: create a security + // wrapper. + if (originalScope->getCompartment() != scope->getCompartment()) + return obj; + + // Note: this penalizes objects that only have one wrapper, but are + // being accessed across compartments. We would really prefer to + // replace the above code with a test that says "do you only have one + // wrapper?" + } + + // The object we're looking at might allow us to create a new wrapped + // native in the new scope. Try it and continue wrapping on the + // possibly-new object. + JSAutoEnterCompartment ac; + if (!ac.enter(cx, scope)) + return obj; + jsval v; + nsresult rv = + nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, scope, wn->Native(), nsnull, + &NS_GET_IID(nsISupports), PR_FALSE, + &v, nsnull); + NS_ENSURE_SUCCESS(rv, obj); + return JSVAL_TO_OBJECT(v); +} + JSObject * WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, uintN flags) @@ -73,9 +139,6 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO "wrapped object passed to rewrap"); NS_ASSERTION(JS_GET_CLASS(cx, obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); - if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj)) - return nsnull; - JSCompartment *origin = obj->getCompartment(); JSCompartment *target = cx->compartment; JSObject *xrayHolder = nsnull; diff --git a/js/src/xpconnect/wrappers/WrapperFactory.h b/js/src/xpconnect/wrappers/WrapperFactory.h index dc14bdf83057..93a8b59512bd 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.h +++ b/js/src/xpconnect/wrappers/WrapperFactory.h @@ -58,6 +58,12 @@ class WrapperFactory { return HasWrapperFlag(wrapper, IS_XRAY_WRAPPER_FLAG); } + // Prepare a given object for wrapping in a new compartment. + static JSObject *PrepareForWrapping(JSContext *cx, + JSObject *scope, + JSObject *obj, + uintN flags); + // Rewrap an object that is about to cross compartment boundaries. static JSObject *Rewrap(JSContext *cx, JSObject *obj, From 8f2f47a9d01b38a87970b61f67cd4aa42c830082 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:36:41 -0700 Subject: [PATCH 163/284] bug 580128 - Clean up our passing from JS-into-C++ story. r=peterv/mrbkap --- js/src/xpconnect/src/XPCWrapper.cpp | 8 ++-- js/src/xpconnect/src/XPCWrapper.h | 2 +- js/src/xpconnect/src/xpcjsruntime.cpp | 3 +- js/src/xpconnect/src/xpcprivate.h | 1 + js/src/xpconnect/tests/mochitest/Makefile.in | 1 + .../tests/mochitest/file_bug505915.html | 10 ++++ .../tests/mochitest/test_bug505915.html | 48 ++++++++++++++++++- js/src/xpconnect/wrappers/AccessCheck.cpp | 10 ++-- js/src/xpconnect/wrappers/AccessCheck.h | 2 +- .../xpconnect/wrappers/FilteringWrapper.cpp | 5 +- js/src/xpconnect/wrappers/WrapperFactory.cpp | 28 +++++++++++ js/src/xpconnect/wrappers/WrapperFactory.h | 5 +- 12 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 js/src/xpconnect/tests/mochitest/file_bug505915.html diff --git a/js/src/xpconnect/src/XPCWrapper.cpp b/js/src/xpconnect/src/XPCWrapper.cpp index a359916e1821..756b980c9682 100644 --- a/js/src/xpconnect/src/XPCWrapper.cpp +++ b/js/src/xpconnect/src/XPCWrapper.cpp @@ -45,6 +45,8 @@ #include "nsPIDOMWindow.h" #include "jswrapper.h" #include "XrayWrapper.h" +#include "AccessCheck.h" +#include "WrapperFactory.h" namespace XPCWrapper { @@ -64,10 +66,8 @@ JSObject * Unwrap(JSContext *cx, JSObject *wrapper) { if (wrapper->isProxy()) { - if (wrapper->getProxyHandler() != &JSCrossCompartmentWrapper::singleton) { - // XXX Security check! - } - + if (xpc::WrapperFactory::IsScriptAccessOnly(cx, wrapper)) + return nsnull; return wrapper->unwrap(); } diff --git a/js/src/xpconnect/src/XPCWrapper.h b/js/src/xpconnect/src/XPCWrapper.h index 9caf194045bc..06f16f4589b6 100644 --- a/js/src/xpconnect/src/XPCWrapper.h +++ b/js/src/xpconnect/src/XPCWrapper.h @@ -314,7 +314,7 @@ MaybePreserveWrapper(JSContext *cx, XPCWrappedNative *wn, uintN flags) inline JSBool IsSecurityWrapper(JSObject *wrapper) { - return !!wrapper->getClass()->ext.wrappedObject; + return wrapper->isWrapper() || !!wrapper->getClass()->ext.wrappedObject; } /** diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index ebf95f3c4c42..e584b48fffbb 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -67,7 +67,8 @@ const char* XPCJSRuntime::mStrings[] = { "item", // IDX_ITEM "__proto__", // IDX_PROTO "__iterator__", // IDX_ITERATOR - "__exposedProps__" // IDX_EXPOSEDPROPS + "__exposedProps__", // IDX_EXPOSEDPROPS + "__scriptOnly__" // IDX_SCRIPTONLY }; /***************************************************************************/ diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 70613379b7bd..a592d9618313 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -661,6 +661,7 @@ public: IDX_PROTO , IDX_ITERATOR , IDX_EXPOSEDPROPS , + IDX_SCRIPTONLY , IDX_TOTAL_COUNT // just a count of the above }; diff --git a/js/src/xpconnect/tests/mochitest/Makefile.in b/js/src/xpconnect/tests/mochitest/Makefile.in index 589ffa694258..67223f14151a 100644 --- a/js/src/xpconnect/tests/mochitest/Makefile.in +++ b/js/src/xpconnect/tests/mochitest/Makefile.in @@ -65,6 +65,7 @@ _TEST_FILES = bug500931_helper.html \ test_bug503926.html \ test_bug504877.html \ test_bug505915.html \ + file_bug505915.html \ test_bug517163.html \ test_bug553407.html \ test_bug560351.html \ diff --git a/js/src/xpconnect/tests/mochitest/file_bug505915.html b/js/src/xpconnect/tests/mochitest/file_bug505915.html new file mode 100644 index 000000000000..512912691458 --- /dev/null +++ b/js/src/xpconnect/tests/mochitest/file_bug505915.html @@ -0,0 +1,10 @@ + + + + + + + diff --git a/js/src/xpconnect/tests/mochitest/test_bug505915.html b/js/src/xpconnect/tests/mochitest/test_bug505915.html index 5ad9b947a7b2..59226bdad55e 100644 --- a/js/src/xpconnect/tests/mochitest/test_bug505915.html +++ b/js/src/xpconnect/tests/mochitest/test_bug505915.html @@ -16,9 +16,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=505915
-
 
- + diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 44a6e982ec55..3cdc5fe795d6 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -64,19 +64,15 @@ AccessCheck::isSameOrigin(JSCompartment *a, JSCompartment *b) } bool -AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *obj) +AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper) { - JSCompartment *compartment = obj->compartment(); - - obj = obj->unwrap()->getParent(); + JSObject *obj = wrapper->unwrap()->getParent(); if (!obj->getClass()->ext.innerObject) { obj = obj->unwrap(); JS_ASSERT(obj->getClass()->ext.innerObject); } OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return false; - return isSameOrigin(compartment, obj->compartment()); + return obj && isSameOrigin(wrapper->compartment(), obj->compartment()); } bool diff --git a/js/src/xpconnect/wrappers/AccessCheck.h b/js/src/xpconnect/wrappers/AccessCheck.h index 7642d032c24a..7bdec2ec46e5 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.h +++ b/js/src/xpconnect/wrappers/AccessCheck.h @@ -52,7 +52,7 @@ class AccessCheck { static bool isCrossOriginAccessPermitted(JSContext *cx, JSObject *obj, jsid id, JSWrapper::Action act); static bool isSystemOnlyAccessPermitted(JSContext *cx); - static bool isLocationObjectSameOrigin(JSContext *cx, JSObject *obj); + static bool isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper); static bool needsSystemOnlyWrapper(JSObject *obj); diff --git a/js/src/xpconnect/wrappers/FilteringWrapper.cpp b/js/src/xpconnect/wrappers/FilteringWrapper.cpp index 7348ad8562b4..e3b6716b39aa 100644 --- a/js/src/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/src/xpconnect/wrappers/FilteringWrapper.cpp @@ -41,6 +41,7 @@ #include "AccessCheck.h" #include "CrossOriginWrapper.h" #include "XrayWrapper.h" +#include "WrapperFactory.h" #include "XPCWrapper.h" @@ -160,8 +161,8 @@ FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, template<> SOW SOW::singleton(0); template<> COW COW::singleton(0); -template<> XOW XOW::singleton(0); -template<> NNXOW NNXOW::singleton(0); +template<> XOW XOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG); +template<> NNXOW NNXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG); template<> LW LW::singleton(0); template<> XLW XLW::singleton(0); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 71210c35886e..4b596206c8cd 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -241,4 +241,32 @@ WrapperFactory::WrapLocationObject(JSContext *cx, JSObject *obj) return wrapperObj; } +bool +WrapperFactory::IsScriptAccessOnly(JSContext *cx, JSObject *wrapper) +{ + JS_ASSERT(wrapper->isWrapper()); + + uintN flags; + JSObject *obj = wrapper->unwrap(&flags); + + // If the wrapper indicates script-only access, we are done. + if (flags & SCRIPT_ACCESS_ONLY_FLAG) + return true; + + // In addition, chrome objects can explicitly opt-in by setting .scriptOnly to true. + if (wrapper->getProxyHandler() == &FilteringWrapper::singleton) { + jsid scriptOnlyId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_SCRIPTONLY); + jsval scriptOnly; + if (JS_LookupPropertyById(cx, obj, scriptOnlyId, &scriptOnly) && + scriptOnly == JSVAL_TRUE) + return true; // script-only + } + + // Allow non-script access to same-origin location objects and any other + // objects. + return IsLocationObject(obj) && + !xpc::AccessCheck::isLocationObjectSameOrigin(cx, wrapper); +} + } diff --git a/js/src/xpconnect/wrappers/WrapperFactory.h b/js/src/xpconnect/wrappers/WrapperFactory.h index 93a8b59512bd..b8b518bd4e46 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.h +++ b/js/src/xpconnect/wrappers/WrapperFactory.h @@ -45,7 +45,8 @@ namespace xpc { class WrapperFactory { public: enum { WAIVE_XRAY_WRAPPER_FLAG = (1<<0), - IS_XRAY_WRAPPER_FLAG = (1<<1) }; + IS_XRAY_WRAPPER_FLAG = (1<<1), + SCRIPT_ACCESS_ONLY_FLAG = (1<<2) }; // Return true if any of any of the nested wrappers have the flag set. static bool HasWrapperFlag(JSObject *wrapper, uintN flag) { @@ -58,6 +59,8 @@ class WrapperFactory { return HasWrapperFlag(wrapper, IS_XRAY_WRAPPER_FLAG); } + static bool IsScriptAccessOnly(JSContext *cx, JSObject *wrapper); + // Prepare a given object for wrapping in a new compartment. static JSObject *PrepareForWrapping(JSContext *cx, JSObject *scope, From 969bd2267872199e4e37695d14dd435f7b011495 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:36:59 -0700 Subject: [PATCH 164/284] bug 580128 - Add a test about double wrapped native objects in security wrappers. r=jst --- js/src/xpconnect/tests/chrome/Makefile.in | 1 + .../chrome/test_doublewrappedcompartments.xul | 46 +++++++++++++++++++ js/src/xpconnect/tests/mochitest/Makefile.in | 1 + .../file_doublewrappedcompartments.html | 18 ++++++++ 4 files changed, 66 insertions(+) create mode 100644 js/src/xpconnect/tests/chrome/test_doublewrappedcompartments.xul create mode 100644 js/src/xpconnect/tests/mochitest/file_doublewrappedcompartments.html diff --git a/js/src/xpconnect/tests/chrome/Makefile.in b/js/src/xpconnect/tests/chrome/Makefile.in index 5f4ee1a4b15b..c316c246e635 100644 --- a/js/src/xpconnect/tests/chrome/Makefile.in +++ b/js/src/xpconnect/tests/chrome/Makefile.in @@ -49,6 +49,7 @@ _CHROME_FILES = \ bug503926.xul \ test_bug503926.xul \ test_bug533596.xul \ + test_doublewrappedcompartments.xul \ test_evalInSandbox.xul \ test_sandboxImport.xul \ test_wrappers.xul \ diff --git a/js/src/xpconnect/tests/chrome/test_doublewrappedcompartments.xul b/js/src/xpconnect/tests/chrome/test_doublewrappedcompartments.xul new file mode 100644 index 000000000000..fd76c9205e8e --- /dev/null +++ b/js/src/xpconnect/tests/chrome/test_doublewrappedcompartments.xul @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + diff --git a/js/src/xpconnect/tests/mochitest/Makefile.in b/js/src/xpconnect/tests/mochitest/Makefile.in index 67223f14151a..d8f882d63222 100644 --- a/js/src/xpconnect/tests/mochitest/Makefile.in +++ b/js/src/xpconnect/tests/mochitest/Makefile.in @@ -49,6 +49,7 @@ _TEST_FILES = bug500931_helper.html \ bug92773_helper.html \ bug504877_helper.html \ chrome_wrappers_helper.html \ + file_doublewrappedcompartments.html \ file_evalInSandbox.html \ test_bug92773.html \ test_bug361111.xul \ diff --git a/js/src/xpconnect/tests/mochitest/file_doublewrappedcompartments.html b/js/src/xpconnect/tests/mochitest/file_doublewrappedcompartments.html new file mode 100644 index 000000000000..b983bf863266 --- /dev/null +++ b/js/src/xpconnect/tests/mochitest/file_doublewrappedcompartments.html @@ -0,0 +1,18 @@ + + + + From 644aa16e5437957590698c619cb534bab14a1e80 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:37:19 -0700 Subject: [PATCH 165/284] bug 580128 - Fix more compartment warnings. Work of multiple people. r=gal/peterv/jst --- content/xbl/src/nsXBLProtoImplMethod.cpp | 6 +++++- content/xbl/src/nsXBLProtoImplProperty.cpp | 5 +++++ js/src/jsapi.cpp | 4 +++- js/src/jsapi.h | 4 +++- js/src/xpconnect/loader/mozJSSubScriptLoader.cpp | 8 ++++++++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/content/xbl/src/nsXBLProtoImplMethod.cpp b/content/xbl/src/nsXBLProtoImplMethod.cpp index f26a6ec38080..5c7b9222e136 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.cpp +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -151,7 +151,7 @@ nsXBLProtoImplMethod::InstallMember(nsIScriptContext* aContext, JSAutoRequest ar(cx); JSAutoEnterCompartment ac; - if (!ac.enter(cx, mJSMethodObject)) { + if (!ac.enter(cx, globalObject)) { return NS_ERROR_UNEXPECTED; } @@ -312,6 +312,10 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement) JSObject* thisObject = JSVAL_TO_OBJECT(v); JSAutoRequest ar(cx); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, thisObject)) + return NS_ERROR_UNEXPECTED; // Clone the function object, using thisObject as the parent so "this" is in // the scope chain of the resulting function (for backwards compat to the diff --git a/content/xbl/src/nsXBLProtoImplProperty.cpp b/content/xbl/src/nsXBLProtoImplProperty.cpp index 25d9593392f5..c941e879fd28 100644 --- a/content/xbl/src/nsXBLProtoImplProperty.cpp +++ b/content/xbl/src/nsXBLProtoImplProperty.cpp @@ -176,6 +176,11 @@ nsXBLProtoImplProperty::InstallMember(nsIScriptContext* aContext, if ((mJSGetterObject || mJSSetterObject) && targetClassObject) { JSObject * getter = nsnull; JSAutoRequest ar(cx); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, globalObject)) + return NS_ERROR_UNEXPECTED; + if (mJSGetterObject) if (!(getter = ::JS_CloneFunctionObject(cx, mJSGetterObject, globalObject))) return NS_ERROR_OUT_OF_MEMORY; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index f35666fdde5c..cefdab43b5fd 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1187,8 +1187,10 @@ bool JSAutoEnterCompartment::enter(JSContext *cx, JSObject *target) { JS_ASSERT(!call); - if (cx->compartment == target->getCompartment()) + if (cx->compartment == target->getCompartment()) { + call = reinterpret_cast(1); return true; + } call = JS_EnterCrossCompartmentCall(cx, target); return call != NULL; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 14379c86d293..0ba0c614c49b 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -979,12 +979,14 @@ class JS_PUBLIC_API(JSAutoEnterCompartment) bool entered() const { return call != NULL; } ~JSAutoEnterCompartment() { - if (call) + if (call && call != reinterpret_cast(1)) JS_LeaveCrossCompartmentCall(call); } void swap(JSAutoEnterCompartment &other) { JSCrossCompartmentCall *tmp = call; + if (tmp == reinterpret_cast(1)) + tmp = NULL; call = other.call; other.call = tmp; } diff --git a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp index 2c5bd3fb1400..38897b0bd9eb 100644 --- a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -217,6 +217,14 @@ mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL fprintf (stderr, "Final global: %p\n", target_obj); #endif } + else if (target_obj->isWrapper()) + { + target_obj = target_obj->unwrap(); + } + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, target_obj)) + return NS_ERROR_UNEXPECTED; /* load up the url. From here on, failures are reflected as ``custom'' * js exceptions */ From efbae541a4859358926b8758923b5e52639709bf Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:37:22 -0700 Subject: [PATCH 166/284] Bug 580128 - Small cleanups in XrayWrapper and first stab at enumeration. r=mrbkap --- js/src/jsiter.cpp | 10 +++++++++- js/src/jsiter.h | 6 +++++- js/src/jsobj.h | 8 ++++++-- js/src/jsobjinlines.h | 6 +++--- js/src/xpconnect/wrappers/XrayWrapper.cpp | 19 ++++++++++++------- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 96e04aad7bcb..b66ecd9cbe0c 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -372,6 +372,8 @@ Snapshot(JSContext *cx, JSObject *obj, uintN flags, typename EnumPolicy::ResultV return true; } +namespace js { + bool VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap) { @@ -389,12 +391,14 @@ VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap) return true; } -bool +JS_FRIEND_API(bool) GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props) { return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props); } +} + static inline bool GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) { @@ -559,6 +563,8 @@ VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &key return true; } +namespace js { + bool VectorToKeyIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector &props, Value *vp) { @@ -725,6 +731,8 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) return true; } +} + static JSObject * iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly) { diff --git a/js/src/jsiter.h b/js/src/jsiter.h index d47cb565f284..2cfee0cf0522 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -64,6 +64,8 @@ */ #define JSITER_ACTIVE 0x1000 +namespace js { + struct NativeIterator { JSObject *obj; void *props_array; @@ -137,7 +139,7 @@ struct NativeIterator { bool VectorToIdArray(JSContext *cx, js::AutoIdVector &props, JSIdArray **idap); -bool +JS_FRIEND_API(bool) GetPropertyNames(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVector *props); bool @@ -156,6 +158,8 @@ VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, js::AutoValueVe bool EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVector &props, js::Value *vp); +} + /* * Convert the value stored in *vp to its iteration object. The flags should * contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating diff --git a/js/src/jsobj.h b/js/src/jsobj.h index d3f85a3ae2ce..a35a90b87874 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -231,8 +231,12 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, extern JSType js_TypeOf(JSContext *cx, JSObject *obj); +namespace js { + struct NativeIterator; +} + const uint32 JS_INITIAL_NSLOTS = 3; /* @@ -909,8 +913,8 @@ struct JSObject : js::gc::Cell { * Iterator-specific getters and setters. */ - inline NativeIterator *getNativeIterator() const; - inline void setNativeIterator(NativeIterator *); + inline js::NativeIterator *getNativeIterator() const; + inline void setNativeIterator(js::NativeIterator *); /* * XML-related getters and setters. diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 3caafb16bd81..aa2467098573 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -563,14 +563,14 @@ JSObject::setMethodObj(JSObject& obj) fslots[JSSLOT_FUN_METHOD_OBJ].setObject(obj); } -inline NativeIterator * +inline js::NativeIterator * JSObject::getNativeIterator() const { - return (NativeIterator *) getPrivate(); + return (js::NativeIterator *) getPrivate(); } inline void -JSObject::setNativeIterator(NativeIterator *ni) +JSObject::setNativeIterator(js::NativeIterator *ni) { setPrivate(ni); } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 4818f9881bfa..a3528adbbdcc 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -44,6 +44,7 @@ #include "WrapperFactory.h" #include "jscntxt.h" +#include "jsiter.h" #include "XPCWrapper.h" #include "xpcprivate.h" @@ -484,7 +485,7 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid return false; if (existing_desc.obj && (existing_desc.attrs & JSPROP_PERMANENT)) - return true; // XXX throw? + return true; // silently ignore attempt to overwrite native property JSPropertyDescriptor *jsdesc = Jsvalify(desc); if (!(jsdesc->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { @@ -503,15 +504,20 @@ bool XrayWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props) { - // XXX implement me. - return true; + JSObject *holder = GetHolder(wrapper); + return js::GetPropertyNames(cx, holder, JSITER_OWNONLY | JSITER_HIDDEN, &props); } template bool XrayWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { - // XXX implement me. + JSObject *holder = GetHolder(wrapper); + jsval v; + JSBool b; + if (!JS_DeletePropertyById2(cx, holder, id, &v) || !JS_ValueToBoolean(cx, v, &b)) + return false; + *bp = !!b; return true; } @@ -519,8 +525,8 @@ template bool XrayWrapper::enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props) { - // XXX implement me. - return true; + JSObject *holder = GetHolder(wrapper); + return js::GetPropertyNames(cx, holder, 0, &props); } template @@ -604,7 +610,6 @@ CrossCompartmentXray::enter(JSContext *cx, JSObject *wrapper, jsid *idp, return false; *priv = call; - // XXX wrap id return true; } From 21cdd79153a0dd51514865748eeadcad0d374209 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Wed, 29 Sep 2010 23:17:29 -0700 Subject: [PATCH 167/284] Make functions per compartment, and deep copy instead of clone them if needed (584789, r=mrbkap). --- js/src/jsapi-tests/Makefile.in | 1 + js/src/jsapi-tests/testCloneScript.cpp | 50 +++++++++++++++++ js/src/jsapi.cpp | 1 + js/src/jscntxtinlines.h | 7 ++- js/src/jsfun.cpp | 49 ++++++++++++++--- js/src/jsscript.cpp | 76 +++++++++++++++++++++++++- js/src/jsscript.h | 5 ++ 7 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 js/src/jsapi-tests/testCloneScript.cpp diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index 9ce4138f65b3..014162a7fa29 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -50,6 +50,7 @@ CPPSRCS = \ tests.cpp \ selfTest.cpp \ testClassGetter.cpp \ + testCloneScript.cpp \ testConservativeGC.cpp \ testContexts.cpp \ testDebugger.cpp \ diff --git a/js/src/jsapi-tests/testCloneScript.cpp b/js/src/jsapi-tests/testCloneScript.cpp new file mode 100644 index 000000000000..690642a5adc4 --- /dev/null +++ b/js/src/jsapi-tests/testCloneScript.cpp @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * Test script cloning. + */ + +#include "tests.h" +#include "jsapi.h" + +BEGIN_TEST(test_cloneScript) +{ + JSObject *A, *B; + + CHECK(A = createGlobal()); + CHECK(B = createGlobal()); + + const char *source = + "var i = 0;\n" + "var sum = 0;\n" + "while (i < 10) {\n" + " sum += i;\n" + " ++i;\n" + "}\n" + "(sum);\n"; + + JSObject *obj; + + // compile for A + { + JSAutoEnterCompartment a; + if (!a.enter(cx, A)) + return false; + + JSFunction *fun; + CHECK(fun = JS_CompileFunction(cx, A, "f", 0, NULL, source, strlen(source), __FILE__, 1)); + CHECK(obj = JS_GetFunctionObject(fun)); + } + + // clone into B + { + JSAutoEnterCompartment b; + if (!b.enter(cx, B)) + return false; + + CHECK(JS_CloneFunctionObject(cx, obj, B)); + } + + return true; +} +END_TEST(test_cloneScript) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index cefdab43b5fd..f7965d8c6a78 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4621,6 +4621,7 @@ JS_NewScriptObject(JSContext *cx, JSScript *script) * described in the comment for JSScript::u.object. */ JS_ASSERT(script->u.object); + JS_ASSERT(script != JSScript::emptyScript()); return script->u.object; } diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index eea500249050..4e02ffe5255b 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -563,8 +563,11 @@ class CompartmentChecker } void check(JSScript *script) { - if (script && script->u.object) - check(script->u.object); + if (script && script != JSScript::emptyScript()) { + check(script->compartment); + if (script->u.object) + check(script->u.object); + } } void check(JSString *) { /* nothing for now */ } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 2b56b50ab46c..04cdef40d8a5 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2936,14 +2936,47 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JS_ASSERT(parent); JS_ASSERT(proto); - /* - * The cloned function object does not need the extra JSFunction members - * beyond JSObject as it points to fun via the private slot. - */ - JSObject *clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent); - if (!clone) - return NULL; - clone->setPrivate(fun); + JSObject *clone; + if (cx->compartment == fun->compartment()) { + /* + * The cloned function object does not need the extra JSFunction members + * beyond JSObject as it points to fun via the private slot. + */ + clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent); + if (!clone) + return NULL; + clone->setPrivate(fun); + } else { + /* + * Across compartments we have to deep copy JSFunction and clone the + * script (for interpreted functions). + */ + clone = NewFunction(cx, parent); + if (!clone) + return NULL; + JSFunction *cfun = (JSFunction *) clone; + cfun->nargs = fun->nargs; + cfun->flags = fun->flags; + cfun->u = fun->getFunctionPrivate()->u; + cfun->atom = fun->atom; + clone->setPrivate(cfun); + if (cfun->isInterpreted()) { + JSScript *script = cfun->u.i.script; + JS_ASSERT(script); + if (script != JSScript::emptyScript()) { + JS_ASSERT(script->compartment == fun->compartment()); + JS_ASSERT(script->compartment != cx->compartment); + cfun->u.i.script = js_CloneScript(cx, script); + if (!cfun->u.i.script) + return NULL; + JS_ASSERT(cfun->u.i.script != JSScript::emptyScript()); +#ifdef CHECK_SCRIPT_OWNER + cfun->u.i.script->owner = NULL; +#endif + js_CallNewScriptHook(cx, cfun->u.i.script, cfun); + } + } + } return clone; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index db8af79d5ed3..44874583d86c 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -86,7 +86,7 @@ static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL}; false, /* debugMode */ #endif const_cast(emptyScriptCode), - {0, NULL}, NULL, 0, 0, 0, + {0, NULL}, NULL, NULL, 0, 0, 0, 0, /* nClosedArgs */ 0, /* nClosedVars */ NULL, {NULL}, @@ -1065,6 +1065,7 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom nsrcnotes * sizeof(jssrcnote) == (uint8 *)script + size); + script->compartment = cx->compartment; #ifdef CHECK_SCRIPT_OWNER script->owner = cx->thread; #endif @@ -1641,6 +1642,79 @@ js_GetScriptLineExtent(JSScript *script) return 1 + lineno - script->lineno; } +class DisablePrincipalsTranscoding { + JSSecurityCallbacks *callbacks; + JSPrincipalsTranscoder temp; + + public: + DisablePrincipalsTranscoding(JSContext *cx) { + callbacks = JS_GetRuntimeSecurityCallbacks(cx->runtime); + if (callbacks) { + temp = callbacks->principalsTranscoder; + callbacks->principalsTranscoder = NULL; + } + } + + ~DisablePrincipalsTranscoding() { + if (callbacks) + callbacks->principalsTranscoder = temp; + } +}; + +JSScript * +js_CloneScript(JSContext *cx, JSScript *script) +{ + JS_ASSERT(script != JSScript::emptyScript()); + JS_ASSERT(cx->compartment != script->compartment); + JS_ASSERT(script->compartment); + + // serialize script + JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE); + if (!w) + return NULL; + + // we don't want gecko to transcribe our principals for us + DisablePrincipalsTranscoding disable(cx); + + if (!JS_XDRScript(w, &script)) { + JS_XDRDestroy(w); + return NULL; + } + + uint32 nbytes; + void *p = JS_XDRMemGetData(w, &nbytes); + if (!p) { + JS_XDRDestroy(w); + return NULL; + } + + // de-serialize script + JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE); + if (!r) { + JS_XDRDestroy(w); + return NULL; + } + + // 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); + + // We can't use the public API because it makes a script object. + if (!js_XDRScript(r, &script, true, NULL)) + return NULL; + + JS_XDRDestroy(r); + JS_XDRDestroy(w); + + // set the proper principals for the script + script->principals = script->compartment->principals; + if (script->principals) + JSPRINCIPALS_HOLD(cx, script->principals); + + return script; +} + void JSScript::copyClosedSlotsTo(JSScript *other) { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index cef37ec6af31..bd7908712263 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -249,6 +249,7 @@ struct JSScript { jsbytecode *main; /* main entry point, after predef'ing prolog */ JSAtomMap atomMap; /* maps immediate index to literal struct */ + JSCompartment *compartment; /* compartment the script was compiled for */ const char *filename; /* source filename or null */ uint32 lineno; /* base line number of script */ uint16 nslots; /* vars plus maximum stack depth */ @@ -276,6 +277,7 @@ struct JSScript { JSObject *object; JSScript *nextToGC; /* next to GC in rt->scriptsToGC list */ } u; + #ifdef CHECK_SCRIPT_OWNER JSThread *owner; /* for thread-safe life-cycle assertions */ #endif @@ -583,6 +585,9 @@ js_GetOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) return op; } +extern JSScript * +js_CloneScript(JSContext *cx, JSScript *script); + /* * If magic is non-null, js_XDRScript succeeds on magic number mismatch but * returns false in *magic; it reflects a match via a true *magic out param. From c6b8f8093f5b261f6c9bda3e7503c8d43a331bd1 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:37:28 -0700 Subject: [PATCH 168/284] Bug 580128. Fix various mochi chrome tests to work with the new wrappers. r=mrbkap@gmail.com --- docshell/test/chrome/test_bug454235.xul | 8 ++++---- js/src/xpconnect/tests/chrome/test_bug500931.xul | 4 ++-- js/src/xpconnect/tests/chrome/test_bug533596.xul | 8 ++++---- .../chrome/test_doublewrappedcompartments.xul | 2 +- .../tests/chrome/test_evalInSandbox.xul | 6 +++--- .../tests/chrome/test_sandboxImport.xul | 2 +- js/src/xpconnect/tests/chrome/test_wrappers.xul | 16 ++++++++-------- .../tests/mochitest/chrome_wrappers_helper.html | 6 +++--- .../content/tests/chrome/window_browser_drop.xul | 4 ++-- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docshell/test/chrome/test_bug454235.xul b/docshell/test/chrome/test_bug454235.xul index 4e13b89df4a9..88c396f7def2 100644 --- a/docshell/test/chrome/test_bug454235.xul +++ b/docshell/test/chrome/test_bug454235.xul @@ -39,11 +39,11 @@ function doTest() { offScreenBrowser.contentWindow.focus(); ok(offScreenBrowser.contentDocument.hasFocus(),"offscreen browser is not visible"); - offScreenSubBrowser.wrappedJSObject.contentWindow.focus(); - todo(offScreenSubBrowser.wrappedJSObject.contentDocument.hasFocus(),"visible browser in offscreen browser is not visible"); + offScreenSubBrowser.contentWindow.focus(); + todo(offScreenSubBrowser.contentDocument.hasFocus(),"visible browser in offscreen browser is not visible"); - offScreenBurriedBrowser.wrappedJSObject.contentWindow.focus(); - ok(!offScreenBurriedBrowser.wrappedJSObject.contentDocument.hasFocus(),"hidden browser in offscreen browser is visible"); + offScreenBurriedBrowser.contentWindow.focus(); + ok(!offScreenBurriedBrowser.contentDocument.hasFocus(),"hidden browser in offscreen browser is visible"); SimpleTest.finish(); } diff --git a/js/src/xpconnect/tests/chrome/test_bug500931.xul b/js/src/xpconnect/tests/chrome/test_bug500931.xul index e2ff2cb6612d..ff26a5188450 100644 --- a/js/src/xpconnect/tests/chrome/test_bug500931.xul +++ b/js/src/xpconnect/tests/chrome/test_bug500931.xul @@ -26,9 +26,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 function go() { var ifr = document.getElementById("ifr"); var doc = ifr.contentDocument; - ok(doc.toString().indexOf("XPCNativeWrapper") >= 0, "doc is an XPCNativeWrapper"); + ok(doc.toString().indexOf("XrayWrapper") >= 0, "doc is an XrayWrapper"); var weak = Components.utils.getWeakReference(doc); - ok(weak.get().toString().indexOf("XPCNativeWrapper") >= 0, "weak reference returns a wrapper"); + ok(weak.get().toString().indexOf("XrayWrapper") >= 0, "weak reference returns a wrapper"); SimpleTest.finish(); } diff --git a/js/src/xpconnect/tests/chrome/test_bug533596.xul b/js/src/xpconnect/tests/chrome/test_bug533596.xul index ad450f2d0a37..452d8ad7c6b6 100644 --- a/js/src/xpconnect/tests/chrome/test_bug533596.xul +++ b/js/src/xpconnect/tests/chrome/test_bug533596.xul @@ -34,11 +34,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=533596 var win = $('ifr').contentWindow; var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); - is(utils.getClassName(win), "XPCNativeWrapper", "win is an XPCNW"); + is(utils.getClassName(win), "Proxy", "win is a Proxy"); ok("x" in XPCNativeWrapper.unwrap(win), "actually unwrapped"); - is(utils.getClassName(XPCNativeWrapper.unwrap(win)), "XPCSafeJSObjectWrapper", - "unwrap on an NW returns the same object"); - is(utils.getClassName(XPCNativeWrapper.unwrap(new XPCNativeWrapper(win))), "XPCSafeJSObjectWrapper", + is(utils.getClassName(XPCNativeWrapper.unwrap(win)), "Proxy", + "unwrap on an Proxy returns the same object"); + is(utils.getClassName(XPCNativeWrapper.unwrap(new XPCNativeWrapper(win))), "Proxy", "unwrap on an explicit NW works too"); ok(utils.getClassName(window) !== "XPCNativeWrapper", "window is not a native wrapper"); diff --git a/js/src/xpconnect/tests/chrome/test_doublewrappedcompartments.xul b/js/src/xpconnect/tests/chrome/test_doublewrappedcompartments.xul index fd76c9205e8e..d44ae2daf22c 100644 --- a/js/src/xpconnect/tests/chrome/test_doublewrappedcompartments.xul +++ b/js/src/xpconnect/tests/chrome/test_doublewrappedcompartments.xul @@ -37,7 +37,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=533596 is(typeof readystatechange.QueryInterface, 'function', 'double wrapped'); ok(unwrapped.testme(readystatechange), - 'content didn't get a proxy, but another double wrapped object'); + 'content didn\'t get a proxy, but another double wrapped object'); SimpleTest.finish(); } diff --git a/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul b/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul index 612d31f48546..d09d1cf91632 100644 --- a/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul +++ b/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul @@ -33,14 +33,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=533596 var win = $('ifr').contentWindow; var sandbox = new Cu.Sandbox(win); is(utils.getClassName(sandbox), - "XPCSafeJSObjectWrapper", + "Proxy", "sandbox was wrapped correctly"); sandbox.__proto__ = new XPCNativeWrapper(win); is(utils.getClassName(Cu.evalInSandbox("this.document", sandbox)), - "XPCSafeJSObjectWrapper", + "Proxy", "return value was rewrapped correctly"); - ok(Cu.evalInSandbox("(this.document + '').indexOf('XPCNativeWrapper') >= 0", sandbox), + ok(Cu.evalInSandbox("('wrappedJSObject' in this.document);", sandbox), "wrappers inside eIS are XPCNativeWrappers"); SimpleTest.finish(); diff --git a/js/src/xpconnect/tests/chrome/test_sandboxImport.xul b/js/src/xpconnect/tests/chrome/test_sandboxImport.xul index f465d5c932ce..e050603f9fe2 100644 --- a/js/src/xpconnect/tests/chrome/test_sandboxImport.xul +++ b/js/src/xpconnect/tests/chrome/test_sandboxImport.xul @@ -22,7 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=533596 function checkWrapped(obj) { var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); - is(utils.getClassName(obj), "XPCSafeJSObjectWrapper", "right type of wrapper"); + is(utils.getClassName(obj), "Proxy", "right type of wrapper"); } var sandbox = new Components.utils.Sandbox("about:blank"); diff --git a/js/src/xpconnect/tests/chrome/test_wrappers.xul b/js/src/xpconnect/tests/chrome/test_wrappers.xul index 14d412986f90..ce30df6068aa 100644 --- a/js/src/xpconnect/tests/chrome/test_wrappers.xul +++ b/js/src/xpconnect/tests/chrome/test_wrappers.xul @@ -27,28 +27,28 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 var win = $('ifr').contentWindow; var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); - todo_is(utils.getClassName(window), "XPCSafeJSObjectWrapper", "our window is wrapped correctly") - todo_is(utils.getClassName(location), "XPCSafeJSObjectWrapper", "our location is wrapped correctly") - is(utils.getClassName(win), "XPCNativeWrapper", "win is an XPCNW"); - is(utils.getClassName(win.location), "XPCNativeWrapper", "deep wrapping works"); + is(utils.getClassName(window), "Proxy", "our window is wrapped correctly") + is(utils.getClassName(location), "Proxy", "our location is wrapped correctly") + is(utils.getClassName(win), "Proxy", "win is an Proxy"); + is(utils.getClassName(win.location), "Proxy", "deep wrapping works"); is(win.location.href, "http://example.org/tests/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html", "can still get strings out"); var unsafeWin = win.wrappedJSObject; - is(utils.getClassName(unsafeWin), "XPCSafeJSObjectWrapper", "can get a SJOW"); - is(utils.getClassName(unsafeWin.location), "XPCSafeJSObjectWrapper", "deep wrapping works"); + is(utils.getClassName(unsafeWin), "Proxy", "can get a Proxy"); + is(utils.getClassName(unsafeWin.location), "Proxy", "deep wrapping works"); Object.defineProperty(unsafeWin, "defprop1", { value: 1, writable: true, enumerable: true }); /* TODO (bug 552854): the getter isn't visible in content. function checkWrapper(val) { - ok(utils.getClassName(val) == "XPCSafeJSObjectWrapper", "wrapped properly"); + ok(utils.getClassName(val) == "Proxy", "wrapped properly"); } Object.defineProperty(unsafeWin, "defprop2", { set: checkWrapper, enumerable: true }); */ unsafeWin.run_test(ok, win, unsafeWin); win.setTimeout(function() { - is(utils.getClassName(this), "XPCNativeWrapper", + is(utils.getClassName(this), "Proxy", "this is wrapped correctly"); SimpleTest.finish(); }, 0) diff --git a/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html b/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html index 4f7170eb1cc7..746f64d743aa 100644 --- a/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html +++ b/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html @@ -15,10 +15,10 @@ } function run_test(ok, xpcnw, sjow) { // both wrappers should point to our window: XOW - check_wrapper(ok, ok, "Function", "functions are wrapped properly") + check_wrapper(ok, ok, "Proxy", "functions are wrapped properly") check_parent(ok, ok, window, "ok is parented correctly"); - check_wrapper(ok, xpcnw, "XPCCrossOriginWrapper", "XPCNWs are transformed correctly"); - check_wrapper(ok, sjow, "XPCCrossOriginWrapper", "SJOWs are transformed correctly"); + check_wrapper(ok, xpcnw, "Proxy", "XPCNWs are transformed correctly"); + check_wrapper(ok, sjow, "Proxy", "SJOWs are transformed correctly"); ok(defprop1 === 1, "defprop1 exists"); window.defprop1 = 2; diff --git a/toolkit/content/tests/chrome/window_browser_drop.xul b/toolkit/content/tests/chrome/window_browser_drop.xul index a1f0972cbb6e..fb31c4556a63 100644 --- a/toolkit/content/tests/chrome/window_browser_drop.xul +++ b/toolkit/content/tests/chrome/window_browser_drop.xul @@ -52,14 +52,14 @@ function runTest() }); // stopPropagation should not prevent the browser link handling from occuring - frames[1].stopMode = true; + frames[1].wrappedJSObject.stopMode = true; var body = document.getElementById("contentchild").contentDocument.body; expectLink(body, "http://www.mozilla.org", "http://www.mozilla.org", [ [ { type: "text/uri-list", data: "http://www.mozilla.org" } ] ], "text/x-moz-url drop on browser with stopPropagation drop event"); // canceling the event, however, should prevent the link from being handled - frames[1].cancelMode = true; + frames[1].wrappedJSObject.cancelMode = true; expectLink(body, "", "", [ [ { type: "text/uri-list", data: "http://www.example.org" } ] ], "text/x-moz-url drop on browser with cancelled drop event"); From 186109950a47cbd7a55c6946706a50bcb1deac6d Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:37:44 -0700 Subject: [PATCH 169/284] bug 580128 - Attempt to make the XPCNativeWrapper constructor create XrayWrappers. r=jst --- js/src/xpconnect/src/XPCNativeWrapper.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/js/src/xpconnect/src/XPCNativeWrapper.cpp b/js/src/xpconnect/src/XPCNativeWrapper.cpp index c43571b4c532..e3fe3fe1afbd 100644 --- a/js/src/xpconnect/src/XPCNativeWrapper.cpp +++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp @@ -908,19 +908,12 @@ XrayWrapperConstructor(JSContext *cx, uintN argc, jsval *vp) } JSObject *obj = JSVAL_TO_OBJECT(vp[2]); - if (!obj->isProxy()) { + if (!xpc::WrapperFactory::IsXrayWrapper(obj)) { *vp = OBJECT_TO_JSVAL(obj); return JS_TRUE; } - if (obj->isWrapper()) { - uintN flags; - obj = obj->unwrap(&flags); - if (!(flags & xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG)) { - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - } + obj = obj->unwrap(); *vp = OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, vp); From 42c68caa598a3a6d2c48512f2945b0e0fb4537c6 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:38:04 -0700 Subject: [PATCH 170/284] bug 580128 - Bad attempt to make .wrappedJSObject on raw windows only apply to chrome. Fixed later r=nobody I hope! --- dom/base/nsDOMClassInfo.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 2b8aa89024e1..50f701147cfd 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -5307,6 +5307,7 @@ nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } if (id == sWrappedJSObject_id) { + OBJ_TO_OUTER_OBJECT(cx, obj); *vp = OBJECT_TO_JSVAL(obj); return NS_SUCCESS_I_DID_SOMETHING; } From a1eae3cb88b9286d445b9ef4d2cae9d3df046c5e Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:38:10 -0700 Subject: [PATCH 171/284] Bug 580128. Call JS_ClearScope on the holder object for XrayWrappers around windows when navigating. r=mrbkap --- dom/base/Makefile.in | 4 ++++ dom/base/nsJSEnvironment.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/dom/base/Makefile.in b/dom/base/Makefile.in index 0b5b19c459ce..8a8a12bf7655 100644 --- a/dom/base/Makefile.in +++ b/dom/base/Makefile.in @@ -125,6 +125,10 @@ endif include $(topsrcdir)/config/rules.mk +LOCAL_INCLUDES += \ + -I$(srcdir)/../../js/src/xpconnect/wrappers \ + $(NULL) + ifdef MOZ_X11 CXXFLAGS += $(TK_CFLAGS) LDFLAGS += $(TK_LIBS) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index c4496665672e..9a58788aa93f 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -102,6 +102,7 @@ #include "nsITimelineService.h" #include "nsDOMScriptObjectHolder.h" #include "prmem.h" +#include "WrapperFactory.h" #include "nsGlobalWindow.h" #ifdef MOZ_JSDEBUGGER @@ -3386,6 +3387,9 @@ nsJSContext::ClearScope(void *aGlobalObj, PRBool aClearFromProtoChain) ac.enterAndIgnoreErrors(mContext, obj); JS_ClearScope(mContext, obj); + if (xpc::WrapperFactory::IsXrayWrapper(obj)) { + JS_ClearScope(mContext, &obj->getProxyExtra().toObject()); + } if (!obj->getParent()) { JS_ClearRegExpStatics(mContext, obj); } From b08d9a62c90513993eb03acf0de54a453d26bf4c Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:38:13 -0700 Subject: [PATCH 172/284] Bug 580128. Add more asserts to the interpreter to check that we're on the right compartment. r=mrbkap@gmail.com --- js/src/jsinterp.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 9dbda4bd914f..e609eef63c00 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1859,15 +1859,15 @@ namespace reprmeter { } #endif /* JS_REPRMETER */ -#define PUSH_COPY(v) *regs.sp++ = v +#define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartment(cx, regs.sp[-1]); } while (0) #define PUSH_NULL() regs.sp++->setNull() #define PUSH_UNDEFINED() regs.sp++->setUndefined() #define PUSH_BOOLEAN(b) regs.sp++->setBoolean(b) #define PUSH_DOUBLE(d) regs.sp++->setDouble(d) #define PUSH_INT32(i) regs.sp++->setInt32(i) -#define PUSH_STRING(s) regs.sp++->setString(s) -#define PUSH_OBJECT(obj) regs.sp++->setObject(obj) -#define PUSH_OBJECT_OR_NULL(obj) regs.sp++->setObjectOrNull(obj) +#define PUSH_STRING(s) do { regs.sp++->setString(s); assertSameCompartment(cx, regs.sp[-1]); } while (0) +#define PUSH_OBJECT(obj) do { regs.sp++->setObject(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0) +#define PUSH_OBJECT_OR_NULL(obj) do { regs.sp++->setObjectOrNull(obj); assertSameCompartment(cx, regs.sp[-1]); } while (0) #define PUSH_HOLE() regs.sp++->setMagic(JS_ARRAY_HOLE) #define POP_COPY_TO(v) v = *--regs.sp #define POP_RETURN_VALUE() regs.fp->setReturnValue(*--regs.sp) @@ -4093,6 +4093,7 @@ BEGIN_CASE(JSOP_GETXPROP) } while (0); regs.sp[-1] = rval; + assertSameCompartment(cx, regs.sp[-1]); JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length); len = JSOP_GETPROP_LENGTH + i; } @@ -4170,6 +4171,7 @@ BEGIN_CASE(JSOP_CALLPROP) NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval); } regs.sp[-1] = rval; + assertSameCompartment(cx, regs.sp[-1]); PUSH_COPY(lval); goto end_callprop; } @@ -4192,6 +4194,7 @@ BEGIN_CASE(JSOP_CALLPROP) } regs.sp[-1] = objv; regs.sp[-2] = rval; + assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]); } else { JS_ASSERT(!objv.toObject().getOps()->getProperty); if (!js_GetPropertyHelper(cx, &objv.toObject(), id, @@ -4201,6 +4204,7 @@ BEGIN_CASE(JSOP_CALLPROP) } regs.sp[-1] = lval; regs.sp[-2] = rval; + assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]); } end_callprop: @@ -4472,6 +4476,7 @@ BEGIN_CASE(JSOP_GETELEM) end_getelem: regs.sp--; regs.sp[-1] = *copyFrom; + assertSameCompartment(cx, regs.sp[-1]); } END_CASE(JSOP_GETELEM) @@ -5846,6 +5851,7 @@ BEGIN_CASE(JSOP_SETTER) if (js_CodeSpec[op2].ndefs > js_CodeSpec[op2].nuses) { JS_ASSERT(js_CodeSpec[op2].ndefs == js_CodeSpec[op2].nuses + 1); regs.sp[-1] = rval; + assertSameCompartment(cx, regs.sp[-1]); } len = js_CodeSpec[op2].length; DO_NEXT_OP(len); From 79395ab7c300a6d6af0a4005ff1f91128fc8abb2 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:38:24 -0700 Subject: [PATCH 173/284] bug 580128 - Try to make test_wrappers-2.xul work a little better. --HG-- rename : js/src/xpconnect/tests/chrome/file_wrappers-2.html => js/src/xpconnect/tests/mochitest/file_wrappers-2.html --- js/src/xpconnect/tests/chrome/Makefile.in | 1 - js/src/xpconnect/tests/chrome/test_wrappers-2.xul | 13 +++++++------ js/src/xpconnect/tests/mochitest/Makefile.in | 1 + .../{chrome => mochitest}/file_wrappers-2.html | 0 4 files changed, 8 insertions(+), 7 deletions(-) rename js/src/xpconnect/tests/{chrome => mochitest}/file_wrappers-2.html (100%) diff --git a/js/src/xpconnect/tests/chrome/Makefile.in b/js/src/xpconnect/tests/chrome/Makefile.in index c316c246e635..42d361fc1fc7 100644 --- a/js/src/xpconnect/tests/chrome/Makefile.in +++ b/js/src/xpconnect/tests/chrome/Makefile.in @@ -55,7 +55,6 @@ _CHROME_FILES = \ test_wrappers.xul \ test_wrappers-2.xul \ test_bug484459.xul \ - file_wrappers-2.html \ test_cows.xul \ $(NULL) diff --git a/js/src/xpconnect/tests/chrome/test_wrappers-2.xul b/js/src/xpconnect/tests/chrome/test_wrappers-2.xul index 3a802f7d19cc..2ae2892c3d47 100644 --- a/js/src/xpconnect/tests/chrome/test_wrappers-2.xul +++ b/js/src/xpconnect/tests/chrome/test_wrappers-2.xul @@ -31,6 +31,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 { ok(window instanceof Object, "window is instanceof Object"); + let wrap = $('ifr').contentWindow.wrappedJSObject; + ok(!('obj' in XPCNativeWrapper(wrap)), "XPCNativeWrapper constructor works"); + let iwin = $('ifr').contentWindow; + location.foopy = 3; var xow_answer = []; @@ -81,9 +85,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 for_each_expected.sort().toString(), 'wrapper iterators are properly iterators'); - let wrap = $('ifr').contentWindow.wrappedJSObject; - let iwin = $('ifr').contentWindow; - let sjow_answer = []; let (obj = { a: 3, next:1 }) { for (let i in wrap.to_iterate) @@ -207,8 +208,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 } ]]> diff --git a/js/src/xpconnect/tests/mochitest/Makefile.in b/js/src/xpconnect/tests/mochitest/Makefile.in index d8f882d63222..f8738c22f006 100644 --- a/js/src/xpconnect/tests/mochitest/Makefile.in +++ b/js/src/xpconnect/tests/mochitest/Makefile.in @@ -51,6 +51,7 @@ _TEST_FILES = bug500931_helper.html \ chrome_wrappers_helper.html \ file_doublewrappedcompartments.html \ file_evalInSandbox.html \ + file_wrappers-2.html \ test_bug92773.html \ test_bug361111.xul \ test_bug384632.html \ diff --git a/js/src/xpconnect/tests/chrome/file_wrappers-2.html b/js/src/xpconnect/tests/mochitest/file_wrappers-2.html similarity index 100% rename from js/src/xpconnect/tests/chrome/file_wrappers-2.html rename to js/src/xpconnect/tests/mochitest/file_wrappers-2.html From 9973d0f31de148b0704dda726f9c6b330f33903b Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:38:39 -0700 Subject: [PATCH 174/284] bug 580128 - Deal better with compartments around the xray holder object. r=gal --- js/src/xpconnect/wrappers/XrayWrapper.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index a3528adbbdcc..6a194d56949f 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -158,6 +158,9 @@ holder_get(JSContext *cx, JSObject *wrapper, jsid id, jsval *vp) JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); XPCWrappedNative *wn = GetWrappedNative(wnObject); if (NATIVE_HAS_FLAG(wn, WantGetProperty)) { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, holder)) + return false; JSBool retval = true; nsresult rv = wn->GetScriptableCallback()->GetProperty(wn, cx, wrapper, id, vp, &retval); if (NS_FAILED(rv)) { @@ -178,6 +181,9 @@ holder_set(JSContext *cx, JSObject *wrapper, jsid id, jsval *vp) XPCWrappedNative *wn = GetWrappedNative(wnObject); if (NATIVE_HAS_FLAG(wn, WantSetProperty)) { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, holder)) + return false; JSBool retval = true; nsresult rv = wn->GetScriptableCallback()->SetProperty(wn, cx, wrapper, id, vp, &retval); if (NS_FAILED(rv)) { @@ -303,7 +309,15 @@ static JSBool holder_enumerate(JSContext *cx, JSObject *holder) { // Ask the native wrapper for all its ids - JSIdArray *ida = JS_Enumerate(cx, GetWrappedNativeObjectFromHolder(cx, holder)); + JSIdArray *ida; + { + JSObject *wrappednative = GetWrappedNativeObjectFromHolder(cx, holder); + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wrappednative)) + return false; + ida = JS_Enumerate(cx, wrappednative); + } + if (!ida) return false; From 4e1b8c7a56ad1f16370125d0abaa5ce9bcccf794 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:38:56 -0700 Subject: [PATCH 175/284] bug 580128 - Make the XPCNativeWrapper constructor work to actually construct XrayWrappers. r=jst --- js/src/xpconnect/src/XPCNativeWrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/xpconnect/src/XPCNativeWrapper.cpp b/js/src/xpconnect/src/XPCNativeWrapper.cpp index e3fe3fe1afbd..4a42db1c35fc 100644 --- a/js/src/xpconnect/src/XPCNativeWrapper.cpp +++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp @@ -908,7 +908,7 @@ XrayWrapperConstructor(JSContext *cx, uintN argc, jsval *vp) } JSObject *obj = JSVAL_TO_OBJECT(vp[2]); - if (!xpc::WrapperFactory::IsXrayWrapper(obj)) { + if (!obj->isWrapper()) { *vp = OBJECT_TO_JSVAL(obj); return JS_TRUE; } From 33995dacf892cce043c4d568b6b456da074a2ad7 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:39:02 -0700 Subject: [PATCH 176/284] Bug 580128 - Fix XPCVariant::VariantDataToJS to properly rewrap, r=gal. --- js/src/xpconnect/src/xpcvariant.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/js/src/xpconnect/src/xpcvariant.cpp b/js/src/xpconnect/src/xpcvariant.cpp index f21dec780465..5df1b4c45ae8 100644 --- a/js/src/xpconnect/src/xpcvariant.cpp +++ b/js/src/xpconnect/src/xpcvariant.cpp @@ -425,6 +425,8 @@ XPCVariant::VariantDataToJS(XPCLazyCallContext& lccx, { // It's not a JSObject (or it's a JSArray or a JSObject representing an // nsID). Just pass through the underlying data. + if (!JS_WrapValue(lccx.GetJSContext(), &realVal)) + return JS_FALSE; *pJSVal = realVal; return JS_TRUE; } @@ -436,9 +438,10 @@ XPCVariant::VariantDataToJS(XPCLazyCallContext& lccx, type == nsIDataType::VTYPE_INTERFACE_IS, "Weird variant"); - return XPCWrapper::RewrapObject(lccx.GetJSContext(), scope, - JSVAL_TO_OBJECT(realVal), - XPCWrapper::UNKNOWN, pJSVal); + if (!JS_WrapValue(lccx.GetJSContext(), &realVal)) + return JS_FALSE; + *pJSVal = realVal; + return JS_TRUE; } // else, it's an object and we really need to double wrap it if we've From 7f9d12d8d0c9a0d198fc39df2970214530f0f5ee Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:39:04 -0700 Subject: [PATCH 177/284] bug 580128 - Fix wrapping of jsval parameters. r=peterv --- js/src/xpconnect/src/xpcconvert.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 882ce67e90ee..d1545d2aef0f 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -291,6 +291,8 @@ XPCConvert::NativeData2JS(XPCLazyCallContext& lccx, jsval* d, const void* s, case nsXPTType::T_JSVAL : JS_STATIC_ASSERT(sizeof(jsval) <= sizeof(uint64)); *d = **((jsval**)s); + if (!JS_WrapValue(cx, d)) + return JS_FALSE; break; default: From 6a4e9945b0c3c87dea2b5a0c50870f51f48a4aa1 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:39:08 -0700 Subject: [PATCH 178/284] Bug 580128. Don't let script access only wrappers be passed to C++ code. r=peterv@propagandism.org --- js/src/xpconnect/src/XPCWrapper.cpp | 3 +- js/src/xpconnect/wrappers/AccessCheck.cpp | 37 ++++++++++++++++++++ js/src/xpconnect/wrappers/AccessCheck.h | 2 ++ js/src/xpconnect/wrappers/WrapperFactory.cpp | 28 --------------- js/src/xpconnect/wrappers/WrapperFactory.h | 2 -- 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/js/src/xpconnect/src/XPCWrapper.cpp b/js/src/xpconnect/src/XPCWrapper.cpp index 756b980c9682..3af9c615a625 100644 --- a/js/src/xpconnect/src/XPCWrapper.cpp +++ b/js/src/xpconnect/src/XPCWrapper.cpp @@ -46,7 +46,6 @@ #include "jswrapper.h" #include "XrayWrapper.h" #include "AccessCheck.h" -#include "WrapperFactory.h" namespace XPCWrapper { @@ -66,7 +65,7 @@ JSObject * Unwrap(JSContext *cx, JSObject *wrapper) { if (wrapper->isProxy()) { - if (xpc::WrapperFactory::IsScriptAccessOnly(cx, wrapper)) + if (xpc::AccessCheck::isScriptAccessOnly(cx, wrapper)) return nsnull; return wrapper->unwrap(); } diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 3cdc5fe795d6..cdbe8b337814 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -46,6 +46,8 @@ #include "XPCWrapper.h" #include "XrayWrapper.h" +#include "FilteringWrapper.h" +#include "WrapperFactory.h" namespace xpc { @@ -274,6 +276,41 @@ AccessCheck::needsSystemOnlyWrapper(JSObject *obj) return wn->NeedsSOW(); } +bool +AccessCheck::isScriptAccessOnly(JSContext *cx, JSObject *wrapper) +{ + JS_ASSERT(wrapper->isWrapper()); + + uintN flags; + JSObject *obj = wrapper->unwrap(&flags); + + // If the wrapper indicates script-only access, we are done. + if (flags & WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG) { + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if (!ssm) + return true; + + // Bypass script-only status if UniversalXPConnect is enabled. + PRBool privileged; + return !NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) || + !privileged; + } + + // In addition, chrome objects can explicitly opt-in by setting .scriptOnly to true. + if (wrapper->getProxyHandler() == &FilteringWrapper::singleton) { + jsid scriptOnlyId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_SCRIPTONLY); + jsval scriptOnly; + if (JS_LookupPropertyById(cx, obj, scriptOnlyId, &scriptOnly) && + scriptOnly == JSVAL_TRUE) + return true; // script-only + } + + // Allow non-script access to same-origin location objects and any other + // objects. + return WrapperFactory::IsLocationObject(obj) && !isLocationObjectSameOrigin(cx, wrapper); +} + void AccessCheck::deny(JSContext *cx, jsid id) { diff --git a/js/src/xpconnect/wrappers/AccessCheck.h b/js/src/xpconnect/wrappers/AccessCheck.h index 7bdec2ec46e5..e3217b9473cc 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.h +++ b/js/src/xpconnect/wrappers/AccessCheck.h @@ -56,6 +56,8 @@ class AccessCheck { static bool needsSystemOnlyWrapper(JSObject *obj); + static bool isScriptAccessOnly(JSContext *cx, JSObject *wrapper); + static void deny(JSContext *cx, jsid id); }; diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 4b596206c8cd..71210c35886e 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -241,32 +241,4 @@ WrapperFactory::WrapLocationObject(JSContext *cx, JSObject *obj) return wrapperObj; } -bool -WrapperFactory::IsScriptAccessOnly(JSContext *cx, JSObject *wrapper) -{ - JS_ASSERT(wrapper->isWrapper()); - - uintN flags; - JSObject *obj = wrapper->unwrap(&flags); - - // If the wrapper indicates script-only access, we are done. - if (flags & SCRIPT_ACCESS_ONLY_FLAG) - return true; - - // In addition, chrome objects can explicitly opt-in by setting .scriptOnly to true. - if (wrapper->getProxyHandler() == &FilteringWrapper::singleton) { - jsid scriptOnlyId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_SCRIPTONLY); - jsval scriptOnly; - if (JS_LookupPropertyById(cx, obj, scriptOnlyId, &scriptOnly) && - scriptOnly == JSVAL_TRUE) - return true; // script-only - } - - // Allow non-script access to same-origin location objects and any other - // objects. - return IsLocationObject(obj) && - !xpc::AccessCheck::isLocationObjectSameOrigin(cx, wrapper); -} - } diff --git a/js/src/xpconnect/wrappers/WrapperFactory.h b/js/src/xpconnect/wrappers/WrapperFactory.h index b8b518bd4e46..f5183860af10 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.h +++ b/js/src/xpconnect/wrappers/WrapperFactory.h @@ -59,8 +59,6 @@ class WrapperFactory { return HasWrapperFlag(wrapper, IS_XRAY_WRAPPER_FLAG); } - static bool IsScriptAccessOnly(JSContext *cx, JSObject *wrapper); - // Prepare a given object for wrapping in a new compartment. static JSObject *PrepareForWrapping(JSContext *cx, JSObject *scope, From 09878ba8cc46bda52439dd1a8af6ed343dbb8c2a Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Sun, 10 Oct 2010 15:39:11 -0700 Subject: [PATCH 179/284] Bug 580128. Add cross compartment JSObject clone hooks. r=jst@mozilla.org --- dom/base/domerr.msg | 4 ++++ dom/base/nsDOMError.h | 4 ++++ dom/base/nsJSEnvironment.cpp | 37 +++++++++++++++++++++++++++++ dom/src/threads/nsDOMWorker.cpp | 3 +-- dom/src/threads/test/json_worker.js | 3 +++ 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/dom/base/domerr.msg b/dom/base/domerr.msg index 258af1573a05..415ca7bc15a0 100644 --- a/dom/base/domerr.msg +++ b/dom/base/domerr.msg @@ -82,6 +82,10 @@ DOM_MSG_DEF(NS_ERROR_DOM_SVG_MATRIX_NOT_INVERTABLE, "The matrix could not be com DOM_MSG_DEF(NS_ERROR_DOM_INVALID_EXPRESSION_ERR, "The expression is not a legal expression.") DOM_MSG_DEF(NS_ERROR_DOM_TYPE_ERR, "The expression cannot be converted to return the specified type.") +/* HTML5 error codes http://dev.w3.org/html5/spec/Overview.html */ + +DOM_MSG_DEF(NS_ERROR_DOM_DATA_CLONE_ERR, "The object could not be cloned.") + /* DOM error codes defined by us */ /* XXX string should be specified by norris */ diff --git a/dom/base/nsDOMError.h b/dom/base/nsDOMError.h index 16515bb14337..e5b7840e7428 100644 --- a/dom/base/nsDOMError.h +++ b/dom/base/nsDOMError.h @@ -87,6 +87,10 @@ #define NS_ERROR_DOM_INVALID_EXPRESSION_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_XPATH, 51) #define NS_ERROR_DOM_TYPE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_XPATH, 52) +/* HTML5 error codes http://dev.w3.org/html5/spec/Overview.html */ + +#define NS_ERROR_DOM_DATA_CLONE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,18) + /* DOM error codes defined by us */ #define NS_ERROR_DOM_SECURITY_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1000) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 9a58788aa93f..5504c53000e8 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3980,6 +3980,35 @@ ObjectPrincipalFinder(JSContext *cx, JSObject *obj) return jsPrincipals; } +static JSObject* +DOMReadStructuredClone(JSContext* cx, + JSStructuredCloneReader* reader, + uint32 tag, + uint32 data) +{ + // We don't currently support any extensions to structured cloning. + nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR); + return nsnull; +} + +static JSBool +DOMWriteStructuredClone(JSContext* cx, + JSStructuredCloneWriter* writer, + JSObject* obj) +{ + // We don't currently support any extensions to structured cloning. + nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR); + return JS_FALSE; +} + +static void +DOMStructuredCloneError(JSContext* cx, + uint32 errorid) +{ + // We don't currently support any extensions to structured cloning. + nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR); +} + //static nsresult nsJSRuntime::Init() @@ -4018,6 +4047,14 @@ nsJSRuntime::Init() callbacks->findObjectPrincipals = ObjectPrincipalFinder; + // Set up the structured clone callbacks. + static JSStructuredCloneCallbacks cloneCallbacks = { + DOMReadStructuredClone, + DOMWriteStructuredClone, + DOMStructuredCloneError + }; + JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks); + // Set these global xpconnect options... nsContentUtils::RegisterPrefCallback("dom.max_script_run_time", MaxScriptRunTimePrefChangedCallback, diff --git a/dom/src/threads/nsDOMWorker.cpp b/dom/src/threads/nsDOMWorker.cpp index fee7aad4b521..ec1a5b5e6c8f 100644 --- a/dom/src/threads/nsDOMWorker.cpp +++ b/dom/src/threads/nsDOMWorker.cpp @@ -1542,8 +1542,7 @@ nsDOMWorker::PostMessageInternal(PRBool aToInner) JSAutoStructuredCloneBuffer buffer(cx); if (!buffer.write(argv[0])) { - NS_WARNING("Failed to serialize!"); - return NS_ERROR_FAILURE; + return NS_ERROR_DOM_DATA_CLONE_ERR; } nsRefPtr message = new nsDOMWorkerMessageEvent(); diff --git a/dom/src/threads/test/json_worker.js b/dom/src/threads/test/json_worker.js index d2d715a232cc..77075a20e486 100644 --- a/dom/src/threads/test/json_worker.js +++ b/dom/src/threads/test/json_worker.js @@ -315,6 +315,9 @@ function onmessage(event) { postMessage(messages[index].value); } catch (e) { + if (e.result != 2152923154) { + throw "Exception of the wrong type: " + e.result; + } exception = e; } From b7e20670931d32b8d778d27e4469b7aa0a4d972d Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Sun, 10 Oct 2010 15:39:14 -0700 Subject: [PATCH 180/284] Bug 580128. Fix plugin stream test to work with the new wrappers. r=jst@mozilla.org --- modules/plugin/test/mochitest/pluginstream.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/plugin/test/mochitest/pluginstream.js b/modules/plugin/test/mochitest/pluginstream.js index cf872459d478..affc4b221290 100644 --- a/modules/plugin/test/mochitest/pluginstream.js +++ b/modules/plugin/test/mochitest/pluginstream.js @@ -24,7 +24,13 @@ // For file:// url's, such as we get during the NPN_NewStream test, // attempting to access the frame content throws an exception. // For this case, we just verify the onload event is called. - ok(e.message.indexOf("file:") > -1, "exception " + e.message + " thrown"); + + // XXXbent Need to fix the underlying change to this message here! Should + // the message include 'file://'? Message needs to be localized + // and include both origins in the error console too! + ok(e.message.indexOf("Permission denied") > -1 && + e.message.indexOf("access property 'body'") > -1, + "Unexpected exception thrown: " + e.message); } is(embed.getError(), "pass", "plugin reported error"); SimpleTest.finish(); From a0fe32698845f9a9cfdaf3a73ca700f30d09d135 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:39:17 -0700 Subject: [PATCH 181/284] Bug 580128. Use JS_WrapObject() in xpcJSWeakReference::Get() since it's the right API to use here. r=mrbkap@gmail.com --- js/src/xpconnect/src/xpcJSWeakReference.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/js/src/xpconnect/src/xpcJSWeakReference.cpp b/js/src/xpconnect/src/xpcJSWeakReference.cpp index b849889970e8..ebafdc7a25e6 100644 --- a/js/src/xpconnect/src/xpcJSWeakReference.cpp +++ b/js/src/xpconnect/src/xpcJSWeakReference.cpp @@ -1,3 +1,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 * @@ -130,12 +131,9 @@ xpcJSWeakReference::Get() // re-wrapping because things are implicitly rewrapped by // xpcconvert. However, because we're doing this directly // through the native call context, we need to call - // nsXPConnect::GetWrapperForObject. But it takes a lot of - // arguments! It turns out that the thisObject hook on XPConnect - // objects does the right thing though, so... + // JS_WrapObject(). - if (obj->getOps()->thisObject && - !(obj = obj->getOps()->thisObject(cx, obj))) + if (!JS_WrapObject(cx, &obj)) { return NS_ERROR_FAILURE; } From f8bd40201e679286b2b3d53691f226619b8edd21 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:39:19 -0700 Subject: [PATCH 182/284] bug 580128 - Allow access when both sites have set document.domain. r=mrbkap --- js/src/xpconnect/wrappers/AccessCheck.cpp | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index cdbe8b337814..acb608a04cb9 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -214,6 +214,42 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid if (IsWindow(name) && IsFrameId(cx, obj, id)) return true; + nsIXPConnect *xpc = nsXPConnect::GetRuntimeInstance()->GetXPConnect(); + + JSObject *scope = nsnull; + JSStackFrame *fp = nsnull; + JS_FrameIterator(cx, &fp); + if (fp) { + while (fp->isDummyFrame()) { + if (!JS_FrameIterator(cx, &fp)) + break; + } + + if (fp) + scope = &fp->scopeChain(); + } + + if (!scope) + scope = JS_GetScopeChain(cx); + + nsIPrincipal *subject; + + { + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, scope)) + return false; + + subject = xpc->GetPrincipal(JS_GetGlobalForObject(cx, scope), PR_TRUE); + } + + nsIPrincipal *objprin = + xpc->GetPrincipal(JS_GetGlobalForObject(cx, obj), PR_TRUE); + + PRBool subsumes; + if (NS_SUCCEEDED(subject->Subsumes(objprin, &subsumes)) && subsumes) + return true; + return (act == JSWrapper::SET) ? nsContentUtils::IsCallerTrustedForWrite() : nsContentUtils::IsCallerTrustedForRead(); From dc1082fc97a213f5fe67a27022925d9de2742c89 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:39:23 -0700 Subject: [PATCH 183/284] bug 580128 - Allow NewResolve to set properties on wrappers. r=gal --- js/src/xpconnect/wrappers/XrayWrapper.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 6a194d56949f..2ed802308469 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -177,6 +177,10 @@ holder_set(JSContext *cx, JSObject *wrapper, jsid id, jsval *vp) { NS_ASSERTION(wrapper->isProxy(), "bad this object in set"); JSObject *holder = GetHolder(wrapper); + if (IsResolving(holder, id)) { + return true; + } + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); XPCWrappedNative *wn = GetWrappedNative(wnObject); @@ -427,14 +431,14 @@ bool XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, PropertyDescriptor *desc_in) { - if (!this->enter(cx, wrapper, id, set ? JSWrapper::SET : JSWrapper::GET)) - return false; - - AutoLeaveHelper helper(*this, cx, wrapper); - JSPropertyDescriptor *desc = Jsvalify(desc_in); if (id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) { + if (!this->enter(cx, wrapper, id, set ? JSWrapper::SET : JSWrapper::GET)) + return false; + + AutoLeaveHelper helper(*this, cx, wrapper); + desc->obj = wrapper; desc->attrs = JSPROP_ENUMERATE|JSPROP_SHARED; desc->getter = wrappedJSObject_getter; @@ -450,6 +454,11 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe return true; } + if (!this->enter(cx, wrapper, id, set ? JSWrapper::SET : JSWrapper::GET)) + return false; + + AutoLeaveHelper helper(*this, cx, wrapper); + ResolvingId resolving(holder, id); void *priv; From 15df9eaaec39ba11925094a8d9c981548441fb63 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:39:26 -0700 Subject: [PATCH 184/284] bug 580128 - Allow proxies to answer the "hasInstance" question. r=mrbkap --- js/src/jscompartment.h | 2 +- js/src/jsproxy.cpp | 26 ++++++++++++++++++++++---- js/src/jsproxy.h | 1 + js/src/jswrapper.cpp | 21 +++++++++++++++++++++ js/src/jswrapper.h | 2 ++ 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 944b2dcd24b2..7add238c7d6c 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -49,7 +49,7 @@ #include "jsclist.h" #include "jsxml.h" -struct JSCompartment { +struct JS_FRIEND_API(JSCompartment) { JSRuntime *rt; JSPrincipals *principals; js::gc::Chunk *chunk; diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 2daa9587acb7..85877cda3bc0 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -284,6 +284,15 @@ JSProxyHandler::construct(JSContext *cx, JSObject *proxy, return ExternalInvoke(cx, thisobj, fval, argc, argv, rval); } +bool +JSProxyHandler::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp) +{ + JS_ASSERT(OperationInProgress(cx, proxy)); + js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, + JSDVG_SEARCH_STACK, ObjectValue(*proxy), NULL); + return false; +} + void JSProxyHandler::finalize(JSContext *cx, JSObject *proxy) { @@ -920,6 +929,17 @@ proxy_Finalize(JSContext *cx, JSObject *obj) obj->getProxyHandler()->finalize(cx, obj); } +static JSBool +proxy_HasInstance(JSContext *cx, JSObject *proxy, const Value *v, JSBool *bp) +{ + AutoPendingProxyOperation pending(cx, proxy); + bool b; + if (!proxy->getProxyHandler()->hasInstance(cx, proxy, v, &b)) + return false; + *bp = !!b; + return true; +} + JS_FRIEND_API(Class) ObjectProxyClass = { "Proxy", Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(3), @@ -936,7 +956,7 @@ JS_FRIEND_API(Class) ObjectProxyClass = { NULL, /* call */ NULL, /* construct */ NULL, /* xdrObject */ - NULL, /* hasInstance */ + proxy_HasInstance, /* hasInstance */ NULL, /* mark */ JS_NULL_CLASS_EXT, { @@ -1022,8 +1042,6 @@ proxy_TypeOf_fun(JSContext *cx, JSObject *obj) return JSTYPE_FUNCTION; } -#define proxy_HasInstance js_FunctionClass.hasInstance - JS_FRIEND_API(Class) FunctionProxyClass = { "Proxy", Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(5), @@ -1040,7 +1058,7 @@ JS_FRIEND_API(Class) FunctionProxyClass = { proxy_Call, proxy_Construct, NULL, /* xdrObject */ - proxy_HasInstance, + js_FunctionClass.hasInstance, NULL, /* mark */ JS_NULL_CLASS_EXT, { diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 53f447576e34..3f560e955971 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -79,6 +79,7 @@ class JS_FRIEND_API(JSProxyHandler) { virtual bool call(JSContext *cx, JSObject *proxy, uintN argc, js::Value *vp); virtual bool construct(JSContext *cx, JSObject *proxy, uintN argc, js::Value *argv, js::Value *rval); + virtual bool hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *bp); virtual JSString *obj_toString(JSContext *cx, JSObject *proxy); virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent); virtual void finalize(JSContext *cx, JSObject *proxy); diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 003d138d40a1..9669b7da87d3 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -239,6 +239,14 @@ JSWrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv, GET(JSProxyHandler::construct(cx, wrapper, argc, argv, rval)); } +bool +JSWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) +{ + const jsid id = JSID_VOID; + JSBool b; + GET(JS_HasInstance(cx, wrappedObject(wrapper), Jsvalify(*vp), &b) && Cond(b, bp)); +} + JSString * JSWrapper::obj_toString(JSContext *cx, JSObject *wrapper) { @@ -604,6 +612,19 @@ JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN arg call.origin->wrapException(cx); } +bool +JSCrossCompartmentWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) +{ + AutoCompartment call(cx, wrappedObject(wrapper)); + if (!call.enter()) + return false; + + Value v = *vp; + if (!call.destination->wrap(cx, &v)) + return false; + return JSWrapper::hasInstance(cx, wrapper, &v, bp); +} + JSString * JSCrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *wrapper) { diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index a7ff77102462..7840be30ab3b 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -83,6 +83,7 @@ class JS_FRIEND_API(JSWrapper) : public js::JSProxyHandler { virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp); virtual bool construct(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *argv, js::Value *rval); + virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const js::Value *vp, bool *bp); virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper); virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent); @@ -135,6 +136,7 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper { virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp); virtual bool construct(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *argv, js::Value *rval); + virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const js::Value *vp, bool *bp); virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper); virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent); From 046cfa050e74206137faf9d460d8baeb6b3fc894 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:39:29 -0700 Subject: [PATCH 185/284] bug 580128 - Fix compartment mistakes around document.domain stuff. r=mrbkap --- js/src/xpconnect/wrappers/AccessCheck.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index acb608a04cb9..faa12d9d7702 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -233,6 +233,7 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid scope = JS_GetScopeChain(cx); nsIPrincipal *subject; + nsIPrincipal *object; { JSAutoEnterCompartment ac; @@ -243,11 +244,17 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid subject = xpc->GetPrincipal(JS_GetGlobalForObject(cx, scope), PR_TRUE); } - nsIPrincipal *objprin = - xpc->GetPrincipal(JS_GetGlobalForObject(cx, obj), PR_TRUE); + { + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, obj)) + return false; + + object = xpc->GetPrincipal(JS_GetGlobalForObject(cx, obj), PR_TRUE); + } PRBool subsumes; - if (NS_SUCCEEDED(subject->Subsumes(objprin, &subsumes)) && subsumes) + if (NS_SUCCEEDED(subject->Subsumes(object, &subsumes)) && subsumes) return true; return (act == JSWrapper::SET) From bd7d445d26473dca3fb5f4d07775dfbca5f96cb3 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:41:14 -0700 Subject: [PATCH 186/284] Bug 580128. Update mochi tests to the new wrappers. r=mrbkap@gmail.com --- js/src/xpconnect/tests/mochitest/test_bug553407.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/src/xpconnect/tests/mochitest/test_bug553407.html b/js/src/xpconnect/tests/mochitest/test_bug553407.html index 3e028b0502b6..0fb0ae1ee177 100644 --- a/js/src/xpconnect/tests/mochitest/test_bug553407.html +++ b/js/src/xpconnect/tests/mochitest/test_bug553407.html @@ -21,8 +21,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=517163 /** Test for Bug 553407 **/ is(typeof new XPCNativeWrapper(location), "object", "XPCNativeWrapper(location) is an object") is(typeof new XPCNativeWrapper(XMLHttpRequest), "function", "XPCNativeWrapper(XMLHttpRequest) is a function") -is(typeof new XPCNativeWrapper(location).wrappedJSObject, "object", "XPCNativeWrapper(location).wrappedJSObject is an object") -is(typeof new XPCNativeWrapper(XMLHttpRequest).wrappedJSObject, "function", "XPCNativeWrapper(XMLHttpRequest).wrappedJSObject is a function") +// We no longer support .wrappedJSObject on NW since for same-origin there is no wrapper in between. +// is(typeof new XPCNativeWrapper(location).wrappedJSObject, "object", "XPCNativeWrapper(location).wrappedJSObject is an object") +// is(typeof new XPCNativeWrapper(XMLHttpRequest).wrappedJSObject, "function", "XPCNativeWrapper(XMLHttpRequest).wrappedJSObject is a function") ok("a".replace("a", new XPCNativeWrapper(location)).indexOf("mochi.test") >= 0, "XPCNativeWrappers can be used as the replacement value for .replace"); From 1539341407987bf695f13224ffd4af9ed1065eb7 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:41:20 -0700 Subject: [PATCH 187/284] bug 580128 - Don't think the outer window isn't "native". r=mrbkap --- js/src/xpconnect/wrappers/WrapperFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 71210c35886e..3a70e1eb4080 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -184,7 +184,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO // a predefined set of properties. XrayWrapper adds a property // (.wrappedJSObject) which allows bypassing the XrayWrapper, but // we filter out access to that property. - if (!IS_WN_WRAPPER(obj)) { + if (!IS_WN_WRAPPER(obj) && !obj->getClass()->ext.innerObject) { wrapper = &FilteringWrapper::singleton; } else { From fa5d3f82f57941b36862c0c594caf9bc0fc41187 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:41:24 -0700 Subject: [PATCH 188/284] bug 580128 - Try to allow UniversalXPConnect to have full access through "XOWs". --- js/src/xpconnect/wrappers/XrayWrapper.cpp | 89 ++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 2ed802308469..f95b474aded3 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -426,6 +426,19 @@ class AutoLeaveHelper JSObject *wrapper; }; +static bool +UniversalXPConnect() +{ + // Redirect access straight to the wrapper if UniversalXPConnect is enabled. + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); + if (ssm) { + PRBool privileged; + if (NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) && privileged) + return true; + } + return false; +} + template bool XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, @@ -484,6 +497,25 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe return true; } + // Redirect access straight to the wrapper if UniversalXPConnect is enabled. + if (UniversalXPConnect()) { + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); + + { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wnObject)) + return false; + + if (!JS_GetPropertyDescriptorById(cx, wnObject, id, + (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED, + desc)) + return false; + } + + desc->obj = wrapper; + return cx->compartment->wrap(cx, desc_in); + } + return JS_GetPropertyDescriptorById(cx, holder, id, (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED, desc); @@ -503,6 +535,23 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid js::PropertyDescriptor *desc) { JSObject *holder = GetHolder(wrapper); + JSPropertyDescriptor *jsdesc = Jsvalify(desc); + + // Redirect access straight to the wrapper if UniversalXPConnect is enabled. + if (UniversalXPConnect()) { + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wnObject)) + return false; + + if (!cx->compartment->wrap(cx, desc)) + return false; + + return JS_DefinePropertyById(cx, wnObject, id, jsdesc->value, jsdesc->getter, jsdesc->setter, + jsdesc->attrs); + } + PropertyDescriptor existing_desc; if (!getOwnPropertyDescriptor(cx, wrapper, id, true, &existing_desc)) return false; @@ -510,7 +559,6 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid if (existing_desc.obj && (existing_desc.attrs & JSPROP_PERMANENT)) return true; // silently ignore attempt to overwrite native property - JSPropertyDescriptor *jsdesc = Jsvalify(desc); if (!(jsdesc->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { if (!desc->getter) jsdesc->getter = holder_get; @@ -528,6 +576,18 @@ XrayWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props) { JSObject *holder = GetHolder(wrapper); + + // Redirect access straight to the wrapper if UniversalXPConnect is enabled. + if (UniversalXPConnect()) { + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wnObject)) + return false; + + return js::GetPropertyNames(cx, wnObject, JSITER_OWNONLY | JSITER_HIDDEN, &props); + } + return js::GetPropertyNames(cx, holder, JSITER_OWNONLY | JSITER_HIDDEN, &props); } @@ -538,6 +598,21 @@ XrayWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bo JSObject *holder = GetHolder(wrapper); jsval v; JSBool b; + + // Redirect access straight to the wrapper if UniversalXPConnect is enabled. + if (UniversalXPConnect()) { + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wnObject)) + return false; + + if (!JS_DeletePropertyById2(cx, wnObject, id, &v) || !JS_ValueToBoolean(cx, v, &b)) + return false; + *bp = !!b; + return true; + } + if (!JS_DeletePropertyById2(cx, holder, id, &v) || !JS_ValueToBoolean(cx, v, &b)) return false; *bp = !!b; @@ -549,6 +624,18 @@ bool XrayWrapper::enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props) { JSObject *holder = GetHolder(wrapper); + + // Redirect access straight to the wrapper if UniversalXPConnect is enabled. + if (UniversalXPConnect()) { + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wnObject)) + return false; + + return js::GetPropertyNames(cx, wnObject, 0, &props); + } + return js::GetPropertyNames(cx, holder, 0, &props); } From 1219a88f7e2509a59c19337c768fb42e3b2914c1 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:41:27 -0700 Subject: [PATCH 189/284] bug 580128 - Remove vestiges of old wrappers from nsDOMClassInfo. r=mrbkap --- dom/base/nsDOMClassInfo.cpp | 20 ++------------------ dom/base/nsDOMClassInfo.h | 18 ------------------ dom/base/nsJSEnvironment.cpp | 13 ------------- 3 files changed, 2 insertions(+), 49 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 50f701147cfd..b2b1cdaa73be 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -43,6 +43,7 @@ #include "jscntxt.h" #include "jsobj.h" #include "jsdbgapi.h" +#include "WrapperFactory.h" #include "nscore.h" #include "nsDOMClassInfo.h" @@ -1621,8 +1622,6 @@ jsid nsDOMClassInfo::sOnafterscriptexecute_id = JSID_VOID; jsid nsDOMClassInfo::sWrappedJSObject_id = JSID_VOID; static const JSClass *sObjectClass = nsnull; -JSPropertyOp nsDOMClassInfo::sXPCNativeWrapperGetPropertyOp = nsnull; -JSPropertyOp nsDOMClassInfo::sXrayWrapperPropertyHolderGetPropertyOp = nsnull; /** * Set our JSClass pointer for the Object class @@ -1918,22 +1917,7 @@ nsDOMClassInfo::ThrowJSException(JSContext *cx, nsresult aResult) PRBool nsDOMClassInfo::ObjectIsNativeWrapper(JSContext* cx, JSObject* obj) { -#ifdef DEBUG - { - nsIScriptContext *scx = GetScriptContextFromJSContext(cx); - - NS_PRECONDITION(!scx || !scx->IsContextInitialized() || - sXPCNativeWrapperGetPropertyOp, - "Must know what the XPCNativeWrapper class GetProperty op is!"); - } -#endif - - if (obj->isWrapper()) - return PR_TRUE; - - JSPropertyOp op = obj->getJSClass()->getProperty; - return !!op && (op == sXPCNativeWrapperGetPropertyOp || - op == sXrayWrapperPropertyHolderGetPropertyOp); + return xpc::WrapperFactory::IsXrayWrapper(obj); } nsDOMClassInfo::nsDOMClassInfo(nsDOMClassInfoData* aData) : mData(aData) diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 21b2832409fe..31ca748fc5d0 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -177,24 +177,6 @@ public: static nsresult ThrowJSException(JSContext *cx, nsresult aResult); - static JSPropertyOp GetXPCNativeWrapperGetPropertyOp() { - return sXPCNativeWrapperGetPropertyOp; - } - - static void SetXPCNativeWrapperGetPropertyOp(JSPropertyOp getPropertyOp) { - NS_ASSERTION(!sXPCNativeWrapperGetPropertyOp, - "Double set of sXPCNativeWrapperGetPropertyOp"); - sXPCNativeWrapperGetPropertyOp = getPropertyOp; - } - - static JSPropertyOp GetXrayWrapperPropertyHolderGetPropertyOp() { - return sXrayWrapperPropertyHolderGetPropertyOp; - } - - static void SetXrayWrapperPropertyHolderGetPropertyOp(JSPropertyOp getPropertyOp) { - sXrayWrapperPropertyHolderGetPropertyOp = getPropertyOp; - } - static PRBool ObjectIsNativeWrapper(JSContext* cx, JSObject* obj); static nsISupports *GetNative(nsIXPConnectWrappedNative *wrapper, JSObject *obj); diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 5504c53000e8..c7fdfccbfdbb 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2571,19 +2571,6 @@ nsJSContext::InitContext() ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter); - nsIXPConnect *xpc = nsContentUtils::XPConnect(); - if (!nsDOMClassInfo::GetXPCNativeWrapperGetPropertyOp()) { - JSPropertyOp getProperty; - xpc->GetNativeWrapperGetPropertyOp(&getProperty); - nsDOMClassInfo::SetXPCNativeWrapperGetPropertyOp(getProperty); - } - - if (!nsDOMClassInfo::GetXrayWrapperPropertyHolderGetPropertyOp()) { - JSPropertyOp getProperty; - xpc->GetXrayWrapperPropertyHolderGetPropertyOp(&getProperty); - nsDOMClassInfo::SetXrayWrapperPropertyHolderGetPropertyOp(getProperty); - } - return NS_OK; } From 84f62c2607dca805078c8f9386edae7df889d957 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:41:30 -0700 Subject: [PATCH 190/284] bug 580128 - Try to allow "XOWs" to see named frames. --- dom/base/nsDOMClassInfo.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index b2b1cdaa73be..98432a98ec3d 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -6574,8 +6574,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSString *str = JSID_TO_STRING(id); - // Don't resolve named frames on native wrappers - if (!ObjectIsNativeWrapper(cx, obj)) { + { nsCOMPtr dsn(do_QueryInterface(win->GetDocShell())); PRInt32 count = 0; From 6c7cd79252a6da4ebece112f09964df47acfd51b Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:41:33 -0700 Subject: [PATCH 191/284] bug 580128 - Fix evalInSandbox returning objects in the wrong compartment. r=jst --- js/src/jsinterp.cpp | 2 +- js/src/xpconnect/src/xpccomponents.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index e609eef63c00..cab81834ddc5 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -4698,7 +4698,7 @@ BEGIN_CASE(JSOP_APPLY) JS_ASSERT(vp[1].isObjectOrNull() || PrimitiveThisTest(newfun, vp[1])); Probes::enterJSFun(cx, newfun); - JSBool ok = newfun->u.n.native(cx, argc, vp); + JSBool ok = CallJSNative(cx, newfun->u.n.native, argc, vp); Probes::exitJSFun(cx, newfun); regs.sp = vp + 1; if (!ok) diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index 628d16bc8e21..8240c0c6944e 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -3669,8 +3669,10 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, JSString *str = nsnull; if (!ac.enter(sandcx->GetJSContext(), sandbox)) { - // XXX HELP! - NS_ABORT(); + if (stack) { + stack->Pop(nsnull); + } + return NS_ERROR_FAILURE; } JSBool ok = @@ -3729,11 +3731,12 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, } else { // Convert the result into something safe for our caller. JSAutoRequest req(cx); + JSAutoEnterCompartment ac; if (str) { v = STRING_TO_JSVAL(str); } - if (!JS_WrapValue(cx, &v)) { + if (!ac.enter(cx, callingScope) || !JS_WrapValue(cx, &v)) { rv = NS_ERROR_FAILURE; } From 36c5c9ec0f733b65b207fe7d1c3686a5b5d91838 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:41:37 -0700 Subject: [PATCH 192/284] Bug 580128. Remove usage of old wrapper type which isn't needed any more. r=mrbkap@gmail.com --- layout/tools/reftest/reftest.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index d44bc879111e..c1f88476fc77 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -432,14 +432,14 @@ function BuildConditionSandbox(aURL) { sandbox.nativeThemePref = true; } - new XPCSafeJSObjectWrapper(sandbox).prefs = { - __exposedProps__: { - getBoolPref: 'r', - getIntPref: 'r', - }, - _prefs: prefs, - getBoolPref: function(p) { return this._prefs.getBoolPref(p); }, - getIntPref: function(p) { return this._prefs.getIntPref(p); } + sandbox.prefs = { + __exposedProps__: { + getBoolPref: 'r', + getIntPref: 'r', + }, + _prefs: prefs, + getBoolPref: function(p) { return this._prefs.getBoolPref(p); }, + getIntPref: function(p) { return this._prefs.getIntPref(p); } } sandbox.testPluginIsOOP = function () { From f2855434db89171c0dfd5aed9de53b1754e0bd37 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:41:40 -0700 Subject: [PATCH 193/284] bug 580128 - Remove old wrappers. r=mrbkap --- dom/base/nsDOMWindowUtils.cpp | 58 - dom/interfaces/base/nsIDOMWindowUtils.idl | 13 +- js/src/xpconnect/idl/nsIXPConnect.idl | 74 +- js/src/xpconnect/src/Makefile.in | 5 - .../xpconnect/src/XPCChromeObjectWrapper.cpp | 814 ---------- .../xpconnect/src/XPCCrossOriginWrapper.cpp | 1331 ----------------- js/src/xpconnect/src/XPCNativeWrapper.cpp | 1114 -------------- js/src/xpconnect/src/XPCNativeWrapper.h | 96 -- .../xpconnect/src/XPCSafeJSObjectWrapper.cpp | 1087 -------------- js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp | 704 --------- js/src/xpconnect/src/XPCWrapper.cpp | 1083 +------------- js/src/xpconnect/src/XPCWrapper.h | 454 +----- js/src/xpconnect/src/nsXPConnect.cpp | 366 +---- js/src/xpconnect/src/xpccomponents.cpp | 59 +- js/src/xpconnect/src/xpcconvert.cpp | 1 - js/src/xpconnect/src/xpcprivate.h | 8 - js/src/xpconnect/src/xpcquickstubs.cpp | 1 - js/src/xpconnect/src/xpcwrappednative.cpp | 32 +- .../xpconnect/src/xpcwrappednativejsops.cpp | 48 +- .../xpconnect/src/xpcwrappednativescope.cpp | 158 -- 20 files changed, 141 insertions(+), 7365 deletions(-) delete mode 100644 js/src/xpconnect/src/XPCChromeObjectWrapper.cpp delete mode 100644 js/src/xpconnect/src/XPCCrossOriginWrapper.cpp delete mode 100644 js/src/xpconnect/src/XPCNativeWrapper.cpp delete mode 100644 js/src/xpconnect/src/XPCNativeWrapper.h delete mode 100644 js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp delete mode 100644 js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 34f3479cc27a..abbcd21e75dd 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -979,64 +979,6 @@ nsDOMWindowUtils::GetScreenPixelsPerCSSPixel(float* aScreenPixels) return NS_OK; } -NS_IMETHODIMP -nsDOMWindowUtils::GetCOWForObject() -{ - if (!IsUniversalXPConnectCapable()) { - return NS_ERROR_DOM_SECURITY_ERR; - } - - nsCOMPtr xpc = nsContentUtils::XPConnect(); - - // get the xpconnect native call context - nsAXPCNativeCallContext *cc = nsnull; - xpc->GetCurrentNativeCallContext(&cc); - if(!cc) - return NS_ERROR_FAILURE; - - // Get JSContext of current call - JSContext* cx; - nsresult rv = cc->GetJSContext(&cx); - if(NS_FAILED(rv) || !cx) - return NS_ERROR_FAILURE; - - // get place for return value - jsval *rval = nsnull; - rv = cc->GetRetValPtr(&rval); - if(NS_FAILED(rv) || !rval) - return NS_ERROR_FAILURE; - - // get argc and argv and verify arg count - PRUint32 argc; - rv = cc->GetArgc(&argc); - if(NS_FAILED(rv)) - return NS_ERROR_FAILURE; - - if(argc < 2) - return NS_ERROR_XPC_NOT_ENOUGH_ARGS; - - jsval* argv; - rv = cc->GetArgvPtr(&argv); - if(NS_FAILED(rv) || !argv) - return NS_ERROR_FAILURE; - - // first and second params must be JSObjects - if(JSVAL_IS_PRIMITIVE(argv[0]) || - JSVAL_IS_PRIMITIVE(argv[1])) - return NS_ERROR_XPC_BAD_CONVERT_JS; - - JSObject *scope = JSVAL_TO_OBJECT(argv[0]); - JSObject *object = JSVAL_TO_OBJECT(argv[1]); - rv = xpc->GetCOWForObject(cx, JS_GetGlobalForObject(cx, scope), - object, rval); - - if (NS_FAILED(rv)) - return rv; - - cc->SetReturnValueWasSet(PR_TRUE); - return NS_OK; -} - NS_IMETHODIMP nsDOMWindowUtils::DispatchDOMEventViaPresShell(nsIDOMNode* aTarget, nsIDOMEvent* aEvent, diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 8e7dc99bdeb3..3663922fda1e 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -60,7 +60,7 @@ interface nsIDOMEvent; interface nsITransferable; interface nsIQueryContentEventResult; -[scriptable, uuid(8707ed93-3277-42a5-a235-533aa661a263)] +[scriptable, uuid(43fd9eb5-9e08-4d60-a305-3f87b1d5c398)] interface nsIDOMWindowUtils : nsISupports { /** @@ -470,17 +470,6 @@ interface nsIDOMWindowUtils : nsISupports { */ void getScrollXY(in boolean aFlushLayout, out long aScrollX, out long aScrollY); - /** - * Creates a ChromeObjectWrapper for the object and returns it. - * - * @param scope The JavaScript object whose scope we'll use as the - * parent of the wrapper. - * @param objToWrap The JavaScript object to wrap. - * @return the wrapped object. - */ - - void getCOWForObject(/* scope, objToWrap */); - /** * Get IME open state. TRUE means 'Open', otherwise, 'Close'. * This property works only when IMEEnabled is IME_STATUS_ENABLED. diff --git a/js/src/xpconnect/idl/nsIXPConnect.idl b/js/src/xpconnect/idl/nsIXPConnect.idl index 60afa69072fb..31e2323d98a0 100644 --- a/js/src/xpconnect/idl/nsIXPConnect.idl +++ b/js/src/xpconnect/idl/nsIXPConnect.idl @@ -399,7 +399,7 @@ interface nsIXPCFunctionThisTranslator : nsISupports { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } } %} -[uuid(227ab41e-24dc-42a6-9e7b-e3a62132d781)] +[uuid(74b5811e-92d1-468a-a59a-47760200370f)] interface nsIXPConnect : nsISupports { %{ C++ @@ -735,44 +735,6 @@ interface nsIXPConnect : nsISupports in nsIXPConnectJSObjectHolder sandbox, in PRBool returnStringOnly); - /** - * Wrap a jsval in a cross origin wrapper. - * @param aJSContext A context to use to create objects. - * @param aParent The parent to create the wrapper with. - * @param aWrappedObj The object to wrap. - */ - [noscript] jsval getXOWForObject(in JSContextPtr aJSContext, - in JSObjectPtr aParent, - in JSObjectPtr aWrappedObj); - - /** - * Wrap a jsval in a chrome object wrapper. - * @param aJSContext A context to use to create objects. - * @param aParent The parent to create the wrapper with. - * @param aWrappedObj The object to wrap. - */ - [noscript] jsval getCOWForObject(in JSContextPtr aJSContext, - in JSObjectPtr aParent, - in JSObjectPtr aWrappedObj); - - /** - * Tells updateXOWs to clear the scope of all of the XOWs it finds. - */ - const PRUint32 XPC_XOW_CLEARSCOPE = 1; - const PRUint32 XPC_XOW_NAVIGATED = 2; - - /** - * Performs an operation over all of |object|'s XOWs such as clearing - * their scopes or updating their concept of the current principal. - * - * @param aJSContext A context to use to perform JS operations. - * @param aObject Which XPCWrappedNative we should find the XOWs for. - * @param aWay What operation to perform. - */ - [noscript] void updateXOWs(in JSContextPtr aJSContext, - in nsIXPConnectWrappedNative aObject, - in PRUint32 aWay); - /** * Root JS objects held by aHolder. * @param aHolder The object that hold the JS objects that should be rooted. @@ -839,37 +801,6 @@ interface nsIXPConnect : nsISupports in PRUint32 interfaceCount, [array, size_is(interfaceCount)] in nsIIDPtr interfaceArray); - /** - * Returns a XPCNativeWrapper, XPCSafeJSObjectWrapper, or - * XPCCrossOriginWrapper for the given object based on the principal, scope, - * and filename flags that are passed in. - * - * Note: In C++, the out jsval parameter must already be a strong GC root. - * - * @param aJSContext - * A JSContext. - * @param aObject - * The object to wrap. - * @param aScope - * The scope to be used in the event that we create a - * XPCCrossOriginWrapper. Can be null. - * @param aPrincipal - * The principal that should be used for the wrapper. If this parameter - * is given then aFilenameFlags will not be calculated and will be - * assumed to be 0 unless another value is given. If this parameter is - * null then aFilenameFlags will be calculated and the value of that - * argument will be ignored. - * @param aFilenameFlags - * The filename flags from the script that will use this wrapper. See - * above (aPrincipal) for details. - */ - [noscript] jsval getWrapperForObject( - in JSContextPtr aJSContext, - in JSObjectPtr aObject, - in JSObjectPtr aScope, - in nsIPrincipal aPrincipal, - in unsigned long aFilenameFlags); - %{C++ #ifndef XPCONNECT_STANDALONE /** @@ -886,9 +817,6 @@ interface nsIXPConnect : nsISupports PRBool showThisProps) = 0; %} - [notxpcom] void getNativeWrapperGetPropertyOp(out JSPropertyOp getProperty); - [notxpcom] void getXrayWrapperPropertyHolderGetPropertyOp(out JSPropertyOp getProperty); - /** * Creates a JS object holder around aObject that will hold the object * alive for as long as the holder stays alive. diff --git a/js/src/xpconnect/src/Makefile.in b/js/src/xpconnect/src/Makefile.in index 2136fa675e9a..f3cd79b000d9 100644 --- a/js/src/xpconnect/src/Makefile.in +++ b/js/src/xpconnect/src/Makefile.in @@ -91,12 +91,7 @@ CPPSRCS = \ xpcwrappednativejsops.cpp \ xpcwrappednativeproto.cpp \ xpcwrappednativescope.cpp \ - XPCNativeWrapper.cpp \ xpcJSWeakReference.cpp \ - XPCSafeJSObjectWrapper.cpp \ - XPCCrossOriginWrapper.cpp \ - XPCChromeObjectWrapper.cpp \ - XPCSystemOnlyWrapper.cpp \ XPCWrapper.cpp \ xpcquickstubs.cpp \ dom_quickstubs.cpp \ diff --git a/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp b/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp deleted file mode 100644 index 43ef81281e4b..000000000000 --- a/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp +++ /dev/null @@ -1,814 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 sw=2 et tw=78 sts=2: */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * The Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Blake Kaplan (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "xpcprivate.h" -#include "nsDOMError.h" -#include "jsdbgapi.h" -#include "jscntxt.h" // For js::AutoValueRooter. -#include "jsobj.h" -#include "XPCNativeWrapper.h" -#include "XPCWrapper.h" - -// This file implements a wrapper around trusted objects that allows them to -// be safely injected into untrusted code. - -namespace { - -const PRUint32 sPropIsReadable = 0x1; -const PRUint32 sPropIsWritable = 0x2; - -const PRUint32 sExposedPropsSlot = XPCWrapper::sNumSlots; - -class AutoIdArray { -public: - AutoIdArray(JSContext *cx, JSIdArray *ida) : cx(cx), ida(ida) { - } - - ~AutoIdArray() { - if (ida) { - JS_DestroyIdArray(cx, ida); - } - } - - JSIdArray *array() { - return ida; - } - -private: - JSContext *cx; - JSIdArray *ida; -}; - -JSBool -GetExposedProperties(JSContext *cx, JSObject *obj, jsval *rval) -{ - jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS); - - JSBool found = JS_FALSE; - if (!JS_HasPropertyById(cx, obj, exposedPropsId, &found)) - return JS_FALSE; - if (!found) { - *rval = JSVAL_VOID; - return JS_TRUE; - } - - *rval = JSVAL_NULL; - jsval exposedProps; - if (!JS_LookupPropertyById(cx, obj, exposedPropsId, &exposedProps)) - return JS_FALSE; - - if (JSVAL_IS_VOID(exposedProps) || JSVAL_IS_NULL(exposedProps)) - return JS_TRUE; - - if (!JSVAL_IS_OBJECT(exposedProps)) { - JS_ReportError(cx, - "__exposedProps__ must be undefined, null, or an Object"); - return JS_FALSE; - } - - obj = JSVAL_TO_OBJECT(exposedProps); - - AutoIdArray guard(cx, JS_Enumerate(cx, obj)); - JSIdArray *props = guard.array(); - if (!props) - return JS_FALSE; - - if (props->length == 0) - return JS_TRUE; - - JSObject *info = JS_NewObjectWithGivenProto(cx, NULL, NULL, obj); - if (!info) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(info); - - for (int i = 0; i < props->length; i++) { - jsid propId = props->vector[i]; - - jsval propVal; - if (!JS_LookupPropertyById(cx, obj, propId, &propVal)) - return JS_FALSE; - - if (!JSVAL_IS_STRING(propVal)) { - JS_ReportError(cx, "property must be a string"); - return JS_FALSE; - } - - JSString *str = JSVAL_TO_STRING(propVal); - const jschar *chars = JS_GetStringChars(str); - size_t length = JS_GetStringLength(str); - int32 propPerms = 0; - for (size_t i = 0; i < length; ++i) { - switch (chars[i]) { - case 'r': - if (propPerms & sPropIsReadable) { - JS_ReportError(cx, "duplicate 'readable' property flag"); - return JS_FALSE; - } - propPerms |= sPropIsReadable; - break; - - case 'w': - if (propPerms & sPropIsWritable) { - JS_ReportError(cx, "duplicate 'writable' property flag"); - return JS_FALSE; - } - propPerms |= sPropIsWritable; - break; - - default: - JS_ReportError(cx, "properties can only be readable or read and writable"); - return JS_FALSE; - } - } - - if (propPerms == 0) { - JS_ReportError(cx, "specified properties must have a permission bit set"); - return JS_FALSE; - } - - if (!JS_DefinePropertyById(cx, info, propId, INT_TO_JSVAL(propPerms), - NULL, NULL, - JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { - return JS_FALSE; - } - } - - return JS_TRUE; -} - -JSBool -CanTouchProperty(JSContext *cx, JSObject *wrapperObj, jsid id, JSBool isSet, - JSBool *allowedp) -{ - jsval exposedProps; - if (!JS_GetReservedSlot(cx, wrapperObj, sExposedPropsSlot, &exposedProps)) { - return JS_FALSE; - } - - if (JSVAL_IS_PRIMITIVE(exposedProps)) { - // TODO For now, if the object doesn't ask for security, provide full - // access. In the future, we want to default to false here. - // NB: We differentiate between void (no __exposedProps__ property at all) - // and null (__exposedProps__ exists but didn't specify any properties) - // here. - *allowedp = JSVAL_IS_VOID(exposedProps); - return JS_TRUE; - } - - JSObject *hash = JSVAL_TO_OBJECT(exposedProps); - - jsval allowedval; - if (!JS_LookupPropertyById(cx, hash, id, &allowedval)) { - return JS_FALSE; - } - - const PRUint32 wanted = isSet ? sPropIsWritable : sPropIsReadable; - - // We test JSVAL_IS_INT to protect against unknown ids. - *allowedp = JSVAL_IS_INT(allowedval) && - (JSVAL_TO_INT(allowedval) & wanted) != 0; - - return JS_TRUE; -} - -} - -static JSBool -XPC_COW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_COW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_COW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_COW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_COW_Enumerate(JSContext *cx, JSObject *obj); - -static JSBool -XPC_COW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp); - -static JSBool -XPC_COW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp); - -static JSBool -XPC_COW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp); - -static JSBool -XPC_COW_Equality(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp); - -static JSObject * -XPC_COW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly); - -static JSObject * -XPC_COW_WrappedObject(JSContext *cx, JSObject *obj); - -static JSBool -WrapFunction(JSContext *cx, JSObject *scope, JSObject *funobj, jsval *vp); - -using namespace XPCWrapper; - -namespace ChromeObjectWrapper { - -js::Class COWClass = { - "ChromeObjectWrapper", - JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(XPCWrapper::sNumSlots + 1), - JS_VALUEIFY(js::PropertyOp, XPC_COW_AddProperty), - JS_VALUEIFY(js::PropertyOp, XPC_COW_DelProperty), - JS_VALUEIFY(js::PropertyOp, XPC_COW_GetProperty), - JS_VALUEIFY(js::PropertyOp, XPC_COW_SetProperty), - XPC_COW_Enumerate, - (JSResolveOp)XPC_COW_NewResolve, - JS_VALUEIFY(js::ConvertOp, XPC_COW_Convert), - JS_FinalizeStub, - nsnull, // reserved0 - JS_VALUEIFY(js::CheckAccessOp, XPC_COW_CheckAccess), - nsnull, // call - nsnull, // construct - nsnull, // xdrObject - nsnull, // hasInstance - nsnull, // mark - - // ClassExtension - { - JS_VALUEIFY(js::EqualityOp, XPC_COW_Equality), - nsnull, // outerObject - nsnull, // innerObject - XPC_COW_Iterator, - XPC_COW_WrappedObject - } -}; - -JSBool -WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp) -{ - if (JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v))) { - return WrapFunction(cx, parent, JSVAL_TO_OBJECT(v), vp); - } - - JSObject *wrapperObj = - JS_NewObjectWithGivenProto(cx, js::Jsvalify(&COWClass), NULL, parent); - if (!wrapperObj) { - return JS_FALSE; - } - - *vp = OBJECT_TO_JSVAL(wrapperObj); - - js::AutoValueRooter exposedProps(cx); - - if (!GetExposedProperties(cx, JSVAL_TO_OBJECT(v), exposedProps.jsval_addr())) { - return JS_FALSE; - } - - if (!JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sWrappedObjSlot, v) || - !JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO) || - !JS_SetReservedSlot(cx, wrapperObj, sExposedPropsSlot, - exposedProps.jsval_value())) { - return JS_FALSE; - } - - return JS_TRUE; -} - -} // namespace ChromeObjectWrapper - -using namespace ChromeObjectWrapper; - -// Throws an exception on context |cx|. -static inline JSBool -ThrowException(nsresult rv, JSContext *cx) -{ - return XPCWrapper::DoThrowException(rv, cx); -} - -// Like GetWrappedObject, but works on other types of wrappers, too. -// See also JSObject::wrappedObject in jsobj.cpp. -// TODO Move to XPCWrapper? -static inline JSObject * -GetWrappedJSObject(JSContext *cx, JSObject *obj) -{ - JSObjectOp op = obj->getClass()->ext.wrappedObject; - if (!op) { - if (XPCNativeWrapper::IsNativeWrapper(obj)) { - XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); - return wn ? wn->GetFlatJSObject() : nsnull; - } - - return obj; - } - - return op(cx, obj); -} - -// Get the (possibly nonexistent) COW off of an object -// TODO Move to XPCWrapper and share with other wrappers. -static inline -JSObject * -GetWrapper(JSObject *obj) -{ - while (obj->getClass() != &COWClass) { - obj = obj->getProto(); - if (!obj) { - break; - } - } - - return obj; -} - -// TODO Templatize, move to XPCWrapper and share with other wrappers! -static inline -JSObject * -GetWrappedObject(JSContext *cx, JSObject *wrapper) -{ - return XPCWrapper::UnwrapGeneric(cx, &COWClass, wrapper); -} - -// Forward declaration for the function wrapper. -JSBool -RewrapForChrome(JSContext *cx, JSObject *wrapperObj, jsval *vp); -JSBool -RewrapForContent(JSContext *cx, JSObject *wrapperObj, jsval *vp); - -// This function wrapper calls a function from untrusted content into chrome. - -static JSBool -XPC_COW_FunctionWrapper(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - jsval funToCall; - if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), - XPCWrapper::eWrappedFunctionSlot, &funToCall)) { - return JS_FALSE; - } - - JSObject *scope = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(funToCall)); - jsval *argv = JS_ARGV(cx, vp); - for (uintN i = 0; i < argc; ++i) { - if (!JSVAL_IS_PRIMITIVE(argv[i]) && - !RewrapObject(cx, scope, JSVAL_TO_OBJECT(argv[i]), XPCNW_EXPLICIT, - &argv[i])) { - return JS_FALSE; - } - } - - if (!RewrapObject(cx, scope, obj, XPCNW_EXPLICIT, vp) || - !JS_CallFunctionValue(cx, JSVAL_TO_OBJECT(*vp), funToCall, argc, - JS_ARGV(cx, vp), vp)) { - return JS_FALSE; - } - - scope = JS_GetScopeChain(cx); - if (!scope) { - return JS_FALSE; - } - - return JSVAL_IS_PRIMITIVE(*vp) || - RewrapObject(cx, JS_GetGlobalForObject(cx, scope), - JSVAL_TO_OBJECT(*vp), COW, vp); -} - -static JSBool -WrapFunction(JSContext *cx, JSObject *scope, JSObject *funobj, jsval *rval) -{ - scope = JS_GetGlobalForObject(cx, scope); - jsval funobjVal = OBJECT_TO_JSVAL(funobj); - JSFunction *wrappedFun = - reinterpret_cast(xpc_GetJSPrivate(funobj)); - JSNative native = JS_GetFunctionNative(cx, wrappedFun); - if (native == XPC_COW_FunctionWrapper) { - if (funobj->getParent() == scope) { - *rval = funobjVal; - return JS_TRUE; - } - - JS_GetReservedSlot(cx, funobj, XPCWrapper::eWrappedFunctionSlot, &funobjVal); - } - - JSFunction *funWrapper = - JS_NewFunction(cx, XPC_COW_FunctionWrapper, - JS_GetFunctionArity(wrappedFun), 0, - scope, - JS_GetFunctionName(wrappedFun)); - if (!funWrapper) { - return JS_FALSE; - } - - JSObject *funWrapperObj = JS_GetFunctionObject(funWrapper); - *rval = OBJECT_TO_JSVAL(funWrapperObj); - - return JS_SetReservedSlot(cx, funWrapperObj, - XPCWrapper::eWrappedFunctionSlot, - funobjVal); -} - -JSBool -RewrapForChrome(JSContext *cx, JSObject *wrapperObj, jsval *vp) -{ - jsval v = *vp; - if (JSVAL_IS_PRIMITIVE(v)) { - return JS_TRUE; - } - - JSObject *scope = - JS_GetGlobalForObject(cx, GetWrappedObject(cx, wrapperObj)); - return RewrapObject(cx, scope, JSVAL_TO_OBJECT(v), XPCNW_EXPLICIT, vp); -} - -JSBool -RewrapForContent(JSContext *cx, JSObject *wrapperObj, jsval *vp) -{ - jsval v = *vp; - if (JSVAL_IS_PRIMITIVE(v)) { - return JS_TRUE; - } - - JSObject *scope = JS_GetScopeChain(cx); - if (!scope) { - return JS_FALSE; - } - - return RewrapObject(cx, JS_GetGlobalForObject(cx, scope), - JSVAL_TO_OBJECT(v), COW, vp); -} - -static JSBool -CheckSOW(JSContext *cx, JSObject *wrapperObj, jsid id) -{ - jsval flags; - JS_GetReservedSlot(cx, wrapperObj, sFlagsSlot, &flags); - - return HAS_FLAGS(flags, FLAG_SOW) - ? SystemOnlyWrapper::AllowedToAct(cx, id) : JS_TRUE; -} - -static JSBool -XPC_COW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - obj = GetWrapper(obj); - jsval flags; - if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, &flags)) { - return JS_FALSE; - } - - if (HAS_FLAGS(flags, FLAG_RESOLVING)) { - // Allow us to define a property on ourselves. - return JS_TRUE; - } - - if (!CheckSOW(cx, obj, id)) { - return JS_FALSE; - } - - // Someone's adding a property to us. We need to protect ourselves from - // getters and setters. - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - JSPropertyDescriptor desc; - - if (!XPCWrapper::GetPropertyAttrs(cx, obj, id, JSRESOLVE_QUALIFIED, - JS_TRUE, &desc)) { - return JS_FALSE; - } - - NS_ASSERTION(desc.obj == obj, "The JS engine lies!"); - - if (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) { - // Only chrome is allowed to add getters or setters to our object. - // NB: We don't have to do this check again if we're already FLAG_SOW'd. - if (!HAS_FLAGS(flags, FLAG_SOW) && !SystemOnlyWrapper::AllowedToAct(cx, id)) { - return JS_FALSE; - } - } - - return RewrapForChrome(cx, obj, vp) && - JS_DefinePropertyById(cx, wrappedObj, id, *vp, - desc.getter, desc.setter, desc.attrs); -} - -static JSBool -XPC_COW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - if (!CheckSOW(cx, obj, id)) { - return JS_FALSE; - } - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - JSBool canTouch; - if (!CanTouchProperty(cx, obj, id, JS_TRUE, &canTouch)) { - return JS_FALSE; - } - - if (!canTouch) { - return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); - } - - // Deleting a property is safe. - return XPCWrapper::DelProperty(cx, wrappedObj, id, vp); -} - -static JSBool -XPC_COW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp, - JSBool isSet) -{ - obj = GetWrapper(obj); - if (!obj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - if (!CheckSOW(cx, obj, id)) { - return JS_FALSE; - } - - AUTO_MARK_JSVAL(ccx, vp); - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTO) || - id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS)) { - // No getting or setting __proto__ on my object. - return ThrowException(NS_ERROR_INVALID_ARG, cx); // XXX better error message - } - - JSBool canTouch; - if (!CanTouchProperty(cx, obj, id, isSet, &canTouch)) { - return JS_FALSE; - } - - if (!canTouch) { - return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); - } - - if (isSet && !RewrapForChrome(cx, obj, vp)) { - return JS_FALSE; - } - - JSBool ok = isSet ? JS_SetPropertyById(cx, wrappedObj, id, vp) - : JS_GetPropertyById(cx, wrappedObj, id, vp); - if (!ok) { - return JS_FALSE; - } - - return RewrapForContent(cx, obj, vp); -} - -static JSBool -XPC_COW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_COW_GetOrSetProperty(cx, obj, id, vp, JS_FALSE); -} - -static JSBool -XPC_COW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_COW_GetOrSetProperty(cx, obj, id, vp, JS_TRUE); -} - -static JSBool -XPC_COW_Enumerate(JSContext *cx, JSObject *obj) -{ - obj = GetWrapper(obj); - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // Nothing to enumerate. - return JS_TRUE; - } - - if (!CheckSOW(cx, obj, JSID_VOID)) { - return JS_FALSE; - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - jsval exposedProps; - if (!JS_GetReservedSlot(cx, obj, sExposedPropsSlot, &exposedProps)) { - return JS_FALSE; - } - - JSObject *propertyContainer; - if (JSVAL_IS_VOID(exposedProps)) { - // TODO For now, expose whatever properties are on our object. - propertyContainer = wrappedObj; - } else if (!JSVAL_IS_PRIMITIVE(exposedProps)) { - // Just expose whatever the object exposes through __exposedProps__. - propertyContainer = JSVAL_TO_OBJECT(exposedProps); - } else { - return JS_TRUE; - } - - return XPCWrapper::Enumerate(cx, obj, propertyContainer); -} - -static JSBool -XPC_COW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp) -{ - obj = GetWrapper(obj); - - if (!CheckSOW(cx, obj, id)) { - return JS_FALSE; - } - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // No wrappedObj means that this is probably the prototype. - *objp = nsnull; - return JS_TRUE; - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - JSBool canTouch; - if (!CanTouchProperty(cx, obj, id, (flags & JSRESOLVE_ASSIGNING) != 0, - &canTouch)) { - return JS_FALSE; - } - - if (!canTouch) { - return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); - } - - return XPCWrapper::NewResolve(cx, obj, JS_FALSE, wrappedObj, id, flags, - objp); -} - -static JSBool -XPC_COW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - // Don't do any work to convert to object. - if (type == JSTYPE_OBJECT) { - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // Converting the prototype to something. - - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - if (!wrappedObj->getJSClass()->convert(cx, wrappedObj, type, vp)) { - return JS_FALSE; - } - - return RewrapForContent(cx, obj, vp); -} - -static JSBool -XPC_COW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp) -{ - // Simply forward checkAccess to our wrapped object. It's already expecting - // untrusted things to ask it about accesses. - - uintN junk; - return JS_CheckAccess(cx, GetWrappedObject(cx, obj), id, mode, vp, &junk); -} - -static JSBool -XPC_COW_Equality(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) -{ - // Convert both sides to XPCWrappedNative and see if they match. - if (JSVAL_IS_PRIMITIVE(*valp)) { - *bp = JS_FALSE; - return JS_TRUE; - } - - JSObject *test = GetWrappedJSObject(cx, JSVAL_TO_OBJECT(*valp)); - - obj = GetWrappedObject(cx, obj); - if (!obj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - XPCWrappedNative *other = - XPCWrappedNative::GetWrappedNativeOfJSObject(cx, test); - if (!other) { - *bp = JS_FALSE; - return JS_TRUE; - } - - XPCWrappedNative *me = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); - obj = me->GetFlatJSObject(); - test = other->GetFlatJSObject(); - jsval testVal = OBJECT_TO_JSVAL(test); - return js::Jsvalify(obj->getClass()->ext.equality)(cx, obj, &testVal, bp); -} - -static JSObject * -XPC_COW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) -{ - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - ThrowException(NS_ERROR_INVALID_ARG, cx); - return nsnull; - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - ThrowException(NS_ERROR_FAILURE, cx); - return nsnull; - } - - jsval exposedProps; - if (!JS_GetReservedSlot(cx, obj, sExposedPropsSlot, &exposedProps)) { - return JS_FALSE; - } - - JSObject *propertyContainer; - if (JSVAL_IS_VOID(exposedProps)) { - // TODO For now, expose whatever properties are on our object. - propertyContainer = wrappedObj; - } else if (JSVAL_IS_PRIMITIVE(exposedProps)) { - // Expose no properties at all. - propertyContainer = nsnull; - } else { - // Just expose whatever the object exposes through __exposedProps__. - propertyContainer = JSVAL_TO_OBJECT(exposedProps); - } - - return CreateSimpleIterator(cx, obj, keysonly, propertyContainer); -} - -static JSObject * -XPC_COW_WrappedObject(JSContext *cx, JSObject *obj) -{ - return GetWrappedObject(cx, obj); -} diff --git a/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp b/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp deleted file mode 100644 index d69b80db8005..000000000000 --- a/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp +++ /dev/null @@ -1,1331 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 sw=2 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.org code. - * - * The Initial Developer of the Original Code is - * The Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Blake Kaplan (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "xpcprivate.h" -#include "nsDOMError.h" -#include "jsdbgapi.h" -#include "jscntxt.h" // For js::AutoValueRooter. -#include "XPCWrapper.h" -#include "nsIDOMWindow.h" -#include "nsIDOMWindowCollection.h" - -// This file implements a wrapper around objects that allows them to be -// accessed safely from across origins. - -static JSBool -XPC_XOW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_XOW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_XOW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_XOW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_XOW_Enumerate(JSContext *cx, JSObject *obj); - -static JSBool -XPC_XOW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp); - -static JSBool -XPC_XOW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp); - -static void -XPC_XOW_Finalize(JSContext *cx, JSObject *obj); - -static JSBool -XPC_XOW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp); - -static JSBool -XPC_XOW_Call(JSContext *cx, uintN argc, jsval *vp); - -static JSBool -XPC_XOW_Construct(JSContext *cx, uintN argc, jsval *vp); - -static JSBool -XPC_XOW_HasInstance(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp); - -static JSBool -XPC_XOW_Equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp); - -static JSObject * -XPC_XOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly); - -static JSObject * -XPC_XOW_WrappedObject(JSContext *cx, JSObject *obj); - -// The slot that we stick our scope into. -// This is used in the finalizer to see if we actually need to remove -// ourselves from our scope's map. Because we cannot outlive our scope -// (the parent link ensures this), we know that, when we're being -// finalized, either our scope is still alive (i.e. we became garbage -// due to no more references) or it is being garbage collected right now. -// Therefore, we can look in gDyingScopes, and if our scope is there, -// then the map is about to be destroyed anyway, so we don't need to -// do anything. -static const int XPC_XOW_ScopeSlot = XPCWrapper::sNumSlots; -static const int sUXPCObjectSlot = XPCWrapper::sNumSlots + 1; - -using namespace XPCWrapper; - -// Throws an exception on context |cx|. -static inline -JSBool -ThrowException(nsresult ex, JSContext *cx) -{ - XPCWrapper::DoThrowException(ex, cx); - - return JS_FALSE; -} - -// Get the (possibly nonexistent) XOW off of an object -static inline -JSObject * -GetWrapper(JSObject *obj) -{ - while (obj->getClass() != &XPCCrossOriginWrapper::XOWClass) { - obj = obj->getProto(); - if (!obj) { - break; - } - } - - return obj; -} - -static inline -JSObject * -GetWrappedObject(JSContext *cx, JSObject *wrapper) -{ - return XPCWrapper::UnwrapGeneric(cx, &XPCCrossOriginWrapper::XOWClass, wrapper); -} - -static JSBool -XPC_XOW_FunctionWrapper(JSContext *cx, uintN argc, jsval *vp); - -// This flag is set on objects that were created for UniversalXPConnect- -// enabled code. -static const PRUint32 FLAG_IS_UXPC_OBJECT = XPCWrapper::LAST_FLAG << 1; - -// This flag is set on objects that have to clear their wrapped native's XOW -// cache when they get finalized. -static const PRUint32 FLAG_IS_CACHED = XPCWrapper::LAST_FLAG << 2; - -namespace XPCCrossOriginWrapper { - -js::Class XOWClass = { - "XPCCrossOriginWrapper", - JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(XPCWrapper::sNumSlots + 2), - js::Valueify(XPC_XOW_AddProperty), - js::Valueify(XPC_XOW_DelProperty), - js::Valueify(XPC_XOW_GetProperty), - js::Valueify(XPC_XOW_SetProperty), - XPC_XOW_Enumerate, - (JSResolveOp)XPC_XOW_NewResolve, - js::Valueify(XPC_XOW_Convert), - XPC_XOW_Finalize, - nsnull, // reserved0 - js::Valueify(XPC_XOW_CheckAccess), - js::Valueify(XPC_XOW_Call), - js::Valueify(XPC_XOW_Construct), - nsnull, // xdrObject - js::Valueify(XPC_XOW_HasInstance), - nsnull, // mark - - // ClassExtension - { - js::Valueify(XPC_XOW_Equality), - nsnull, // outerObject - nsnull, // innerObject - XPC_XOW_Iterator, - XPC_XOW_WrappedObject - } -}; - -JSBool -WrapperMoved(JSContext *cx, XPCWrappedNative *innerObj, - XPCWrappedNativeScope *newScope) -{ - typedef WrappedNative2WrapperMap::Link Link; - XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); - WrappedNative2WrapperMap *map = innerObj->GetScope()->GetWrapperMap(); - Link *link; - - { // Scoped lock - XPCAutoLock al(rt->GetMapLock()); - link = map->FindLink(innerObj->GetFlatJSObject()); - } - - if (!link) { - // No link here means that there were no XOWs for this object. - return JS_TRUE; - } - - JSObject *xow = link->obj; - - { // Scoped lock. - XPCAutoLock al(rt->GetMapLock()); - if (!newScope->GetWrapperMap()->AddLink(innerObj->GetFlatJSObject(), link)) - return JS_FALSE; - map->Remove(innerObj->GetFlatJSObject()); - } - - if (!xow) { - // Nothing else to do. - return JS_TRUE; - } - - return JS_SetReservedSlot(cx, xow, XPC_XOW_ScopeSlot, - PRIVATE_TO_JSVAL(newScope)) && - JS_SetParent(cx, xow, newScope->GetGlobalJSObject()); -} - -void -WindowNavigated(JSContext *cx, XPCWrappedNative *innerObj) -{ - NS_ABORT_IF_FALSE(innerObj->NeedsXOW(), "About to write to unowned memory"); - - // First, disconnect the old XOW from the XOW cache. - XPCWrappedNativeWithXOW *wnxow = - static_cast(innerObj); - JSObject *oldXOW = wnxow->GetXOW(); - if (oldXOW) { - jsval flags = GetFlags(cx, oldXOW); - NS_ASSERTION(HAS_FLAGS(flags, FLAG_IS_CACHED), "Wrapper should be cached"); - - SetFlags(cx, oldXOW, RemoveFlags(flags, FLAG_IS_CACHED)); - - NS_ASSERTION(wnxow->GetXOW() == oldXOW, "bad XOW in cache"); - wnxow->SetXOW(nsnull); - } -} - -// Returns whether the currently executing code is allowed to access -// the wrapper. Uses nsIPrincipal::Subsumes. -// |cx| must be the top context on the context stack. -// If the subject is allowed to access the object returns NS_OK. If not, -// returns NS_ERROR_DOM_PROP_ACCESS_DENIED, returns another error code on -// failure. -nsresult -CanAccessWrapper(JSContext *cx, JSObject *outerObj, JSObject *wrappedObj, - JSBool *privilegeEnabled) -{ - // Fast path: If the wrapper and the wrapped object have the same global - // object (or if the wrapped object is a outer for the same inner that - // the wrapper is parented to), then we don't need to do any more work. - - if (privilegeEnabled) { - *privilegeEnabled = JS_FALSE; - } - - if (outerObj) { - JSObject *outerParent = outerObj->getParent(); - JSObject *innerParent = wrappedObj->getParent(); - if (!innerParent) { - innerParent = wrappedObj; - OBJ_TO_INNER_OBJECT(cx, innerParent); - if (!innerParent) { - return NS_ERROR_FAILURE; - } - } else { - innerParent = JS_GetGlobalForObject(cx, innerParent); - } - - if (outerParent == innerParent) { - return NS_OK; - } - } - - // TODO bug 508928: Refactor this with the XOW security checking code. - // Get the subject principal from the execution stack. - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if (!ssm) { - ThrowException(NS_ERROR_NOT_INITIALIZED, cx); - return NS_ERROR_NOT_INITIALIZED; - } - - JSStackFrame *fp = nsnull; - nsIPrincipal *subjectPrin = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp); - - if (!subjectPrin) { - ThrowException(NS_ERROR_FAILURE, cx); - return NS_ERROR_FAILURE; - } - - PRBool isSystem = PR_FALSE; - nsresult rv = ssm->IsSystemPrincipal(subjectPrin, &isSystem); - NS_ENSURE_SUCCESS(rv, rv); - - if (privilegeEnabled) { - *privilegeEnabled = JS_FALSE; - } - - nsCOMPtr objectPrin; - rv = ssm->GetObjectPrincipal(cx, wrappedObj, getter_AddRefs(objectPrin)); - if (NS_FAILED(rv)) { - return rv; - } - NS_ASSERTION(objectPrin, "Object didn't have principals?"); - - // Micro-optimization: don't call into caps if we know the answer. - if (subjectPrin == objectPrin) { - return NS_OK; - } - - // Now, we have our two principals, compare them! - PRBool subsumes; - rv = subjectPrin->Subsumes(objectPrin, &subsumes); - if (NS_SUCCEEDED(rv) && !subsumes) { - // We're about to fail, but make a last effort to see if - // UniversalXPConnect was enabled anywhere else on the stack. - rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &isSystem); - if (NS_SUCCEEDED(rv) && isSystem) { - rv = NS_OK; - if (privilegeEnabled) { - *privilegeEnabled = JS_TRUE; - } - } else { - rv = NS_ERROR_DOM_PROP_ACCESS_DENIED; - } - } - return rv; -} - -JSBool -WrapFunction(JSContext *cx, JSObject *outerObj, JSObject *funobj, jsval *rval) -{ - jsval funobjVal = OBJECT_TO_JSVAL(funobj); - JSFunction *wrappedFun = - reinterpret_cast(xpc_GetJSPrivate(funobj)); - JSNative native = JS_GetFunctionNative(cx, wrappedFun); - if (!native || native == XPC_XOW_FunctionWrapper) { - *rval = funobjVal; - return JS_TRUE; - } - - JSFunction *funWrapper = - JS_NewFunction(cx, XPC_XOW_FunctionWrapper, - JS_GetFunctionArity(wrappedFun), 0, - JS_GetGlobalForObject(cx, outerObj), - JS_GetFunctionName(wrappedFun)); - if (!funWrapper) { - return JS_FALSE; - } - - JSObject *funWrapperObj = JS_GetFunctionObject(funWrapper); - *rval = OBJECT_TO_JSVAL(funWrapperObj); - - if (!JS_SetReservedSlot(cx, funWrapperObj, eWrappedFunctionSlot, funobjVal) || - !JS_SetReservedSlot(cx, funWrapperObj, eAllAccessSlot, JSVAL_FALSE)) { - return JS_FALSE; - } - - return JS_TRUE; -} - -JSBool -RewrapIfNeeded(JSContext *cx, JSObject *outerObj, jsval *vp) -{ - // Don't need to wrap primitive values. - if (JSVAL_IS_PRIMITIVE(*vp)) { - return JS_TRUE; - } - - JSObject *obj = JSVAL_TO_OBJECT(*vp); - - if (JS_ObjectIsFunction(cx, obj)) { - return WrapFunction(cx, outerObj, obj, vp); - } - - XPCWrappedNative *wn = nsnull; - if (obj->getClass() == &XOWClass && - outerObj->getParent() != obj->getParent()) { - *vp = OBJECT_TO_JSVAL(GetWrappedObject(cx, obj)); - } else if (!(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, obj))) { - return JS_TRUE; - } - - return WrapObject(cx, JS_GetGlobalForObject(cx, outerObj), vp, wn); -} - -JSBool -WrapObject(JSContext *cx, JSObject *parent, jsval *vp, XPCWrappedNative* wn) -{ - NS_ASSERTION(XPCPerThreadData::IsMainThread(cx), - "Can't do this off the main thread!"); - - // Our argument should be a wrapped native object, but the caller may have - // passed it in as an optimization. - JSObject *wrappedObj; - if (JSVAL_IS_PRIMITIVE(*vp) || - !(wrappedObj = JSVAL_TO_OBJECT(*vp)) || - wrappedObj->getClass() == &XOWClass) { - return JS_TRUE; - } - - if (!wn && - !(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, wrappedObj))) { - return JS_TRUE; - } - - CheckWindow(wn); - - // The parent must be the inner global object for its scope. - parent = JS_GetGlobalForObject(cx, parent); - OBJ_TO_INNER_OBJECT(cx, parent); - if (!parent) { - return JS_FALSE; - } - - XPCWrappedNativeWithXOW *wnxow = nsnull; - if (wn->NeedsXOW()) { - JSObject *innerWrappedObj = wrappedObj; - OBJ_TO_INNER_OBJECT(cx, innerWrappedObj); - if (!innerWrappedObj) { - return JS_FALSE; - } - - if (innerWrappedObj == parent) { - wnxow = static_cast(wn); - JSObject *xow = wnxow->GetXOW(); - if (xow) { - *vp = OBJECT_TO_JSVAL(xow); - return JS_TRUE; - } - } - } - - XPCWrappedNative *parentwn = - XPCWrappedNative::GetWrappedNativeOfJSObject(cx, parent); - XPCWrappedNativeScope *parentScope; - if (NS_LIKELY(parentwn)) { - parentScope = parentwn->GetScope(); - } else { - parentScope = XPCWrappedNativeScope::FindInJSObjectScope(cx, parent); - } - - JSObject *outerObj = nsnull; - WrappedNative2WrapperMap *map = parentScope->GetWrapperMap(); - - outerObj = map->Find(wrappedObj); - if (outerObj) { - NS_ASSERTION(outerObj->getClass() == &XOWClass, - "What crazy object are we getting here?"); - *vp = OBJECT_TO_JSVAL(outerObj); - - if (wnxow) { - // NB: wnxow->GetXOW() must have returned false. - SetFlags(cx, outerObj, AddFlags(GetFlags(cx, outerObj), FLAG_IS_CACHED)); - wnxow->SetXOW(outerObj); - } - - return JS_TRUE; - } - - outerObj = JS_NewObjectWithGivenProto(cx, js::Jsvalify(&XOWClass), nsnull, - parent); - if (!outerObj) { - return JS_FALSE; - } - - jsval flags = INT_TO_JSVAL(wnxow ? FLAG_IS_CACHED : 0); - if (!JS_SetReservedSlot(cx, outerObj, sWrappedObjSlot, *vp) || - !JS_SetReservedSlot(cx, outerObj, sFlagsSlot, flags) || - !JS_SetReservedSlot(cx, outerObj, XPC_XOW_ScopeSlot, - PRIVATE_TO_JSVAL(parentScope))) { - return JS_FALSE; - } - - *vp = OBJECT_TO_JSVAL(outerObj); - - map->Add(wn->GetScope()->GetWrapperMap(), wrappedObj, outerObj); - if(wnxow) { - wnxow->SetXOW(outerObj); - } - - return JS_TRUE; -} - -} // namespace XPCCrossOriginWrapper - -using namespace XPCCrossOriginWrapper; - -static JSBool -XPC_XOW_toString(JSContext *cx, uintN argc, jsval *vp); - -static JSBool -IsValFrame(JSObject *obj, jsid id, XPCWrappedNative *wn) -{ - // Fast path for the common case. - if (obj->getClass()->name[0] != 'W') { - return JS_FALSE; - } - - nsCOMPtr domwin(do_QueryWrappedNative(wn)); - if (!domwin) { - return JS_FALSE; - } - - nsCOMPtr col; - domwin->GetFrames(getter_AddRefs(col)); - if (!col) { - return JS_FALSE; - } - - if (JSID_IS_INT(id)) { - col->Item(JSID_TO_INT(id), getter_AddRefs(domwin)); - } else { - nsAutoString str(reinterpret_cast - (JS_GetStringChars(JSID_TO_STRING(id)))); - col->NamedItem(str, getter_AddRefs(domwin)); - } - - return domwin != nsnull; -} - -static JSBool -WrapSameOriginProp(JSContext *cx, JSObject *outerObj, jsval *vp); - -static JSBool -XPC_XOW_FunctionWrapper(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - JSObject *wrappedObj, *outerObj = obj; - - // Allow 'this' to be either an XOW, in which case we unwrap it. - // We disallow invalid XOWs that have no wrapped object. Otherwise, - // if it isn't an XOW, then pass it through as-is. - - outerObj = wrappedObj = GetWrapper(obj); - if (wrappedObj) { - wrappedObj = GetWrappedObject(cx, wrappedObj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - } else { - wrappedObj = obj; - } - - JSObject *funObj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - jsval funToCall; - if (!JS_GetReservedSlot(cx, funObj, eWrappedFunctionSlot, &funToCall)) { - return JS_FALSE; - } - - JSFunction *fun = JS_ValueToFunction(cx, funToCall); - if (!fun) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - nsresult rv = CanAccessWrapper(cx, outerObj, JSVAL_TO_OBJECT(funToCall), nsnull); - if (NS_FAILED(rv) && rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) { - return ThrowException(rv, cx); - } - -#ifdef DEBUG - JSNative native = JS_GetFunctionNative(cx, fun); - NS_ASSERTION(native, "How'd we get here with a scripted function?"); -#endif - - if (!JS_CallFunctionValue(cx, wrappedObj, funToCall, argc, JS_ARGV(cx, vp), vp)) { - return JS_FALSE; - } - - if (NS_SUCCEEDED(rv)) { - return WrapSameOriginProp(cx, outerObj, vp); - } - - return RewrapIfNeeded(cx, obj, vp); -} - -static JSBool -WrapSameOriginProp(JSContext *cx, JSObject *outerObj, jsval *vp) -{ - // Don't call RewrapIfNeeded for same origin properties. We only - // need to wrap window, document and location. - if (JSVAL_IS_PRIMITIVE(*vp)) { - return JS_TRUE; - } - - JSObject *wrappedObj = JSVAL_TO_OBJECT(*vp); - js::Class *clasp = wrappedObj->getClass(); - if (ClassNeedsXOW(clasp->name)) { - return WrapObject(cx, JS_GetGlobalForObject(cx, outerObj), vp); - } - - // Check if wrappedObj is an XOW. If so, verify that it's from the - // right scope. - if (clasp == &XOWClass && - wrappedObj->getParent() != outerObj->getParent()) { - *vp = OBJECT_TO_JSVAL(GetWrappedObject(cx, wrappedObj)); - return WrapObject(cx, outerObj->getParent(), vp); - } - - return JS_TRUE; -} - -static JSBool -XPC_XOW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - // All AddProperty needs to do is pass on addProperty requests to - // same-origin objects, and throw for all else. - - obj = GetWrapper(obj); - jsval resolving; - if (!JS_GetReservedSlot(cx, obj, sFlagsSlot, &resolving)) { - return JS_FALSE; - } - - if (!JSVAL_IS_PRIMITIVE(*vp)) { - JSObject *addedObj = JSVAL_TO_OBJECT(*vp); - if (addedObj->getClass() == &XOWClass && - addedObj->getParent() != obj->getParent()) { - *vp = OBJECT_TO_JSVAL(GetWrappedObject(cx, addedObj)); - if (!WrapObject(cx, obj->getParent(), vp, nsnull)) { - return JS_FALSE; - } - } - } - - if (HAS_FLAGS(resolving, FLAG_RESOLVING)) { - // Allow us to define a property on ourselves. - return JS_TRUE; - } - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - JSBool privilegeEnabled = JS_FALSE; - nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, &privilegeEnabled); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { - // Can't override properties on foreign objects. - return ThrowException(rv, cx); - } - return JS_FALSE; - } - - // Same origin, pass this request along. - return AddProperty(cx, obj, JS_TRUE, wrappedObj, id, vp); -} - -static JSBool -XPC_XOW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { - // Can't delete properties on foreign objects. - return ThrowException(rv, cx); - } - return JS_FALSE; - } - - // Same origin, pass this request along. - return DelProperty(cx, wrappedObj, id, vp); -} - -static JSBool -XPC_XOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp, - JSBool isSet) -{ - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { - return JS_TRUE; - } - - // Don't do anything if we already resolved to a wrapped function in - // NewResolve. In practice, this means that this is a wrapped eval - // function. - jsval v = *vp; - if (!JSVAL_IS_PRIMITIVE(v) && - JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v)) && - JS_GetFunctionNative(cx, JS_ValueToFunction(cx, v)) == - XPC_XOW_FunctionWrapper) { - return JS_TRUE; - } - - JSObject *origObj = obj; - obj = GetWrapper(obj); - if (!obj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - js::AutoArrayRooter rooter(cx, 1, vp); - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - JSBool privilegeEnabled; - nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, &privilegeEnabled); - if (NS_FAILED(rv)) { - if (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) { - return JS_FALSE; - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - // This is a request to get a property across origins. We need to - // determine if this property is allAccess. If it is, then we need to - // actually get the property. If not, we simply need to throw an - // exception. - - XPCWrappedNative *wn = - XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); - NS_ASSERTION(wn, "How did we wrap a non-WrappedNative?"); - if (!IsValFrame(wrappedObj, id, wn)) { - nsIScriptSecurityManager *ssm = GetSecurityManager(); - if (!ssm) { - return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); - } - rv = ssm->CheckPropertyAccess(cx, wrappedObj, - wrappedObj->getClass()->name, - id, isSet ? sSecMgrSetProp - : sSecMgrGetProp); - if (NS_FAILED(rv)) { - // The security manager threw an exception for us. - return JS_FALSE; - } - } - - return GetOrSetNativeProperty(cx, obj, wn, id, vp, isSet, JS_FALSE); - } - - JSObject *proto = nsnull; // Initialize this to quiet GCC. - JSBool checkProto = - (isSet && id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTO)); - if (checkProto) { - proto = wrappedObj->getProto(); - } - - JSBool ok = isSet - ? JS_SetPropertyById(cx, wrappedObj, id, vp) - : JS_GetPropertyById(cx, wrappedObj, id, vp); - if (!ok) { - return JS_FALSE; - } - - if (checkProto) { - JSObject *newProto = wrappedObj->getProto(); - - // If code is trying to set obj.__proto__ and we're on obj's - // prototype chain, then the JS_GetPropertyById above will do the - // wrong thing if wrappedObj still delegates to Object.prototype. - // However, it's hard to figure out if wrappedObj still does - // delegate to Object.prototype so check to see if proto changed as a - // result of setting __proto__. - - if (origObj != obj) { - // Undo the damage. - if (!JS_SetPrototype(cx, wrappedObj, proto) || - !JS_SetPrototype(cx, origObj, newProto)) { - return JS_FALSE; - } - } else if (newProto) { - // __proto__ setting is a bad hack, people shouldn't do it. In - // this case we're setting the direct prototype of a XOW object, - // in the interests of sanity only allow it to be set to null in - // this case. - - JS_SetPrototype(cx, wrappedObj, proto); - JS_ReportError(cx, "invalid __proto__ value (can only be set to null)"); - return JS_FALSE; - } - } - - return WrapSameOriginProp(cx, obj, vp); -} - -static JSBool -XPC_XOW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_XOW_GetOrSetProperty(cx, obj, id, vp, JS_FALSE); -} - -static JSBool -XPC_XOW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_XOW_GetOrSetProperty(cx, obj, id, vp, JS_TRUE); -} - -static JSBool -XPC_XOW_Enumerate(JSContext *cx, JSObject *obj) -{ - obj = GetWrapper(obj); - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // Nothing to enumerate. - return JS_TRUE; - } - - nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { - // Can't enumerate on foreign objects. - return ThrowException(rv, cx); - } - - return JS_FALSE; - } - - return Enumerate(cx, obj, wrappedObj); -} - -// Because of the drastically different ways that same- and cross-origin XOWs -// work, we have to call JS_ClearScope when a XOW changes from being same- -// origin to cross-origin. Normally, there are defined places in Gecko where -// this happens and they notify us. However, UniversalXPConnect causes the -// same transition without any notifications. We could try to detect when this -// happens, but doing so would require calling JS_ClearScope from random -// hooks, which is bad. -// -// The compromise is the UXPCObject. When resolving a property on a XOW as -// same-origin because of UniversalXPConnect, we actually resolve it on the -// UXPCObject (which is just a XOW for the same object). This causes the JS -// engine to do all of its work on another object, not polluting the main -// object. However, if the get results in calling a setter, the engine still -// uses the regular object as 'this', ensuring that the UXPCObject doesn't -// leak to script. -static JSObject * -GetUXPCObject(JSContext *cx, JSObject *obj) -{ - NS_ASSERTION(obj->getClass() == &XOWClass, "wrong object"); - - jsval v; - if (!JS_GetReservedSlot(cx, obj, sFlagsSlot, &v)) { - return nsnull; - } - - if (HAS_FLAGS(v, FLAG_IS_UXPC_OBJECT)) { - return obj; - } - - if (!JS_GetReservedSlot(cx, obj, sUXPCObjectSlot, &v)) { - return nsnull; - } - - if (JSVAL_IS_OBJECT(v)) { - return JSVAL_TO_OBJECT(v); - } - - JSObject *uxpco = - JS_NewObjectWithGivenProto(cx, js::Jsvalify(&XOWClass), nsnull, - obj->getParent()); - if (!uxpco) { - return nsnull; - } - - js::AutoObjectRooter tvr(cx, uxpco); - - jsval wrappedObj, parentScope; - if (!JS_GetReservedSlot(cx, obj, sWrappedObjSlot, &wrappedObj) || - !JS_GetReservedSlot(cx, obj, XPC_XOW_ScopeSlot, &parentScope)) { - return nsnull; - } - - if (!JS_SetReservedSlot(cx, uxpco, sWrappedObjSlot, wrappedObj) || - !JS_SetReservedSlot(cx, uxpco, sFlagsSlot, - INT_TO_JSVAL(FLAG_IS_UXPC_OBJECT)) || - !JS_SetReservedSlot(cx, uxpco, XPC_XOW_ScopeSlot, parentScope)) { - return nsnull; - } - - if (!JS_SetReservedSlot(cx, obj, sUXPCObjectSlot, OBJECT_TO_JSVAL(uxpco))) { - return nsnull; - } - - return uxpco; -} - -static JSBool -XPC_XOW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp) -{ - obj = GetWrapper(obj); - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // No wrappedObj means that this is probably the prototype. - *objp = nsnull; - return JS_TRUE; - } - - JSBool privilegeEnabled; - nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, &privilegeEnabled); - if (NS_FAILED(rv)) { - if (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) { - return JS_FALSE; - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - // We're dealing with a cross-origin lookup. Ensure that we're allowed to - // resolve this property and resolve it if so. Otherwise, we deny access - // and throw a security error. Note that this code does not actually check - // to see if the property exists, that's dealt with below. - - XPCWrappedNative *wn = - XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); - NS_ASSERTION(wn, "How did we wrap a non-WrappedNative?"); - if (!IsValFrame(wrappedObj, id, wn)) { - nsIScriptSecurityManager *ssm = GetSecurityManager(); - if (!ssm) { - return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); - } - - PRUint32 action = (flags & JSRESOLVE_ASSIGNING) - ? sSecMgrSetProp - : sSecMgrGetProp; - rv = ssm->CheckPropertyAccess(cx, wrappedObj, - wrappedObj->getClass()->name, - id, action); - if (NS_FAILED(rv)) { - // The security manager threw an exception for us. - return JS_FALSE; - } - } - - // We're out! We're allowed to resolve this property. - return ResolveNativeProperty(cx, obj, wrappedObj, wn, id, - flags, objp, JS_FALSE); - - } - - if (privilegeEnabled && !(obj = GetUXPCObject(cx, obj))) { - return JS_FALSE; - } - - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { - jsval oldSlotVal; - if (!JS_GetReservedSlot(cx, obj, sFlagsSlot, &oldSlotVal) || - !JS_SetReservedSlot(cx, obj, sFlagsSlot, - INT_TO_JSVAL(JSVAL_TO_INT(oldSlotVal) | - FLAG_RESOLVING))) { - return JS_FALSE; - } - - JSBool ok = JS_DefineFunction(cx, obj, "toString", - XPC_XOW_toString, 0, 0) != nsnull; - - JS_SetReservedSlot(cx, obj, sFlagsSlot, oldSlotVal); - - if (ok) { - *objp = obj; - } - - return ok; - } - - return NewResolve(cx, obj, JS_TRUE, wrappedObj, id, flags, objp); -} - -static JSBool -XPC_XOW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - // Don't do any work to convert to object. - if (type == JSTYPE_OBJECT) { - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // Converting the prototype to something. - - if (type == JSTYPE_STRING || type == JSTYPE_VOID) { - jsval args[2] = { JSVAL_VOID, OBJECT_TO_JSVAL(obj) }; - return XPC_XOW_toString(cx, 0, args); - } - - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - // Note: JSTYPE_VOID and JSTYPE_STRING are equivalent. - nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull); - if (NS_FAILED(rv) && - (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED || - (type != JSTYPE_STRING && type != JSTYPE_VOID))) { - // Ensure that we report some kind of error. - if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { - ThrowException(rv, cx); - } - return JS_FALSE; - } - - if (!wrappedObj->getJSClass()->convert(cx, wrappedObj, type, vp)) { - return JS_FALSE; - } - - return NS_SUCCEEDED(rv) - ? WrapSameOriginProp(cx, obj, vp) - : RewrapIfNeeded(cx, obj, vp); -} - -static void -XPC_XOW_Finalize(JSContext *cx, JSObject *obj) -{ - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return; - } - - jsval flags = GetFlags(cx, obj); - if (HAS_FLAGS(flags, FLAG_IS_CACHED)) { - // We know that it's safe to access our wrapped object. - XPCWrappedNativeWithXOW *wnxow = - static_cast(xpc_GetJSPrivate(wrappedObj)); - wnxow->SetXOW(nsnull); - - JS_SetReservedSlot(cx, obj, sWrappedObjSlot, JSVAL_VOID); - SetFlags(cx, obj, RemoveFlags(flags, FLAG_IS_CACHED)); - } - - // Get our scope. - jsval scopeVal; - if (!JS_GetReservedSlot(cx, obj, XPC_XOW_ScopeSlot, &scopeVal)) { - return; - } - - // Now that we have our scope, see if it's going away. If it is, - // then our work here is going to be done when we destroy the scope - // entirely. Scope can be null if we're an enumerating XOW. - XPCWrappedNativeScope *scope = reinterpret_cast - (JSVAL_TO_PRIVATE(scopeVal)); - if (!scope) { - return; - } - - // Remove ourselves from the map. - scope->GetWrapperMap()->Remove(wrappedObj); -} - -static JSBool -XPC_XOW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp) -{ - // Simply forward checkAccess to our wrapped object. It's already expecting - // untrusted things to ask it about accesses. - - uintN junk; - return JS_CheckAccess(cx, GetWrappedObject(cx, obj), id, mode, vp, &junk); -} - -static JSBool -XPC_XOW_Call(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) { - return JS_FALSE; - } - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // Nothing to call. - return JS_TRUE; - } - - nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { - // Can't call. - return ThrowException(rv, cx); - } - - return JS_FALSE; - } - - JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - NS_ASSERTION(GetWrappedObject(cx, callee), "How'd we get here?"); - callee = GetWrappedObject(cx, callee); - if (!JS_CallFunctionValue(cx, obj, OBJECT_TO_JSVAL(callee), argc, - JS_ARGV(cx, vp), vp)) { - return JS_FALSE; - } - - return RewrapIfNeeded(cx, callee, vp); -} - -static JSBool -XPC_XOW_Construct(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *realObj = GetWrapper(JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); - JSObject *wrappedObj = GetWrappedObject(cx, realObj); - if (!wrappedObj) { - // Nothing to construct. - return JS_TRUE; - } - - nsresult rv = CanAccessWrapper(cx, realObj, wrappedObj, nsnull); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { - // Can't construct. - return ThrowException(rv, cx); - } - return JS_FALSE; - } - - JSObject *obj = JS_New(cx, wrappedObj, argc, JS_ARGV(cx, vp)); - if (!obj) { - return JS_FALSE; - } - - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); - return RewrapIfNeeded(cx, wrappedObj, vp); -} - -static JSBool -XPC_XOW_HasInstance(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) -{ - JSObject *iface = GetWrappedObject(cx, obj); - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - nsresult rv = CanAccessWrapper(cx, obj, iface, nsnull); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { - // Don't do this test across origins. - return ThrowException(rv, cx); - } - return JS_FALSE; - } - - JSClass *clasp = iface->getJSClass(); - - *bp = JS_FALSE; - if (!clasp->hasInstance) { - return JS_TRUE; - } - - // Prematurely unwrap the left hand side. - jsval v = *valp; - if (!JSVAL_IS_PRIMITIVE(*valp)) { - JSObject *test = JSVAL_TO_OBJECT(v); - - // GetWrappedObject does an instanceof check. - test = GetWrappedObject(cx, test); - if (test) { - v = OBJECT_TO_JSVAL(test); - } - } - - return clasp->hasInstance(cx, iface, &v, bp); -} - -static JSBool -XPC_XOW_Equality(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) -{ - jsval v = *valp; - - // Convert both sides to XPCWrappedNative and see if they match. - if (JSVAL_IS_PRIMITIVE(v)) { - *bp = JS_FALSE; - return JS_TRUE; - } - - JSObject *test = JSVAL_TO_OBJECT(v); - if (test->getClass() == &XOWClass) { - if (!JS_GetReservedSlot(cx, test, sWrappedObjSlot, &v)) { - return JS_FALSE; - } - - if (JSVAL_IS_PRIMITIVE(v)) { - *bp = JS_FALSE; - return JS_TRUE; - } - - test = JSVAL_TO_OBJECT(v); - } - - obj = GetWrappedObject(cx, obj); - if (!obj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - XPCWrappedNative *other = - XPCWrappedNative::GetWrappedNativeOfJSObject(cx, test); - if (!other) { - *bp = JS_FALSE; - return JS_TRUE; - } - - XPCWrappedNative *me = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); - obj = me->GetFlatJSObject(); - test = other->GetFlatJSObject(); - jsval testVal = OBJECT_TO_JSVAL(test); - return js::Jsvalify(obj->getClass()->ext.equality)(cx, obj, &testVal, bp); -} - -static JSObject * -XPC_XOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) -{ - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - ThrowException(NS_ERROR_INVALID_ARG, cx); - return nsnull; - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - ThrowException(NS_ERROR_FAILURE, cx); - return nsnull; - } - - nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { - // Can't create iterators for foreign objects. - ThrowException(rv, cx); - return nsnull; - } - - ThrowException(NS_ERROR_FAILURE, cx); - return nsnull; - } - - JSObject *wrapperIter = JS_NewObject(cx, js::Jsvalify(&XOWClass), nsnull, - JS_GetGlobalForObject(cx, obj)); - if (!wrapperIter) { - return nsnull; - } - - js::AutoObjectRooter tvr(cx, wrapperIter); - - // Initialize our XOW. - jsval v = OBJECT_TO_JSVAL(wrappedObj); - if (!JS_SetReservedSlot(cx, wrapperIter, sWrappedObjSlot, v) || - !JS_SetReservedSlot(cx, wrapperIter, sFlagsSlot, JSVAL_ZERO) || - !JS_SetReservedSlot(cx, wrapperIter, XPC_XOW_ScopeSlot, - PRIVATE_TO_JSVAL(nsnull))) { - return nsnull; - } - - return CreateIteratorObj(cx, wrapperIter, obj, wrappedObj, keysonly); -} - -static JSObject * -XPC_XOW_WrappedObject(JSContext *cx, JSObject *obj) -{ - return GetWrappedObject(cx, obj); -} - -static JSBool -XPC_XOW_toString(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - obj = GetWrapper(obj); - if (!obj) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // Someone's calling toString on our prototype. - NS_NAMED_LITERAL_CSTRING(protoString, "[object XPCCrossOriginWrapper]"); - JSString *str = - JS_NewStringCopyN(cx, protoString.get(), protoString.Length()); - if (!str) { - return JS_FALSE; - } - JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); - return JS_TRUE; - } - - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - nsresult rv = CanAccessWrapper(cx, obj, wrappedObj, nsnull); - if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { - nsIScriptSecurityManager *ssm = GetSecurityManager(); - if (!ssm) { - return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); - } - rv = ssm->CheckPropertyAccess(cx, wrappedObj, - wrappedObj->getClass()->name, - GetRTIdByIndex(cx, XPCJSRuntime::IDX_TO_STRING), - nsIXPCSecurityManager::ACCESS_GET_PROPERTY); - } - if (NS_FAILED(rv)) { - return JS_FALSE; - } - - XPCWrappedNative *wn = - XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); - return NativeToString(cx, wn, argc, JS_ARGV(cx, vp), vp, JS_FALSE); -} diff --git a/js/src/xpconnect/src/XPCNativeWrapper.cpp b/js/src/xpconnect/src/XPCNativeWrapper.cpp deleted file mode 100644 index 4a42db1c35fc..000000000000 --- a/js/src/xpconnect/src/XPCNativeWrapper.cpp +++ /dev/null @@ -1,1114 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 sw=2 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.org code. - * - * The Initial Developer of the Original Code is - * The Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2005 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Johnny Stenback (original author) - * Brendan Eich - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "xpcprivate.h" -#include "XPCNativeWrapper.h" -#include "XPCWrapper.h" -#include "jsdbgapi.h" -#include "WrapperFactory.h" - -static JSBool -XPC_NW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_NW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_NW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_NW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_NW_Enumerate(JSContext *cx, JSObject *obj); - -static JSBool -XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp); - -static JSBool -XPC_NW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp); - -static void -XPC_NW_Finalize(JSContext *cx, JSObject *obj); - -static JSBool -XPC_NW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, - JSAccessMode mode, jsval *vp); - -static JSBool -XPC_NW_Call(JSContext *cx, uintN argc, jsval *vp); - -static JSBool -XPC_NW_Construct(JSContext *cx, uintN argc, jsval *vp); - -static JSBool -XPC_NW_HasInstance(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp); - -static void -XPC_NW_Trace(JSTracer *trc, JSObject *obj); - -static JSBool -XPC_NW_Equality(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp); - -static JSObject * -XPC_NW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly); - -static JSBool -XPC_NW_FunctionWrapper(JSContext *cx, uintN argc, jsval *vp); - -using namespace XPCWrapper; - -// If this flag is set, then this XPCNativeWrapper is *not* the implicit -// wrapper stored in XPCWrappedNative::mWrapperWord. These wrappers may -// be exposed to content script and because they are not shared, they do -// not have expando properties set on implicit native wrappers. -static const PRUint32 FLAG_EXPLICIT = XPCWrapper::LAST_FLAG << 1; - -namespace XPCNativeWrapper { namespace internal { - -// JS class for XPCNativeWrapper (and this doubles as the constructor -// for XPCNativeWrapper for the moment too...) - -js::Class NW_NoCall_Class = { - "XPCNativeWrapper", - JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | - // Our one reserved slot holds a jsint of flag bits - JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_MARK_IS_TRACE | JSCLASS_CONSTRUCT_PROTOTYPE, - JS_VALUEIFY(js::PropertyOp, XPC_NW_AddProperty), - JS_VALUEIFY(js::PropertyOp, XPC_NW_DelProperty), - JS_VALUEIFY(js::PropertyOp, XPC_NW_GetProperty), - JS_VALUEIFY(js::PropertyOp, XPC_NW_SetProperty), - XPC_NW_Enumerate, - (JSResolveOp)XPC_NW_NewResolve, - JS_VALUEIFY(js::ConvertOp, XPC_NW_Convert), - XPC_NW_Finalize, - nsnull, // reserved0 - JS_VALUEIFY(js::CheckAccessOp, XPC_NW_CheckAccess), - nsnull, // call - JS_VALUEIFY(js::CallOp, XPC_NW_Construct), - nsnull, // xdrObject - JS_VALUEIFY(js::HasInstanceOp, XPC_NW_HasInstance), - JS_CLASS_TRACE(XPC_NW_Trace), - - // ClassExtension - { - JS_VALUEIFY(js::EqualityOp, XPC_NW_Equality), - nsnull, // outerObject - nsnull, // innerObject - XPC_NW_Iterator, - nsnull, // wrappedObject - } -}; - -js::Class NW_Call_Class = { - "XPCNativeWrapper", - JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | - // Our one reserved slot holds a jsint of flag bits - JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_MARK_IS_TRACE | JSCLASS_CONSTRUCT_PROTOTYPE, - JS_VALUEIFY(js::PropertyOp, XPC_NW_AddProperty), - JS_VALUEIFY(js::PropertyOp, XPC_NW_DelProperty), - JS_VALUEIFY(js::PropertyOp, XPC_NW_GetProperty), - JS_VALUEIFY(js::PropertyOp, XPC_NW_SetProperty), - XPC_NW_Enumerate, - (JSResolveOp)XPC_NW_NewResolve, - JS_VALUEIFY(js::ConvertOp, XPC_NW_Convert), - XPC_NW_Finalize, - nsnull, // reserved0 - JS_VALUEIFY(js::CheckAccessOp, XPC_NW_CheckAccess), - JS_VALUEIFY(js::CallOp, XPC_NW_Call), - JS_VALUEIFY(js::CallOp, XPC_NW_Construct), - nsnull, // xdrObject - JS_VALUEIFY(js::HasInstanceOp, XPC_NW_HasInstance), - JS_CLASS_TRACE(XPC_NW_Trace), - - // ClassExtension - { - JS_VALUEIFY(js::EqualityOp, XPC_NW_Equality), - nsnull, // outerObject - nsnull, // innerObject - XPC_NW_Iterator, - nsnull, // wrappedObject - } -}; - -} // namespace internal - -JSBool -GetWrappedNative(JSContext *cx, JSObject *obj, - XPCWrappedNative **aWrappedNative) -{ - XPCWrappedNative *wn = static_cast(xpc_GetJSPrivate(obj)); - *aWrappedNative = wn; - if (!wn) { - return JS_TRUE; - } - - nsIScriptSecurityManager *ssm = GetSecurityManager(); - if (!ssm) { - return JS_TRUE; - } - - nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx); - if (!subjectPrincipal) { - return JS_TRUE; - } - - XPCWrappedNativeScope *scope = wn->GetScope(); - nsIPrincipal *objectPrincipal = scope->GetPrincipal(); - - PRBool subsumes; - nsresult rv = subjectPrincipal->Subsumes(objectPrincipal, &subsumes); - if (NS_FAILED(rv) || !subsumes) { - PRBool isPrivileged; - rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &isPrivileged); - return NS_SUCCEEDED(rv) && isPrivileged; - } - - return JS_TRUE; -} - -JSBool -WrapFunction(JSContext* cx, JSObject* funobj, jsval *rval) -{ - // If funobj is already a wrapped function, just return it. - if (JS_GetFunctionNative(cx, - JS_ValueToFunction(cx, OBJECT_TO_JSVAL(funobj))) == - XPC_NW_FunctionWrapper) { - *rval = OBJECT_TO_JSVAL(funobj); - return JS_TRUE; - } - - // Ensure that we've been called from JS. Native code should extract - // the wrapped native and deal with that directly. - // XXX Can we simply trust |cx| here? - JSStackFrame *iterator = nsnull; - if (!::JS_FrameIterator(cx, &iterator)) { - ::JS_ReportError(cx, "XPCNativeWrappers must be used from script"); - return JS_FALSE; - } - - // Create a new function that'll call our given function. This new - // function's parent will be the original function and that's how we - // get the right thing to call when this function is called. - // Note that we pass nsnull as the nominal parent so that we'll inherit - // our caller's Function.prototype. - JSFunction *funWrapper = - ::JS_NewFunction(cx, XPC_NW_FunctionWrapper, 0, 0, nsnull, - "XPCNativeWrapper function wrapper"); - if (!funWrapper) { - return JS_FALSE; - } - - JSObject* funWrapperObj = ::JS_GetFunctionObject(funWrapper); - ::JS_SetParent(cx, funWrapperObj, funobj); - *rval = OBJECT_TO_JSVAL(funWrapperObj); - - JS_SetReservedSlot(cx, funWrapperObj, eAllAccessSlot, JSVAL_FALSE); - - return JS_TRUE; -} - -JSBool -RewrapValue(JSContext *cx, JSObject *obj, jsval v, jsval *rval) -{ - NS_ASSERTION(XPCNativeWrapper::IsNativeWrapper(obj), - "Unexpected object"); - - if (JSVAL_IS_PRIMITIVE(v)) { - *rval = v; - return JS_TRUE; - } - - JSObject* nativeObj = JSVAL_TO_OBJECT(v); - - // Wrap function objects specially. - if (JS_ObjectIsFunction(cx, nativeObj)) { - return WrapFunction(cx, nativeObj, rval); - } - - JSObject *scope = JS_GetScopeChain(cx); - if (!scope) { - return JS_FALSE; - } - - jsval flags; - ::JS_GetReservedSlot(cx, obj, 0, &flags); - WrapperType type = HAS_FLAGS(flags, FLAG_EXPLICIT) - ? XPCNW_EXPLICIT : XPCNW_IMPLICIT; - return RewrapObject(cx, JS_GetGlobalForObject(cx, scope), - nativeObj, type, rval); -} - -} // namespace XPCNativeWrapper - -using namespace XPCNativeWrapper; - -static JSBool -XPC_NW_toString(JSContext *cx, uintN argc, jsval *vp); - -static inline -JSBool -ThrowException(nsresult ex, JSContext *cx) -{ - XPCThrower::Throw(ex, cx); - - return JS_FALSE; -} - -static inline -JSBool -EnsureLegalActivity(JSContext *cx, JSObject *obj, - jsid id = JSID_VOID, PRUint32 accessType = 0) -{ - nsIScriptSecurityManager *ssm = GetSecurityManager(); - if (!ssm) { - // If there's no security manager, then we're not running in a browser - // context: allow access. - return JS_TRUE; - } - - JSStackFrame *fp; - nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp); - if (!subjectPrincipal || !fp) { - // We must allow access if there is no code running. - return JS_TRUE; - } - - PRBool isSystem; - if (NS_SUCCEEDED(ssm->IsSystemPrincipal(subjectPrincipal, &isSystem)) && - isSystem) { - // Chrome code is running. - return JS_TRUE; - } - - jsval flags; - - JS_GetReservedSlot(cx, obj, sFlagsSlot, &flags); - if (HAS_FLAGS(flags, FLAG_SOW) && !SystemOnlyWrapper::CheckFilename(cx, id, fp)) { - return JS_FALSE; - } - - // We're in unprivileged code, ensure that we're allowed to access the - // underlying object. - XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); - if (wn) { - nsIPrincipal *objectPrincipal = wn->GetScope()->GetPrincipal(); - PRBool subsumes; - if (NS_FAILED(subjectPrincipal->Subsumes(objectPrincipal, &subsumes)) || - !subsumes) { - // This might be chrome code or content code with UniversalXPConnect. - PRBool isPrivileged = PR_FALSE; - nsresult rv = - ssm->IsCapabilityEnabled("UniversalXPConnect", &isPrivileged); - if (NS_SUCCEEDED(rv) && isPrivileged) { - return JS_TRUE; - } - - JSObject* flatObj; - if (!JSID_IS_VOID(id) && - (accessType & (sSecMgrSetProp | sSecMgrGetProp)) && - (flatObj = wn->GetFlatJSObject())) { - rv = ssm->CheckPropertyAccess(cx, flatObj, - flatObj->getClass()->name, - id, accessType); - return NS_SUCCEEDED(rv); - } - - return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); - } - } - -#ifdef DEBUG - // The underlying object is accessible, but this might be the wrong - // type of wrapper to access it through. - - if (HAS_FLAGS(flags, FLAG_EXPLICIT)) { - // Can't make any assertions about the owner of this wrapper. - return JS_TRUE; - } - - JSScript *script = JS_GetFrameScript(cx, fp); - if (!script) { - // This is likely a SJOW around an XPCNativeWrapper. We don't know - // who is accessing us, but given the TODO above, allow access. - return JS_TRUE; - } - - uint32 fileFlags = JS_GetScriptFilenameFlags(script); - if (fileFlags == JSFILENAME_NULL || (fileFlags & JSFILENAME_SYSTEM)) { - // We expect implicit native wrappers in system files. - return JS_TRUE; - } - - // Otherwise, we're looking at a non-system file with a handle on an - // implicit wrapper. This is a bug! Deny access. - NS_WARNING("Implicit native wrapper in content code"); - return JS_TRUE; -#else - return JS_TRUE; -#endif - - // NB: Watch for early returns in the ifdef DEBUG code above. -} - -static JSBool -XPC_NW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSPropertyDescriptor desc; - - if (!JS_GetPropertyDescriptorById(cx, obj, id, JSRESOLVE_QUALIFIED, - &desc)) { - return JS_FALSE; - } - - // Do not allow scripted getters or setters on XPCNativeWrappers. - if (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - jsval flags = JSVAL_VOID; - JS_GetReservedSlot(cx, obj, 0, &flags); - // The purpose of XPC_NW_AddProperty is to wrap any object set on the - // XPCNativeWrapper by the wrapped object's scriptable helper, so bail - // here if the scriptable helper is not currently adding a property. - // See comment above #define FLAG_RESOLVING in XPCWrapper.h. - if (!HAS_FLAGS(flags, FLAG_RESOLVING)) { - return JS_TRUE; - } - - // Note: no need to protect *vp from GC here, since it's already in the slot - // on |obj|. - return EnsureLegalActivity(cx, obj, id, sSecMgrSetProp) && - RewrapValue(cx, obj, *vp, vp); -} - -static JSBool -XPC_NW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return EnsureLegalActivity(cx, obj); -} - -static JSBool -XPC_NW_FunctionWrapper(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - JSObject *funObj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - if (!::JS_ObjectIsFunction(cx, funObj)) { - obj = nsnull; - } - - while (obj && !XPCNativeWrapper::IsNativeWrapper(obj)) { - obj = obj->getProto(); - } - - if (!obj) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - - // The real method we're going to call is the parent of this - // function's JSObject. - JSObject *methodToCallObj = funObj->getParent(); - XPCWrappedNative* wrappedNative = nsnull; - - jsval isAllAccess; - if (::JS_GetReservedSlot(cx, funObj, eAllAccessSlot, &isAllAccess) && - JSVAL_TO_BOOLEAN(isAllAccess)) { - wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); - } else if (!XPCNativeWrapper::GetWrappedNative(cx, obj, &wrappedNative)) { - wrappedNative = nsnull; - } - - if (!wrappedNative || !::JS_ObjectIsFunction(cx, methodToCallObj)) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - - jsval v; - if (!::JS_CallFunctionValue(cx, wrappedNative->GetFlatJSObject(), - OBJECT_TO_JSVAL(methodToCallObj), argc, - JS_ARGV(cx, vp), &v)) { - return JS_FALSE; - } - - XPCCallContext ccx(JS_CALLER, cx, obj); - - // Make sure v doesn't get collected while we're re-wrapping it. - AUTO_MARK_JSVAL(ccx, v); - - return RewrapValue(cx, obj, v, vp); -} - -static JSBool -GetwrappedJSObject(JSContext *cx, JSObject *obj, jsval *vp) -{ - // If we're wrapping an untrusted content wrapper, then we should - // return a safe wrapper for the underlying native object. Otherwise, - // such a wrapper would be superfluous. - - nsIScriptSecurityManager *ssm = GetSecurityManager(); - nsCOMPtr prin; - nsresult rv = ssm->GetObjectPrincipal(cx, obj, getter_AddRefs(prin)); - if (NS_FAILED(rv)) { - return ThrowException(rv, cx); - } - - jsval v = OBJECT_TO_JSVAL(obj); - - PRBool isSystem; - if (NS_SUCCEEDED(ssm->IsSystemPrincipal(prin, &isSystem)) && isSystem) { - *vp = v; - return JS_TRUE; - } - - return XPCSafeJSObjectWrapper::WrapObject(cx, JS_GetScopeChain(cx), v, vp); -} - -static JSBool -XPC_NW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp, - JSBool aIsSet) -{ - // We don't deal with the following properties here. - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE) || - id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { - return JS_TRUE; - } - - while (!XPCNativeWrapper::IsNativeWrapper(obj)) { - obj = obj->getProto(); - if (!obj) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - } - - if (!EnsureLegalActivity(cx, obj, id, - aIsSet ? sSecMgrSetProp : sSecMgrGetProp)) { - return JS_FALSE; - } - - // Protected by EnsureLegalActivity. - XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); - - if (!wrappedNative) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - - JSObject *nativeObj = wrappedNative->GetFlatJSObject(); - - if (!aIsSet && - id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) { - return GetwrappedJSObject(cx, nativeObj, vp); - } - - return GetOrSetNativeProperty(cx, obj, wrappedNative, id, vp, aIsSet, - JS_TRUE); -} - -static JSBool -XPC_NW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_NW_GetOrSetProperty(cx, obj, id, vp, PR_FALSE); -} - -static JSBool -XPC_NW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_NW_GetOrSetProperty(cx, obj, id, vp, PR_TRUE); -} - -static JSBool -XPC_NW_Enumerate(JSContext *cx, JSObject *obj) -{ - // We are being notified of a for-in loop or similar operation on this - // XPCNativeWrapper, so forward to the correct high-level object hook, - // OBJ_ENUMERATE on the XPCWrappedNative's object, called via the - // JS_Enumerate API. Then reflect properties named by the enumerated - // identifiers from the wrapped native to the native wrapper. - - if (!EnsureLegalActivity(cx, obj)) { - return JS_FALSE; - } - - // Protected by EnsureLegalActivity. - XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); - if (!wn) { - return JS_TRUE; - } - - return Enumerate(cx, obj, wn->GetFlatJSObject()); -} - -static JSBool -XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp) -{ - // No need to preserve on sets of wrappedJSObject or toString, since callers - // couldn't get at those values anyway. Also, we always deal with - // wrappedJSObject and toString before looking at our scriptable hooks, so no - // need to mess with our flags yet. - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) { - return JS_TRUE; - } - - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { - *objp = obj; - - // See the comment in WrapFunction for why we create this function - // like this. - JSFunction *fun = JS_NewFunction(cx, XPC_NW_toString, 0, 0, nsnull, - "toString"); - if (!fun) { - return JS_FALSE; - } - - JSObject *funobj = JS_GetFunctionObject(fun); - funobj->setParent(obj); - - return JS_DefineProperty(cx, obj, "toString", OBJECT_TO_JSVAL(funobj), - nsnull, nsnull, 0); - } - - PRUint32 accessType = - (flags & JSRESOLVE_ASSIGNING) ? sSecMgrSetProp : sSecMgrGetProp; - if (!EnsureLegalActivity(cx, obj, id, accessType)) { - return JS_FALSE; - } - - while (!XPCNativeWrapper::IsNativeWrapper(obj)) { - obj = obj->getProto(); - if (!obj) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - } - - // Protected by EnsureLegalActivity. - XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); - - if (!wrappedNative) { - // No wrapped native, no properties. - - return JS_TRUE; - } - - return ResolveNativeProperty(cx, obj, wrappedNative->GetFlatJSObject(), - wrappedNative, id, flags, objp, JS_TRUE); -} - -static JSBool -XPC_NW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - return EnsureLegalActivity(cx, obj); -} - -static void -XPC_NW_Finalize(JSContext *cx, JSObject *obj) -{ - // We must not use obj's private data here since it's likely that it - // has already been finalized. - XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); - - { - // scoped lock - XPCAutoLock lock(rt->GetMapLock()); - rt->GetExplicitNativeWrapperMap()->Remove(obj); - } -} - -static JSBool -XPC_NW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, - JSAccessMode mode, jsval *vp) -{ - // Prevent setting __proto__ on an XPCNativeWrapper - if ((mode & JSACC_WATCH) == JSACC_PROTO && (mode & JSACC_WRITE)) { - return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); - } - - // Forward to the checkObjectAccess hook in the JSContext, if any. - JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); - if (callbacks && callbacks->checkObjectAccess && - !callbacks->checkObjectAccess(cx, obj, id, mode, vp)) { - return JS_FALSE; - } - - // This function does its own security checks. - XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); - if (!wrappedNative) { - return JS_TRUE; - } - - JSObject *wrapperJSObject = wrappedNative->GetFlatJSObject(); - - JSClass *clazz = wrapperJSObject->getJSClass(); - return !clazz->checkAccess || - clazz->checkAccess(cx, wrapperJSObject, id, mode, vp); -} - -static JSBool -XPC_NW_Call(JSContext *cx, uintN argc, jsval *vp) -{ -#ifdef DEBUG - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - if (!XPCNativeWrapper::IsNativeWrapper(obj) && - !JS_ObjectIsFunction(cx, obj)) { - NS_WARNING("Ignoring a call for a weird object"); - } -#endif - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; -} - -static JSBool -XPC_NW_Construct(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - if (!EnsureLegalActivity(cx, obj)) { - return JS_FALSE; - } - - // Protected by EnsureLegalActivity. - XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); - if (!wrappedNative) { - return JS_TRUE; - } - - JSBool retval = JS_TRUE; - - if (!NATIVE_HAS_FLAG(wrappedNative, WantConstruct)) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - - XPCCallContext ccx(JS_CALLER, cx, obj, nsnull, JSID_VOID, - argc, JS_ARGV(cx, vp), vp); - if(!ccx.IsValid()) - return JS_FALSE; - - JS_ASSERT(obj == ccx.GetFlattenedJSObject()); - - nsresult rv = wrappedNative->GetScriptableInfo()-> - GetCallback()->Construct(wrappedNative, cx, obj, argc, JS_ARGV(cx, vp), vp, - &retval); - if (NS_FAILED(rv)) { - return ThrowException(rv, cx); - } - - if (!retval) { - return JS_FALSE; - } - - if (JSVAL_IS_PRIMITIVE(*vp)) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - return RewrapValue(cx, obj, *vp, vp); -} - -static JSBool -XPC_NW_HasInstance(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) -{ - return JS_TRUE; -} - -static void -XPC_NW_Trace(JSTracer *trc, JSObject *obj) -{ - // Untrusted code can't trigger this. - XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); - - if (wrappedNative && wrappedNative->IsValid()) { - JS_CALL_OBJECT_TRACER(trc, wrappedNative->GetFlatJSObject(), - "wrappedNative.flatJSObject"); - } -} - -static JSBool -XPC_NW_Equality(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) -{ - NS_ASSERTION(XPCNativeWrapper::IsNativeWrapper(obj), - "Uh, we should only ever be called for XPCNativeWrapper " - "objects!"); - - if (!EnsureLegalActivity(cx, obj)) { - return JS_FALSE; - } - - jsval v = *valp; - if (JSVAL_IS_PRIMITIVE(v)) { - *bp = JS_FALSE; - - return JS_TRUE; - } - - // Protected by EnsureLegalActivity. - XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); - - if (wrappedNative && wrappedNative->IsValid() && - NATIVE_HAS_FLAG(wrappedNative, WantEquality)) { - // Forward the call to the wrapped native's Equality() hook. - nsresult rv = wrappedNative->GetScriptableCallback()-> - Equality(wrappedNative, cx, obj, v, bp); - - if (NS_FAILED(rv)) { - return ThrowException(rv, cx); - } - } else { - JSObject *other = JSVAL_TO_OBJECT(v); - - *bp = (obj == other || - XPC_GetIdentityObject(cx, obj) == XPC_GetIdentityObject(cx, other)); - } - - return JS_TRUE; -} - -static JSObject * -XPC_NW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) -{ - XPCCallContext ccx(JS_CALLER, cx); - if (!ccx.IsValid()) { - ThrowException(NS_ERROR_FAILURE, cx); - return nsnull; - } - - JSObject *wrapperIter = - JS_NewObjectWithGivenProto(cx, XPCNativeWrapper::GetJSClass(false), nsnull, - obj->getParent()); - if (!wrapperIter) { - return nsnull; - } - - js::AutoObjectRooter tvr(cx, wrapperIter); - - // Initialize our native wrapper. - XPCWrappedNative *wn = static_cast(JS_GetPrivate(cx, obj)); - JS_SetPrivate(cx, wrapperIter, wn); - if (!JS_SetReservedSlot(cx, wrapperIter, 0, INT_TO_JSVAL(FLAG_EXPLICIT))) { - return nsnull; - } - - return CreateIteratorObj(cx, wrapperIter, obj, wn->GetFlatJSObject(), - keysonly); -} - -static JSBool -XPC_NW_toString(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - while (!XPCNativeWrapper::IsNativeWrapper(obj)) { - obj = obj->getProto(); - if (!obj) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - } - - if (!EnsureLegalActivity(cx, obj, - GetRTIdByIndex(cx, XPCJSRuntime::IDX_TO_STRING), - sSecMgrGetProp)) { - return JS_FALSE; - } - - // Protected by EnsureLegalActivity. - XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj); - - if (!wrappedNative) { - // toString() called on XPCNativeWrapper.prototype - NS_NAMED_LITERAL_STRING(protoString, "[object XPCNativeWrapper]"); - JSString *str = - ::JS_NewUCStringCopyN(cx, reinterpret_cast - (protoString.get()), - protoString.Length()); - NS_ENSURE_TRUE(str, JS_FALSE); - JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); - return JS_TRUE; - } - - return NativeToString(cx, wrappedNative, argc, JS_ARGV(cx, vp), vp, JS_TRUE); -} - -static JSBool -UnwrapNW(JSContext *cx, uintN argc, jsval *vp) -{ - if (argc != 1) { - return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); - } - - jsval v = JS_ARGV(cx, vp)[0]; - if (JSVAL_IS_PRIMITIVE(v)) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - - JSObject *obj = JSVAL_TO_OBJECT(v); - if (!obj->isWrapper()) { - JS_SET_RVAL(cx, vp, v); - return JS_TRUE; - } - - if (xpc::WrapperFactory::IsXrayWrapper(obj)) { - return JS_GetProperty(cx, obj, "wrappedJSObject", vp); - } - - JS_SET_RVAL(cx, vp, v); - return JS_TRUE; -} - -static JSBool -XrayWrapperConstructor(JSContext *cx, uintN argc, jsval *vp) -{ - if (argc == 0) { - return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); - } - - if (JSVAL_IS_PRIMITIVE(vp[2])) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - JSObject *obj = JSVAL_TO_OBJECT(vp[2]); - if (!obj->isWrapper()) { - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - obj = obj->unwrap(); - - *vp = OBJECT_TO_JSVAL(obj); - return JS_WrapValue(cx, vp); -} - -// static -PRBool -XPCNativeWrapper::AttachNewConstructorObject(XPCCallContext &ccx, - JSObject *aGlobalObject) -{ - JSObject *xpcnativewrapper = - JS_DefineFunction(ccx, aGlobalObject, "XPCNativeWrapper", - XrayWrapperConstructor, 1, - JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_STUB_GSOPS); - if (!xpcnativewrapper) { - return PR_FALSE; - } - - return JS_DefineFunction(ccx, xpcnativewrapper, "unwrap", UnwrapNW, 1, - JSPROP_READONLY | JSPROP_PERMANENT) != nsnull; -} - -// static -JSObject * -XPCNativeWrapper::GetNewOrUsed(JSContext *cx, XPCWrappedNative *wrapper, - JSObject *scope, nsIPrincipal *aObjectPrincipal) -{ - CheckWindow(wrapper); - - if (aObjectPrincipal) { - nsIScriptSecurityManager *ssm = GetSecurityManager(); - - PRBool isSystem; - nsresult rv = ssm->IsSystemPrincipal(aObjectPrincipal, &isSystem); - if (NS_SUCCEEDED(rv) && !isSystem) { - jsval v = OBJECT_TO_JSVAL(wrapper->GetFlatJSObject()); - if (!CreateExplicitWrapper(cx, wrapper, &v)) { - return nsnull; - } - return JSVAL_TO_OBJECT(v); - } - } - - // Prevent wrapping a double-wrapped JS object in an - // XPCNativeWrapper! - nsCOMPtr xpcwrappedjs(do_QueryWrappedNative(wrapper)); - - if (xpcwrappedjs) { - JSObject *flat = wrapper->GetFlatJSObject(); - jsval v = OBJECT_TO_JSVAL(flat); - - XPCCallContext ccx(JS_CALLER, cx); - - // Make sure v doesn't get collected while we're re-wrapping it. - AUTO_MARK_JSVAL(ccx, v); - - if (XPCSafeJSObjectWrapper::WrapObject(cx, scope, v, &v)) - return JSVAL_TO_OBJECT(v); - - return nsnull; - } - - JSObject *obj = wrapper->GetWrapper(); - if (obj) { - return obj; - } - - JSObject *nw_parent = wrapper->GetScope()->GetGlobalJSObject(); - - bool call = NATIVE_HAS_FLAG(wrapper, WantCall) || - NATIVE_HAS_FLAG(wrapper, WantConstruct); - obj = JS_NewObjectWithGivenProto(cx, GetJSClass(call), nsnull, nw_parent); - - if (!obj || - !JS_SetPrivate(cx, obj, wrapper) || - !JS_SetReservedSlot(cx, obj, 0, JSVAL_ZERO)) { - return nsnull; - } - - wrapper->SetWrapper(obj); - -#if defined(DEBUG_XPCNativeWrapper) || defined(DEBUG_xpc_leaks) - { - XPCCallContext ccx(NATIVE_CALLER, cx); - - // Keep obj alive while we mess with strings - AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(obj)); - - char *s = wrapper->ToString(ccx); - printf("Created new XPCNativeWrapper %p for wrapped native %s\n", - (void*)obj, s); - if (s) - JS_smprintf_free(s); - } -#endif - - return obj; -} - -// static -JSBool -XPCNativeWrapper::CreateExplicitWrapper(JSContext *cx, - XPCWrappedNative *wrappedNative, - jsval *rval) -{ -#ifdef DEBUG_XPCNativeWrapper - printf("Creating new JSObject\n"); -#endif - - bool call = NATIVE_HAS_FLAG(wrappedNative, WantCall) || - NATIVE_HAS_FLAG(wrappedNative, WantConstruct); - JSObject *wrapperObj = - JS_NewObjectWithGivenProto(cx, XPCNativeWrapper::GetJSClass(call), nsnull, - wrappedNative->GetScope()->GetGlobalJSObject()); - - if (!wrapperObj) { - // JS_NewObject already threw (or reported OOM). - return JS_FALSE; - } - - if (!JS_SetReservedSlot(cx, wrapperObj, 0, INT_TO_JSVAL(FLAG_EXPLICIT))) { - return JS_FALSE; - } - - // Set the XPCWrappedNative as private data in the native wrapper. - if (!JS_SetPrivate(cx, wrapperObj, wrappedNative)) { - return JS_FALSE; - } - -#if defined(DEBUG_XPCNativeWrapper) || defined(DEBUG_xpc_leaks) - { - XPCCallContext ccx(JS_CALLER, cx); - - // Keep wrapperObj alive while we mess with strings - AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(wrapperObj)); - - char *s = wrappedNative->ToString(ccx); - printf("Created new XPCNativeWrapper %p for wrapped native %s\n", - (void*)wrapperObj, s); - if (s) - JS_smprintf_free(s); - } -#endif - - *rval = OBJECT_TO_JSVAL(wrapperObj); - - { - XPCJSRuntime *rt = wrappedNative->GetRuntime(); - - // scoped lock - XPCAutoLock lock(rt->GetMapLock()); - rt->GetExplicitNativeWrapperMap()->Add(wrapperObj); - } - - return JS_TRUE; -} - -struct WrapperAndCxHolder -{ - XPCWrappedNative* wrapper; - JSContext* cx; -}; - -static JSDHashOperator -ClearNativeWrapperScope(JSDHashTable *table, JSDHashEntryHdr *hdr, - uint32 number, void *arg) -{ - JSDHashEntryStub* entry = (JSDHashEntryStub*)hdr; - WrapperAndCxHolder* d = (WrapperAndCxHolder*)arg; - - if (d->wrapper->GetWrapper() == (JSObject*)entry->key) - { - ::JS_ClearScope(d->cx, (JSObject*)entry->key); - } - - return JS_DHASH_NEXT; -} - -// static -void -XPCNativeWrapper::ClearWrappedNativeScopes(JSContext* cx, - XPCWrappedNative* wrapper) -{ - JSObject *nativeWrapper = wrapper->GetWrapper(); - - if (nativeWrapper) { - ::JS_ClearScope(cx, nativeWrapper); - } - - WrapperAndCxHolder d = - { - wrapper, - cx - }; - - wrapper->GetRuntime()->GetExplicitNativeWrapperMap()-> - Enumerate(ClearNativeWrapperScope, &d); -} diff --git a/js/src/xpconnect/src/XPCNativeWrapper.h b/js/src/xpconnect/src/XPCNativeWrapper.h deleted file mode 100644 index 927b9b05bf02..000000000000 --- a/js/src/xpconnect/src/XPCNativeWrapper.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * The Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2005 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Johnny Stenback (original author) - * Brendan Eich - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nscore.h" -#include "jsapi.h" - -class nsIPrincipal; - -namespace XPCNativeWrapper { - -namespace internal { -extern js::Class NW_NoCall_Class; -extern js::Class NW_Call_Class; -} - -PRBool -AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject); - -JSObject * -GetNewOrUsed(JSContext *cx, XPCWrappedNative *wrapper, - JSObject *scope, nsIPrincipal *aObjectPrincipal); -JSBool -CreateExplicitWrapper(JSContext *cx, XPCWrappedNative *wrapper, jsval *rval); - -inline PRBool -IsNativeWrapperClass(js::Class *clazz) -{ - return clazz == &internal::NW_NoCall_Class || - clazz == &internal::NW_Call_Class; -} - -inline PRBool -IsNativeWrapper(JSObject *obj) -{ - return IsNativeWrapperClass(obj->getClass()); -} - -JSBool -GetWrappedNative(JSContext *cx, JSObject *obj, - XPCWrappedNative **aWrappedNative); - -// NB: Use the following carefully. -inline XPCWrappedNative * -SafeGetWrappedNative(JSObject *obj) -{ - return static_cast(xpc_GetJSPrivate(obj)); -} - -inline JSClass * -GetJSClass(bool call) -{ - return call - ? js::Jsvalify(&internal::NW_Call_Class) - : js::Jsvalify(&internal::NW_NoCall_Class); -} - -void -ClearWrappedNativeScopes(JSContext* cx, XPCWrappedNative* wrapper); - -} diff --git a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp deleted file mode 100644 index e5baced3b6a8..000000000000 --- a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp +++ /dev/null @@ -1,1087 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 sw=2 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.org code. - * - * The Initial Developer of the Original Code is - * The Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Johnny Stenback (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "xpcprivate.h" -#include "jsdbgapi.h" -#include "jsscript.h" // for js_ScriptClass -#include "XPCWrapper.h" -#include "jsregexp.h" -#include "nsJSPrincipals.h" - -static JSBool -XPC_SJOW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_SJOW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_SJOW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_SJOW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_SJOW_Enumerate(JSContext *cx, JSObject *obj); - -static JSBool -XPC_SJOW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp); - -static JSBool -XPC_SJOW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp); - -static void -XPC_SJOW_Finalize(JSContext *cx, JSObject *obj); - -static JSBool -XPC_SJOW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp); - -static JSBool -XPC_SJOW_Call(JSContext *cx, uintN argc, jsval *vp); - -static JSBool -XPC_SJOW_Construct(JSContext *cx, uintN argc, jsval *vp); - -static JSBool -XPC_SJOW_Create(JSContext *cx, uintN argc, jsval *vp); - -static JSBool -XPC_SJOW_Equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp); - -static JSObject * -XPC_SJOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly); - -static JSObject * -XPC_SJOW_WrappedObject(JSContext *cx, JSObject *obj); - -using namespace XPCSafeJSObjectWrapper; -using namespace XPCWrapper; - -static inline -JSBool -ThrowException(nsresult ex, JSContext *cx) -{ - DoThrowException(ex, cx); - - return JS_FALSE; -} - -// Find the subject and object principal. The argument -// subjectPrincipal can be null if the caller doesn't care about the -// subject principal, and secMgr can also be null if the caller -// doesn't need the security manager. -static nsresult -FindPrincipals(JSContext *cx, JSObject *obj, nsIPrincipal **objectPrincipal, - nsIPrincipal **subjectPrincipal, - nsIScriptSecurityManager **secMgr, - JSStackFrame **fp = nsnull) -{ - XPCCallContext ccx(JS_CALLER, cx); - - if (!ccx.IsValid()) { - return NS_ERROR_UNEXPECTED; - } - - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - - if (subjectPrincipal) { - JSStackFrame *fp2; - NS_IF_ADDREF(*subjectPrincipal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp2)); - if (fp) { - *fp = fp2; - } - } - - ssm->GetObjectPrincipal(cx, obj, objectPrincipal); - - if (secMgr) { - NS_ADDREF(*secMgr = ssm); - } - - return *objectPrincipal ? NS_OK : NS_ERROR_XPC_SECURITY_MANAGER_VETO; -} - -static PRBool -CanCallerAccess(JSContext *cx, JSObject *wrapperObj, JSObject *unsafeObj) -{ - // TODO bug 508928: Refactor this with the XOW security checking code. - nsCOMPtr subjPrincipal, objPrincipal; - nsCOMPtr ssm; - JSStackFrame *fp; - nsresult rv = FindPrincipals(cx, unsafeObj, getter_AddRefs(objPrincipal), - getter_AddRefs(subjPrincipal), - getter_AddRefs(ssm), &fp); - if (NS_FAILED(rv)) { - return ThrowException(rv, cx); - } - - // Assume that we're trusted if there's no running code. - if (!subjPrincipal || !fp) { - return PR_TRUE; - } - - PRBool subsumes; - rv = subjPrincipal->Subsumes(objPrincipal, &subsumes); - - if (NS_FAILED(rv) || !subsumes) { - PRBool enabled = PR_FALSE; - rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &enabled); - if (NS_FAILED(rv)) { - return ThrowException(rv, cx); - } - - if (!enabled) { - return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); - } - } - - if (wrapperObj) { - jsval flags; - JS_GetReservedSlot(cx, wrapperObj, sFlagsSlot, &flags); - if (HAS_FLAGS(flags, FLAG_SOW) && - !SystemOnlyWrapper::CheckFilename(cx, JSID_VOID, fp)) { - return JS_FALSE; - } - } - - return PR_TRUE; -} - -// Reserved slot indexes on safe wrappers. - -// Slot for holding on to the principal to use if a principal other -// than that of the unsafe object is desired for this wrapper -// (nsIPrincipal, strong reference). -static const PRUint32 sPrincipalSlot = sNumSlots; - -// Slot for holding the function that we fill our fake frame with. -static const PRUint32 sScopeFunSlot = sNumSlots + 1; - -static const PRUint32 sSJOWSlots = sNumSlots + 2; - -// Returns a weak reference. -static nsIPrincipal * -FindObjectPrincipals(JSContext *cx, JSObject *safeObj, JSObject *innerObj) -{ - // Check if we have a cached principal first. - jsval v; - if (!JS_GetReservedSlot(cx, safeObj, sPrincipalSlot, &v)) { - return nsnull; - } - - if (!JSVAL_IS_VOID(v)) { - // Found one! No need to do any more refcounting. - return static_cast(JSVAL_TO_PRIVATE(v)); - } - - nsCOMPtr objPrincipal; - nsresult rv = FindPrincipals(cx, innerObj, getter_AddRefs(objPrincipal), nsnull, - nsnull); - if (NS_FAILED(rv)) { - return nsnull; - } - - if (!JS_SetReservedSlot(cx, safeObj, sPrincipalSlot, - PRIVATE_TO_JSVAL(objPrincipal.get()))) { - return nsnull; - } - - // The wrapper owns the principal now. - return objPrincipal.forget().get(); -} - -static inline JSObject * -FindSafeObject(JSObject *obj) -{ - while (obj->getClass() != &SJOWClass) { - obj = obj->getProto(); - - if (!obj) { - break; - } - } - - return obj; -} - -static JSBool -XPC_SJOW_toString(JSContext *cx, uintN argc, jsval *vp); - -namespace XPCSafeJSObjectWrapper { - -// JS class for XPCSafeJSObjectWrapper (and this doubles as the -// constructor for XPCSafeJSObjectWrapper for the moment too...) - -js::Class SJOWClass = { - "XPCSafeJSObjectWrapper", - JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(sSJOWSlots), - JS_VALUEIFY(js::PropertyOp, XPC_SJOW_AddProperty), - JS_VALUEIFY(js::PropertyOp, XPC_SJOW_DelProperty), - JS_VALUEIFY(js::PropertyOp, XPC_SJOW_GetProperty), - JS_VALUEIFY(js::PropertyOp, XPC_SJOW_SetProperty), - XPC_SJOW_Enumerate, - (JSResolveOp)XPC_SJOW_NewResolve, - JS_VALUEIFY(js::ConvertOp, XPC_SJOW_Convert), - XPC_SJOW_Finalize, - nsnull, // reserved0 - JS_VALUEIFY(js::CheckAccessOp, XPC_SJOW_CheckAccess), - JS_VALUEIFY(js::CallOp, XPC_SJOW_Call), - JS_VALUEIFY(js::CallOp, XPC_SJOW_Create), - nsnull, // xdrObject - nsnull, // hasInstance - nsnull, // mark - - // ClassExtension - { - JS_VALUEIFY(js::EqualityOp, XPC_SJOW_Equality), - nsnull, // outerObject - nsnull, // innerObject - XPC_SJOW_Iterator, - XPC_SJOW_WrappedObject - } -}; - -JSBool -WrapObject(JSContext *cx, JSObject *scope, jsval v, jsval *vp) -{ - // This might be redundant if called from XPC_SJOW_Construct, but it should - // be cheap in that case. - JSObject *objToWrap = UnsafeUnwrapSecurityWrapper(cx, JSVAL_TO_OBJECT(v)); - if (!objToWrap || - JS_TypeOfValue(cx, OBJECT_TO_JSVAL(objToWrap)) == JSTYPE_XML) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - - // Prevent script created Script objects from ever being wrapped - // with XPCSafeJSObjectWrapper, and never let the eval function - // object be directly wrapped. - - if (objToWrap->getClass() == &js_ScriptClass || - (JS_ObjectIsFunction(cx, objToWrap) && - JS_GetFunctionNative(cx, JS_ValueToFunction(cx, v)) == - XPCWrapper::sEvalNative)) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - - XPCWrappedNativeScope *xpcscope = - XPCWrappedNativeScope::FindInJSObjectScope(cx, scope); - NS_ASSERTION(xpcscope, "what crazy scope are we in?"); - - XPCWrappedNative *wrappedNative; - WrapperType type = xpcscope->GetWrapperFor(cx, objToWrap, SJOW, - &wrappedNative); - - // NB: We allow XOW here because we're as restrictive as it is (and we know - // we're same origin here). - if (type != NONE && type != XOW && !(type & SJOW)) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - - SLIM_LOG_WILL_MORPH(cx, objToWrap); - if (IS_SLIM_WRAPPER(objToWrap) && !MorphSlimWrapper(cx, objToWrap)) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - XPCWrappedNative *wn = - XPCWrappedNative::GetWrappedNativeOfJSObject(cx, objToWrap); - if (wn) { - CheckWindow(wn); - } - - JSObject *wrapperObj = - JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SJOWClass), nsnull, scope); - - if (!wrapperObj) { - // JS_NewObjectWithGivenProto already threw. - return JS_FALSE; - } - - *vp = OBJECT_TO_JSVAL(wrapperObj); - if (!JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sWrappedObjSlot, - OBJECT_TO_JSVAL(objToWrap)) || - !JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO)) { - return JS_FALSE; - } - - return JS_TRUE; -} - -PRBool -AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject) -{ - // Initialize sEvalNative the first time we attach a constructor. - // NB: This always happens before any cross origin wrappers are - // created, so it's OK to do this here. - if (!XPCWrapper::FindEval(ccx, aGlobalObject)) { - return PR_FALSE; - } - - JSObject *class_obj = - ::JS_InitClass(ccx, aGlobalObject, nsnull, js::Jsvalify(&SJOWClass), - XPC_SJOW_Construct, 0, nsnull, nsnull, nsnull, nsnull); - if (!class_obj) { - NS_WARNING("can't initialize the XPCSafeJSObjectWrapper class"); - return PR_FALSE; - } - - if (!::JS_DefineFunction(ccx, class_obj, "toString", XPC_SJOW_toString, - 0, 0)) { - return PR_FALSE; - } - - // Make sure our prototype chain is empty and that people can't mess - // with XPCSafeJSObjectWrapper.prototype. - ::JS_SetPrototype(ccx, class_obj, nsnull); - if (!::JS_FreezeObject(ccx, class_obj)) { - NS_WARNING("Failed to seal XPCSafeJSObjectWrapper.prototype"); - return PR_FALSE; - } - - JSBool found; - return ::JS_SetPropertyAttributes(ccx, aGlobalObject, - SJOWClass.name, - JSPROP_READONLY | JSPROP_PERMANENT, - &found); -} - -JSObject * -GetUnsafeObject(JSContext *cx, JSObject *obj) -{ - obj = FindSafeObject(obj); - - if (!obj) { - return nsnull; - } - - jsval v; - if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sWrappedObjSlot, &v)) { - JS_ClearPendingException(cx); - return nsnull; - } - - return JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : nsnull; -} - -} // namespace XPCSafeJSObjectWrapper - -static JSObject * -GetScopeChainForSafeCall(JSContext *cx, JSObject *outerObj) -{ - JSObject *unsafeObj = GetUnsafeObject(cx, outerObj); - JSObject *scopeobj = JS_GetGlobalForObject(cx, unsafeObj); - OBJ_TO_INNER_OBJECT(cx, scopeobj); - return scopeobj; -} - -// Wrap a JS value in a safe wrapper of a function wrapper if -// needed. Note that rval must point to something rooted when calling -// this function. -static JSBool -WrapJSValue(JSContext *cx, JSObject *obj, jsval val, jsval *rval) -{ - JSBool ok = JS_TRUE; - - if (JSVAL_IS_PRIMITIVE(val)) { - *rval = val; - } else { - if (!RewrapObject(cx, obj->getParent(), JSVAL_TO_OBJECT(val), SJOW, - rval)) { - return JS_FALSE; - } - // Construct a new safe wrapper. Note that it doesn't matter what - // parent we pass in here, the construct hook will ensure we get - // the right parent for the wrapper. - JSObject *safeObj = JSVAL_TO_OBJECT(*rval); - if (safeObj->getClass() == &SJOWClass && - JS_GetGlobalForObject(cx, obj) != JS_GetGlobalForObject(cx, safeObj)) { - // Check to see if the new object we just wrapped is accessible - // from the unsafe object we got the new object through. If not, - // force the new wrapper to use the principal of the unsafe - // object we got the new object from. - nsCOMPtr srcObjPrincipal; - nsCOMPtr subjPrincipal; - nsCOMPtr valObjPrincipal; - - nsresult rv = FindPrincipals(cx, obj, getter_AddRefs(srcObjPrincipal), - getter_AddRefs(subjPrincipal), nsnull); - if (NS_FAILED(rv)) { - return ThrowException(rv, cx); - } - - rv = FindPrincipals(cx, JSVAL_TO_OBJECT(val), - getter_AddRefs(valObjPrincipal), nsnull, nsnull); - if (NS_FAILED(rv)) { - return ThrowException(rv, cx); - } - - PRBool subsumes = PR_FALSE; - rv = srcObjPrincipal->Subsumes(valObjPrincipal, &subsumes); - if (NS_FAILED(rv)) { - return ThrowException(rv, cx); - } - - // If the subject can access both the source and object principals, then - // don't bother forcing the principal below. - if (!subsumes && subjPrincipal) { - PRBool subjSubsumes = PR_FALSE; - rv = subjPrincipal->Subsumes(srcObjPrincipal, &subjSubsumes); - if (NS_SUCCEEDED(rv) && subjSubsumes) { - rv = subjPrincipal->Subsumes(valObjPrincipal, &subjSubsumes); - if (NS_SUCCEEDED(rv) && subjSubsumes) { - subsumes = PR_TRUE; - } - } - } - - if (!subsumes) { - // The unsafe object we got the new object from can not access - // the new object, force the wrapper we just created to use - // the principal of the unsafe object to prevent users of the - // new object wrapper from evaluating code through the new - // wrapper with the principal of the new object. - if (!::JS_SetReservedSlot(cx, safeObj, sPrincipalSlot, - PRIVATE_TO_JSVAL(srcObjPrincipal.get()))) { - return JS_FALSE; - } - - // Pass on ownership of the new object principal to the - // wrapper. - nsIPrincipal *tmp = nsnull; - srcObjPrincipal.swap(tmp); - } - } - } - - return ok; -} - -static JSBool -XPC_SJOW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - // The constructor and toString properties needs to live on the safe - // wrapper. - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR) || - id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { - return JS_TRUE; - } - - obj = FindSafeObject(obj); - NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); - - // Do nothing here if we're in the middle of resolving a property on - // this safe wrapper. - jsval isResolving; - JSBool ok = ::JS_GetReservedSlot(cx, obj, sFlagsSlot, &isResolving); - if (!ok || HAS_FLAGS(isResolving, FLAG_RESOLVING)) { - return ok; - } - - JSObject *unsafeObj = GetUnsafeObject(cx, obj); - if (!unsafeObj) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - - // Check that the caller can access the unsafe object. - if (!CanCallerAccess(cx, obj, unsafeObj)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - if (!JSVAL_IS_PRIMITIVE(*vp)) { - // Adding an object of some type to the content object, make sure it's - // properly wrapped. - JSObject *added = JSVAL_TO_OBJECT(*vp); - if (!RewrapObject(cx, JS_GetGlobalForObject(cx, unsafeObj), added, - UNKNOWN, vp)) { - return JS_FALSE; - } - } - - return XPCWrapper::AddProperty(cx, obj, JS_FALSE, unsafeObj, id, vp); -} - -static JSBool -XPC_SJOW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *unsafeObj = GetUnsafeObject(cx, obj); - if (!unsafeObj) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - - // Check that the caller can access the unsafe object. - if (!CanCallerAccess(cx, obj, unsafeObj)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - return XPCWrapper::DelProperty(cx, unsafeObj, id, vp); -} - -NS_STACK_CLASS class SafeCallGuard { -public: - SafeCallGuard(JSContext *cx, nsIPrincipal *principal) - : cx(cx) { - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if (ssm) { - // Note: We pass null as the target frame pointer because we know that - // we're about to set aside the frame chain. - nsresult rv = ssm->PushContextPrincipal(cx, nsnull, principal); - if (NS_FAILED(rv)) { - NS_WARNING("Not allowing call because we're out of memory"); - JS_ReportOutOfMemory(cx); - this->cx = nsnull; - return; - } - } - - fp = JS_SaveFrameChain(cx); - options = - JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT); - } - - JSBool ready() { - return cx != nsnull; - } - - ~SafeCallGuard() { - if (cx) { - JS_SetOptions(cx, options); - JS_RestoreFrameChain(cx, fp); - nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); - if (ssm) { - ssm->PopContextPrincipal(cx); - } - } - } - -private: - JSContext *cx; - uint32 options; - JSStackFrame *fp; -}; - -static JSBool -XPC_SJOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp, - JSBool aIsSet) -{ - // We resolve toString to a function in our resolve hook. - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { - return JS_TRUE; - } - - obj = FindSafeObject(obj); - NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); - - JSObject *unsafeObj = GetUnsafeObject(cx, obj); - if (!unsafeObj) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - - // Check that the caller can access the unsafe object. - if (!CanCallerAccess(cx, obj, unsafeObj)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - JSObject *scopeChain = GetScopeChainForSafeCall(cx, obj); - if (!scopeChain) { - return JS_FALSE; - } - - { - SafeCallGuard guard(cx, FindObjectPrincipals(cx, obj, unsafeObj)); - if (!guard.ready()) { - return JS_FALSE; - } - - if (aIsSet && - !JSVAL_IS_PRIMITIVE(*vp) && - !RewrapObject(cx, JS_GetGlobalForObject(cx, unsafeObj), - JSVAL_TO_OBJECT(*vp), UNKNOWN, vp)) { - return JS_FALSE; - } - - JSBool ok = aIsSet - ? js_SetPropertyByIdWithFakeFrame(cx, unsafeObj, scopeChain, id, vp) - : js_GetPropertyByIdWithFakeFrame(cx, unsafeObj, scopeChain, id, vp); - if (!ok) { - return JS_FALSE; - } - } - - return WrapJSValue(cx, obj, *vp, vp); -} - -static JSBool -XPC_SJOW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_SJOW_GetOrSetProperty(cx, obj, id, vp, PR_FALSE); -} - -static JSBool -XPC_SJOW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_SJOW_GetOrSetProperty(cx, obj, id, vp, PR_TRUE); -} - -static JSBool -XPC_SJOW_Enumerate(JSContext *cx, JSObject *obj) -{ - obj = FindSafeObject(obj); - NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); - - // We are being notified of a for-in loop or similar operation on - // this XPCSafeJSObjectWrapper. Forward to the correct high-level - // object hook, OBJ_ENUMERATE on the unsafe object, called via the - // JS_Enumerate API. Then reflect properties named by the - // enumerated identifiers from the unsafe object to the safe - // wrapper. - - JSObject *unsafeObj = GetUnsafeObject(cx, obj); - if (!unsafeObj) { - return JS_TRUE; - } - - // Check that the caller can access the unsafe object. - if (!CanCallerAccess(cx, obj, unsafeObj)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - // Since we enumerate using JS_Enumerate() on the unsafe object here - // we don't need to do a security check since JS_Enumerate() will - // look up unsafeObj.__iterator__ and if we don't have permission to - // access that, it'll throw and we'll be safe. - - return XPCWrapper::Enumerate(cx, obj, unsafeObj); -} - -static JSBool -XPC_SJOW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp) -{ - obj = FindSafeObject(obj); - NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); - - JSObject *unsafeObj = GetUnsafeObject(cx, obj); - if (!unsafeObj) { - // No unsafe object, nothing to resolve here. - - return JS_TRUE; - } - - // Check that the caller can access the unsafe object. - if (!CanCallerAccess(cx, obj, unsafeObj)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - // Resolve toString specially. - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { - *objp = obj; - return JS_DefineFunction(cx, obj, "toString", - XPC_SJOW_toString, 0, 0) != nsnull; - } - - return XPCWrapper::NewResolve(cx, obj, JS_FALSE, unsafeObj, id, flags, - objp); -} - -static JSBool -XPC_SJOW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - NS_ASSERTION(type != JSTYPE_STRING, "toString failed us"); - return JS_TRUE; -} - -static void -XPC_SJOW_Finalize(JSContext *cx, JSObject *obj) -{ - // Release the reference to the cached principal if we have one. - jsval v; - if (::JS_GetReservedSlot(cx, obj, sPrincipalSlot, &v) && !JSVAL_IS_VOID(v)) { - nsIPrincipal *principal = (nsIPrincipal *)JSVAL_TO_PRIVATE(v); - - NS_RELEASE(principal); - } -} - -static JSBool -XPC_SJOW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, - JSAccessMode mode, jsval *vp) -{ - // Prevent setting __proto__ on an XPCSafeJSObjectWrapper - if ((mode & JSACC_WATCH) == JSACC_PROTO && (mode & JSACC_WRITE)) { - return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); - } - - // Forward to the checkObjectAccess hook in the runtime, if any. - JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); - if (callbacks && callbacks->checkObjectAccess && - !callbacks->checkObjectAccess(cx, obj, id, mode, vp)) { - return JS_FALSE; - } - - JSObject *unsafeObj = GetUnsafeObject(cx, obj); - if (!unsafeObj) { - return JS_TRUE; - } - - // Forward the unsafe object to the checkObjectAccess hook in the - // runtime too, if any. - if (callbacks && callbacks->checkObjectAccess && - !callbacks->checkObjectAccess(cx, unsafeObj, id, mode, vp)) { - return JS_FALSE; - } - - JSClass *clazz = unsafeObj->getJSClass(); - return !clazz->checkAccess || - clazz->checkAccess(cx, unsafeObj, id, mode, vp); -} - -static JSBool -XPC_SJOW_Call(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - JSObject *tmp = FindSafeObject(obj); - JSObject *unsafeObj, *callThisObj = nsnull; - - if (tmp) { - // A function wrapped in an XPCSafeJSObjectWrapper is being called - // directly (i.e. safeObj.fun()), set obj to be the safe object - // wrapper. In this case, the "this" object used when calling the - // function will be the unsafe object gotten off of the safe - // object. - obj = tmp; - } else { - // A function wrapped in an XPCSafeJSObjectWrapper is being called - // indirectly off of an object that's not a safe wrapper - // (i.e. foo.bar = safeObj.fun; foo.bar()), set obj to be the safe - // wrapper for the function, and use the object passed in as the - // "this" object when calling the function. - callThisObj = obj; - - // Check that the caller can access the object we're about to pass - // in as "this" for the call we're about to make. - if (!CanCallerAccess(cx, nsnull, callThisObj)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - obj = FindSafeObject(JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); - - if (!obj) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - } - - unsafeObj = GetUnsafeObject(cx, obj); - if (!unsafeObj) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - - if (!callThisObj) { - callThisObj = unsafeObj; - } - - JSObject *safeObj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - JSObject *funToCall = GetUnsafeObject(cx, safeObj); - - if (!funToCall) { - // Someone has called XPCSafeJSObjectWrapper.prototype() causing - // us to find a safe object wrapper without an unsafeObject as - // its parent. That call shouldn't do anything, so bail here. - return JS_TRUE; - } - - // Check that the caller can access the unsafe object on which the - // call is being made, and the actual function we're about to call. - if (!CanCallerAccess(cx, safeObj, unsafeObj) || - !CanCallerAccess(cx, nsnull, funToCall)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - JSObject *scopeChain = GetScopeChainForSafeCall(cx, safeObj); - if (!scopeChain) { - return JS_FALSE; - } - - { - SafeCallGuard guard(cx, FindObjectPrincipals(cx, safeObj, funToCall)); - - JSObject *scope = JS_GetGlobalForObject(cx, funToCall); - jsval *argv = JS_ARGV(cx, vp); - for (uintN i = 0; i < argc; ++i) { - // NB: Passing NONE for a hint here. - if (!JSVAL_IS_PRIMITIVE(argv[i]) && - !RewrapObject(cx, scope, JSVAL_TO_OBJECT(argv[i]), NONE, &argv[i])) { - return JS_FALSE; - } - } - - jsval v; - if (!RewrapObject(cx, scope, callThisObj, NONE, &v)) { - return JS_FALSE; - } - - if (!js_CallFunctionValueWithFakeFrame(cx, JSVAL_TO_OBJECT(v), scopeChain, - OBJECT_TO_JSVAL(funToCall), - argc, argv, vp)) { - return JS_FALSE; - } - } - - return WrapJSValue(cx, safeObj, *vp, vp); -} - -static JSBool -XPC_SJOW_Construct(JSContext *cx, uintN argc, jsval *vp) -{ - if (argc < 1) { - return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); - } - - JSObject *scope = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); - - jsval *argv = JS_ARGV(cx, vp); - if (JSVAL_IS_PRIMITIVE(argv[0])) { - if (JS_IsConstructing(cx, vp)) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - JS_SET_RVAL(cx, vp, argv[0]); - return JS_TRUE; - } - - JSObject *objToWrap = UnsafeUnwrapSecurityWrapper(cx, JSVAL_TO_OBJECT(argv[0])); - if (!objToWrap) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - - // Check that the caller can access the unsafe object. - if (!CanCallerAccess(cx, nsnull, objToWrap)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - return WrapObject(cx, scope, OBJECT_TO_JSVAL(objToWrap), vp); -} - -static JSBool -XPC_SJOW_Create(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - NS_ASSERTION(GetUnsafeObject(cx, callee), "How'd we get here?"); - JSObject *unsafeObj = GetUnsafeObject(cx, callee); - - // Check that the caller can access the unsafe object. - if (!CanCallerAccess(cx, callee, unsafeObj)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - JSObject *scopeChain = GetScopeChainForSafeCall(cx, callee); - if (!scopeChain) { - return JS_FALSE; - } - - { - SafeCallGuard guard(cx, FindObjectPrincipals(cx, callee, unsafeObj)); - if (!guard.ready()) { - return JS_FALSE; - } - - JSAutoEnterCompartment ac; - if (!ac.enter(cx, unsafeObj)) { - return JS_FALSE; - } - - JSObject *scope = JS_GetGlobalForObject(cx, unsafeObj); - jsval *argv = JS_ARGV(cx, vp); - for (uintN i = 0; i < argc; ++i) { - // NB: Passing NONE for a hint here. - if (!JSVAL_IS_PRIMITIVE(argv[i]) && - !RewrapObject(cx, scope, JSVAL_TO_OBJECT(argv[i]), NONE, &argv[i])) { - return JS_FALSE; - } - } - - JSObject *obj = JS_New(cx, unsafeObj, argc, argv); - if (!obj) - return JS_FALSE; - - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); - } - - return WrapJSValue(cx, callee, *vp, vp); -} - -static JSBool -XPC_SJOW_Equality(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) -{ - if (JSVAL_IS_PRIMITIVE(*valp)) { - *bp = JS_FALSE; - } else { - JSObject *unsafeObj = GetUnsafeObject(cx, obj); - - JSObject *other = JSVAL_TO_OBJECT(*valp); - JSObject *otherUnsafe = GetUnsafeObject(cx, other); - - // An object is equal to a SJOW if: - // - The other object is the same SJOW. - // - The other object is the object that this SJOW is wrapping. - // - The other object is a SJOW wrapping the same object as this one. - // or - // - Both objects somehow wrap the same native object. - if (obj == other || unsafeObj == other || - (unsafeObj && unsafeObj == otherUnsafe)) { - *bp = JS_TRUE; - } else { - nsISupports *objIdentity = XPC_GetIdentityObject(cx, obj); - nsISupports *otherIdentity = XPC_GetIdentityObject(cx, other); - - *bp = objIdentity && objIdentity == otherIdentity; - } - } - - return JS_TRUE; -} - -static JSObject * -XPC_SJOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) -{ - obj = FindSafeObject(obj); - NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); - - JSObject *unsafeObj = GetUnsafeObject(cx, obj); - if (!unsafeObj) { - ThrowException(NS_ERROR_INVALID_ARG, cx); - - return nsnull; - } - - // Check that the caller can access the unsafe object. - if (!CanCallerAccess(cx, obj, unsafeObj)) { - // CanCallerAccess() already threw for us. - return nsnull; - } - - // Create our dummy SJOW. - JSObject *wrapperIter = - JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SJOWClass), nsnull, - JS_GetGlobalForObject(cx, obj)); - if (!wrapperIter) { - return nsnull; - } - - if (!JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sWrappedObjSlot, - OBJECT_TO_JSVAL(unsafeObj)) || - !JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sFlagsSlot, - JSVAL_ZERO)) { - return nsnull; - } - - js::AutoObjectRooter tvr(cx, wrapperIter); - - // Initialize the wrapper. - return XPCWrapper::CreateIteratorObj(cx, wrapperIter, obj, unsafeObj, - keysonly); -} - -static JSObject * -XPC_SJOW_WrappedObject(JSContext *cx, JSObject *obj) -{ - return GetUnsafeObject(cx, obj); -} - -static JSBool -XPC_SJOW_toString(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) { - return JS_FALSE; - } - - obj = FindSafeObject(obj); - if (!obj) { - return ThrowException(NS_ERROR_INVALID_ARG, cx); - } - - JSObject *unsafeObj = GetUnsafeObject(cx, obj); - - if (!unsafeObj) { - // No unsafe object, nothing to stringify here, return "[object - // XPCSafeJSObjectWrapper]" so callers know what they're looking - // at. - - JSString *str = JS_NewStringCopyZ(cx, "[object XPCSafeJSObjectWrapper]"); - if (!str) { - return JS_FALSE; - } - - JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); - return JS_TRUE; - } - - // Check that the caller can access the unsafe object. - if (!CanCallerAccess(cx, obj, unsafeObj)) { - // CanCallerAccess() already threw for us. - return JS_FALSE; - } - - { - SafeCallGuard guard(cx, FindObjectPrincipals(cx, obj, unsafeObj)); - if (!guard.ready()) { - return JS_FALSE; - } - - JSString *str = JS_ValueToString(cx, OBJECT_TO_JSVAL(unsafeObj)); - if (!str) { - return JS_FALSE; - } - JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); - } - return JS_TRUE; -} diff --git a/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp b/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp deleted file mode 100644 index c9d1a8b92b96..000000000000 --- a/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp +++ /dev/null @@ -1,704 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 sw=2 et tw=78 sts=2: */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * The Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Blake Kaplan (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "xpcprivate.h" -#include "nsDOMError.h" -#include "jsdbgapi.h" -#include "jscntxt.h" // For js::AutoValueRooter. -#include "XPCNativeWrapper.h" -#include "XPCWrapper.h" - -// This file implements a wrapper around trusted objects that allows them to -// be safely injected into untrusted code. - -static JSBool -XPC_SOW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_SOW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_SOW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_SOW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -static JSBool -XPC_SOW_Enumerate(JSContext *cx, JSObject *obj); - -static JSBool -XPC_SOW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp); - -static JSBool -XPC_SOW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp); - -static JSBool -XPC_SOW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp); - -static JSBool -XPC_SOW_HasInstance(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp); - -static JSBool -XPC_SOW_Equality(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp); - -static JSObject * -XPC_SOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly); - -static JSObject * -XPC_SOW_WrappedObject(JSContext *cx, JSObject *obj); - -using namespace XPCWrapper; - -// Throws an exception on context |cx|. -static inline JSBool -ThrowException(nsresult rv, JSContext *cx) -{ - return DoThrowException(rv, cx); -} - -static const char prefix[] = "chrome://global/"; - -namespace SystemOnlyWrapper { - -js::Class SOWClass = { - "SystemOnlyWrapper", - JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(XPCWrapper::sNumSlots), - JS_VALUEIFY(js::PropertyOp, XPC_SOW_AddProperty), - JS_VALUEIFY(js::PropertyOp, XPC_SOW_DelProperty), - JS_VALUEIFY(js::PropertyOp, XPC_SOW_GetProperty), - JS_VALUEIFY(js::PropertyOp, XPC_SOW_SetProperty), - XPC_SOW_Enumerate, - (JSResolveOp)XPC_SOW_NewResolve, - JS_VALUEIFY(js::ConvertOp, XPC_SOW_Convert), - nsnull, // finalize - nsnull, // reserved0 - JS_VALUEIFY(js::CheckAccessOp, XPC_SOW_CheckAccess), - nsnull, // call - nsnull, // construct - nsnull, // xdrObject - JS_VALUEIFY(js::HasInstanceOp, XPC_SOW_HasInstance), - nsnull, // mark - - // ClassExtension - { - JS_VALUEIFY(js::EqualityOp, XPC_SOW_Equality), - nsnull, // outerObject - nsnull, // innerObject - XPC_SOW_Iterator, - XPC_SOW_WrappedObject - } -}; - -JSBool -WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp) -{ - // Slim wrappers don't expect to be wrapped, so morph them to fat wrappers - // if we're about to wrap one. - JSObject *innerObj = JSVAL_TO_OBJECT(v); - if (IS_SLIM_WRAPPER(innerObj) && !MorphSlimWrapper(cx, innerObj)) { - return ThrowException(NS_ERROR_FAILURE, cx); - } - - JSObject *wrapperObj = - JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SOWClass), NULL, parent); - if (!wrapperObj) { - return JS_FALSE; - } - - *vp = OBJECT_TO_JSVAL(wrapperObj); - js::AutoObjectRooter tvr(cx, wrapperObj); - - if (!JS_SetReservedSlot(cx, wrapperObj, sWrappedObjSlot, v) || - !JS_SetReservedSlot(cx, wrapperObj, sFlagsSlot, JSVAL_ZERO)) { - return JS_FALSE; - } - - return JS_TRUE; -} - -JSBool -MakeSOW(JSContext *cx, JSObject *obj) -{ -#ifdef DEBUG - { - js::Class *clasp = obj->getClass(); - NS_ASSERTION(clasp != &SystemOnlyWrapper::SOWClass && - clasp != &XPCCrossOriginWrapper::XOWClass, - "bad call"); - } -#endif - - jsval flags; - return JS_GetReservedSlot(cx, obj, sFlagsSlot, &flags) && - JS_SetReservedSlot(cx, obj, sFlagsSlot, - INT_TO_JSVAL(JSVAL_TO_INT(flags) | FLAG_SOW)); -} - - -// If you change this code, change also nsContentUtils::CanAccessNativeAnon()! -JSBool -AllowedToAct(JSContext *cx, jsid id) -{ - // TODO bug 508928: Refactor this with the XOW security checking code. - nsIScriptSecurityManager *ssm = GetSecurityManager(); - if (!ssm) { - return JS_TRUE; - } - - JSStackFrame *fp; - nsIPrincipal *principal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp); - if (!principal) { - return ThrowException(NS_ERROR_UNEXPECTED, cx); - } - - if (!fp) { - if (!JS_FrameIterator(cx, &fp)) { - // No code at all is running. So we must be arriving here as the result - // of C++ code asking us to do something. Allow access. - return JS_TRUE; - } - - // Some code is running, we can't make the assumption, as above, but we - // can't use a native frame, so clear fp. - fp = nsnull; - } else if (!JS_IsScriptFrame(cx, fp)) { - fp = nsnull; - } - - PRBool privileged; - if (NS_SUCCEEDED(ssm->IsSystemPrincipal(principal, &privileged)) && - privileged) { - // Chrome things are allowed to touch us. - return JS_TRUE; - } - - // XXX HACK EWW! Allow chrome://global/ access to these things, even - // if they've been cloned into less privileged contexts. - const char *filename; - if (fp && - (filename = JS_GetFrameScript(cx, fp)->filename) && - !strncmp(filename, prefix, NS_ARRAY_LENGTH(prefix) - 1)) { - return JS_TRUE; - } - - // Before we throw, check for UniversalXPConnect. - nsresult rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged); - if (NS_SUCCEEDED(rv) && privileged) { - return JS_TRUE; - } - - if (JSID_IS_VOID(id)) { - ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); - } else { - // TODO Localize me? - jsval idval; - JSString *str; - if (JS_IdToValue(cx, id, &idval) && (str = JS_ValueToString(cx, idval))) { - JS_ReportError(cx, "Permission denied to access property '%hs' from a non-chrome context", - JS_GetStringChars(str)); - } - } - - return JS_FALSE; -} - -JSBool -CheckFilename(JSContext *cx, jsid id, JSStackFrame *fp) -{ - const char *filename; - if (fp && - (filename = JS_GetFrameScript(cx, fp)->filename) && - !strncmp(filename, prefix, NS_ARRAY_LENGTH(prefix) - 1)) { - return JS_TRUE; - } - - if (JSID_IS_VOID(id)) { - ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); - } else { - jsval idval; - JSString *str; - if (JS_IdToValue(cx, id, &idval) && (str = JS_ValueToString(cx, idval))) { - JS_ReportError(cx, "Permission denied to access property '%hs' from a non-chrome context", - JS_GetStringChars(str)); - } - } - - return JS_FALSE; -} - -} // namespace SystemOnlyWrapper - -using namespace SystemOnlyWrapper; - -// Like GetWrappedObject, but works on other types of wrappers, too. -// TODO Move to XPCWrapper? -static inline JSObject * -GetWrappedJSObject(JSContext *cx, JSObject *obj) -{ - if (JSObjectOp op = obj->getClass()->ext.wrappedObject) - return op(cx, obj); - return obj; -} - -// Get the (possibly nonexistent) SOW off of an object -static inline -JSObject * -GetWrapper(JSObject *obj) -{ - while (obj->getClass() != &SOWClass) { - obj = obj->getProto(); - if (!obj) { - break; - } - } - - return obj; -} - -static inline -JSObject * -GetWrappedObject(JSContext *cx, JSObject *wrapper) -{ - return UnwrapGeneric(cx, &SOWClass, wrapper); -} - -static JSBool -XPC_SOW_FunctionWrapper(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - if (!AllowedToAct(cx, JSID_VOID)) { - return JS_FALSE; - } - - JSObject *wrappedObj; - - // Allow 'this' to be either a SOW, in which case we unwrap it or something - // that isn't a SOW. We disallow invalid SOWs that have no wrapped object. - // We do this so that it's possible to use this function with .call on - // related objects that are not system only. - - wrappedObj = GetWrapper(obj); - if (wrappedObj) { - wrappedObj = GetWrappedObject(cx, wrappedObj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - } else { - wrappedObj = obj; - } - - JSObject *funObj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); - jsval funToCall; - if (!JS_GetReservedSlot(cx, funObj, eWrappedFunctionSlot, &funToCall)) { - return JS_FALSE; - } - - return JS_CallFunctionValue(cx, wrappedObj, funToCall, argc, JS_ARGV(cx, vp), vp); -} - -JSBool -XPC_SOW_WrapFunction(JSContext *cx, JSObject *outerObj, JSObject *funobj, - jsval *rval) -{ - jsval funobjVal = OBJECT_TO_JSVAL(funobj); - JSFunction *wrappedFun = - reinterpret_cast(xpc_GetJSPrivate(funobj)); - JSNative native = JS_GetFunctionNative(cx, wrappedFun); - if (!native || native == XPC_SOW_FunctionWrapper) { - *rval = funobjVal; - return JS_TRUE; - } - - JSFunction *funWrapper = - JS_NewFunction(cx, XPC_SOW_FunctionWrapper, - JS_GetFunctionArity(wrappedFun), 0, - JS_GetGlobalForObject(cx, outerObj), - JS_GetFunctionName(wrappedFun)); - if (!funWrapper) { - return JS_FALSE; - } - - JSObject *funWrapperObj = JS_GetFunctionObject(funWrapper); - *rval = OBJECT_TO_JSVAL(funWrapperObj); - - return JS_SetReservedSlot(cx, funWrapperObj, - eWrappedFunctionSlot, - funobjVal); -} - -static JSBool -XPC_SOW_RewrapValue(JSContext *cx, JSObject *wrapperObj, jsval *vp) -{ - jsval v = *vp; - if (JSVAL_IS_PRIMITIVE(v)) { - return JS_TRUE; - } - - JSObject *obj = JSVAL_TO_OBJECT(v); - - if (JS_ObjectIsFunction(cx, obj)) { - // NB: The JS_ValueToFunction call is guaranteed to succeed. - JSNative native = JS_GetFunctionNative(cx, JS_ValueToFunction(cx, v)); - - // This is really tricky! We need to unwrap this when calling native - // functions to preserve their assumptions, but *not* when calling - // scripted functions, since they expect 'this' to be wrapped. - if (!native) { - return JS_TRUE; - } - - if (native == XPC_SOW_FunctionWrapper) { - // If this is a system function wrapper, make sure its ours, otherwise, - // its prototype could come from the wrong scope. - if (wrapperObj->getProto() == obj->getParent()) { - return JS_TRUE; - } - - // It isn't ours, rewrap the wrapped function. - if (!JS_GetReservedSlot(cx, obj, eWrappedFunctionSlot, &v)) { - return JS_FALSE; - } - obj = JSVAL_TO_OBJECT(v); - } - - return XPC_SOW_WrapFunction(cx, wrapperObj, obj, vp); - } - - if (obj->getClass() == &SOWClass) { - // We are extra careful about content-polluted wrappers here. I don't know - // if it's possible to reach them through objects that we wrap, but figuring - // that out is more expensive (and harder) than simply checking and - // rewrapping here. - if (wrapperObj->getParent() == obj->getParent()) { - // Already wrapped. - return JS_TRUE; - } - - obj = GetWrappedObject(cx, obj); - if (!obj) { - // XXX Can this happen? - *vp = JSVAL_NULL; - return JS_TRUE; - } - v = *vp = OBJECT_TO_JSVAL(obj); - } - - return WrapObject(cx, wrapperObj->getParent(), v, vp); -} - -static JSBool -XPC_SOW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - NS_ASSERTION(obj->getClass() == &SOWClass, "Wrong object"); - - jsval resolving; - if (!JS_GetReservedSlot(cx, obj, sFlagsSlot, &resolving)) { - return JS_FALSE; - } - - if (HAS_FLAGS(resolving, FLAG_RESOLVING)) { - // Allow us to define a property on ourselves. - return JS_TRUE; - } - - if (!AllowedToAct(cx, id)) { - return JS_FALSE; - } - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return JS_TRUE; - } - - return AddProperty(cx, obj, JS_TRUE, wrappedObj, id, vp); -} - -static JSBool -XPC_SOW_DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - if (!AllowedToAct(cx, id)) { - return JS_FALSE; - } - - return DelProperty(cx, wrappedObj, id, vp); -} - -static JSBool -XPC_SOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp, - JSBool isSet) -{ - obj = GetWrapper(obj); - if (!obj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - if (!AllowedToAct(cx, id)) { - return JS_FALSE; - } - - js::AutoArrayRooter tvr(cx, 1, vp); - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); - } - - if (isSet && id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTO)) { - // No setting __proto__ on my object. - return ThrowException(NS_ERROR_INVALID_ARG, cx); // XXX better error message - } - - JSBool ok = isSet - ? JS_SetPropertyById(cx, wrappedObj, id, vp) - : JS_GetPropertyById(cx, wrappedObj, id, vp); - if (!ok) { - return JS_FALSE; - } - - return XPC_SOW_RewrapValue(cx, obj, vp); -} - -static JSBool -XPC_SOW_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_SOW_GetOrSetProperty(cx, obj, id, vp, JS_FALSE); -} - -static JSBool -XPC_SOW_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return XPC_SOW_GetOrSetProperty(cx, obj, id, vp, JS_TRUE); -} - -static JSBool -XPC_SOW_Enumerate(JSContext *cx, JSObject *obj) -{ - obj = GetWrapper(obj); - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // Nothing to enumerate. - return JS_TRUE; - } - - if (!AllowedToAct(cx, JSID_VOID)) { - return JS_FALSE; - } - - return Enumerate(cx, obj, wrappedObj); -} - -static JSBool -XPC_SOW_NewResolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp) -{ - obj = GetWrapper(obj); - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // No wrappedObj means that this is probably the prototype. - *objp = nsnull; - return JS_TRUE; - } - - if (!AllowedToAct(cx, id)) { - return JS_FALSE; - } - - return NewResolve(cx, obj, JS_FALSE, wrappedObj, id, flags, objp); -} - -static JSBool -XPC_SOW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - if (!AllowedToAct(cx, JSID_VOID)) { - return JS_FALSE; - } - - // Don't do any work to convert to object. - if (type == JSTYPE_OBJECT) { - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - // Converting the prototype to something. - // XXX Can this happen? - return JS_TRUE; - } - - return wrappedObj->getJSClass()->convert(cx, wrappedObj, type, vp); -} - -static JSBool -XPC_SOW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp) -{ - // Simply forward checkAccess to our wrapped object. It's already expecting - // untrusted things to ask it about accesses. - - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - - uintN junk; - return JS_CheckAccess(cx, wrappedObj, id, mode, vp, &junk); -} - -static JSBool -XPC_SOW_HasInstance(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) -{ - if (!AllowedToAct(cx, JSID_VOID)) { - return JS_FALSE; - } - - JSObject *iface = GetWrappedObject(cx, obj); - if (!iface) { - *bp = JS_FALSE; - return JS_TRUE; - } - - JSClass *clasp = iface->getJSClass(); - - *bp = JS_FALSE; - if (!clasp->hasInstance) { - return JS_TRUE; - } - - // Prematurely unwrap the left hand side. This isn't necessary, but could be - // faster than waiting until XPCWrappedNative::GetWrappedNativeOfJSObject to - // do it. - jsval v = *valp; - if (!JSVAL_IS_PRIMITIVE(v)) { - JSObject *test = JSVAL_TO_OBJECT(v); - - // GetWrappedObject does a class check. - test = GetWrappedObject(cx, test); - if (test) { - v = OBJECT_TO_JSVAL(test); - } - } - - return clasp->hasInstance(cx, iface, &v, bp); -} - -static JSBool -XPC_SOW_Equality(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) -{ - // Delegate to our wrapped object. - jsval v = *valp; - if (JSVAL_IS_PRIMITIVE(v)) { - *bp = JS_FALSE; - return JS_TRUE; - } - - if (obj == JSVAL_TO_OBJECT(v)) { - *bp = JS_TRUE; - return JS_TRUE; - } - - JSObject *lhs = GetWrappedObject(cx, obj); - JSObject *rhs = GetWrappedJSObject(cx, JSVAL_TO_OBJECT(v)); - if (lhs == rhs) { - *bp = JS_TRUE; - return JS_TRUE; - } - - if (lhs) { - if (JSEqualityOp op = js::Jsvalify(lhs->getClass()->ext.equality)) { - jsval rhsVal = OBJECT_TO_JSVAL(rhs); - return op(cx, lhs, &rhsVal, bp); - } - } - - // We know rhs is non-null. - if (JSEqualityOp op = js::Jsvalify(rhs->getClass()->ext.equality)) { - jsval lhsVal = OBJECT_TO_JSVAL(lhs); - return op(cx, rhs, &lhsVal, bp); - } - - *bp = JS_FALSE; - return JS_TRUE; -} - -static JSObject * -XPC_SOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) -{ - JSObject *wrappedObj = GetWrappedObject(cx, obj); - if (!wrappedObj) { - ThrowException(NS_ERROR_INVALID_ARG, cx); - return nsnull; - } - - JSObject *wrapperIter = JS_NewObject(cx, js::Jsvalify(&SOWClass), nsnull, - JS_GetGlobalForObject(cx, obj)); - if (!wrapperIter) { - return nsnull; - } - - js::AutoObjectRooter tvr(cx, wrapperIter); - - // Initialize our SOW. - jsval v = OBJECT_TO_JSVAL(wrappedObj); - if (!JS_SetReservedSlot(cx, wrapperIter, sWrappedObjSlot, v) || - !JS_SetReservedSlot(cx, wrapperIter, sFlagsSlot, JSVAL_ZERO)) { - return nsnull; - } - - return CreateIteratorObj(cx, wrapperIter, obj, wrappedObj, keysonly); -} - -static JSObject * -XPC_SOW_WrappedObject(JSContext *cx, JSObject *obj) -{ - return GetWrappedObject(cx, obj); -} diff --git a/js/src/xpconnect/src/XPCWrapper.cpp b/js/src/xpconnect/src/XPCWrapper.cpp index 3af9c615a625..2c2162538f60 100644 --- a/js/src/xpconnect/src/XPCWrapper.cpp +++ b/js/src/xpconnect/src/XPCWrapper.cpp @@ -41,26 +41,89 @@ * ***** END LICENSE BLOCK ***** */ #include "XPCWrapper.h" -#include "XPCNativeWrapper.h" -#include "nsPIDOMWindow.h" -#include "jswrapper.h" -#include "XrayWrapper.h" #include "AccessCheck.h" +#include "WrapperFactory.h" + +namespace XPCNativeWrapper { + +static inline +JSBool +ThrowException(nsresult ex, JSContext *cx) +{ + XPCThrower::Throw(ex, cx); + + return JS_FALSE; +} + +static JSBool +UnwrapNW(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc != 1) { + return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); + } + + jsval v = JS_ARGV(cx, vp)[0]; + if (JSVAL_IS_PRIMITIVE(v)) { + return ThrowException(NS_ERROR_INVALID_ARG, cx); + } + + JSObject *obj = JSVAL_TO_OBJECT(v); + if (!obj->isWrapper()) { + JS_SET_RVAL(cx, vp, v); + return JS_TRUE; + } + + if (xpc::WrapperFactory::IsXrayWrapper(obj)) { + return JS_GetProperty(cx, obj, "wrappedJSObject", vp); + } + + JS_SET_RVAL(cx, vp, v); + return JS_TRUE; +} + +static JSBool +XrayWrapperConstructor(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); + } + + if (JSVAL_IS_PRIMITIVE(vp[2])) { + return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); + } + + JSObject *obj = JSVAL_TO_OBJECT(vp[2]); + if (!obj->isWrapper()) { + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + + obj = obj->unwrap(); + + *vp = OBJECT_TO_JSVAL(obj); + return JS_WrapValue(cx, vp); +} + +// static +PRBool +AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject) +{ + JSObject *xpcnativewrapper = + JS_DefineFunction(ccx, aGlobalObject, "XPCNativeWrapper", + XrayWrapperConstructor, 1, + JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_STUB_GSOPS); + if (!xpcnativewrapper) { + return PR_FALSE; + } + + return JS_DefineFunction(ccx, xpcnativewrapper, "unwrap", UnwrapNW, 1, + JSPROP_READONLY | JSPROP_PERMANENT) != nsnull; +} + +} namespace XPCWrapper { -const PRUint32 sWrappedObjSlot = 1; -const PRUint32 sFlagsSlot = 0; -const PRUint32 sNumSlots = 2; -JSNative sEvalNative = nsnull; - -const PRUint32 FLAG_RESOLVING = 0x1; -const PRUint32 FLAG_SOW = 0x2; -const PRUint32 LAST_FLAG = FLAG_SOW; - -const PRUint32 sSecMgrSetProp = nsIXPCSecurityManager::ACCESS_SET_PROPERTY; -const PRUint32 sSecMgrGetProp = nsIXPCSecurityManager::ACCESS_GET_PROPERTY; - JSObject * Unwrap(JSContext *cx, JSObject *wrapper) { @@ -70,189 +133,9 @@ Unwrap(JSContext *cx, JSObject *wrapper) return wrapper->unwrap(); } - js::Class *clasp = wrapper->getClass(); - if (clasp == &XPCCrossOriginWrapper::XOWClass) { - return UnwrapXOW(cx, wrapper); - } - - if (XPCNativeWrapper::IsNativeWrapperClass(clasp)) { - XPCWrappedNative *wrappedObj; - if (!XPCNativeWrapper::GetWrappedNative(cx, wrapper, &wrappedObj) || - !wrappedObj) { - return nsnull; - } - - return wrappedObj->GetFlatJSObject(); - } - - if (clasp == &XPCSafeJSObjectWrapper::SJOWClass) { - JSObject *wrappedObj = - XPCSafeJSObjectWrapper::GetUnsafeObject(cx, wrapper); - - if (NS_FAILED(XPCCrossOriginWrapper::CanAccessWrapper(cx, nsnull, wrappedObj, nsnull))) { - JS_ClearPendingException(cx); - - return nsnull; - } - - return wrappedObj; - } - - if (clasp == &SystemOnlyWrapper::SOWClass) { - return UnwrapSOW(cx, wrapper); - } - if (clasp == &ChromeObjectWrapper::COWClass) { - return UnwrapCOW(cx, wrapper); - } - return nsnull; } -static void -IteratorFinalize(JSContext *cx, JSObject *obj) -{ - jsval v; - JS_GetReservedSlot(cx, obj, 0, &v); - - JSIdArray *ida = reinterpret_cast(JSVAL_TO_PRIVATE(v)); - if (ida) { - JS_DestroyIdArray(cx, ida); - } -} - -static JSBool -IteratorNext(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *obj; - jsval v; - - obj = JS_THIS_OBJECT(cx, vp); - if (!obj) - return JS_FALSE; - - JS_GetReservedSlot(cx, obj, 0, &v); - JSIdArray *ida = reinterpret_cast(JSVAL_TO_PRIVATE(v)); - if (!ida) { - return JS_ThrowStopIteration(cx); - } - - JS_GetReservedSlot(cx, obj, 1, &v); - jsint idx = JSVAL_TO_INT(v); - - if (idx == ida->length) { - return JS_ThrowStopIteration(cx); - } - - JS_GetReservedSlot(cx, obj, 2, &v); - jsid id = ida->vector[idx++]; - if (JSVAL_TO_BOOLEAN(v)) { - JSString *str; - if (!JS_IdToValue(cx, id, &v) || - !(str = JS_ValueToString(cx, v))) { - return JS_FALSE; - } - - *vp = STRING_TO_JSVAL(str); - } else { - // We need to return an [id, value] pair. - if (!JS_GetPropertyById(cx, obj->getParent(), id, vp)) { - return JS_FALSE; - } - } - - JS_SetReservedSlot(cx, obj, 1, INT_TO_JSVAL(idx)); - return JS_TRUE; -} - -static JSObject * -IteratorIterator(JSContext *, JSObject *obj, JSBool) -{ - return obj; -} - -static js::Class IteratorClass = { - "Wrapper iterator", - JSCLASS_HAS_RESERVED_SLOTS(3), - js::PropertyStub, // addProperty - js::PropertyStub, // delProperty - js::PropertyStub, // getProperty - js::PropertyStub, // setProperty - JS_EnumerateStub, - JS_ResolveStub, - js::ConvertStub, - IteratorFinalize, - nsnull, // reserved0 - nsnull, // checkAccess - nsnull, // call - nsnull, // construct - nsnull, // xdrObject - nsnull, // hasInstance - nsnull, // mark - - // ClassExtension - { - nsnull, // equality - nsnull, // outerObject - nsnull, // innerObject - IteratorIterator, - nsnull, // wrappedObject - } -}; - -void -CheckWindow(XPCWrappedNative *wn) -{ - JSClass *clasp = wn->GetFlatJSObject()->getJSClass(); - - // Censor objects that can't be windows. - switch (clasp->name[0]) { - case 'C': // ChromeWindow? - if (clasp->name[1] != 'h') { - return; - } - - break; - case 'M': // ModalContentWindow - break; - case 'W': // Window - break; - default: - return; - } - - nsCOMPtr pwin(do_QueryWrappedNative(wn)); - if (!pwin || pwin->IsInnerWindow()) { - return; - } - - pwin->EnsureInnerWindow(); -} - -JSBool -RewrapObject(JSContext *cx, JSObject *scope, JSObject *obj, WrapperType hint, - jsval *vp) -{ - obj = UnsafeUnwrapSecurityWrapper(cx, obj); - if (!obj) { - // A wrapper wrapping NULL (such as XPCNativeWrapper.prototype). - *vp = JSVAL_NULL; - return JS_TRUE; - } - - XPCWrappedNativeScope *nativescope = - XPCWrappedNativeScope::FindInJSObjectScope(cx, scope); - XPCWrappedNative *wn; - WrapperType answer = nativescope->GetWrapperFor(cx, obj, hint, &wn); - - *vp = OBJECT_TO_JSVAL(obj); - if (answer == NONE) { - return JS_TRUE; - } - - - return CreateWrapperFromType(cx, scope, wn, answer, vp); -} - JSObject * UnsafeUnwrapSecurityWrapper(JSContext *cx, JSObject *obj) { @@ -260,815 +143,7 @@ UnsafeUnwrapSecurityWrapper(JSContext *cx, JSObject *obj) return obj->unwrap(); } - if (IsSecurityWrapper(obj)) { - jsval v; - JS_GetReservedSlot(cx, obj, sWrappedObjSlot, &v); - NS_ASSERTION(!JSVAL_IS_PRIMITIVE(v), "bad object"); - return JSVAL_TO_OBJECT(v); - } - - if (XPCNativeWrapper::IsNativeWrapper(obj)) { - XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); - if (!wn) { - return nsnull; - } - - return wn->GetFlatJSObject(); - } - return obj; } -JSBool -CreateWrapperFromType(JSContext *cx, JSObject *scope, XPCWrappedNative *wn, - WrapperType hint, jsval *vp) -{ -#ifdef DEBUG - NS_ASSERTION(!wn || wn->GetFlatJSObject() == JSVAL_TO_OBJECT(*vp), - "bad wrapped native"); -#endif - - JSObject *obj = JSVAL_TO_OBJECT(*vp); - - if ((hint & XPCNW) && !wn) { - // We know that this is a WN, otherwise the hint would not be XPCNW. - wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, obj); - if (!wn) { - return JS_FALSE; - } - } - - if (hint == XOW) { - // NB: This morphs. - if (!XPCCrossOriginWrapper::WrapObject(cx, scope, vp, wn)) { - return JS_FALSE; - } - - return JS_TRUE; - } - - if (hint == XPCNW_IMPLICIT) { - JSObject *wrapper; - if (!(wrapper = XPCNativeWrapper::GetNewOrUsed(cx, wn, scope, nsnull))) { - return JS_FALSE; - } - - *vp = OBJECT_TO_JSVAL(wrapper); - return JS_TRUE; - } - - if (hint & XPCNW_EXPLICIT) { - if (!XPCNativeWrapper::CreateExplicitWrapper(cx, wn, vp)) { - return JS_FALSE; - } - } else if (hint & SJOW) { - if (!XPCSafeJSObjectWrapper::WrapObject(cx, scope, *vp, vp)) { - return JS_FALSE; - } - } else if (hint & COW) { - if (!ChromeObjectWrapper::WrapObject(cx, scope, *vp, vp)) { - return JS_FALSE; - } - } - - if (hint & SOW) { - if (OBJECT_TO_JSVAL(obj) == *vp) { - if (!SystemOnlyWrapper::WrapObject(cx, scope, *vp, vp)) { - return JS_FALSE; - } - } else { - if (!SystemOnlyWrapper::MakeSOW(cx, JSVAL_TO_OBJECT(*vp))) { - return JS_FALSE; - } - } - } - - return JS_TRUE; -} - -static JSObject * -FinishCreatingIterator(JSContext *cx, JSObject *iterObj, JSBool keysonly) -{ - JSIdArray *ida = JS_Enumerate(cx, iterObj); - if (!ida) { - return nsnull; - } - - // Initialize iterObj. - if (!JS_DefineFunction(cx, iterObj, "next", IteratorNext, 0, 0)) { - return nsnull; - } - - if (!JS_SetReservedSlot(cx, iterObj, 0, PRIVATE_TO_JSVAL(ida)) || - !JS_SetReservedSlot(cx, iterObj, 1, JSVAL_ZERO) || - !JS_SetReservedSlot(cx, iterObj, 2, BOOLEAN_TO_JSVAL(keysonly))) { - return nsnull; - } - - if (!JS_SetPrototype(cx, iterObj, nsnull)) { - return nsnull; - } - - return iterObj; -} - -JSObject * -CreateIteratorObj(JSContext *cx, JSObject *tempWrapper, - JSObject *wrapperObj, JSObject *innerObj, - JSBool keysonly) -{ - // This is rather ugly: we want to use the trick seen in Enumerate, - // where we use our wrapper's resolve hook to determine if we should - // enumerate a given property. However, we don't want to pollute the - // identifiers with a next method, so we create an object that - // delegates (via the __proto__ link) to the wrapper. - - JSObject *iterObj = - JS_NewObjectWithGivenProto(cx, js::Jsvalify(&IteratorClass), tempWrapper, wrapperObj); - if (!iterObj) { - return nsnull; - } - - js::AutoObjectRooter tvr(cx, iterObj); - - // Do this sooner rather than later to avoid complications in - // IteratorFinalize. - if (!JS_SetReservedSlot(cx, iterObj, 0, PRIVATE_TO_JSVAL(nsnull))) { - return nsnull; - } - - if (XPCNativeWrapper::IsNativeWrapper(wrapperObj)) { - // For native wrappers, expandos on the wrapper itself aren't propagated - // to the wrapped object, so we have to actually iterate the wrapper here. - // In order to do so, we set the prototype of the iter to the wrapper, - // call enumerate, and then re-set the prototype. As we do this, we have - // to protec the temporary wrapper from garbage collection. - - js::AutoObjectRooter tvr(cx, tempWrapper); - if (!JS_SetPrototype(cx, iterObj, wrapperObj) || - !XPCWrapper::Enumerate(cx, iterObj, wrapperObj) || - !JS_SetPrototype(cx, iterObj, tempWrapper)) { - return nsnull; - } - } - - // Start enumerating over all of our properties. - do { - if (!XPCWrapper::Enumerate(cx, iterObj, innerObj)) { - return nsnull; - } - } while ((innerObj = innerObj->getProto()) != nsnull); - - return FinishCreatingIterator(cx, iterObj, keysonly); -} - -static JSBool -SimpleEnumerate(JSContext *cx, JSObject *iterObj, JSObject *properties) -{ - JSIdArray *ida = JS_Enumerate(cx, properties); - if (!ida) { - return JS_FALSE; - } - - for (jsint i = 0, n = ida->length; i < n; ++i) { - if (!JS_DefinePropertyById(cx, iterObj, ida->vector[i], JSVAL_VOID, - nsnull, nsnull, - JSPROP_ENUMERATE | JSPROP_SHARED)) { - return JS_FALSE; - } - } - - JS_DestroyIdArray(cx, ida); - - return JS_TRUE; -} - -JSObject * -CreateSimpleIterator(JSContext *cx, JSObject *scope, JSBool keysonly, - JSObject *propertyContainer) -{ - JSObject *iterObj = JS_NewObjectWithGivenProto(cx, js::Jsvalify(&IteratorClass), - propertyContainer, scope); - if (!iterObj) { - return nsnull; - } - - js::AutoObjectRooter tvr(cx, iterObj); - if (!propertyContainer) { - if (!JS_SetReservedSlot(cx, iterObj, 0, PRIVATE_TO_JSVAL(nsnull)) || - !JS_SetReservedSlot(cx, iterObj, 1, JSVAL_ZERO) || - !JS_SetReservedSlot(cx, iterObj, 2, JSVAL_TRUE)) { - return nsnull; - } - - if (!JS_DefineFunction(cx, iterObj, "next", IteratorNext, 0, 0)) { - return nsnull; - } - - return iterObj; - } - - do { - if (!SimpleEnumerate(cx, iterObj, propertyContainer)) { - return nsnull; - } - } while ((propertyContainer = propertyContainer->getProto())); - - return FinishCreatingIterator(cx, iterObj, keysonly); -} - -JSBool -AddProperty(JSContext *cx, JSObject *wrapperObj, JSBool wantGetterSetter, - JSObject *innerObj, jsid id, jsval *vp) -{ - JSPropertyDescriptor desc; - if (!GetPropertyAttrs(cx, wrapperObj, id, JSRESOLVE_QUALIFIED, - wantGetterSetter, &desc)) { - return JS_FALSE; - } - - NS_ASSERTION(desc.obj == wrapperObj, - "What weird wrapper are we using?"); - - return JS_DefinePropertyById(cx, innerObj, id, *vp, - desc.getter, desc.setter, desc.attrs); -} - -JSBool -DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - if (JSID_IS_STRING(id)) { - JSString *str = JSID_TO_STRING(id); - jschar *chars = ::JS_GetStringChars(str); - size_t length = ::JS_GetStringLength(str); - - return ::JS_DeleteUCProperty2(cx, obj, chars, length, vp); - } - - if (!JSID_IS_INT(id)) { - return DoThrowException(NS_ERROR_NOT_IMPLEMENTED, cx); - } - - return ::JS_DeleteElement2(cx, obj, JSID_TO_INT(id), vp); -} - -JSBool -Enumerate(JSContext *cx, JSObject *wrapperObj, JSObject *innerObj) -{ - // We are being notified of a for-in loop or similar operation on - // this wrapper. Forward to the correct high-level object hook, - // OBJ_ENUMERATE on the unsafe object, called via the JS_Enumerate API. - // Then reflect properties named by the enumerated identifiers from the - // unsafe object to the safe wrapper. - - JSBool ok = JS_TRUE; - - JSIdArray *ida = JS_Enumerate(cx, innerObj); - if (!ida) { - return JS_FALSE; - } - - for (jsint i = 0, n = ida->length; i < n; i++) { - JSObject *pobj; - - // Note: v doesn't need to be rooted because it will be read out of a - // rooted object's slots. - jsval v = JSVAL_VOID; - - // Let our NewResolve hook figure out whether this id should be reflected. - ok = JS_LookupPropertyWithFlagsById(cx, wrapperObj, ida->vector[i], - JSRESOLVE_QUALIFIED, &pobj, &v); - if (!ok) { - break; - } - - if (pobj && pobj != wrapperObj) { - // If the resolution actually happened on a different object, define the - // property here so that we're sure that enumeration picks it up. - ok = JS_DefinePropertyById(cx, wrapperObj, ida->vector[i], JSVAL_VOID, - nsnull, nsnull, JSPROP_ENUMERATE | JSPROP_SHARED); - } - - if (!ok) { - break; - } - } - - JS_DestroyIdArray(cx, ida); - - return ok; -} - -JSBool -NewResolve(JSContext *cx, JSObject *wrapperObj, JSBool wantDetails, - JSObject *innerObj, jsid id, uintN flags, JSObject **objp) -{ - JSPropertyDescriptor desc; - if (!GetPropertyAttrs(cx, innerObj, id, flags, wantDetails, &desc)) { - return JS_FALSE; - } - - if (!desc.obj) { - // Nothing to define. - return JS_TRUE; - } - - desc.value = JSVAL_VOID; - - jsval oldFlags; - if (!::JS_GetReservedSlot(cx, wrapperObj, sFlagsSlot, &oldFlags) || - !::JS_SetReservedSlot(cx, wrapperObj, sFlagsSlot, - INT_TO_JSVAL(JSVAL_TO_INT(oldFlags) | - FLAG_RESOLVING))) { - return JS_FALSE; - } - - JSBool ok = JS_DefinePropertyById(cx, wrapperObj, id, desc.value, - desc.getter, desc.setter, desc.attrs); - - JS_SetReservedSlot(cx, wrapperObj, sFlagsSlot, oldFlags); - - if (ok) { - *objp = wrapperObj; - } - - return ok; -} - -JSBool -ResolveNativeProperty(JSContext *cx, JSObject *wrapperObj, - JSObject *innerObj, XPCWrappedNative *wn, - jsid id, uintN flags, JSObject **objp, - JSBool isNativeWrapper) -{ - // This will do verification and the method lookup for us. - XPCCallContext ccx(JS_CALLER, cx, innerObj, nsnull, id); - - // For "constructor" we don't want to call into the resolve hooks on the - // wrapped native, since that would give the wrong constructor. - if (NATIVE_HAS_FLAG(wn, WantNewResolve) && - id != GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) { - - // Mark ourselves as resolving so our AddProperty hook can do the - // right thing here. - jsval oldFlags; - if (!::JS_GetReservedSlot(cx, wrapperObj, sFlagsSlot, &oldFlags) || - !::JS_SetReservedSlot(cx, wrapperObj, sFlagsSlot, - INT_TO_JSVAL(JSVAL_TO_INT(oldFlags) | - FLAG_RESOLVING))) { - return JS_FALSE; - } - - XPCWrappedNative* oldResolvingWrapper = nsnull; - JSBool allowPropMods = - NATIVE_HAS_FLAG(wn, AllowPropModsDuringResolve); - if (allowPropMods) { - oldResolvingWrapper = ccx.SetResolvingWrapper(wn); - } - - JSBool retval = JS_TRUE; - JSObject* newObj = nsnull; - nsresult rv = wn->GetScriptableInfo()-> - GetCallback()->NewResolve(wn, cx, wrapperObj, id, flags, - &newObj, &retval); - - if (allowPropMods) { - ccx.SetResolvingWrapper(oldResolvingWrapper); - } - - if (!::JS_SetReservedSlot(cx, wrapperObj, sFlagsSlot, oldFlags)) { - return JS_FALSE; - } - - if (NS_FAILED(rv)) { - return DoThrowException(rv, cx); - } - - if (newObj) { - if (isNativeWrapper || newObj == wrapperObj) { -#ifdef DEBUG_XPCNativeWrapper - JSString* strId = ::JS_ValueToString(cx, id); - if (strId) { - NS_ConvertUTF16toUTF8 propName((PRUnichar*)::JS_GetStringChars(strId), - ::JS_GetStringLength(strId)); - printf("Resolved via scriptable hooks for '%s'\n", propName.get()); - } -#endif - // Note that we don't need to preserve the wrapper here, since this is - // not an "expando" property if the scriptable newResolve hook found it. - *objp = newObj; - return retval; - } - - // The scriptable helper resolved this property to a *different* object. - // We don't know what to do for now (this can't currently happen in - // Mozilla) so throw. - // I suspect that we'd need to redo the security check on the new object - // (if it has a different class than the original object) and then call - // ResolveNativeProperty with *that* as the inner object. - return DoThrowException(NS_ERROR_NOT_IMPLEMENTED, cx); - } - } - - if (!JSID_IS_STRING(id)) { - // A non-string id is being resolved. Won't be found here, return - // early. - - MaybePreserveWrapper(cx, wn, flags); - - return JS_TRUE; - } - - // Verify that our jsobject really is a wrapped native. - XPCWrappedNative* wrapper = ccx.GetWrapper(); - if (wrapper != wn || !wrapper->IsValid()) { - NS_ASSERTION(wrapper == wn, "Uh, how did this happen!"); - return DoThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx); - } - - // it would be a big surprise if there is a member without an - // interface :) - XPCNativeInterface* iface = ccx.GetInterface(); - if (!iface) { - // No interface, nothing to resolve. - - MaybePreserveWrapper(cx, wn, flags); - - return JS_TRUE; - } - - // did we find a method/attribute by that name? - XPCNativeMember* member = ccx.GetMember(); - if (!member) { - // No member, nothing to resolve. - - MaybePreserveWrapper(cx, wn, flags); - - return JS_TRUE; - } - - JSString *str = JSID_TO_STRING(id); - if (!str) { - return DoThrowException(NS_ERROR_UNEXPECTED, cx); - } - - // Get (and perhaps lazily create) the member's value (commonly a - // cloneable function). - jsval v; - uintN attrs = JSPROP_ENUMERATE; - JSPropertyOp getter = nsnull; - JSPropertyOp setter = nsnull; - - if (member->IsConstant()) { - if (!member->GetConstantValue(ccx, iface, &v)) { - return DoThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx); - } - } else if (member->IsAttribute()) { - // An attribute is being resolved. Define the property, the value - // will be dealt with in the get/set hooks. Use JSPROP_SHARED to - // avoid entraining last-got or last-set garbage beyond the life - // of the value in the getter or setter call site. - - v = JSVAL_VOID; - attrs |= JSPROP_SHARED; - } else { - // We're dealing with a method member here. Clone a function we can - // use for this object. NB: cx's newborn roots will protect funobj - // and funWrapper and its object from GC. - - jsval funval; - if (!member->NewFunctionObject(ccx, iface, wrapper->GetFlatJSObject(), - &funval)) { - return DoThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx); - } - - AUTO_MARK_JSVAL(ccx, funval); - -#ifdef DEBUG_XPCNativeWrapper - printf("Wrapping function object for %s\n", - ::JS_GetStringBytes(JSVAL_TO_STRING(id))); -#endif - - if (!WrapFunction(cx, wrapperObj, JSVAL_TO_OBJECT(funval), &v, - isNativeWrapper)) { - return JS_FALSE; - } - - // Functions shouldn't have a getter or a setter. Without the wrappers, - // they would live on the prototype (and call its getter), since we don't - // have a prototype, and we need to avoid calling the scriptable helper's - // GetProperty method for this property, stub out the getters and setters - // explicitly. - getter = setter = JS_PropertyStub; - - // Since the XPC_*_NewResolve functions ensure that the method's property - // name is accessible, we set the eAllAccessSlot bit, which indicates to - // XPC_NW_FunctionWrapper that the method is safe to unwrap and call, even - // if XPCNativeWrapper::GetWrappedNative disagrees. - JS_SetReservedSlot(cx, JSVAL_TO_OBJECT(v), eAllAccessSlot, JSVAL_TRUE); - } - - // Make sure v doesn't go away while we mess with it. - AUTO_MARK_JSVAL(ccx, v); - - // XPCNativeWrapper doesn't need to do this. - jsval oldFlags; - if (!isNativeWrapper && - (!::JS_GetReservedSlot(cx, wrapperObj, sFlagsSlot, &oldFlags) || - !::JS_SetReservedSlot(cx, wrapperObj, sFlagsSlot, - INT_TO_JSVAL(JSVAL_TO_INT(oldFlags) | - FLAG_RESOLVING)))) { - return JS_FALSE; - } - - if (!::JS_DefinePropertyById(cx, wrapperObj, id, v, getter, setter, attrs)) { - return JS_FALSE; - } - - if (!isNativeWrapper && - !::JS_SetReservedSlot(cx, wrapperObj, sFlagsSlot, oldFlags)) { - return JS_FALSE; - } - - *objp = wrapperObj; - - return JS_TRUE; -} - -JSBool -GetOrSetNativeProperty(JSContext *cx, JSObject *obj, - XPCWrappedNative *wrappedNative, - jsid id, jsval *vp, JSBool aIsSet, - JSBool isNativeWrapper) -{ - // This will do verification and the method lookup for us. - JSObject *nativeObj = wrappedNative->GetFlatJSObject(); - XPCCallContext ccx(JS_CALLER, cx, nativeObj, nsnull, id); - - if (aIsSet ? NATIVE_HAS_FLAG(wrappedNative, WantSetProperty) : - NATIVE_HAS_FLAG(wrappedNative, WantGetProperty)) { - - jsval v = *vp; - // Note that some sets return random DOM objects (setting - // document.location, say), so we want to rewrap for sets too if v != *vp. - JSBool retval = JS_TRUE; - nsresult rv; - if (aIsSet) { - rv = wrappedNative->GetScriptableCallback()-> - SetProperty(wrappedNative, cx, obj, id, &v, &retval); - } else { - rv = wrappedNative->GetScriptableCallback()-> - GetProperty(wrappedNative, cx, obj, id, &v, &retval); - } - - if (NS_FAILED(rv)) { - return DoThrowException(rv, cx); - } - if (!retval) { - return JS_FALSE; - } - - if (rv == NS_SUCCESS_I_DID_SOMETHING) { - // Make sure v doesn't get collected while we're re-wrapping it. - AUTO_MARK_JSVAL(ccx, v); - -#ifdef DEBUG_XPCNativeWrapper - JSString* strId = ::JS_ValueToString(cx, id); - if (strId) { - NS_ConvertUTF16toUTF8 propName((PRUnichar*)::JS_GetStringChars(strId), - ::JS_GetStringLength(strId)); - printf("%s via scriptable hooks for '%s'\n", - aIsSet ? "Set" : "Got", propName.get()); - } -#endif - - return RewrapIfDeepWrapper(cx, obj, v, vp, isNativeWrapper); - } - } - - if (!JSID_IS_STRING(id)) { - // Not going to be found here - return JS_TRUE; - } - - // Verify that our jsobject really is a wrapped native. - XPCWrappedNative* wrapper = ccx.GetWrapper(); - if (wrapper != wrappedNative || !wrapper->IsValid()) { - NS_ASSERTION(wrapper == wrappedNative, "Uh, how did this happen!"); - return DoThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx); - } - - // it would be a big surprise if there is a member without an - // interface :) - XPCNativeInterface* iface = ccx.GetInterface(); - if (!iface) { - - return JS_TRUE; - } - - // did we find a method/attribute by that name? - XPCNativeMember* member = ccx.GetMember(); - if (!member) { - // No member, no IDL property to expose. - - return JS_TRUE; - } - - if (member->IsConstant()) { - jsval memberval; - if (!member->GetConstantValue(ccx, iface, &memberval)) { - return DoThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx); - } - - // Getting the value of constants is easy, just return the - // value. Setting is not supported (obviously). - if (aIsSet) { - return DoThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx); - } - - *vp = memberval; - - return JS_TRUE; - } - - if (!member->IsAttribute()) { - // Getting the value of a method. Just return and let the value - // from XPC_NW_NewResolve() be used. - - return JS_TRUE; - } - - jsval funval; - if (!member->NewFunctionObject(ccx, iface, wrapper->GetFlatJSObject(), - &funval)) { - return DoThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx); - } - - AUTO_MARK_JSVAL(ccx, funval); - - jsval *argv = nsnull; - uintN argc = 0; - - if (aIsSet) { - if (member->IsReadOnlyAttribute()) { - // Trying to set a property for which there is no setter! - return DoThrowException(NS_ERROR_NOT_AVAILABLE, cx); - } - -#ifdef DEBUG_XPCNativeWrapper - printf("Calling setter for %s\n", - ::JS_GetStringBytes(JSVAL_TO_STRING(id))); -#endif - - argv = vp; - argc = 1; - } else { -#ifdef DEBUG_XPCNativeWrapper - printf("Calling getter for %s\n", - ::JS_GetStringBytes(JSVAL_TO_STRING(id))); -#endif - } - - // Call the getter - jsval v; - if (!::JS_CallFunctionValue(cx, wrapper->GetFlatJSObject(), funval, argc, - argv, &v)) { - return JS_FALSE; - } - - if (aIsSet) { - return JS_TRUE; - } - - { - // Make sure v doesn't get collected while we're re-wrapping it. - AUTO_MARK_JSVAL(ccx, v); - - return RewrapIfDeepWrapper(cx, obj, v, vp, isNativeWrapper); - } -} - -JSBool -NativeToString(JSContext *cx, XPCWrappedNative *wrappedNative, - uintN argc, jsval *argv, jsval *rval, - JSBool isNativeWrapper) -{ - // Check whether toString was overridden in any object along - // the wrapped native's object's prototype chain. - XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); - - jsid id = rt->GetStringID(XPCJSRuntime::IDX_TO_STRING); - - // Someone is trying to call toString on our wrapped object. - JSObject *wn_obj = wrappedNative->GetFlatJSObject(); - XPCCallContext ccx(JS_CALLER, cx, wn_obj, nsnull, id); - if (!ccx.IsValid()) { - // Shouldn't really happen. - return DoThrowException(NS_ERROR_FAILURE, cx); - } - - XPCNativeInterface *iface = ccx.GetInterface(); - XPCNativeMember *member = ccx.GetMember(); - JSString* str = nsnull; - - // First, try to see if the object declares a toString in its IDL. If it does, - // then we need to defer to that. - if (iface && member && member->IsMethod()) { - jsval toStringVal; - if (!member->NewFunctionObject(ccx, iface, wn_obj, &toStringVal)) { - return JS_FALSE; - } - - // Defer to the IDL-declared toString. - - AUTO_MARK_JSVAL(ccx, toStringVal); - - jsval v; - if (!::JS_CallFunctionValue(cx, wn_obj, toStringVal, argc, argv, &v)) { - return JS_FALSE; - } - - if (JSVAL_IS_STRING(v)) { - str = JSVAL_TO_STRING(v); - } - } - - if (!str) { - // Ok, we do no damage, and add value, by returning our own idea - // of what toString() should be. - // Note: We can't just call JS_ValueToString on the wrapped object. Instead, - // we need to call the wrapper's ToString in order to safely convert our - // object to a string. - - char *wrapperStr = nsnull; - nsAutoString resultString; - if (isNativeWrapper) { - resultString.AppendLiteral("[object XPCNativeWrapper "); - - wrapperStr = wrappedNative->ToString(ccx); - if (!wrapperStr) { - return JS_FALSE; - } - } else { - wrapperStr = wrappedNative->ToString(ccx); - if (!wrapperStr) { - return JS_FALSE; - } - } - - resultString.AppendASCII(wrapperStr); - JS_smprintf_free(wrapperStr); - - if (isNativeWrapper) { - resultString.Append(']'); - } - - str = ::JS_NewUCStringCopyN(cx, reinterpret_cast - (resultString.get()), - resultString.Length()); - } - - NS_ENSURE_TRUE(str, JS_FALSE); - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -JSBool -GetPropertyAttrs(JSContext *cx, JSObject *obj, jsid interned_id, - uintN flags, JSBool wantDetails, - JSPropertyDescriptor *desc) -{ - if (!JS_GetPropertyDescriptorById(cx, obj, interned_id, flags, desc)) { - return JS_FALSE; - } - - const uintN interesting_attrs = wantDetails - ? (JSPROP_ENUMERATE | - JSPROP_READONLY | - JSPROP_PERMANENT | - JSPROP_SHARED | - JSPROP_GETTER | - JSPROP_SETTER) - : JSPROP_ENUMERATE; - desc->attrs &= interesting_attrs; - - if (wantDetails) { - // JS_GetPropertyDescriptorById returns non scripted getters and setters. - // If wantDetails is true, then we need to censor them. - if (!(desc->attrs & JSPROP_GETTER)) { - desc->getter = nsnull; - } - if (!(desc->attrs & JSPROP_SETTER)) { - desc->setter = nsnull; - } - } else { - // Clear out all but attrs and obj. - desc->getter = desc->setter = nsnull; - desc->value = JSVAL_VOID; - } - - return JS_TRUE; -} - } diff --git a/js/src/xpconnect/src/XPCWrapper.h b/js/src/xpconnect/src/XPCWrapper.h index 06f16f4589b6..834e436c0c80 100644 --- a/js/src/xpconnect/src/XPCWrapper.h +++ b/js/src/xpconnect/src/XPCWrapper.h @@ -55,110 +55,10 @@ namespace XPCNativeWrapper { ((_wn)->GetScriptableInfo() && \ (_wn)->GetScriptableInfo()->GetFlags()._flag()) -// Wraps a function in an XPCNativeWrapper function wrapper. -JSBool -WrapFunction(JSContext* cx, JSObject* funobj, jsval *rval); - -// If v is an object, set *rval to a new XPCNativeWrapper around it. Otherwise -// set *rval = v. -JSBool -RewrapValue(JSContext *cx, JSObject *obj, jsval v, jsval *rval); - -} // namespace XPCNativeWrapper - -namespace XPCCrossOriginWrapper { - -// Wraps an object in an XPCCrossOriginWrapper. -JSBool -WrapObject(JSContext *cx, JSObject *parent, jsval *vp, - XPCWrappedNative *wn = nsnull); - -// Wraps a function in an XPCCrossOriginWrapper function wrapper. -JSBool -WrapFunction(JSContext *cx, JSObject *wrapperObj, JSObject *funobj, - jsval *rval); - -// Wraps a value in a XOW if we need to wrap it (either it's cross-scope -// or ClassNeedsXOW returns true). -JSBool -RewrapIfNeeded(JSContext *cx, JSObject *wrapperObj, jsval *vp); - -// Notify a wrapper's XOWs that the wrapper has been reparented. -JSBool -WrapperMoved(JSContext *cx, XPCWrappedNative *innerObj, - XPCWrappedNativeScope *newScope); - -void -WindowNavigated(JSContext *cx, XPCWrappedNative *innerObj); - -// Returns 'true' if the current context is same-origin to the wrappedObject. -// If we are "same origin" because UniversalXPConnect is enabled and -// privilegeEnabled is non-null, then privilegeEnabled is set to true. -nsresult -CanAccessWrapper(JSContext *cx, JSObject *outerObj, JSObject *wrappedObj, - JSBool *privilegeEnabled); - -// Some elements can change their principal or otherwise need XOWs, even -// if they're same origin. This function returns 'true' if the element's -// JSClass's name matches one such element. -inline JSBool -ClassNeedsXOW(const char *name) -{ - switch (*name) { - case 'W': - return strcmp(++name, "indow") == 0; - case 'L': - return strcmp(++name, "ocation") == 0; - default: - break; - } - - return JS_FALSE; -} - -} // namespace XPCCrossOriginWrapper - -namespace ChromeObjectWrapper { - -JSBool -WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp); - -} - -namespace XPCSafeJSObjectWrapper { - -JSObject * -GetUnsafeObject(JSContext *cx, JSObject *obj); - -JSBool -WrapObject(JSContext *cx, JSObject *scope, jsval v, jsval *vp); - PRBool AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject); -} - -namespace SystemOnlyWrapper { - -JSBool -WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp); - -JSBool -MakeSOW(JSContext *cx, JSObject *obj); - -// Used by UnwrapSOW below. -JSBool -AllowedToAct(JSContext *cx, jsid id); - -JSBool -CheckFilename(JSContext *cx, jsid id, JSStackFrame *fp); - -} - -namespace ChromeObjectWrapper { extern js::Class COWClass; } -namespace XPCSafeJSObjectWrapper { extern js::Class SJOWClass; } -namespace SystemOnlyWrapper { extern js::Class SOWClass; } -namespace XPCCrossOriginWrapper { extern js::Class XOWClass; } +} // namespace XPCNativeWrapper extern nsIScriptSecurityManager *gScriptSecurityManager; @@ -169,123 +69,6 @@ extern nsIScriptSecurityManager *gScriptSecurityManager; // objects). namespace XPCWrapper { -// FLAG_RESOLVING is used to tag a wrapper when while it's calling -// the newResolve. It tells the addProperty hook to not worry about -// what's being defined. -extern const PRUint32 FLAG_RESOLVING; - -// When a wrapper is meant to act like a SOW, this flag will be set. -extern const PRUint32 FLAG_SOW; - -// This is used by individual wrappers as a starting point to stick -// per-wrapper flags into the flags slot. This is guaranteed to only -// have one bit set. -extern const PRUint32 LAST_FLAG; - -inline JSBool -HAS_FLAGS(jsval v, PRUint32 flags) -{ - return (PRUint32(JSVAL_TO_INT(v)) & flags) != 0; -} - -/** - * Used by the cross origin and safe wrappers: the slot that the wrapped - * object is held in. - */ -extern const PRUint32 sWrappedObjSlot; - -/** - * Used by all wrappers to store flags about their state. For example, - * it is used when resolving a property to tell to the addProperty hook - * that it shouldn't perform any security checks. - */ -extern const PRUint32 sFlagsSlot; - -/** - * The base number of slots needed by code using the above constants. - */ -extern const PRUint32 sNumSlots; - -/** - * Cross origin wrappers and safe JSObject wrappers both need to know - * which native is 'eval' for various purposes. - */ -extern JSNative sEvalNative; - -enum FunctionObjectSlot { - eWrappedFunctionSlot = 0, - eAllAccessSlot = 1 -}; - -// Helpful for keeping lines short: -extern const PRUint32 sSecMgrSetProp, sSecMgrGetProp; - -inline jsval -GetFlags(JSContext *cx, JSObject *wrapper) -{ - jsval flags; - JS_GetReservedSlot(cx, wrapper, sFlagsSlot, &flags); - return flags; -} - -inline void -SetFlags(JSContext *cx, JSObject *wrapper, jsval flags) -{ - JS_SetReservedSlot(cx, wrapper, sFlagsSlot, flags); -} - -inline jsval -AddFlags(jsval origflags, PRInt32 newflags) -{ - return INT_TO_JSVAL(JSVAL_TO_INT(origflags) | newflags); -} - -inline jsval -RemoveFlags(jsval origflags, PRInt32 oldflags) -{ - return INT_TO_JSVAL(JSVAL_TO_INT(origflags) & ~oldflags); -} - -/** - * A useful function that throws an exception onto cx. - */ -inline JSBool -DoThrowException(nsresult ex, JSContext *cx) -{ - XPCThrower::Throw(ex, cx); - return JS_FALSE; -} - -/** - * Given a context and a global object, fill our eval native. - */ -inline JSBool -FindEval(XPCCallContext &ccx, JSObject *obj) -{ - if (sEvalNative) { - return JS_TRUE; - } - - jsval eval_val; - if (!::JS_GetProperty(ccx, obj, "eval", &eval_val)) { - return DoThrowException(NS_ERROR_UNEXPECTED, ccx); - } - - if (JSVAL_IS_PRIMITIVE(eval_val) || - !::JS_ObjectIsFunction(ccx, JSVAL_TO_OBJECT(eval_val))) { - return DoThrowException(NS_ERROR_UNEXPECTED, ccx); - } - - sEvalNative = - ::JS_GetFunctionNative(ccx, ::JS_ValueToFunction(ccx, eval_val)); - - if (!sEvalNative) { - return DoThrowException(NS_ERROR_UNEXPECTED, ccx); - } - - return JS_TRUE; -} - /** * Returns the script security manager used by XPConnect. */ @@ -295,26 +78,10 @@ GetSecurityManager() return ::gScriptSecurityManager; } -/** - * Used to ensure that an XPCWrappedNative stays alive when its scriptable - * helper defines an "expando" property on it. - */ -inline void -MaybePreserveWrapper(JSContext *cx, XPCWrappedNative *wn, uintN flags) -{ - if ((flags & JSRESOLVE_ASSIGNING)) { - nsRefPtr ci; - CallQueryInterface(wn->Native(), getter_AddRefs(ci)); - if (ci) { - ci->PreserveWrapper(wn->Native()); - } - } -} - inline JSBool IsSecurityWrapper(JSObject *wrapper) { - return wrapper->isWrapper() || !!wrapper->getClass()->ext.wrappedObject; + return wrapper->isWrapper(); } /** @@ -330,226 +97,9 @@ IsSecurityWrapper(JSObject *wrapper) JSObject * Unwrap(JSContext *cx, JSObject *wrapper); -/** - * Unwraps objects whose class is |xclasp|. - */ -inline JSObject * -UnwrapGeneric(JSContext *cx, const js::Class *xclasp, JSObject *wrapper) -{ - if (wrapper->getClass() != xclasp) { - return nsnull; - } - - jsval v; - if (!JS_GetReservedSlot(cx, wrapper, XPCWrapper::sWrappedObjSlot, &v)) { - JS_ClearPendingException(cx); - return nsnull; - } - - if (JSVAL_IS_PRIMITIVE(v)) { - return nsnull; - } - - return JSVAL_TO_OBJECT(v); -} - -inline JSObject * -UnwrapSOW(JSContext *cx, JSObject *wrapper) -{ - wrapper = UnwrapGeneric(cx, &SystemOnlyWrapper::SOWClass, wrapper); - if (!wrapper) { - return nsnull; - } - - if (!SystemOnlyWrapper::AllowedToAct(cx, JSID_VOID)) { - JS_ClearPendingException(cx); - wrapper = nsnull; - } - - return wrapper; -} - -/** - * Unwraps a XOW into its wrapped native. - */ -inline JSObject * -UnwrapXOW(JSContext *cx, JSObject *wrapper) -{ - JSObject *innerObj = - UnwrapGeneric(cx, &XPCCrossOriginWrapper::XOWClass, wrapper); - if (!innerObj) { - return nsnull; - } - - nsresult rv = - XPCCrossOriginWrapper::CanAccessWrapper(cx, wrapper, innerObj, nsnull); - if (NS_FAILED(rv)) { - JS_ClearPendingException(cx); - return nsnull; - } - - return innerObj; -} - -inline JSObject * -UnwrapCOW(JSContext *cx, JSObject *wrapper) -{ - wrapper = UnwrapGeneric(cx, &ChromeObjectWrapper::COWClass, wrapper); - if (!wrapper) { - return nsnull; - } - - nsresult rv = XPCCrossOriginWrapper::CanAccessWrapper(cx, nsnull, wrapper, nsnull); - if (NS_FAILED(rv)) { - JS_ClearPendingException(cx); - wrapper = nsnull; - } - - return wrapper; -} - -/** - * Rewraps a property if it needs to be rewrapped. Used by - * GetOrSetNativeProperty to rewrap the return value. - */ -inline JSBool -RewrapIfDeepWrapper(JSContext *cx, JSObject *obj, jsval v, jsval *rval, - JSBool isNativeWrapper) -{ - *rval = v; - return isNativeWrapper - ? XPCNativeWrapper::RewrapValue(cx, obj, v, rval) - : XPCCrossOriginWrapper::RewrapIfNeeded(cx, obj, rval); -} - -/** - * Creates a wrapper around a JSObject function object. Note - * XPCSafeJSObjectWrapper code doesn't have special function wrappers, - * obviating the need for this function. Instead, this is used by - * XPCNativeWrapper and the cross origin wrapper. - */ -inline JSBool -WrapFunction(JSContext *cx, JSObject *wrapperObj, JSObject *funobj, jsval *v, - JSBool isNativeWrapper) -{ - return isNativeWrapper - ? XPCNativeWrapper::WrapFunction(cx, funobj, v) - : XPCCrossOriginWrapper::WrapFunction(cx, wrapperObj, funobj, v); -} - -/** - * Given a JSObject that might represent a Window object, ensures that the - * window object has an inner window. - */ -void -CheckWindow(XPCWrappedNative *wn); - -/** - * Given a potentially-wrapped object, creates a wrapper for it. - */ -JSBool -RewrapObject(JSContext *cx, JSObject *scope, JSObject *obj, WrapperType hint, - jsval *vp); - JSObject * UnsafeUnwrapSecurityWrapper(JSContext *cx, JSObject *obj); -JSBool -CreateWrapperFromType(JSContext *cx, JSObject *scope, XPCWrappedNative *wn, - WrapperType hint, jsval *vp); - -/** - * Creates an iterator object that walks up the prototype of - * wrappedObj. This is suitable for for-in loops over a wrapper. If - * a property is not supposed to be reflected, the resolve hook - * is expected to censor it. tempWrapper must be rooted already. - */ -JSObject * -CreateIteratorObj(JSContext *cx, JSObject *tempWrapper, - JSObject *wrapperObj, JSObject *innerObj, - JSBool keysonly); - -/** - * Like CreateIteratorObj, but doesn't need a security wrapper. If - * propertyContainer is null, creates an empty iterator. - */ -JSObject * -CreateSimpleIterator(JSContext *cx, JSObject *scope, JSBool keysonly, - JSObject *propertyContainer); - -/** - * Called for the common part of adding a property to obj. - */ -JSBool -AddProperty(JSContext *cx, JSObject *wrapperObj, - JSBool wantGetterSetter, JSObject *innerObj, - jsid id, jsval *vp); - -/** - * Called for the common part of deleting a property from obj. - */ -JSBool -DelProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -/** - * Called to enumerate the properties of |innerObj| onto |wrapperObj|. - */ -JSBool -Enumerate(JSContext *cx, JSObject *wrapperObj, JSObject *innerObj); - -/** - * Resolves a property (that may be) defined on |innerObj| onto - * |wrapperObj|. This will also resolve random, page-defined objects - * and is therefore unsuitable for cross-origin resolution. - * - * If |caller| is not NONE, then we will call the proper WrapObject - * hook for any getters or setters about to be lifted onto - * |wrapperObj|. - */ -JSBool -NewResolve(JSContext *cx, JSObject *wrapperObj, JSBool preserveVal, - JSObject *innerObj, jsid id, uintN flags, JSObject **objp); - -/** - * Resolve a native property named id from innerObj onto wrapperObj. The - * native wrapper will be preserved if necessary. Note that if we resolve - * an attribute here, we don't deal with the value until later. - */ -JSBool -ResolveNativeProperty(JSContext *cx, JSObject *wrapperObj, - JSObject *innerObj, XPCWrappedNative *wn, - jsid id, uintN flags, JSObject **objp, - JSBool isNativeWrapper); - -/** - * Gets a native property from obj. This goes directly through XPConnect, it - * does not look at Javascript-defined getters or setters. This ensures that - * the caller gets a real answer. - */ -JSBool -GetOrSetNativeProperty(JSContext *cx, JSObject *obj, - XPCWrappedNative *wrappedNative, - jsid id, jsval *vp, JSBool aIsSet, - JSBool isNativeWrapper); - -/** - * Gets a string representation of wrappedNative, going through IDL. - */ -JSBool -NativeToString(JSContext *cx, XPCWrappedNative *wrappedNative, - uintN argc, jsval *argv, jsval *rval, - JSBool isNativeWrapper); - -/** - * Looks up a property on obj. If it exists, then the parameters are filled - * in with useful values. - * - * NB: All parameters must be initialized before the call. - */ -JSBool -GetPropertyAttrs(JSContext *cx, JSObject *obj, jsid interned_id, uintN flags, - JSBool wantDetails, JSPropertyDescriptor *desc); - } // namespace XPCWrapper diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 95eeb2471077..d1be4c782b51 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -43,7 +43,6 @@ /* High level class and public functions implementation. */ #include "xpcprivate.h" -#include "XPCNativeWrapper.h" #include "XPCWrapper.h" #include "nsBaseHashtable.h" #include "nsHashKeys.h" @@ -57,6 +56,7 @@ #include "jstypedarray.h" #include "XrayWrapper.h" +#include "WrapperFactory.h" NS_IMPL_THREADSAFE_ISUPPORTS6(nsXPConnect, nsIXPConnect, @@ -674,100 +674,52 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) { JSObject *obj = static_cast(p); js::Class *clazz = obj->getClass(); - if(XPCNativeWrapper::IsNativeWrapperClass(clazz)) + XPCNativeScriptableInfo* si = nsnull; + if(IS_PROTO_CLASS(clazz)) { - XPCWrappedNative* wn; - if(XPCNativeWrapper::GetWrappedNative(cx, obj, &wn) && wn) + XPCWrappedNativeProto* p = + (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); + si = p->GetScriptableInfo(); + } + if(si) + { + JS_snprintf(name, sizeof(name), "JS Object (%s - %s)", + clazz->name, si->GetJSClass()->name); + } + else if(clazz == &js_ScriptClass) + { + JSScript* script = (JSScript*) xpc_GetJSPrivate(obj); + if(script->filename) { - XPCNativeScriptableInfo* si = wn->GetScriptableInfo(); - if(si) - { - JS_snprintf(name, sizeof(name), "XPCNativeWrapper (%s)", - si->GetJSClass()->name); - } - else - { - nsIClassInfo* ci = wn->GetClassInfo(); - char* className = nsnull; - if(ci) - ci->GetClassDescription(&className); - if(className) - { - JS_snprintf(name, sizeof(name), - "XPCNativeWrapper (%s)", className); - PR_Free(className); - } - else - { - XPCNativeSet* set = wn->GetSet(); - XPCNativeInterface** array = - set->GetInterfaceArray(); - PRUint16 count = set->GetInterfaceCount(); - - if(count > 0) - JS_snprintf(name, sizeof(name), - "XPCNativeWrapper (%s)", - array[0]->GetNameString()); - else - JS_snprintf(name, sizeof(name), - "XPCNativeWrapper"); - } - } + JS_snprintf(name, sizeof(name), + "JS Object (Script - %s)", + script->filename); } else { - JS_snprintf(name, sizeof(name), "XPCNativeWrapper"); + JS_snprintf(name, sizeof(name), "JS Object (Script)"); + } + } + else if(clazz == &js_FunctionClass) + { + JSFunction* fun = (JSFunction*) xpc_GetJSPrivate(obj); + JSString* str = JS_GetFunctionId(fun); + if(str) + { + NS_ConvertUTF16toUTF8 + fname(reinterpret_cast(JS_GetStringChars(str))); + JS_snprintf(name, sizeof(name), + "JS Object (Function - %s)", fname.get()); + } + else + { + JS_snprintf(name, sizeof(name), "JS Object (Function)"); } } else { - XPCNativeScriptableInfo* si = nsnull; - if(IS_PROTO_CLASS(clazz)) - { - XPCWrappedNativeProto* p = - (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); - si = p->GetScriptableInfo(); - } - if(si) - { - JS_snprintf(name, sizeof(name), "JS Object (%s - %s)", - clazz->name, si->GetJSClass()->name); - } - else if(clazz == &js_ScriptClass) - { - JSScript* script = (JSScript*) xpc_GetJSPrivate(obj); - if(script->filename) - { - JS_snprintf(name, sizeof(name), - "JS Object (Script - %s)", - script->filename); - } - else - { - JS_snprintf(name, sizeof(name), "JS Object (Script)"); - } - } - else if(clazz == &js_FunctionClass) - { - JSFunction* fun = (JSFunction*) xpc_GetJSPrivate(obj); - JSString* str = JS_GetFunctionId(fun); - if(str) - { - NS_ConvertUTF16toUTF8 - fname(reinterpret_cast(JS_GetStringChars(str))); - JS_snprintf(name, sizeof(name), - "JS Object (Function - %s)", fname.get()); - } - else - { - JS_snprintf(name, sizeof(name), "JS Object (Function)"); - } - } - else - { - JS_snprintf(name, sizeof(name), "JS Object (%s)", - clazz->name); - } + JS_snprintf(name, sizeof(name), "JS Object (%s)", + clazz->name); } } else @@ -820,13 +772,10 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative"); cb.NoteXPCOMChild(to->GetNative()); } - // XXX XPCNativeWrapper seems to be the only class that doesn't hold a - // strong reference to its nsISupports private. This test does seem - // fragile though, we should probably whitelist classes that do hold - // a strong reference, but that might not be possible. + // XXX This test does seem fragile, we should probably whitelist classes + // that do hold a strong reference, but that might not be possible. else if(clazz->flags & JSCLASS_HAS_PRIVATE && - clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS && - !XPCNativeWrapper::IsNativeWrapperClass(clazz)) + clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)"); cb.NoteXPCOMChild(static_cast(xpc_GetJSPrivate(obj))); @@ -968,9 +917,6 @@ nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj) { if (!XPCNativeWrapper::AttachNewConstructorObject(ccx, aGlobalJSObj)) return UnexpectedFailure(NS_ERROR_FAILURE); - - if (!XPCSafeJSObjectWrapper::AttachNewConstructorObject(ccx, aGlobalJSObj)) - return UnexpectedFailure(NS_ERROR_FAILURE); } return NS_OK; @@ -1176,8 +1122,9 @@ NativeInterface2JSObject(XPCLazyCallContext & lccx, return rv; #ifdef DEBUG - NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(JSVAL_TO_OBJECT(*aVal)), - "Shouldn't be returning a native wrapper here"); + NS_ASSERTION(aAllowWrapping || + !xpc::WrapperFactory::IsXrayWrapper(JSVAL_TO_OBJECT(*aVal)), + "Shouldn't be returning a xray wrapper here"); #endif return NS_OK; @@ -2008,87 +1955,6 @@ nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext, return NS_OK; } -/* [noscript] jsval GetCrossOriginWrapperForValue(in JSContextPtr aJSContext, in jsval aCurrentVal); */ -NS_IMETHODIMP -nsXPConnect::GetXOWForObject(JSContext * aJSContext, - JSObject * aParent, - JSObject * aWrappedObj, - jsval * rval) -{ - *rval = OBJECT_TO_JSVAL(aWrappedObj); - return XPCCrossOriginWrapper::WrapObject(aJSContext, aParent, rval) - ? NS_OK : NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -nsXPConnect::GetCOWForObject(JSContext * aJSContext, - JSObject * aParent, - JSObject * aWrappedObj, - jsval * rval) -{ - *rval = OBJECT_TO_JSVAL(aWrappedObj); - return ChromeObjectWrapper::WrapObject(aJSContext, aParent, *rval, rval) - ? NS_OK : NS_ERROR_FAILURE; -} - -static inline PRBool -PerformOp(JSContext *cx, PRUint32 aWay, JSObject *obj) -{ - NS_ASSERTION(aWay == nsIXPConnect::XPC_XOW_CLEARSCOPE, - "Nothing else is implemented yet"); - - JS_ClearScope(cx, obj); - return PR_TRUE; -} - -/* [noscript] void updateXOWs (in JSContextPtr aJSContext, - * in nsIXPConnectJSObjectHolder aObject, - * in PRUint32 aWay); */ -NS_IMETHODIMP -nsXPConnect::UpdateXOWs(JSContext* aJSContext, - nsIXPConnectWrappedNative* aObject, - PRUint32 aWay) -{ - typedef WrappedNative2WrapperMap::Link Link; - XPCWrappedNative* wn = static_cast(aObject); - XPCWrappedNativeScope* scope = wn->GetScope(); - WrappedNative2WrapperMap* map = scope->GetWrapperMap(); - Link* list; - - { - XPCAutoLock al(GetRuntime()->GetMapLock()); - - list = map->FindLink(wn->GetFlatJSObject()); - } - - if(!list) - return NS_OK; // No wrappers to update. - - if(aWay == nsIXPConnect::XPC_XOW_NAVIGATED) - { - XPCWrappedNative *wn = static_cast(aObject); - NS_ASSERTION(wn->NeedsXOW(), "Window isn't a window"); - - XPCCrossOriginWrapper::WindowNavigated(aJSContext, wn); - return NS_OK; - } - - JSAutoRequest req(aJSContext); - - Link* cur = list; - if(cur->obj && !PerformOp(aJSContext, aWay, cur->obj)) - return NS_ERROR_FAILURE; - - for(cur = (Link *)PR_NEXT_LINK(list); cur != list; - cur = (Link *)PR_NEXT_LINK(cur)) - { - if(!PerformOp(aJSContext, aWay, cur->obj)) - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - /* void releaseJSContext (in JSContextPtr aJSContext, in PRBool noGC); */ NS_IMETHODIMP nsXPConnect::ReleaseJSContext(JSContext * aJSContext, PRBool noGC) @@ -2381,134 +2247,6 @@ nsXPConnect::DefineDOMQuickStubs(JSContext * cx, interfaceCount, interfaceArray); } -NS_IMETHODIMP -nsXPConnect::GetWrapperForObject(JSContext* aJSContext, - JSObject* aObject, - JSObject* aScope, - nsIPrincipal* aPrincipal, - PRUint32 aFilenameFlags, - jsval* _retval) -{ - NS_ASSERTION(aFilenameFlags != JSFILENAME_NULL, "Null filename!"); - NS_ASSERTION(XPCPerThreadData::IsMainThread(aJSContext), - "Must only be called from the main thread as these wrappers " - "are not threadsafe!"); - - JSAutoRequest ar(aJSContext); - - XPCWrappedNativeScope *objectscope; - JSObject* obj2; - XPCWrappedNative *wrapper = - XPCWrappedNative::GetWrappedNativeOfJSObject(aJSContext, aObject, - nsnull, &obj2); - if(wrapper) - { - objectscope = wrapper->GetScope(); - } - else if(obj2) - { - objectscope = GetSlimWrapperProto(obj2)->GetScope(); - } - else - { - // Couldn't get the wrapped native (maybe a prototype?) so just return - // the original object. - *_retval = OBJECT_TO_JSVAL(aObject); - return NS_OK; - } - - XPCWrappedNativeScope *xpcscope = - XPCWrappedNativeScope::FindInJSObjectScope(aJSContext, aScope); - if(!xpcscope) - return NS_ERROR_FAILURE; - -#ifdef DEBUG_mrbkap - { - JSObject *scopeobj = xpcscope->GetGlobalJSObject(); - JSObject *toInnerize = scopeobj; - OBJ_TO_INNER_OBJECT(aJSContext, toInnerize); - NS_ASSERTION(toInnerize == scopeobj, "Scope chain ending in outer object?"); - } -#endif - - { - JSObject *possibleOuter = objectscope->GetGlobalJSObject(); - OBJ_TO_INNER_OBJECT(aJSContext, possibleOuter); - if(!possibleOuter) - return NS_ERROR_FAILURE; - - if(objectscope->GetGlobalJSObject() != possibleOuter) - { - objectscope = - XPCWrappedNativeScope::FindInJSObjectScope(aJSContext, - possibleOuter); - NS_ASSERTION(objectscope, "Unable to find a scope from an object"); - } - } - - *_retval = OBJECT_TO_JSVAL(aObject); - - JSBool sameOrigin; - JSBool sameScope = xpc_SameScope(objectscope, xpcscope, &sameOrigin); - JSBool forceXOW = - XPCCrossOriginWrapper::ClassNeedsXOW(aObject->getClass()->name); - - // We can do nothing if: - // - We're wrapping a system object - // or - // - We're from the same *scope* AND - // - We're not about to force a XOW (e.g. for "window") OR - // - We're not actually going to create a XOW (we're wrapping for - // chrome). - if(aObject->isSystem() || - (sameScope && - (!forceXOW || (aFilenameFlags & JSFILENAME_SYSTEM)))) - return NS_OK; - - JSObject* wrappedObj = nsnull; - - if(aFilenameFlags & JSFILENAME_PROTECTED) - { - if(!wrapper) - { - SLIM_LOG_WILL_MORPH(aJSContext, aObject); - if(!MorphSlimWrapper(aJSContext, aObject)) - return NS_ERROR_FAILURE; - - wrapper = static_cast(xpc_GetJSPrivate(aObject)); - } - - wrappedObj = XPCNativeWrapper::GetNewOrUsed(aJSContext, wrapper, - aScope, aPrincipal); - } - else if(aFilenameFlags & JSFILENAME_SYSTEM) - { - jsval val = OBJECT_TO_JSVAL(aObject); - if(XPCSafeJSObjectWrapper::WrapObject(aJSContext, aScope, val, &val)) - wrappedObj = JSVAL_TO_OBJECT(val); - } - else - { - // We don't wrap anything same origin unless the class name requires - // it. - if(sameOrigin && !forceXOW) - return NS_OK; - - jsval val = OBJECT_TO_JSVAL(aObject); - if(XPCCrossOriginWrapper::WrapObject(aJSContext, aScope, &val, wrapper)) - wrappedObj = JSVAL_TO_OBJECT(val); - } - - if(!wrappedObj) - return NS_ERROR_FAILURE; - - *_retval = OBJECT_TO_JSVAL(wrappedObj); - if(wrapper && wrapper->NeedsSOW() && - !SystemOnlyWrapper::WrapObject(aJSContext, aScope, *_retval, _retval)) - return NS_ERROR_FAILURE; - return NS_OK; -} - /* attribute JSRuntime runtime; */ NS_IMETHODIMP nsXPConnect::GetRuntime(JSRuntime **runtime) @@ -2706,22 +2444,6 @@ nsXPConnect::GetPrincipal(JSObject* obj, PRBool allowShortCircuit) const return nsnull; } -NS_IMETHODIMP_(void) -nsXPConnect::GetXrayWrapperPropertyHolderGetPropertyOp(JSPropertyOp *getPropertyPtr) -{ - *getPropertyPtr = xpc::XrayUtils::HolderClass.getProperty; -} - -NS_IMETHODIMP_(void) -nsXPConnect::GetNativeWrapperGetPropertyOp(JSPropertyOp *getPropertyPtr) -{ - NS_ASSERTION(XPCNativeWrapper::GetJSClass(true)->getProperty == - XPCNativeWrapper::GetJSClass(false)->getProperty, - "Call and NoCall XPCNativeWrapper Class must use the same " - "getProperty hook."); - *getPropertyPtr = XPCNativeWrapper::GetJSClass(true)->getProperty; -} - NS_IMETHODIMP nsXPConnect::HoldObject(JSContext *aJSContext, JSObject *aObject, nsIXPConnectJSObjectHolder **aHolder) diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index 8240c0c6944e..bd29442b1be0 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -48,7 +48,6 @@ #include "nsIScriptObjectPrincipal.h" #include "nsIDOMWindow.h" #include "xpcJSWeakReference.h" -#include "XPCNativeWrapper.h" #include "XPCWrapper.h" #include "jsproxy.h" @@ -2725,22 +2724,8 @@ MethodWrapper(JSContext *cx, uintN argc, jsval *vp) jsval *argv = JS_ARGV(cx, vp); jsval v; - if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &v) || - !JS_CallFunctionValue(cx, thisobj, v, argc, argv, vp)) { - return JS_FALSE; - } - - if (JSVAL_IS_PRIMITIVE(*vp)) - return JS_TRUE; - - XPCWrappedNative *wn = - XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, JSVAL_TO_OBJECT(*vp)); - if (!wn) { - XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); - return JS_FALSE; - } - - return XPCNativeWrapper::CreateExplicitWrapper(cx, wn, vp); + return JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &v) && + JS_CallFunctionValue(cx, thisobj, v, argc, argv, vp); } /* void lookupMethod (); */ @@ -3124,49 +3109,11 @@ sandbox_enumerate(JSContext *cx, JSObject *obj) return JS_EnumerateStandardClasses(cx, obj); } -static JSBool -sandbox_getProto(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - uintN attrs; - return JS_CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs); -} - -static JSBool -sandbox_setProto(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - if (!JSVAL_IS_OBJECT(*vp)) { - return JS_TRUE; - } - - JSObject *pobj = JSVAL_TO_OBJECT(*vp); - if (pobj) { - if (pobj->getClass() == &XPCCrossOriginWrapper::XOWClass && - !XPCWrapper::RewrapObject(cx, obj, pobj, - XPCWrapper::XPCNW_EXPLICIT, vp)) { - return JS_FALSE; - } - } - - return JS_SetPrototype(cx, obj, JSVAL_TO_OBJECT(*vp)); -} - static JSBool sandbox_resolve(JSContext *cx, JSObject *obj, jsid id) { JSBool resolved; - if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) { - return JS_FALSE; - } - if (resolved) { - return JS_TRUE; - } - - if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTO)) { - return JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, sandbox_getProto, - sandbox_setProto, JSPROP_SHARED); - } - - return JS_TRUE; + return JS_ResolveStandardClass(cx, obj, id, &resolved); } static void diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index d1545d2aef0f..838b868f0541 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -44,7 +44,6 @@ #include "xpcprivate.h" #include "nsString.h" -#include "XPCNativeWrapper.h" #include "nsIAtom.h" #include "XPCWrapper.h" #include "nsJSPrincipals.h" diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index a592d9618313..125434d10e09 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -1521,14 +1521,6 @@ public: JSBool IsValid() const {return mRuntime != nsnull;} - /** - * Figures out what type of wrapper to create for obj if it were injected - * into 'this's scope. - */ - XPCWrapper::WrapperType - GetWrapperFor(JSContext *cx, JSObject *obj, XPCWrapper::WrapperType hint, - XPCWrappedNative **wn); - static JSBool IsDyingScope(XPCWrappedNativeScope *scope); diff --git a/js/src/xpconnect/src/xpcquickstubs.cpp b/js/src/xpconnect/src/xpcquickstubs.cpp index ed30aab795be..92bf4977e6ce 100644 --- a/js/src/xpconnect/src/xpcquickstubs.cpp +++ b/js/src/xpconnect/src/xpcquickstubs.cpp @@ -45,7 +45,6 @@ #include "xpcinlines.h" #include "xpcquickstubs.h" #include "XPCWrapper.h" -#include "XPCNativeWrapper.h" static inline QITableEntry * GetOffsets(nsISupports *identity, XPCWrappedNativeProto* proto) diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index fc79cbaba2e4..4d2dcec89a51 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -43,7 +43,6 @@ #include "xpcprivate.h" #include "nsCRT.h" -#include "XPCNativeWrapper.h" #include "XPCWrapper.h" #include "nsWrapperCache.h" #include "xpclog.h" @@ -52,6 +51,7 @@ #include "xpcquickstubs.h" #include "jsproxy.h" #include "AccessCheck.h" +#include "WrapperFactory.h" /***************************************************************************/ @@ -445,8 +445,8 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, needsXOW = JS_TRUE; rv = NS_OK; - NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(parent), - "Parent should never be an XPCNativeWrapper here"); + NS_ASSERTION(!xpc::WrapperFactory::IsXrayWrapper(parent), + "Xray wrapper being used to parent XPCWrappedNative?"); if(!ac.enter(ccx, parent)) return NS_ERROR_FAILURE; @@ -577,8 +577,8 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, NS_ADDREF(wrapper); - NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(parent), - "XPCNativeWrapper being used to parent XPCWrappedNative?"); + NS_ASSERTION(!xpc::WrapperFactory::IsXrayWrapper(parent), + "Xray wrapper being used to parent XPCWrappedNative?"); if(!wrapper->Init(ccx, parent, isGlobal, &sciWrapper)) { @@ -757,9 +757,8 @@ XPCWrappedNative::Morph(XPCCallContext& ccx, NS_ADDREF(wrapper); - NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(existingJSObject - ->getParent()), - "XPCNativeWrapper being used to parent XPCWrappedNative?"); + NS_ASSERTION(!xpc::WrapperFactory::IsXrayWrapper(existingJSObject->getParent()), + "Xray wrapper being used to parent XPCWrappedNative?"); JSAutoEnterCompartment ac; if(!ac.enter(ccx, existingJSObject) || !wrapper->Init(ccx, existingJSObject)) @@ -1384,10 +1383,6 @@ XPCWrappedNative::FlatJSObjectFinalized(JSContext *cx) if(JSObject* wrapper = wnxow->GetXOW()) { wrapper->getClass()->finalize(cx, wrapper); - NS_ASSERTION(!XPCWrapper::UnwrapGeneric(cx, - &XPCCrossOriginWrapper::XOWClass, - wrapper), - "finalize didn't do its job"); } } @@ -1540,14 +1535,6 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, if(aOldScope != aNewScope) { // Oh, so now we need to move the wrapper to a different scope. - - // First notify any XOWs. - if(wrapper) - { - nsXPConnect* xpc = nsXPConnect::GetXPConnect(); - xpc->UpdateXOWs(ccx, wrapper, nsIXPConnect::XPC_XOW_CLEARSCOPE); - } - AutoMarkingWrappedNativeProtoPtr oldProto(ccx); AutoMarkingWrappedNativeProtoPtr newProto(ccx); @@ -1575,11 +1562,6 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, if(wrapper) { - if(!XPCCrossOriginWrapper::WrapperMoved(ccx, wrapper, aNewScope)) - { - return NS_ERROR_FAILURE; - } - Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap(); Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap(); diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index 49489f1f574f..f96cf34e4b03 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -42,7 +42,6 @@ /* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */ #include "xpcprivate.h" -#include "XPCNativeWrapper.h" #include "XPCWrapper.h" /***************************************************************************/ @@ -772,24 +771,10 @@ XPC_WN_NoHelper_Resolve(JSContext *cx, JSObject *obj, jsid id) nsISupports * XPC_GetIdentityObject(JSContext *cx, JSObject *obj) { - XPCWrappedNative *wrapper; + XPCWrappedNative *wrapper = + XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); - if(XPCNativeWrapper::IsNativeWrapper(obj)) - // Note: It's okay to use SafeGetWrappedNative here since we only do - // identity checking on the returned object. - wrapper = XPCNativeWrapper::SafeGetWrappedNative(obj); - else - wrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); - - if(!wrapper) { - JSObject *unsafeObj = XPCSafeJSObjectWrapper::GetUnsafeObject(cx, obj); - if(unsafeObj) - return XPC_GetIdentityObject(cx, unsafeObj); - - return nsnull; - } - - return wrapper->GetIdentityObject(); + return wrapper ? wrapper->GetIdentityObject() : nsnull; } JSBool @@ -816,16 +801,6 @@ XPC_WN_Equality(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) nsresult rv = si->GetCallback()->Equality(wrapper, cx, obj, v, bp); if(NS_FAILED(rv)) return Throw(rv, cx); - - if(!*bp && !JSVAL_IS_PRIMITIVE(v) && - JSVAL_TO_OBJECT(v)->getClass() == &XPCSafeJSObjectWrapper::SJOWClass) - { - v = OBJECT_TO_JSVAL(XPCSafeJSObjectWrapper::GetUnsafeObject(cx, JSVAL_TO_OBJECT(v))); - - rv = si->GetCallback()->Equality(wrapper, cx, obj, v, bp); - if(NS_FAILED(rv)) - return Throw(rv, cx); - } } else if(!JSVAL_IS_PRIMITIVE(v)) { @@ -1405,22 +1380,7 @@ XPC_WN_JSOp_TypeOf_Function(JSContext *cx, JSObject *obj) void XPC_WN_JSOp_Clear(JSContext *cx, JSObject *obj) { - // We're likely to enter this JSOp with a wrapper prototype - // object. In that case we won't find a wrapper, so we'll just - // call into js_ObjectOps.clear(), which is exactly what we want. - - // If our scope is cleared, make sure we clear the scope of our - // native wrapper as well. - XPCWrappedNative *wrapper = - XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); - - if(wrapper && wrapper->IsValid()) - { - XPCNativeWrapper::ClearWrappedNativeScopes(cx, wrapper); - - nsXPConnect* xpc = nsXPConnect::GetXPConnect(); - xpc->UpdateXOWs(cx, wrapper, nsIXPConnect::XPC_XOW_CLEARSCOPE); - } + // XXX Clear XrayWrappers? } namespace { diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp index 76c25b3b4e32..1152d57d4984 100644 --- a/js/src/xpconnect/src/xpcwrappednativescope.cpp +++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp @@ -42,9 +42,6 @@ #include "xpcprivate.h" #include "XPCWrapper.h" -#ifdef DEBUG -#include "XPCNativeWrapper.h" -#endif /***************************************************************************/ @@ -738,24 +735,7 @@ GetScopeOfObject(JSObject* obj) return GetSlimWrapperProto(obj)->GetScope(); if(!isWrapper || !(supports = (nsISupports*) xpc_GetJSPrivate(obj))) - { -#ifdef DEBUG - { - if(!(~clazz->flags & (JSCLASS_HAS_PRIVATE | - JSCLASS_PRIVATE_IS_NSISUPPORTS)) && - (supports = (nsISupports*) xpc_GetJSPrivate(obj)) && - !XPCNativeWrapper::IsNativeWrapperClass(clazz)) - { - nsCOMPtr iface = - do_QueryInterface(supports); - - NS_ASSERTION(!iface, "Uh, how'd this happen?"); - } - } -#endif - return nsnull; - } #ifdef DEBUG { @@ -1019,141 +999,3 @@ XPCWrappedNativeScope::DebugDump(PRInt16 depth) XPC_LOG_OUTDENT(); #endif } - -XPCWrapper::WrapperType -XPCWrappedNativeScope::GetWrapperFor(JSContext *cx, JSObject *obj, - XPCWrapper::WrapperType hint, - XPCWrappedNative **wn) -{ - using namespace XPCWrapper; - - // We're going to have to know where obj comes from no matter what. - XPCWrappedNativeScope *other = FindInJSObjectScope(cx, obj); - - // We have two cases to split out: we can either be chrome or content. - nsIPrincipal *principal = GetPrincipal(); - PRBool system; - XPCWrapper::GetSecurityManager()->IsSystemPrincipal(principal, &system); - - PRBool principalEqual = (this == other); - if(!principalEqual) - { - nsIPrincipal *otherprincipal = other->GetPrincipal(); - if(otherprincipal) - otherprincipal->Equals(principal, &principalEqual); - else - principalEqual = PR_TRUE; - } - - PRBool native = IS_WRAPPER_CLASS(obj->getClass()); - XPCWrappedNative *wrapper = (native && IS_WN_WRAPPER_OBJECT(obj)) - ? (XPCWrappedNative *) xpc_GetJSPrivate(obj) - : nsnull; - if(wn) - *wn = wrapper; - - // XXX The isSystem checks shouldn't be needed, but are needed because we - // can get here before nsGlobalChromeWindows have a non-about:blank - // document. - if(system || mGlobalJSObject->isSystem()) - { - NS_ASSERTION(hint != XOW && hint != SOW && hint != COW, - "bad hint in chrome code"); - - // XXX In an ideal world, we would never have a transition from - // chrome -> content in a window. However, as the Gecko platform - // is pretty far from an ideal world, we need to protect against - // principal-changing objects (like window and location objects) - // changing. They are identified by ClassNeedsXOW. - // But note: we don't want to create XOWs in chrome code, so just - // use a SJOW, which does the Right Thing. - JSBool wantsXOW = - XPCCrossOriginWrapper::ClassNeedsXOW(obj->getClass()->name); - - // Is other a chrome object? - if(principalEqual || obj->isSystem()) - { - if(hint & XPCNW) - return native ? hint : NONE; - return wantsXOW ? SJOW : NONE; - } - - // Other isn't a chrome object: we need to wrap it in a SJOW or an - // XPCNW. - - if(!native) - hint = SJOW; - else if(hint == UNKNOWN) - hint = XPCNW_IMPLICIT; - - NS_ASSERTION(hint <= SJOW, "returning the wrong wrapper for chrome code"); - return hint; - } - - // We're content code. We must never return XPCNW_IMPLICIT from here (but - // might return XPCNW_EXPLICIT if hint is already XPCNW_EXPLICIT). - - nsIPrincipal *otherprincipal = other->GetPrincipal(); - XPCWrapper::GetSecurityManager()->IsSystemPrincipal(otherprincipal, &system); - if(system) - { - // Content touching chrome. - NS_ASSERTION(hint != XOW, "bad edge in object graph"); - - if(wrapper) - { - NS_ASSERTION(!wrapper->NeedsCOW(), - "chrome object that's double wrapped makes no sense"); - if(wrapper->NeedsSOW()) - return WrapperType(SOW | hint); - } - - return COW; - } - - // If this object isn't an XPCWrappedNative, then we don't need to create - // any other types of wrapper than the hint. - if(!native) - { -#if 0 - // XXX Re-enable these assertions when we have a better mochitest - // solution than UniversalXPConnect. - NS_ASSERTION(principalEqual || hint == COW, - "touching non-wrappednative object cross origin?"); - NS_ASSERTION(hint == SJOW || hint == COW || hint == UNKNOWN, "bad hint"); -#endif - if(hint & XPCNW) - hint = SJOW; - return hint; - } - - // NB: obj2 controls whether or not this is actually a "wrapped native". - if(wrapper) - { - if(wrapper->NeedsSOW()) - return WrapperType(SOW | (hint & (SJOW | XPCNW_EXPLICIT | COW))); - if(wrapper->NeedsCOW()) - { -#ifdef DEBUG - { - const char *name = obj->getClass()->name; - NS_ASSERTION(!XPCCrossOriginWrapper::ClassNeedsXOW(name), - "bad object combination"); - } -#endif - return COW; // NB: Ignore hint. - } - } - - if(!principalEqual || - XPCCrossOriginWrapper::ClassNeedsXOW(obj->getClass()->name)) - { - // NB: We want to assert that hint is not SJOW here, but it can - // be because of shallow XPCNativeWrappers. In that case, XOW is - // the right return value because XPCNativeWrappers are meant for - // chrome, and we're in content which shouldn't expect SJOWs. - return (hint & XPCNW) ? XPCNW_EXPLICIT : XOW; - } - - return (hint & XPCNW) ? XPCNW_EXPLICIT : (hint == SJOW) ? SJOW : NONE; -} From d1d7718ac8336a53fc59293e843df42a06f2849d Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Sun, 10 Oct 2010 15:41:56 -0700 Subject: [PATCH 194/284] Mainly just fix ExposedPropertiesOnly::check to ignore non-enumerable properties and return true when no property is found. r=mrbkap --- js/src/xpconnect/tests/chrome/test_cows.xul | 25 ++++++++++++--------- js/src/xpconnect/wrappers/AccessCheck.cpp | 11 +++++---- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/js/src/xpconnect/tests/chrome/test_cows.xul b/js/src/xpconnect/tests/chrome/test_cows.xul index 6f9a442d7c4d..f40dcf365098 100644 --- a/js/src/xpconnect/tests/chrome/test_cows.xul +++ b/js/src/xpconnect/tests/chrome/test_cows.xul @@ -48,12 +48,14 @@ sandbox.getCOW = getCOW; const TEST_API = ['is', 'isnot', 'ok', 'todo_is', 'todo_isnot', 'todo']; TEST_API.forEach(function(name) { sandbox[name] = window[name]; }); -sandbox.alienObject = test_utils.getCOWForObject(sandbox, { +sandbox.alienObject = { __exposedProps__: {funProp: 'r'}, funProp: function foo(x) { return x + 1; } -}); +}; + +sandbox.chromeGet = function (obj, prop) { return obj[prop]; }; function COWTests() { // This function is actually decompiled and run inside a @@ -85,8 +87,9 @@ function COWTests() { strictCOW[name]; ok(false, "COWs didn't restrict access to " + uneval(name)); } catch (e) { - ok(/SECURITY_MANAGER/.test(e), - "not able to access arbitrary property " + uneval(name)); + ok(/Permission denied/.test(e.message), + "should get 'Permission denied' trying to access arbitrary property " + + uneval(name) + ", got: " + e.message); } }); @@ -102,18 +105,18 @@ function COWTests() { "obj should not throw"); } - var writable = { __exposedProps__: {foo: 'w'}}; + var writable = getCOW({ __exposedProps__: {foo: 'w'}}); try { - getCOW(writable).foo = 5; - is(writable.foo, 5, "writing to a writable exposed prop works"); + writable.foo = 5; + is(chromeGet(writable, "foo"), 5, "writing to a writable exposed prop works"); } catch (e) { ok(false, "writing to a writable exposed prop shouldn't throw " + e); } try { - getCOW(writable).foo; + writable.foo; ok(false, "reading from a write-only exposed prop should fail"); } catch (e) { - ok(/SECURITY_MANAGER/.test(e), + ok(/Permission denied/.test(e), "reading from a write-only exposed prop should fail"); } @@ -130,7 +133,7 @@ function COWTests() { getCOW(readable).foo = 1; ok(false, "writing to a read-only exposed prop should fail"); } catch (e) { - ok(/SECURITY_MANAGER/.test(e), + ok(/Permission denied/.test(e), "writing to a read-only exposed prop should fail"); } @@ -138,7 +141,7 @@ function COWTests() { delete getCOW(readable).foo; ok(false, "deleting a read-only exposed prop shouldn't work"); } catch (e) { - ok(/SECURITY_MANAGER/.test(e), + ok(/Permission denied/.test(e), "deleting a read-only exposed prop should throw error"); } diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index faa12d9d7702..46e8b0c68541 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -414,17 +414,20 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, JSWrappe Access access = NO_ACCESS; - jsval v; - if (!JS_LookupPropertyById(cx, hallpass, id, &v)) { + JSPropertyDescriptor desc; + if (!JS_GetPropertyDescriptorById(cx, hallpass, id, JSRESOLVE_QUALIFIED, &desc)) { return false; // Error } + if (desc.obj == NULL || !(desc.attrs & JSPROP_ENUMERATE)) { + return true; // Deny + } - if (!JSVAL_IS_STRING(v)) { + if (!JSVAL_IS_STRING(desc.value)) { JS_ReportError(cx, "property must be a string"); return false; } - JSString *str = JSVAL_TO_STRING(v); + JSString *str = JSVAL_TO_STRING(desc.value); const jschar *chars = JS_GetStringChars(str); size_t length = JS_GetStringLength(str); for (size_t i = 0; i < length; ++i) { From 509eb29b871520fd877f1d7d40ce5ce698510e38 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:42:01 -0700 Subject: [PATCH 195/284] bug 580128 - Remove test that uses an obsolete wrapper. r=mrbkap --- js/src/xpconnect/tests/unit/test_iterate.js | 54 --------------------- 1 file changed, 54 deletions(-) delete mode 100644 js/src/xpconnect/tests/unit/test_iterate.js diff --git a/js/src/xpconnect/tests/unit/test_iterate.js b/js/src/xpconnect/tests/unit/test_iterate.js deleted file mode 100644 index 0b7062d6bf27..000000000000 --- a/js/src/xpconnect/tests/unit/test_iterate.js +++ /dev/null @@ -1,54 +0,0 @@ -/* ***** 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 code. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jeff Walden (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -function keys(o) -{ - for (var p in o); -} - -function run_test() -{ - var o1 = { 1: 1, 2: 2, 3: 3, 4: 4 }; - var o2 = new XPCSafeJSObjectWrapper({ a: 'a', b: 'b', c: 'c' }); - var o3 = { 5: 5, 6: 6, 7: 7, 8: 8 }; - - keys(o1); - keys(o2); - keys(o3); - - do_check_true(true); -} From b854afb97058c33531898f024f4f0be43cf99d13 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:42:04 -0700 Subject: [PATCH 196/284] bug 580128 - Fix getting doubled wrapped objects in XPConnect. r=mrbkap --- js/src/xpconnect/loader/mozJSComponentLoader.cpp | 11 +++++++++++ js/src/xpconnect/src/xpcwrappednativejsops.cpp | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/js/src/xpconnect/loader/mozJSComponentLoader.cpp b/js/src/xpconnect/loader/mozJSComponentLoader.cpp index 60d4d0fb7fbc..119ce84796a5 100644 --- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp @@ -1363,9 +1363,20 @@ mozJSComponentLoader::Import(const nsACString & registryLocation) targetObject = JS_GetGlobalForObject(cx, targetObject); } + JSAutoEnterCompartment ac; + if (!ac.enter(cx, targetObject)) { + NS_ERROR("can't enter compartment"); + return NS_ERROR_FAILURE; + } + JSObject *globalObj = nsnull; rv = ImportInto(registryLocation, targetObject, cc, &globalObj); + if (!JS_WrapObject(cx, &globalObj)) { + NS_ERROR("can't wrap return value"); + return NS_ERROR_FAILURE; + } + jsval *retval = nsnull; cc->GetRetValPtr(&retval); if (retval) diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index f96cf34e4b03..3a0d49c89f08 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -186,6 +186,10 @@ GetDoubleWrappedJSObject(XPCCallContext& ccx, XPCWrappedNative* wrapper) jsid id = ccx.GetRuntime()-> GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT); + JSAutoEnterCompartment ac; + if(!ac.enter(ccx, mainObj)) + return NULL; + jsval val; if(JS_GetPropertyById(ccx, mainObj, id, &val) && !JSVAL_IS_PRIMITIVE(val)) @@ -258,7 +262,7 @@ XPC_WN_DoubleWrappedGetter(JSContext *cx, uintN argc, jsval *vp) } } *vp = OBJECT_TO_JSVAL(realObject); - return JS_TRUE; + return JS_WrapValue(cx, vp); } /***************************************************************************/ From af2b530a4d86cde21fccc652dfc159c4fb68c7db Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:42:09 -0700 Subject: [PATCH 197/284] bug 580128 - Disallow cross-compartment adoptNode. r=jst --- content/base/src/nsNodeUtils.cpp | 7 +++++ content/base/test/Makefile.in | 3 +++ content/base/test/file_bug601803a.html | 22 ++++++++++++++++ content/base/test/file_bug601803b.html | 11 ++++++++ content/base/test/test_bug601803.html | 36 ++++++++++++++++++++++++++ 5 files changed, 79 insertions(+) create mode 100644 content/base/test/file_bug601803a.html create mode 100644 content/base/test/file_bug601803b.html create mode 100644 content/base/test/test_bug601803.html diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index c7008ec36a98..84208cff0c7c 100644 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -61,6 +61,8 @@ #include "nsHTMLMediaElement.h" #endif // MOZ_MEDIA #include "nsImageLoadingContent.h" +#include "jsobj.h" +#include "jsgc.h" using namespace mozilla::dom; @@ -486,6 +488,11 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep, } } else if (nodeInfoManager) { + // FIXME Bug 601803 Need to support adopting a node cross-compartment + if (aCx && aOldScope->compartment() != aNewScope->compartment()) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + nsIDocument* oldDoc = aNode->GetOwnerDoc(); PRBool wasRegistered = PR_FALSE; if (oldDoc && aNode->IsElement()) { diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index 37c3722c28b4..bd5f8544484c 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -426,6 +426,9 @@ _TEST_FILES2 = \ test_bug600466.html \ test_bug600468.html \ test_bug600471.html \ + test_bug601803.html \ + file_bug601803a.html \ + file_bug601803b.html \ $(NULL) # This test fails on the Mac for some reason diff --git a/content/base/test/file_bug601803a.html b/content/base/test/file_bug601803a.html new file mode 100644 index 000000000000..7d016558c197 --- /dev/null +++ b/content/base/test/file_bug601803a.html @@ -0,0 +1,22 @@ + + + + Test for Bug 601803 + + + + + + diff --git a/content/base/test/file_bug601803b.html b/content/base/test/file_bug601803b.html new file mode 100644 index 000000000000..0363654c06b1 --- /dev/null +++ b/content/base/test/file_bug601803b.html @@ -0,0 +1,11 @@ + + + + Test for Bug 601803 + + + + + diff --git a/content/base/test/test_bug601803.html b/content/base/test/test_bug601803.html new file mode 100644 index 000000000000..18744efa2230 --- /dev/null +++ b/content/base/test/test_bug601803.html @@ -0,0 +1,36 @@ + + + + + Test for Bug 601803 + + + + + +Mozilla Bug 601803 +

+ +
+
+
+ + From 48b273e97d26eebfc1adfad04ce0ce0cf06bc968 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:42:12 -0700 Subject: [PATCH 198/284] bug 580128 - Use scopes when we have them to wrap into the right scopes. r=peterv --- js/src/xpconnect/src/xpcconvert.cpp | 24 +++++++++++++++++++----- js/src/xpconnect/src/xpcvariant.cpp | 8 ++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 838b868f0541..2a35b8b40844 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -288,11 +288,25 @@ XPCConvert::NativeData2JS(XPCLazyCallContext& lccx, jsval* d, const void* s, } case nsXPTType::T_JSVAL : - JS_STATIC_ASSERT(sizeof(jsval) <= sizeof(uint64)); - *d = **((jsval**)s); - if (!JS_WrapValue(cx, d)) - return JS_FALSE; - break; + { + JS_STATIC_ASSERT(sizeof(jsval) <= sizeof(uint64)); + *d = **((jsval**)s); + + JSAutoEnterCompartment ac; + XPCCallContext &ccx = lccx.GetXPCCallContext(); + if(ccx.GetXPCContext()->CallerTypeIsNative()) + { + JSObject *jsscope = ccx.GetCallee(); + if(!jsscope || !JS_ObjectIsFunction(ccx, jsscope)) + jsscope = JS_GetGlobalForObject(ccx, scope); + if(!ac.enter(ccx, jsscope)) + return JS_FALSE; + } + + if(!JS_WrapValue(cx, d)) + return JS_FALSE; + break; + } default: if(!type.IsPointer()) diff --git a/js/src/xpconnect/src/xpcvariant.cpp b/js/src/xpconnect/src/xpcvariant.cpp index 5df1b4c45ae8..8f3ad86ec8fd 100644 --- a/js/src/xpconnect/src/xpcvariant.cpp +++ b/js/src/xpconnect/src/xpcvariant.cpp @@ -425,7 +425,9 @@ XPCVariant::VariantDataToJS(XPCLazyCallContext& lccx, { // It's not a JSObject (or it's a JSArray or a JSObject representing an // nsID). Just pass through the underlying data. - if (!JS_WrapValue(lccx.GetJSContext(), &realVal)) + JSAutoEnterCompartment ac; + JSContext *cx = lccx.GetJSContext(); + if(!ac.enter(cx, scope) || !JS_WrapValue(cx, &realVal)) return JS_FALSE; *pJSVal = realVal; return JS_TRUE; @@ -438,7 +440,9 @@ XPCVariant::VariantDataToJS(XPCLazyCallContext& lccx, type == nsIDataType::VTYPE_INTERFACE_IS, "Weird variant"); - if (!JS_WrapValue(lccx.GetJSContext(), &realVal)) + JSAutoEnterCompartment ac; + JSContext *cx = lccx.GetJSContext(); + if(!ac.enter(cx, scope) || !JS_WrapValue(cx, &realVal)) return JS_FALSE; *pJSVal = realVal; return JS_TRUE; From facf459aa75e0e847ced9a6b7f71bff8da057a6d Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:42:15 -0700 Subject: [PATCH 199/284] bug 580128 - Only expose same-compartment window.wrappedJSObject to chrome callers. r=jst --- dom/base/nsDOMClassInfo.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 98432a98ec3d..1f0155f234d4 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -44,6 +44,7 @@ #include "jsobj.h" #include "jsdbgapi.h" #include "WrapperFactory.h" +#include "AccessCheck.h" #include "nscore.h" #include "nsDOMClassInfo.h" @@ -5290,7 +5291,8 @@ nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } } - if (id == sWrappedJSObject_id) { + if (id == sWrappedJSObject_id && + xpc::AccessCheck::isChrome(cx->compartment)) { OBJ_TO_OUTER_OBJECT(cx, obj); *vp = OBJECT_TO_JSVAL(obj); return NS_SUCCESS_I_DID_SOMETHING; From 5631a85e40facc7a24fe04de56d1ca0508e51214 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:42:18 -0700 Subject: [PATCH 200/284] bug 580128 - Distinguish between XOW XrayWrappers and XPCNW XrayWrappers and use the new knowledge in a few places. r=gal --- dom/base/nsDOMClassInfo.cpp | 3 ++- js/src/xpconnect/tests/chrome/test_wrappers-2.xul | 2 ++ js/src/xpconnect/wrappers/FilteringWrapper.cpp | 6 ++++-- js/src/xpconnect/wrappers/WrapperFactory.h | 7 ++++++- js/src/xpconnect/wrappers/XrayWrapper.cpp | 10 +++++----- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 1f0155f234d4..d614f02bd612 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -6576,7 +6576,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSString *str = JSID_TO_STRING(id); - { + if (!xpc::WrapperFactory::IsXrayWrapper(obj) || + xpc::WrapperFactory::IsPartiallyTransparent(obj)) { nsCOMPtr dsn(do_QueryInterface(win->GetDocShell())); PRInt32 count = 0; diff --git a/js/src/xpconnect/tests/chrome/test_wrappers-2.xul b/js/src/xpconnect/tests/chrome/test_wrappers-2.xul index 2ae2892c3d47..2613fbac125d 100644 --- a/js/src/xpconnect/tests/chrome/test_wrappers-2.xul +++ b/js/src/xpconnect/tests/chrome/test_wrappers-2.xul @@ -34,6 +34,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 let wrap = $('ifr').contentWindow.wrappedJSObject; ok(!('obj' in XPCNativeWrapper(wrap)), "XPCNativeWrapper constructor works"); let iwin = $('ifr').contentWindow; + is(iwin.document.ELEMENT_NODE, 1, 'constants work through XrayWrapper'); + is(iwin.document.nodeName, "#document", 'attributes work through XrayWrappe location.foopy = 3; diff --git a/js/src/xpconnect/wrappers/FilteringWrapper.cpp b/js/src/xpconnect/wrappers/FilteringWrapper.cpp index e3b6716b39aa..1622408d3839 100644 --- a/js/src/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/src/xpconnect/wrappers/FilteringWrapper.cpp @@ -161,8 +161,10 @@ FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, template<> SOW SOW::singleton(0); template<> COW COW::singleton(0); -template<> XOW XOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG); -template<> NNXOW NNXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG); +template<> XOW XOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG | + WrapperFactory::PARTIALLY_TRANSPARENT); +template<> NNXOW NNXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG | + WrapperFactory::PARTIALLY_TRANSPARENT); template<> LW LW::singleton(0); template<> XLW XLW::singleton(0); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.h b/js/src/xpconnect/wrappers/WrapperFactory.h index f5183860af10..ab4103568042 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.h +++ b/js/src/xpconnect/wrappers/WrapperFactory.h @@ -46,7 +46,8 @@ class WrapperFactory { public: enum { WAIVE_XRAY_WRAPPER_FLAG = (1<<0), IS_XRAY_WRAPPER_FLAG = (1<<1), - SCRIPT_ACCESS_ONLY_FLAG = (1<<2) }; + SCRIPT_ACCESS_ONLY_FLAG = (1<<2), + PARTIALLY_TRANSPARENT = (1<<3) }; // Return true if any of any of the nested wrappers have the flag set. static bool HasWrapperFlag(JSObject *wrapper, uintN flag) { @@ -59,6 +60,10 @@ class WrapperFactory { return HasWrapperFlag(wrapper, IS_XRAY_WRAPPER_FLAG); } + static bool IsPartiallyTransparent(JSObject *wrapper) { + return HasWrapperFlag(wrapper, PARTIALLY_TRANSPARENT); + } + // Prepare a given object for wrapping in a new compartment. static JSObject *PrepareForWrapping(JSContext *cx, JSObject *scope, diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index f95b474aded3..b524a4383c79 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -498,7 +498,7 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe } // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (UniversalXPConnect()) { + if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); { @@ -538,7 +538,7 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid JSPropertyDescriptor *jsdesc = Jsvalify(desc); // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (UniversalXPConnect()) { + if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); JSAutoEnterCompartment ac; @@ -578,7 +578,7 @@ XrayWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, JSObject *holder = GetHolder(wrapper); // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (UniversalXPConnect()) { + if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); JSAutoEnterCompartment ac; @@ -600,7 +600,7 @@ XrayWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bo JSBool b; // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (UniversalXPConnect()) { + if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); JSAutoEnterCompartment ac; @@ -626,7 +626,7 @@ XrayWrapper::enumerate(JSContext *cx, JSObject *wrapper, js::AutoI JSObject *holder = GetHolder(wrapper); // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (UniversalXPConnect()) { + if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); JSAutoEnterCompartment ac; From 316a5324f1006642a1729c42da3b64fd8d0a17d0 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:42:21 -0700 Subject: [PATCH 201/284] bug 580128 - Make document.domain affect how we look up properties in addition to the security check. r=gal --- js/src/xpconnect/wrappers/XrayWrapper.cpp | 64 +++++++++++++++++++---- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index b524a4383c79..fbf5e3ee2ddc 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -427,8 +427,11 @@ class AutoLeaveHelper }; static bool -UniversalXPConnect() +Transparent(JSContext *cx, JSObject *wrapper) { + if (!WrapperFactory::IsPartiallyTransparent(wrapper)) + return false; + // Redirect access straight to the wrapper if UniversalXPConnect is enabled. nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (ssm) { @@ -436,6 +439,49 @@ UniversalXPConnect() if (NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) && privileged) return true; } + + JSObject *scope = nsnull; + JSStackFrame *fp = nsnull; + JS_FrameIterator(cx, &fp); + if (fp) { + while (fp->isDummyFrame()) { + if (!JS_FrameIterator(cx, &fp)) + break; + } + + if (fp) + scope = &fp->scopeChain(); + } + + if (!scope) + scope = JS_GetScopeChain(cx); + + nsIPrincipal *subject; + nsIPrincipal *object; + + nsIXPConnect *xpc = nsXPConnect::GetXPConnect(); + { + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, scope)) + return false; + + subject = xpc->GetPrincipal(JS_GetGlobalForObject(cx, scope), PR_TRUE); + } + + { + JSAutoEnterCompartment ac; + + JSObject *obj = wrapper->unwrap(); + if (!ac.enter(cx, obj)) + return false; + + object = xpc->GetPrincipal(JS_GetGlobalForObject(cx, obj), PR_TRUE); + } + + PRBool subsumes; + if (NS_SUCCEEDED(subject->Subsumes(object, &subsumes)) && subsumes) + return true; return false; } @@ -497,8 +543,8 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe return true; } - // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { + // Redirect access straight to the wrapper if we are transparent. + if (Transparent(cx, wrapper)) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); { @@ -538,7 +584,7 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid JSPropertyDescriptor *jsdesc = Jsvalify(desc); // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { + if (Transparent(cx, wrapper)) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); JSAutoEnterCompartment ac; @@ -578,7 +624,7 @@ XrayWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, JSObject *holder = GetHolder(wrapper); // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { + if (Transparent(cx, wrapper)) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); JSAutoEnterCompartment ac; @@ -599,8 +645,8 @@ XrayWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bo jsval v; JSBool b; - // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { + // Redirect access straight to the wrapper if we should be transparent. + if (Transparent(cx, wrapper)) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); JSAutoEnterCompartment ac; @@ -625,8 +671,8 @@ XrayWrapper::enumerate(JSContext *cx, JSObject *wrapper, js::AutoI { JSObject *holder = GetHolder(wrapper); - // Redirect access straight to the wrapper if UniversalXPConnect is enabled. - if (WrapperFactory::IsPartiallyTransparent(wrapper) && UniversalXPConnect()) { + // Redirect access straight to the wrapper if we are transparent. + if (Transparent(cx, wrapper)) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); JSAutoEnterCompartment ac; From 87581aa250e5a9ca93b2f50ff143af2ee1e50e61 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:42:23 -0700 Subject: [PATCH 202/284] Bug 580128. Resolve class names on the window's own JS object, not on the passed in object. r=peterv@propagandism.org --- dom/base/nsDOMClassInfo.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index d614f02bd612..30018934c687 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -4598,8 +4598,7 @@ GetExternalClassInfo(nsScriptNameSpaceManager *aNameSpaceManager, static nsresult ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx, - JSObject *obj, const PRUnichar *name, - const nsDOMClassInfoData *ci_data, + const PRUnichar *name, const nsDOMClassInfoData *ci_data, const nsGlobalNameStruct *name_struct, nsScriptNameSpaceManager *nameSpaceManager, JSObject *dot_prototype, PRBool install, PRBool *did_resolve); @@ -4698,9 +4697,8 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto) NS_ENSURE_TRUE(nameSpaceManager, NS_OK); PRBool unused; - return ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16, - mData, nsnull, nameSpaceManager, proto, !found, - &unused); + return ResolvePrototype(sXPConnect, win, cx, mData->mNameUTF16, mData, + nsnull, nameSpaceManager, proto, !found, &unused); } // static @@ -6047,8 +6045,7 @@ GetXPCProto(nsIXPConnect *aXPConnect, JSContext *cx, nsGlobalWindow *aWin, // eTypeClassProto. static nsresult ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx, - JSObject *obj, const PRUnichar *name, - const nsDOMClassInfoData *ci_data, + const PRUnichar *name, const nsDOMClassInfoData *ci_data, const nsGlobalNameStruct *name_struct, nsScriptNameSpaceManager *nameSpaceManager, JSObject *dot_prototype, PRBool install, PRBool *did_resolve) @@ -6058,6 +6055,13 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx, name_struct->mType == nsGlobalNameStruct::eTypeClassProto), "Wrong type or missing ci_data!"); + JSObject *obj = aWin->FastGetGlobalJSObject(); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, obj)) { + return NS_ERROR_UNEXPECTED; + } + nsRefPtr constructor; nsresult rv = nsDOMConstructor::Create(name, ci_data, name_struct, aWin, getter_AddRefs(constructor)); @@ -6300,7 +6304,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, ci_data = name_struct->mData; } - return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, ci_data, + return ResolvePrototype(sXPConnect, aWin, cx, class_name, ci_data, name_struct, nameSpaceManager, dot_prototype, PR_TRUE, did_resolve); } @@ -6313,7 +6317,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, if (name_struct->mType == nsGlobalNameStruct::eTypeClassProto) { // We don't have a XPConnect prototype object, let ResolvePrototype create // one. - return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, nsnull, + return ResolvePrototype(sXPConnect, aWin, cx, class_name, nsnull, name_struct, nameSpaceManager, nsnull, PR_TRUE, did_resolve); } @@ -6344,7 +6348,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, return NS_ERROR_UNEXPECTED; } - return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, ci_data, + return ResolvePrototype(sXPConnect, aWin, cx, class_name, ci_data, name_struct, nameSpaceManager, nsnull, PR_TRUE, did_resolve); } From cf2f642d3231b24dc1775d8d7356142de78f3dfe Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:42:36 -0700 Subject: [PATCH 203/284] bug 580128 - Attempt to fix file:// URIs all ending up in the same compartment. r=peterv --- js/src/xpconnect/src/nsXPConnect.cpp | 37 +++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index d1be4c782b51..b567f335ac04 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -52,11 +52,14 @@ #include "jsscript.h" #include "nsThreadUtilsInternal.h" #include "dom_quickstubs.h" +#include "nsNullPrincipal.h" +#include "nsIURI.h" #include "jstypedarray.h" #include "XrayWrapper.h" #include "WrapperFactory.h" +#include "AccessCheck.h" NS_IMPL_THREADSAFE_ISUPPORTS6(nsXPConnect, nsIXPConnect, @@ -942,8 +945,16 @@ xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, JSObject **global, JSCompartment **compartment) { XPCCompartmentMap& map = nsXPConnect::GetRuntimeInstance()->GetCompartmentMap(); + nsCAutoString local_origin(origin); + if(local_origin.EqualsLiteral("file://") && principal) + { + nsCOMPtr uri; + principal->GetURI(getter_AddRefs(uri)); + uri->GetSpec(local_origin); + } + JSObject *tempGlobal; - if(!map.Get(origin, compartment)) + if(!map.Get(local_origin, compartment)) { JSPrincipals *principals = nsnull; if(principal) @@ -960,13 +971,33 @@ xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, js::SwitchToCompartment sc(cx, *compartment); - JS_SetCompartmentPrivate(cx, *compartment, ToNewCString(origin)); - map.Put(origin, *compartment); + JS_SetCompartmentPrivate(cx, *compartment, ToNewCString(local_origin)); + map.Put(local_origin, *compartment); } else { js::SwitchToCompartment sc(cx, *compartment); +#ifdef DEBUG + if(principal) + { + nsIPrincipal *cprincipal = xpc::AccessCheck::getPrincipal(*compartment); + PRBool equals; + nsresult rv = cprincipal->Equals(principal, &equals); + NS_ASSERTION(NS_SUCCEEDED(rv), "bad Equals implementation"); + if(!equals) + { + nsCOMPtr domain; + cprincipal->GetDomain(getter_AddRefs(domain)); + if(!domain) + { + principal->GetDomain(getter_AddRefs(domain)); + NS_ASSERTION(!domain, "bad mixing"); + } + } + } +#endif + tempGlobal = JS_NewGlobalObject(cx, clasp); if(!tempGlobal) return UnexpectedFailure(NS_ERROR_FAILURE); From 59327634835bb32a8aa95a32ff22e78f4c75b799 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:42:45 -0700 Subject: [PATCH 204/284] bug 580128 - Move test that tests for existence of XrayWrapper's into chrome, and fix lookupMethod bug that was triggered by the test move. r=mrbkap --HG-- rename : js/src/xpconnect/tests/mochitest/test_bug517163.html => js/src/xpconnect/tests/chrome/test_bug517163.xul --- js/src/xpconnect/src/xpccomponents.cpp | 61 +++++++------------ js/src/xpconnect/tests/chrome/Makefile.in | 1 + .../xpconnect/tests/chrome/test_bug517163.xul | 53 ++++++++++++++++ js/src/xpconnect/tests/mochitest/Makefile.in | 1 - .../tests/mochitest/test_bug517163.html | 29 --------- 5 files changed, 76 insertions(+), 69 deletions(-) create mode 100644 js/src/xpconnect/tests/chrome/test_bug517163.xul delete mode 100644 js/src/xpconnect/tests/mochitest/test_bug517163.html diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index bd29442b1be0..b7e1fa7589ce 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -2715,19 +2715,6 @@ nsXPCComponents_Utils::GetSandbox(nsIXPCComponents_utils_Sandbox **aSandbox) return NS_OK; } -static JSBool -MethodWrapper(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *thisobj = JS_THIS_OBJECT(cx, vp); - if (!thisobj) - return JS_FALSE; - - jsval *argv = JS_ARGV(cx, vp); - jsval v; - return JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &v) && - JS_CallFunctionValue(cx, thisobj, v, argc, argv, vp); -} - /* void lookupMethod (); */ NS_IMETHODIMP nsXPCComponents_Utils::LookupMethod() @@ -2833,38 +2820,34 @@ nsXPCComponents_Utils::LookupMethod() if(!iface) return NS_ERROR_XPC_BAD_CONVERT_JS; - // get (and perhaps lazily create) the member's cloned function jsval funval; - if(!member->NewFunctionObject(inner_cc, iface, wrapper->GetFlatJSObject(), - &funval)) - return NS_ERROR_XPC_BAD_CONVERT_JS; + JSFunction *oldfunction; + + { + JSAutoEnterCompartment ac; + + if (!ac.enter(inner_cc, wrapper->GetFlatJSObject())) { + return NS_ERROR_UNEXPECTED; + } + + // get (and perhaps lazily create) the member's cloned function + if(!member->NewFunctionObject(inner_cc, iface, wrapper->GetFlatJSObject(), + &funval)) + return NS_ERROR_XPC_BAD_CONVERT_JS; + + oldfunction = JS_ValueToFunction(inner_cc, funval); + NS_ASSERTION(oldfunction, "Function is not a function"); + } // Stick the function in the return value. This roots it. *retval = funval; // Callers of this method are implicitly buying into - // XPCNativeWrapper-like protection. The easiest way - // to enforce this is to use our own wrapper. - // Note: We use the outer call context to ensure that we wrap - // the function in the right scope. - NS_ASSERTION(JSVAL_IS_OBJECT(funval), "Function is not an object"); - JSContext *outercx; - cc->GetJSContext(&outercx); - JSFunction *oldfunction = JS_ValueToFunction(outercx, funval); - NS_ASSERTION(oldfunction, "Function is not a function"); - - JSFunction *f = JS_NewFunction(outercx, MethodWrapper, - JS_GetFunctionArity(oldfunction), 0, - JS_GetScopeChain(outercx), - JS_GetFunctionName(oldfunction)); - if(!f) - return NS_ERROR_FAILURE; - - JSObject *funobj = JS_GetFunctionObject(f); - if(!JS_SetReservedSlot(outercx, funobj, 0, funval)) - return NS_ERROR_FAILURE; - - *retval = OBJECT_TO_JSVAL(funobj); + // XPCNativeWrapper-like protection. The easiest way to enforce + // this is to let the JS engine wrap the function. + if (!JS_WrapValue(inner_cc, retval)) { + return NS_ERROR_UNEXPECTED; + } // Tell XPConnect that we returned the function through the call context. cc->SetReturnValueWasSet(PR_TRUE); diff --git a/js/src/xpconnect/tests/chrome/Makefile.in b/js/src/xpconnect/tests/chrome/Makefile.in index 42d361fc1fc7..ca3417466978 100644 --- a/js/src/xpconnect/tests/chrome/Makefile.in +++ b/js/src/xpconnect/tests/chrome/Makefile.in @@ -56,6 +56,7 @@ _CHROME_FILES = \ test_wrappers-2.xul \ test_bug484459.xul \ test_cows.xul \ + test_bug517163.xul \ $(NULL) libs:: $(_CHROME_FILES) diff --git a/js/src/xpconnect/tests/chrome/test_bug517163.xul b/js/src/xpconnect/tests/chrome/test_bug517163.xul new file mode 100644 index 000000000000..1cee7b9d95b0 --- /dev/null +++ b/js/src/xpconnect/tests/chrome/test_bug517163.xul @@ -0,0 +1,53 @@ + + + + + + + + + + + Mozilla Bug 517163 + + + + + + + diff --git a/js/src/xpconnect/tests/mochitest/Makefile.in b/js/src/xpconnect/tests/mochitest/Makefile.in index f8738c22f006..b8a12be5fbe8 100644 --- a/js/src/xpconnect/tests/mochitest/Makefile.in +++ b/js/src/xpconnect/tests/mochitest/Makefile.in @@ -68,7 +68,6 @@ _TEST_FILES = bug500931_helper.html \ test_bug504877.html \ test_bug505915.html \ file_bug505915.html \ - test_bug517163.html \ test_bug553407.html \ test_bug560351.html \ test_bug564330.html \ diff --git a/js/src/xpconnect/tests/mochitest/test_bug517163.html b/js/src/xpconnect/tests/mochitest/test_bug517163.html deleted file mode 100644 index ff959ad1ce44..000000000000 --- a/js/src/xpconnect/tests/mochitest/test_bug517163.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Test for Bug 517163 - - - - - -Mozilla Bug 517163 -

- -
-
-
- - From 9d595867c489d6397af27c4836666e879508d775 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:43:01 -0700 Subject: [PATCH 205/284] bug 580128 - The window is named Window, not WindowProxy. r=gal --- dom/base/nsGlobalWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 4eb5006069fe..4051ac7e6b98 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -665,7 +665,7 @@ nsOuterWindowProxy::obj_toString(JSContext *cx, JSObject *proxy) { JS_ASSERT(proxy->isProxy()); - return JS_NewStringCopyZ(cx, "[object WindowProxy]"); + return JS_NewStringCopyZ(cx, "[object Window]"); } nsOuterWindowProxy From 926c79e44106116b30cc689e01e0f992fa5bdc99 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:43:06 -0700 Subject: [PATCH 206/284] bug 580128 - Remove test that no longer works right. r=mrbkap --- js/src/tests/js1_5/extensions/jstests.list | 1 - .../tests/js1_5/extensions/regress-340199.js | 51 ------------------- 2 files changed, 52 deletions(-) delete mode 100644 js/src/tests/js1_5/extensions/regress-340199.js diff --git a/js/src/tests/js1_5/extensions/jstests.list b/js/src/tests/js1_5/extensions/jstests.list index c6b68e0b4470..45c3b06736c2 100644 --- a/js/src/tests/js1_5/extensions/jstests.list +++ b/js/src/tests/js1_5/extensions/jstests.list @@ -54,7 +54,6 @@ script regress-338804-01.js script regress-338804-02.js script regress-338804-03.js script regress-339685.js -script regress-340199.js script regress-341956-01.js script regress-341956-02.js script regress-341956-03.js diff --git a/js/src/tests/js1_5/extensions/regress-340199.js b/js/src/tests/js1_5/extensions/regress-340199.js deleted file mode 100644 index 9c7c7d48b877..000000000000 --- a/js/src/tests/js1_5/extensions/regress-340199.js +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Blake Kaplan - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 340199; -var summary = 'User-defined __iterator__ can be called through XPCNativeWrappers'; -var actual = 'Not called'; -var expect = 'Not called'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -if (typeof window == 'undefined' || - typeof XPCNativeWrapper == 'undefined') -{ - reportCompare("window or XPCNativeWrapper not defined, Test skipped.", - "window or XPCNativeWrapper not defined, Test skipped.", - summary); -} -else -{ - Object.prototype.__iterator__ = - function () { actual = "User code called"; print(actual); }; - - try - { - for (var i in XPCNativeWrapper(window)) - { - try - { - print(i); - } - catch(ex) - { - print(ex); - } - } - } - catch(ex) - { - } - - // prevent this from messing up enumerators when shutting down test. - delete Object.prototype.__iterator__; -} - -reportCompare(expect, actual, summary); From c98f6bc71adc29926d0d0dcbe2d19846db623f51 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:46:01 -0700 Subject: [PATCH 207/284] Bug 580128. Don't preserve the navigator object in cross compartment navigation. r=jst@mozilla.org --- dom/base/nsGlobalWindow.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 4051ac7e6b98..4ab748f3e896 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2052,25 +2052,29 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, NS_ENSURE_SUCCESS(rv, rv); if (navigatorHolder) { - // Restore window.navigator onto the new inner window. + JS_ASSERT(JSVAL_IS_OBJECT(nav)); - ::JS_DefineProperty(cx, newInnerWindow->mJSObject, "navigator", - nav, nsnull, nsnull, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_READONLY); + if (JSVAL_TO_OBJECT(nav)->compartment() == newInnerWindow->mJSObject->compartment()) { + // Restore window.navigator onto the new inner window. - // The Navigator's prototype object keeps a reference to the - // window in which it was first created and can thus cause that - // window to stay alive for too long. Reparenting it here allows - // the window to be collected sooner. - nsIDOMNavigator* navigator = - static_cast(mNavigator); + ::JS_DefineProperty(cx, newInnerWindow->mJSObject, "navigator", + nav, nsnull, nsnull, + JSPROP_ENUMERATE | JSPROP_PERMANENT | + JSPROP_READONLY); - xpc-> - ReparentWrappedNativeIfFound(cx, JSVAL_TO_OBJECT(nav), - newInnerWindow->mJSObject, - navigator, - getter_AddRefs(navigatorHolder)); + // The Navigator's prototype object keeps a reference to the + // window in which it was first created and can thus cause that + // window to stay alive for too long. Reparenting it here allows + // the window to be collected sooner. + nsIDOMNavigator* navigator = + static_cast(mNavigator); + + xpc-> + ReparentWrappedNativeIfFound(cx, JSVAL_TO_OBJECT(nav), + newInnerWindow->mJSObject, + navigator, + getter_AddRefs(navigatorHolder)); + } } } From 48d6b2cef08a6db0a0f34f33cbbb5cc83065eab0 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:46:05 -0700 Subject: [PATCH 208/284] bug 580128 - Make e4x anyname etc be per compartment, not in the default compartment. r=mrbkap --- js/src/jscntxt.h | 10 --- js/src/jscompartment.cpp | 3 +- js/src/jscompartment.h | 10 +++ js/src/jsxml.cpp | 128 ++++++++++++++------------------------- 4 files changed, 56 insertions(+), 95 deletions(-) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index ea9cde121893..c6154f66308b 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1526,16 +1526,6 @@ struct JSRuntime { const char *decimalSeparator; const char *numGrouping; - /* - * Weak references to lazily-created, well-known XML singletons. - * - * NB: Singleton objects must be carefully disconnected from the rest of - * the object graph usually associated with a JSContext's global object, - * including the set of standard class objects. See jsxml.c for details. - */ - JSObject *anynameObject; - JSObject *functionNamespaceObject; - #ifdef JS_THREADSAFE /* Number of threads with active requests and unhandled interrupts. */ volatile int32 interruptCounter; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index c53d77a42f4e..40a06d056eb7 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -52,7 +52,8 @@ using namespace js; using namespace js::gc; JSCompartment::JSCompartment(JSRuntime *rt) - : rt(rt), principals(NULL), data(NULL), marked(false), debugMode(false) + : rt(rt), principals(NULL), data(NULL), marked(false), debugMode(false), + anynameObject(NULL), functionNamespaceObject(NULL) { JS_INIT_CLIST(&scripts); } diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 7add238c7d6c..d2d17748841d 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -77,6 +77,16 @@ struct JS_FRIEND_API(JSCompartment) { /* List all scripts in this compartment. */ JSCList scripts; + /* + * Weak references to lazily-created, well-known XML singletons. + * + * NB: Singleton objects must be carefully disconnected from the rest of + * the object graph usually associated with a JSContext's global object, + * including the set of standard class objects. See jsxml.c for details. + */ + JSObject *anynameObject; + JSObject *functionNamespaceObject; + JSCompartment(JSRuntime *cx); ~JSCompartment(); diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index 1fd25b841429..cfaefd084d27 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -232,8 +232,8 @@ DEFINE_GETTER(NameURI_getter, static void namespace_finalize(JSContext *cx, JSObject *obj) { - if (cx->runtime->functionNamespaceObject == obj) - cx->runtime->functionNamespaceObject = NULL; + if (cx->compartment->functionNamespaceObject == obj) + cx->compartment->functionNamespaceObject = NULL; } static JSBool @@ -339,8 +339,8 @@ static void anyname_finalize(JSContext* cx, JSObject* obj) { /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ - if (cx->runtime->anynameObject == obj) - cx->runtime->anynameObject = NULL; + if (cx->compartment->anynameObject == obj) + cx->compartment->anynameObject = NULL; } static JSBool @@ -7148,44 +7148,32 @@ js_InitXMLClasses(JSContext *cx, JSObject *obj) JSBool js_GetFunctionNamespace(JSContext *cx, Value *vp) { - JSRuntime *rt; JSObject *obj; JSString *prefix, *uri; - /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ - rt = cx->runtime; - obj = rt->functionNamespaceObject; + obj = cx->compartment->functionNamespaceObject; if (!obj) { - JS_LOCK_GC(rt); - obj = rt->functionNamespaceObject; - if (!obj) { - JS_UNLOCK_GC(rt); + JSRuntime *rt = cx->runtime; + prefix = ATOM_TO_STRING(rt->atomState.typeAtoms[JSTYPE_FUNCTION]); + uri = ATOM_TO_STRING(rt->atomState.functionNamespaceURIAtom); + obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE); + if (!obj) + return false; - prefix = ATOM_TO_STRING(rt->atomState.typeAtoms[JSTYPE_FUNCTION]); - uri = ATOM_TO_STRING(rt->atomState.functionNamespaceURIAtom); - obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE); - if (!obj) - return JS_FALSE; + /* + * Avoid entraining any in-scope Object.prototype. The loss of + * Namespace.prototype is not detectable, as there is no way to + * refer to this instance in scripts. When used to qualify method + * names, its prefix and uri references are copied to the QName. + */ + obj->clearProto(); + obj->clearParent(); - /* - * Avoid entraining any in-scope Object.prototype. The loss of - * Namespace.prototype is not detectable, as there is no way to - * refer to this instance in scripts. When used to qualify method - * names, its prefix and uri references are copied to the QName. - */ - obj->clearProto(); - obj->clearParent(); - - JS_LOCK_GC(rt); - if (!rt->functionNamespaceObject) - rt->functionNamespaceObject = obj; - else - obj = rt->functionNamespaceObject; - } - JS_UNLOCK_GC(rt); + cx->compartment->functionNamespaceObject = obj; } vp->setObject(*obj); - return JS_TRUE; + + return true; } /* @@ -7329,66 +7317,38 @@ anyname_toString(JSContext *cx, uintN argc, jsval *vp) JSBool js_GetAnyName(JSContext *cx, jsid *idp) { - JSRuntime *rt; JSObject *obj; - JSBool ok; - /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ - rt = cx->runtime; - obj = rt->anynameObject; + obj = cx->compartment->anynameObject; if (!obj) { - JS_LOCK_GC(rt); - obj = rt->anynameObject; - if (!obj) { - JS_UNLOCK_GC(rt); + JSRuntime *rt = cx->runtime; - /* - * Protect multiple newborns created below, in the do-while(0) - * loop used to ensure that we leave this local root scope. - */ - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; + obj = NewNonFunction(cx, &js_AnyNameClass, NULL, NULL); + if (!obj) + return false; - do { - obj = NewNonFunction(cx, &js_AnyNameClass, NULL, NULL); - if (!obj) { - ok = JS_FALSE; - break; - } - InitXMLQName(obj, rt->emptyString, rt->emptyString, - ATOM_TO_STRING(rt->atomState.starAtom)); - METER(xml_stats.qname); + InitXMLQName(obj, rt->emptyString, rt->emptyString, + ATOM_TO_STRING(rt->atomState.starAtom)); + METER(xml_stats.qname); - /* - * Avoid entraining any Object.prototype found via cx's scope - * chain or global object. This loses the default toString, - * but no big deal: we want to customize toString anyway for - * clearer diagnostics. - */ - if (!JS_DefineFunction(cx, obj, js_toString_str, - anyname_toString, 0, 0)) { - ok = JS_FALSE; - break; - } - JS_ASSERT(!obj->getProto()); - JS_ASSERT(!obj->getParent()); - } while (0); + /* + * Avoid entraining any Object.prototype found via cx's scope + * chain or global object. This loses the default toString, + * but no big deal: we want to customize toString anyway for + * clearer diagnostics. + */ + if (!JS_DefineFunction(cx, obj, js_toString_str, + anyname_toString, 0, 0)) + return false; - js_LeaveLocalRootScopeWithResult(cx, obj); - if (!ok) - return JS_FALSE; + JS_ASSERT(!obj->getProto()); + JS_ASSERT(!obj->getParent()); - JS_LOCK_GC(rt); - if (!rt->anynameObject) - rt->anynameObject = obj; - else - obj = rt->anynameObject; - } - JS_UNLOCK_GC(rt); + cx->compartment->anynameObject = obj; } + *idp = OBJECT_TO_JSID(obj); - return JS_TRUE; + return true; } JSBool From 8bf6a41cb7ef647ad5c45ccb8281f1f7da8585ac Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:46:07 -0700 Subject: [PATCH 209/284] bug 580128 - Fix evalInSandbox. r=mrbkap --- content/xbl/src/nsXBLDocumentInfo.cpp | 2 +- .../document/src/nsXULPrototypeDocument.cpp | 2 +- js/src/xpconnect/src/nsXPConnect.cpp | 12 ++-- js/src/xpconnect/src/xpccomponents.cpp | 55 ++++++++++++++-- js/src/xpconnect/src/xpcjsruntime.cpp | 7 ++- js/src/xpconnect/src/xpcprivate.h | 24 +++++-- js/src/xpconnect/src/xpcpublic.h | 3 +- js/src/xpconnect/src/xpcthreadcontext.cpp | 2 +- .../tests/chrome/test_evalInSandbox.xul | 62 ++++++++++++++----- .../tests/mochitest/file_evalInSandbox.html | 3 + js/src/xpconnect/wrappers/WrapperFactory.cpp | 27 +++++++- 11 files changed, 162 insertions(+), 37 deletions(-) diff --git a/content/xbl/src/nsXBLDocumentInfo.cpp b/content/xbl/src/nsXBLDocumentInfo.cpp index 492efeed5f9b..846a061b4bd2 100644 --- a/content/xbl/src/nsXBLDocumentInfo.cpp +++ b/content/xbl/src/nsXBLDocumentInfo.cpp @@ -332,7 +332,7 @@ nsXBLDocGlobalObject::EnsureScriptEnvironment(PRUint32 aLangID) principal->GetOrigin(getter_Copies(origin)); rv = xpc_CreateGlobalObject(cx, &gSharedGlobalClass, origin, principal, - &mJSObject, &compartment); + false, &mJSObject, &compartment); NS_ENSURE_SUCCESS(rv, nsnull); ::JS_SetGlobalObject(cx, mJSObject); diff --git a/content/xul/document/src/nsXULPrototypeDocument.cpp b/content/xul/document/src/nsXULPrototypeDocument.cpp index 5cd7b7f7822e..6ddcfe5f32e0 100644 --- a/content/xul/document/src/nsXULPrototypeDocument.cpp +++ b/content/xul/document/src/nsXULPrototypeDocument.cpp @@ -738,7 +738,7 @@ nsXULPDGlobalObject::EnsureScriptEnvironment(PRUint32 lang_id) principal->GetOrigin(getter_Copies(origin)); rv = xpc_CreateGlobalObject(cx, &gSharedGlobalClass, origin, principal, - &newGlob, &compartment); + false, &newGlob, &compartment); NS_ENSURE_SUCCESS(rv, nsnull); ::JS_SetGlobalObject(cx, newGlob); diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index b567f335ac04..f8dd80866257 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -942,7 +942,8 @@ static JSClass xpcTempGlobalClass = { nsresult xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, const nsACString &origin, nsIPrincipal *principal, - JSObject **global, JSCompartment **compartment) + bool preferXrays, JSObject **global, + JSCompartment **compartment) { XPCCompartmentMap& map = nsXPConnect::GetRuntimeInstance()->GetCompartmentMap(); nsCAutoString local_origin(origin); @@ -971,7 +972,9 @@ xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, js::SwitchToCompartment sc(cx, *compartment); - JS_SetCompartmentPrivate(cx, *compartment, ToNewCString(local_origin)); + xpc::CompartmentPrivate *priv = + new xpc::CompartmentPrivate(ToNewCString(local_origin), preferXrays); + JS_SetCompartmentPrivate(cx, *compartment, priv); map.Put(local_origin, *compartment); } else @@ -1039,7 +1042,8 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext, JSObject* tempGlobal; nsresult rv = xpc_CreateGlobalObject(ccx, &xpcTempGlobalClass, origin, - aPrincipal, &tempGlobal, &compartment); + aPrincipal, false, &tempGlobal, + &compartment); NS_ENSURE_SUCCESS(rv, rv); JSAutoEnterCompartment ac; @@ -1902,7 +1906,7 @@ nsXPConnect::CreateSandbox(JSContext *cx, nsIPrincipal *principal, jsval rval = JSVAL_VOID; AUTO_MARK_JSVAL(ccx, &rval); - nsresult rv = xpc_CreateSandboxObject(cx, &rval, principal); + nsresult rv = xpc_CreateSandboxObject(cx, &rval, principal, NULL, false); NS_ASSERTION(NS_FAILED(rv) || !JSVAL_IS_PRIMITIVE(rval), "Bad return value from xpc_CreateSandboxObject()!"); diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index b7e1fa7589ce..f59c463c734c 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -50,6 +50,9 @@ #include "xpcJSWeakReference.h" #include "XPCWrapper.h" #include "jsproxy.h" +#include "WrapperFactory.h" +#include "XrayWrapper.h" +#include "nsNullPrincipal.h" #ifdef MOZ_JSLOADER #include "mozJSComponentLoader.h" @@ -3163,7 +3166,8 @@ NS_IMPL_THREADSAFE_RELEASE(nsXPCComponents_utils_Sandbox) #ifndef XPCONNECT_STANDALONE nsresult -xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop) +xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop, JSObject *proto, + bool bypassXray) { // Create the sandbox global object nsresult rv; @@ -3202,11 +3206,20 @@ xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop) nsCAutoString origin("sandbox:"); origin.Append(principalorigin); + nsRefPtr nullPrincipal = new nsNullPrincipal(); + rv = nullPrincipal->Init(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = nullPrincipal->GetOrigin(getter_Copies(principalorigin)); + NS_ENSURE_SUCCESS(rv, rv); + + origin.Append(principalorigin); + JSCompartment *compartment; JSObject *sandbox; - rv = xpc_CreateGlobalObject(cx, &SandboxClass, origin, principal, &sandbox, - &compartment); + rv = xpc_CreateGlobalObject(cx, &SandboxClass, origin, principal, + !bypassXray, &sandbox, &compartment); NS_ENSURE_SUCCESS(rv, rv); js::AutoObjectRooter tvr(cx, sandbox); @@ -3216,6 +3229,24 @@ xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop) if (!ac.enter(cx, sandbox)) return NS_ERROR_XPC_UNEXPECTED; + if (proto) { + bool ok = JS_WrapObject(cx, &proto); + if (!ok) + return NS_ERROR_XPC_UNEXPECTED; + + if (xpc::WrapperFactory::IsXrayWrapper(proto) && bypassXray) { + jsval v; + if (!JS_GetProperty(cx, proto, "wrappedJSObject", &v)) + return NS_ERROR_XPC_UNEXPECTED; + + proto = JSVAL_TO_OBJECT(v); + } + + ok = JS_SetPrototype(cx, sandbox, proto); + if (!ok) + return NS_ERROR_XPC_UNEXPECTED; + } + // Pass on ownership of sop to |sandbox|. if (!JS_SetPrivate(cx, sandbox, sop.forget().get())) { return NS_ERROR_XPC_UNEXPECTED; @@ -3345,7 +3376,23 @@ nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrappe return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); } - rv = xpc_CreateSandboxObject(cx, vp, prinOrSop); + JSObject *proto = nsnull; + bool bypassXray = false; + if (argc > 1) { + if (!JSVAL_IS_OBJECT(argv[1])) + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + + proto = JSVAL_TO_OBJECT(argv[1]); + + if (argc > 2) { + if (!JSVAL_IS_BOOLEAN(argv[2])) + return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); + + bypassXray = JSVAL_TO_BOOLEAN(argv[2]); + } + } + + rv = xpc_CreateSandboxObject(cx, vp, prinOrSop, proto, bypassXray); if (NS_FAILED(rv)) { return ThrowAndFail(rv, cx, _retval); diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index e584b48fffbb..e780a3839166 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -247,8 +247,13 @@ CompartmentCallback(JSContext *cx, JSCompartment *compartment, uintN op) return JS_TRUE; XPCCompartmentMap& map = self->GetCompartmentMap(); + nsAutoPtr priv( + static_cast(JS_SetCompartmentPrivate(cx, compartment, nsnull))); + if (!priv) + return JS_TRUE; + nsAdoptingCString origin; - origin.Adopt(static_cast(JS_SetCompartmentPrivate(cx, compartment, nsnull))); + origin.Adopt(static_cast(priv->origin)); #ifdef DEBUG { diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 125434d10e09..b2a19facae8b 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -59,6 +59,7 @@ #include "jsdbgapi.h" #include "jsgc.h" #include "jscompartment.h" +#include "xpcpublic.h" #include "nscore.h" #include "nsXPCOM.h" #include "nsAutoPtr.h" @@ -3927,11 +3928,6 @@ xpc_DumpJSObject(JSObject* obj); extern JSBool xpc_InstallJSDebuggerKeywordHandler(JSRuntime* rt); -nsresult -xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, - const nsACString &origin, nsIPrincipal *principal, - JSObject **global, JSCompartment **compartment); - /***************************************************************************/ // Definition of nsScriptError, defined here because we lack a place to put @@ -4383,7 +4379,8 @@ xpc_GetGlobalForObject(JSObject *obj) // reachable through prinOrSop, a new null principal will be created // and used. nsresult -xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop); +xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop, + JSObject *proto, bool preferXray); // Helper for evaluating scripts in a sandbox object created with // xpc_CreateSandboxObject(). The caller is responsible of ensuring @@ -4427,6 +4424,21 @@ xpc_SameScope(XPCWrappedNativeScope *objectscope, nsISupports * XPC_GetIdentityObject(JSContext *cx, JSObject *obj); +namespace xpc { + +struct CompartmentPrivate +{ + CompartmentPrivate(char *origin, bool preferXrays) + : origin(origin), + preferXrays(preferXrays) + { + } + char *origin; + bool preferXrays; +}; + +} + #ifdef XPC_IDISPATCH_SUPPORT #ifdef WINCE diff --git a/js/src/xpconnect/src/xpcpublic.h b/js/src/xpconnect/src/xpcpublic.h index f03de6510a0c..77a1bbaddff3 100644 --- a/js/src/xpconnect/src/xpcpublic.h +++ b/js/src/xpconnect/src/xpcpublic.h @@ -47,6 +47,7 @@ nsresult xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, const nsACString &origin, nsIPrincipal *principal, - JSObject **global, JSCompartment **compartment); + bool preferXrays, JSObject **global, + JSCompartment **compartment); #endif diff --git a/js/src/xpconnect/src/xpcthreadcontext.cpp b/js/src/xpconnect/src/xpcthreadcontext.cpp index 7df1d889cf4f..ec4e40c8e8ca 100644 --- a/js/src/xpconnect/src/xpcthreadcontext.cpp +++ b/js/src/xpconnect/src/xpcthreadcontext.cpp @@ -267,7 +267,7 @@ XPCJSContextStack::GetSafeJSContext(JSContext * *aSafeJSContext) JSCompartment *compartment; nsresult rv = xpc_CreateGlobalObject(mSafeJSContext, &global_class, - origin, principal, &glob, + origin, principal, false, &glob, &compartment); if(NS_FAILED(rv)) glob = nsnull; diff --git a/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul b/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul index d09d1cf91632..315bbec6acd4 100644 --- a/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul +++ b/js/src/xpconnect/tests/chrome/test_evalInSandbox.xul @@ -17,8 +17,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=533596 + @@ -29,21 +32,50 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=533596 const utils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); - function go() { - var win = $('ifr').contentWindow; - var sandbox = new Cu.Sandbox(win); - is(utils.getClassName(sandbox), - "Proxy", - "sandbox was wrapped correctly"); - sandbox.__proto__ = new XPCNativeWrapper(win); + var testsRun = 0; + function checkCrossOriginSandbox(sandbox) + { + is(utils.getClassName(sandbox), + "Proxy", + "sandbox was wrapped correctly"); - is(utils.getClassName(Cu.evalInSandbox("this.document", sandbox)), - "Proxy", - "return value was rewrapped correctly"); - ok(Cu.evalInSandbox("('wrappedJSObject' in this.document);", sandbox), - "wrappers inside eIS are XPCNativeWrappers"); + is(utils.getClassName(Cu.evalInSandbox("this.document", sandbox)), + "Proxy", + "return value was rewrapped correctly"); + } + function go(ifr, crossOrigin) { + var win = ifr.contentWindow; + if (crossOrigin) { + var sandbox = new Cu.Sandbox(win, win, false); - SimpleTest.finish(); + checkCrossOriginSandbox(sandbox); + + ok(Cu.evalInSandbox("('wrappedJSObject' in this.document);", sandbox), + "wrappers inside eIS are XPCNativeWrappers"); + ok(Cu.evalInSandbox("!('foo' in this.document);", sandbox), + "must not see expandos"); + + sandbox = new Cu.Sandbox(win, win, true); + + checkCrossOriginSandbox(sandbox); + + ok(Cu.evalInSandbox("('foo' in this.document);", sandbox), + "can see expandos"); + } + else { + var sandbox = new Cu.Sandbox(win, win, false); + + ok(Cu.evalInSandbox("!('foo' in this.document);", sandbox), + "must not see expandos"); + + sandbox = new Cu.Sandbox(win, win, true); + + ok(Cu.evalInSandbox("('foo' in this.document);", sandbox), + "can see expandos"); + } + + if (++testsRun == 2) + SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); diff --git a/js/src/xpconnect/tests/mochitest/file_evalInSandbox.html b/js/src/xpconnect/tests/mochitest/file_evalInSandbox.html index 411d31ac735b..6cb7bea016c3 100644 --- a/js/src/xpconnect/tests/mochitest/file_evalInSandbox.html +++ b/js/src/xpconnect/tests/mochitest/file_evalInSandbox.html @@ -1,4 +1,7 @@ + diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 3a70e1eb4080..6b2023b0e5f4 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -146,7 +146,18 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO JSWrapper *wrapper; if (AccessCheck::isChrome(target)) { if (AccessCheck::isChrome(origin)) { - wrapper = &JSCrossCompartmentWrapper::singleton; + // Same origin we use a transparent wrapper, unless the compartment asks + // for an Xray. + if (static_cast(target->data)->preferXrays && + IS_WN_WRAPPER(obj)) { + typedef XrayWrapper Xray; + wrapper = &Xray::singleton; + xrayHolder = Xray::createHolder(cx, obj, parent); + if (!xrayHolder) + return nsnull; + } else { + wrapper = &JSCrossCompartmentWrapper::singleton; + } } else if (flags & WAIVE_XRAY_WRAPPER_FLAG) { // If we waived the X-ray wrapper for this object, wrap it into a // special wrapper to transitively maintain the X-ray waiver. @@ -177,8 +188,18 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO ExposedPropertiesOnly>::singleton; } } else if (AccessCheck::isSameOrigin(origin, target)) { - // Same origin we use a transparent wrapper; - wrapper = &JSCrossCompartmentWrapper::singleton; + // Same origin we use a transparent wrapper, unless the compartment asks + // for an Xray. + if (static_cast(target->data)->preferXrays && + IS_WN_WRAPPER(obj)) { + typedef XrayWrapper Xray; + wrapper = &Xray::singleton; + xrayHolder = Xray::createHolder(cx, obj, parent); + if (!xrayHolder) + return nsnull; + } else { + wrapper = &JSCrossCompartmentWrapper::singleton; + } } else { // Cross origin we want to disallow scripting and limit access to // a predefined set of properties. XrayWrapper adds a property From 13e55b378837e03bd5237e1230657ada4c7f61bd Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:46:10 -0700 Subject: [PATCH 210/284] Bug 580128. Make the editor hold on to the window it's operating on in nsHTMLDocument::EditingStateChanged() so that the window doesn't go away in the middle of this method. r=peterv@propagandism.org --- content/html/document/src/nsHTMLDocument.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/content/html/document/src/nsHTMLDocument.cpp b/content/html/document/src/nsHTMLDocument.cpp index 26211fb74f9d..a476f328a973 100644 --- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -3231,8 +3231,9 @@ nsHTMLDocument::EditingStateChanged() mParentDocument->FlushPendingNotifications(Flush_Style); } - // get editing session - nsPIDOMWindow *window = GetWindow(); + // get editing session, make sure this is a strong reference so the + // window can't get deleted during the rest of this call. + nsCOMPtr window = GetWindow(); if (!window) return NS_ERROR_FAILURE; From 21bee40f233ad67c1c493d820f78fcc1621c925f Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:46:13 -0700 Subject: [PATCH 211/284] Bug 580128. Disable test that is not ready for the new wrappers yet. r=mrbkap@gmail.com --- js/src/xpconnect/tests/chrome/Makefile.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/src/xpconnect/tests/chrome/Makefile.in b/js/src/xpconnect/tests/chrome/Makefile.in index ca3417466978..9deff4410c27 100644 --- a/js/src/xpconnect/tests/chrome/Makefile.in +++ b/js/src/xpconnect/tests/chrome/Makefile.in @@ -53,11 +53,14 @@ _CHROME_FILES = \ test_evalInSandbox.xul \ test_sandboxImport.xul \ test_wrappers.xul \ - test_wrappers-2.xul \ test_bug484459.xul \ test_cows.xul \ test_bug517163.xul \ $(NULL) +# Disabled until this test gets updated to test the new proxy based +# wrappers. +# test_wrappers-2.xul \ + libs:: $(_CHROME_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) From 7eb649b6d9d654e9db23828c4d1b9d8de82d987b Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:46:16 -0700 Subject: [PATCH 212/284] Bug 580128. Properly switch compartments and wrap values when running xpcshell tests. r=mrbkap@gmail.com --- content/base/src/nsFrameMessageManager.cpp | 32 +++++++++++++----- js/jetpack/JetpackActorCommon.cpp | 4 +++ js/jetpack/JetpackChild.cpp | 33 +++++++++++++------ .../xpconnect/loader/mozJSComponentLoader.cpp | 4 +-- .../xpconnect/loader/mozJSSubScriptLoader.cpp | 12 +++++++ .../xpconnect/src/xpcwrappednativescope.cpp | 6 ++++ 6 files changed, 70 insertions(+), 21 deletions(-) diff --git a/content/base/src/nsFrameMessageManager.cpp b/content/base/src/nsFrameMessageManager.cpp index 2245e2022417..c8624155d8a5 100644 --- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -414,6 +414,11 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, nsAutoGCRoot resultGCRoot3(&thisValue, &rv); NS_ENSURE_SUCCESS(rv, rv); + JSAutoEnterCompartment ac; + + if (!ac.enter(ctx, object)) + return PR_FALSE; + jsval funval = JSVAL_VOID; if (JS_ObjectIsFunction(ctx, object)) { // If the listener is a JS function: @@ -449,15 +454,24 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, js::AutoValueRooter argv(ctx); argv.set(OBJECT_TO_JSVAL(param)); - JSObject* thisObject = JSVAL_TO_OBJECT(thisValue); - JS_CallFunctionValue(ctx, thisObject, - funval, 1, argv.jsval_addr(), &rval); - if (aJSONRetVal) { - nsString json; - if (JS_TryJSON(ctx, &rval) && - JS_Stringify(ctx, &rval, nsnull, JSVAL_NULL, - JSONCreator, &json)) { - aJSONRetVal->AppendElement(json); + { + JSAutoEnterCompartment tac; + + JSObject* thisObject = JSVAL_TO_OBJECT(thisValue); + + if (!tac.enter(ctx, thisObject) || + !JS_WrapValue(ctx, argv.jsval_addr())) + return NS_ERROR_UNEXPECTED; + + JS_CallFunctionValue(ctx, thisObject, + funval, 1, argv.jsval_addr(), &rval); + if (aJSONRetVal) { + nsString json; + if (JS_TryJSON(ctx, &rval) && + JS_Stringify(ctx, &rval, nsnull, JSVAL_NULL, + JSONCreator, &json)) { + aJSONRetVal->AppendElement(json); + } } } } diff --git a/js/jetpack/JetpackActorCommon.cpp b/js/jetpack/JetpackActorCommon.cpp index 4c021f0a686e..d75fc5b5bd9c 100644 --- a/js/jetpack/JetpackActorCommon.cpp +++ b/js/jetpack/JetpackActorCommon.cpp @@ -452,6 +452,10 @@ JetpackActorCommon::RecvMessage(JSContext* cx, JSObject* implGlobal = JS_GetGlobalObject(cx); js::AutoValueRooter rval(cx); + JSAutoEnterCompartment ac; + if (!ac.enter(cx, implGlobal)) + return false; + for (PRUint32 i = 0; i < snapshot.Length(); ++i) { Variant* vp = results ? results->AppendElement() : NULL; rval.set(JSVAL_VOID); diff --git a/js/jetpack/JetpackChild.cpp b/js/jetpack/JetpackChild.cpp index 263cd4cd74ac..b629125b8ff4 100644 --- a/js/jetpack/JetpackChild.cpp +++ b/js/jetpack/JetpackChild.cpp @@ -448,11 +448,15 @@ JetpackChild::CreateSandbox(JSContext* cx, uintN argc, jsval* vp) if (!obj) return JS_FALSE; + jsval rval = OBJECT_TO_JSVAL(obj); + if (!JS_WrapValue(cx, &rval)) + return JS_FALSE; + JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return JS_FALSE; - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); + JS_SET_RVAL(cx, vp, rval); return JS_InitStandardClasses(cx, obj); } @@ -466,23 +470,32 @@ JetpackChild::EvalInSandbox(JSContext* cx, uintN argc, jsval* vp) jsval* argv = JS_ARGV(cx, vp); - JSObject* obj; - if (!JSVAL_IS_OBJECT(argv[0]) || - !(obj = JSVAL_TO_OBJECT(argv[0])) || - &sGlobalClass != JS_GetClass(cx, obj) || - obj == JS_GetGlobalObject(cx)) { - JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox."); - return JS_FALSE; - } - JSString* str = JS_ValueToString(cx, argv[1]); if (!str) return JS_FALSE; + JSObject* obj; + if (!JSVAL_IS_OBJECT(argv[0]) || + !(obj = JSVAL_TO_OBJECT(argv[0]))) { + JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox."); + JS_ASSERT(JS_FALSE); + return JS_FALSE; + } + + // Unwrap, and switch compartments + obj = obj->unwrap(); + JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return JS_FALSE; + if (&sGlobalClass != JS_GetClass(cx, obj) || + obj == JS_GetGlobalObject(cx)) { + JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox."); + JS_ASSERT(JS_FALSE); + return JS_FALSE; + } + js::AutoValueRooter ignored(cx); return JS_EvaluateUCScript(cx, obj, JS_GetStringChars(str), JS_GetStringLength(str), "", 1, ignored.jsval_addr()); diff --git a/js/src/xpconnect/loader/mozJSComponentLoader.cpp b/js/src/xpconnect/loader/mozJSComponentLoader.cpp index 119ce84796a5..2f1bc6f188e4 100644 --- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp @@ -1364,7 +1364,7 @@ mozJSComponentLoader::Import(const nsACString & registryLocation) } JSAutoEnterCompartment ac; - if (!ac.enter(cx, targetObject)) { + if (targetObject && !ac.enter(cx, targetObject)) { NS_ERROR("can't enter compartment"); return NS_ERROR_FAILURE; } @@ -1372,7 +1372,7 @@ mozJSComponentLoader::Import(const nsACString & registryLocation) JSObject *globalObj = nsnull; rv = ImportInto(registryLocation, targetObject, cc, &globalObj); - if (!JS_WrapObject(cx, &globalObj)) { + if (globalObj && !JS_WrapObject(cx, &globalObj)) { NS_ERROR("can't wrap return value"); return NS_ERROR_FAILURE; } diff --git a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp index 38897b0bd9eb..c0c49f8cba38 100644 --- a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -207,6 +207,10 @@ mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL #endif } + // Remember an object out of the calling compartment so that we + // can properly wrap the result later. + JSObject *result_obj = target_obj; + // Innerize the target_obj so that we compile the loaded script in the // correct (inner) scope. if (JSObjectOp op = target_obj->getClass()->ext.innerObject) @@ -383,6 +387,14 @@ mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL ok = JS_EvaluateScriptForPrincipals (cx, target_obj, jsPrincipals, buf, len, uriStr.get(), 1, rval); } + + { + JSAutoEnterCompartment rac; + + if (!rac.enter(cx, result_obj) || !JS_WrapValue(cx, rval)) + return NS_ERROR_UNEXPECTED; + } + /* repent for our evil deeds */ JS_SetErrorReporter (cx, er); diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp index 1152d57d4984..b08cb6a40db4 100644 --- a/js/src/xpconnect/src/xpcwrappednativescope.cpp +++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp @@ -693,6 +693,12 @@ XPCWrappedNativeScope::SystemIsBeingShutDown(JSContext* cx) if(cur->mComponents) cur->mComponents->SystemIsBeingShutDown(); + JSAutoEnterCompartment ac; + + // XXX: What if we have no global in the scope??? + if (cur->mGlobalJSObject) + ac.enter(cx, cur->mGlobalJSObject); + // Walk the protos first. Wrapper shutdown can leave dangling // proto pointers in the proto map. cur->mWrappedNativeProtoMap-> From f06c2d00c7157fea353ff6990d6a1253cc2a5591 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:46:20 -0700 Subject: [PATCH 213/284] Bug 580128. Reset gczeal after test completes. r=gal@uci.edu --- js/src/tests/js1_8_5/regress/regress-563210.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/js/src/tests/js1_8_5/regress/regress-563210.js b/js/src/tests/js1_8_5/regress/regress-563210.js index 319dfa512c4e..8197fea43c71 100644 --- a/js/src/tests/js1_8_5/regress/regress-563210.js +++ b/js/src/tests/js1_8_5/regress/regress-563210.js @@ -17,6 +17,10 @@ if (typeof gczeal != 'undefined' && typeof gc != 'undefined') { { } } + +// Reset gczeal. +gczeal(0) + reportCompare("no assertion failure", "no assertion failure", "bug 563210"); From 93dd9116643ec825d0b44c5bf3a7a6c975cc1f15 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:46:23 -0700 Subject: [PATCH 214/284] bug 580128 - Fix compartment bugs in XrayWrapper. r=mrbkap --- js/src/xpconnect/wrappers/XrayWrapper.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index fbf5e3ee2ddc..ec8dc3de338a 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -266,6 +266,11 @@ ResolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid i } } else if (member->IsAttribute()) { // This is a getter/setter. Clone a function for it. + + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wnObject)) + return false; + if (!member->NewFunctionObject(ccx, iface, wnObject, &fval)) { JS_ReportError(cx, "Failed to clone function object for native getter/setter"); return false; @@ -279,6 +284,10 @@ ResolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid i // for it. This avoids keeping garbage alive through that slot. desc->attrs |= JSPROP_SHARED; } else { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wnObject)) + return false; + // This is a method. Clone a function for it. if (!member->NewFunctionObject(ccx, iface, wnObject, &desc->value)) { JS_ReportError(cx, "Failed to clone function object for native function"); @@ -751,6 +760,8 @@ XrayWrapper::createHolder(JSContext *cx, JSObject *wrappedNative, if (!holder) return nsnull; + JS_ASSERT(IS_WN_WRAPPER(wrappedNative) || + wrappedNative->getClass()->ext.innerObject); holder->setSlot(JSSLOT_WN_OBJ, ObjectValue(*wrappedNative)); holder->setSlot(JSSLOT_RESOLVING, PrivateValue(NULL)); return holder; From c47194b5fc2e47b860a25b065dee6c1ee029c1f1 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:46:26 -0700 Subject: [PATCH 215/284] Bug 580128 - Fix NativeWrapper(window).document. r=mrbkap --- js/src/xpconnect/wrappers/XrayWrapper.cpp | 40 ++++++++++++----------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index ec8dc3de338a..f9b1bfa6daec 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -533,6 +533,27 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe if (!Policy::enter(cx, wrapper, &id, set ? JSWrapper::SET : JSWrapper::GET, &priv)) return false; + // Redirect access straight to the wrapper if we are transparent. + if (Transparent(cx, wrapper)) { + Policy::leave(cx, wrapper, priv); + + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); + + { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wnObject)) + return false; + + if (!JS_GetPropertyDescriptorById(cx, wnObject, id, + (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED, + desc)) + return false; + } + + desc->obj = wrapper; + return cx->compartment->wrap(cx, desc_in); + } + bool ok = ResolveNativeProperty(cx, wrapper, holder, id, false, desc); Policy::leave(cx, wrapper, priv); if (!ok || desc->obj) @@ -552,25 +573,6 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe return true; } - // Redirect access straight to the wrapper if we are transparent. - if (Transparent(cx, wrapper)) { - JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); - - { - JSAutoEnterCompartment ac; - if (!ac.enter(cx, wnObject)) - return false; - - if (!JS_GetPropertyDescriptorById(cx, wnObject, id, - (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED, - desc)) - return false; - } - - desc->obj = wrapper; - return cx->compartment->wrap(cx, desc_in); - } - return JS_GetPropertyDescriptorById(cx, holder, id, (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED, desc); From 048f3113215a083e310445fd8cc442b67c6d654f Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:46:34 -0700 Subject: [PATCH 216/284] bug 580128 - Initialize vp before calling property op in JSProxyHandler::get. r=mrbkap --- js/src/jsproxy.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 85877cda3bc0..166771525de5 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -132,6 +132,8 @@ JSProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, } if (!(desc.attrs & JSPROP_SHARED)) *vp = desc.value; + else + vp->setUndefined(); if (desc.attrs & JSPROP_SHORTID) id = INT_TO_JSID(desc.shortid); return CallJSPropertyOp(cx, desc.getter, proxy, id, vp); From f5a023dff11abeaa1aebba7a89d32111979e392f Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:46:42 -0700 Subject: [PATCH 217/284] bug 580128 - Fix js_PrintObjectSlotName. r=mrbkap --- js/src/jsobj.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 7e740bad3841..2cfd02b6c6f0 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -6139,6 +6139,8 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) shape = obj->lastProperty(); while (shape->previous() && shape->slot != slot) shape = shape->previous(); + if (shape->slot != slot) + shape = NULL; } else { shape = NULL; } From 49f5066dde2e1a44060c720985df8537b3067848 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:46:45 -0700 Subject: [PATCH 218/284] Bug 580128. Remove XrayUtils::JSSLOT_PROXY_OBJ to fix leaks. r=mrbkap. --- js/src/xpconnect/wrappers/WrapperFactory.cpp | 2 -- js/src/xpconnect/wrappers/XrayWrapper.cpp | 8 +------- js/src/xpconnect/wrappers/XrayWrapper.h | 1 - 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 6b2023b0e5f4..08b7d579db56 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -234,7 +234,6 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO // NB: The fact that the only wrappers to use ProxyExtra are XrayWrappers // is relied on by XPCNativeWrapper.unwrap. wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder)); - xrayHolder->setSlot(XrayUtils::JSSLOT_PROXY_OBJ, js::ObjectValue(*wrapperObj)); return wrapperObj; } @@ -258,7 +257,6 @@ WrapperFactory::WrapLocationObject(JSContext *cx, JSObject *obj) if (!wrapperObj) return NULL; wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder)); - xrayHolder->setSlot(XrayUtils::JSSLOT_PROXY_OBJ, js::ObjectValue(*wrapperObj)); return wrapperObj; } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index f9b1bfa6daec..8d6cd4b9b73a 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -56,12 +56,6 @@ using namespace js; static const uint32 JSSLOT_WN_OBJ = JSSLOT_PRIVATE; static const uint32 JSSLOT_RESOLVING = JSSLOT_PRIVATE + 1; -namespace XrayUtils { - -const uint32 JSSLOT_PROXY_OBJ = JSSLOT_PRIVATE + 2; - -} - class ResolvingId { public: @@ -113,7 +107,7 @@ namespace XrayUtils { JSClass HolderClass = { "NativePropertyHolder", - JSCLASS_HAS_RESERVED_SLOTS(3), + JSCLASS_HAS_RESERVED_SLOTS(2), JS_PropertyStub, JS_PropertyStub, holder_get, holder_set, holder_enumerate, JS_ResolveStub, JS_ConvertStub, NULL, NULL, NULL, NULL, NULL, diff --git a/js/src/xpconnect/wrappers/XrayWrapper.h b/js/src/xpconnect/wrappers/XrayWrapper.h index 074cd6e90742..751c2ee5e44b 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.h +++ b/js/src/xpconnect/wrappers/XrayWrapper.h @@ -51,7 +51,6 @@ namespace xpc { namespace XrayUtils { extern JSClass HolderClass; -extern const uint32 JSSLOT_PROXY_OBJ; } From 030ab615a576e374a99115a84abdc9cc2f7eab1f Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:46:59 -0700 Subject: [PATCH 219/284] bug 580128 - Revert bogus change to which scope we use in nsXPCWrappedJSClass::CallMethod. r=jst Many thanks to Margaret and sdwilsh who helped track this one down. --- js/src/xpconnect/src/xpcwrappedjsclass.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/js/src/xpconnect/src/xpcwrappedjsclass.cpp b/js/src/xpconnect/src/xpcwrappedjsclass.cpp index 6b2d5386dc7f..ed890a60bb3c 100644 --- a/js/src/xpconnect/src/xpcwrappedjsclass.cpp +++ b/js/src/xpconnect/src/xpcwrappedjsclass.cpp @@ -1509,8 +1509,6 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, // about to call, and that's the global that we want here. In other words: // we're trusting the JS engine to come up with a good global to use for // our object (whatever it was). - JSObject *scopeobj; - scopeobj = JS_GetGlobalForScopeChain(ccx); for(i = 0; i < argc; i++) { const nsXPTParamInfo& param = info->params[i]; @@ -1571,7 +1569,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, if(!XPCConvert::NativeArray2JS(lccx, &val, (const void**)&pv->val, datum_type, ¶m_iid, - array_count, scopeobj, nsnull)) + array_count, obj, nsnull)) goto pre_call_clean_up; } else if(isSizedString) @@ -1585,7 +1583,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, else { if(!XPCConvert::NativeData2JS(ccx, &val, &pv->val, type, - ¶m_iid, scopeobj, nsnull)) + ¶m_iid, obj, nsnull)) goto pre_call_clean_up; } } @@ -1593,7 +1591,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex, if(param.IsOut() || param.IsDipper()) { // create an 'out' object - JSObject* out_obj = NewOutObject(cx, scopeobj); + JSObject* out_obj = NewOutObject(cx, obj); if(!out_obj) { retval = NS_ERROR_OUT_OF_MEMORY; From 9197de0c1ce8eabbfce381c3f46e8187a801f242 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:47:06 -0700 Subject: [PATCH 220/284] Bug 580128. Remove unnecessary .wrappedJSObject gets that don't work with the new wrappers. r=jst@mozilla.com --- browser/components/sessionstore/src/nsSessionStore.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js index bc7c103346e5..31cfe6b0ad63 100644 --- a/browser/components/sessionstore/src/nsSessionStore.js +++ b/browser/components/sessionstore/src/nsSessionStore.js @@ -1690,7 +1690,7 @@ SessionStoreService.prototype = { aTabData._formDataSaved = true; if (aBrowser.currentURI.spec == "about:config") aTabData.entries[tabIndex].formdata = { - "#textbox": aBrowser.contentDocument.getElementById("textbox").wrappedJSObject.value + "#textbox": aBrowser.contentDocument.getElementById("textbox").value }; }, @@ -2836,11 +2836,6 @@ SessionStoreService.prototype = { // away before the loading completed (except for in-page navigation) if (hasExpectedURL(aEvent.originalTarget, aBrowser.__SS_restore_data.url)) { var content = aEvent.originalTarget.defaultView; - if (aBrowser.currentURI.spec == "about:config") { - // unwrap the document for about:config because otherwise the properties - // of the XBL bindings - as the textbox - aren't accessible (see bug 350718) - content = content.wrappedJSObject; - } restoreTextDataAndScrolling(content, aBrowser.__SS_restore_data, ""); aBrowser.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle"; From 9179a3939620365c78cc6cfaadb9c7f036478d60 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 10 Oct 2010 15:47:09 -0700 Subject: [PATCH 221/284] Fix bug 602574 - Assertion failure: constOffset != 0 in JSScript::NewScript() on 64-bit platforms. r=gal@uci.edu --- js/src/jsscript.h | 38 ++++------------------------------ js/src/methodjit/Compiler.cpp | 2 +- js/src/methodjit/MethodJIT.cpp | 2 -- js/src/methodjit/MethodJIT.h | 26 +++++++++++++++++++++++ 4 files changed, 31 insertions(+), 37 deletions(-) diff --git a/js/src/jsscript.h b/js/src/jsscript.h index bd7908712263..1f45d3a34151 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -296,44 +296,14 @@ struct JSScript { js::mjit::JITScript *jitNormal; /* Extra JIT info for normal scripts */ js::mjit::JITScript *jitCtor; /* Extra JIT info for constructors */ - void **nmapNormal; - void **nmapCtor; - bool hasJITCode() { return jitNormal || jitCtor; } - void setNativeMap(bool constructing, void **map) { - if (constructing) - nmapCtor = map; - else - nmapNormal = map; - } - - void **maybeNativeMap(bool constructing) { - return constructing ? nmapCtor : nmapNormal; - } - - void **nativeMap(bool constructing) { - void **nmap = maybeNativeMap(constructing); - JS_ASSERT(nmap); - return nmap; - } - - void *maybeNativeCodeForPC(bool constructing, jsbytecode *pc) { - void **nmap = maybeNativeMap(constructing); - if (!nmap) - return NULL; - JS_ASSERT(pc >= code && pc < code + length); - return nmap[pc - code]; - } - - void *nativeCodeForPC(bool constructing, jsbytecode *pc) { - void **nmap = nativeMap(constructing); - JS_ASSERT(pc >= code && pc < code + length); - JS_ASSERT(nmap[pc - code]); - return nmap[pc - code]; - } + // These methods are implemented in MethodJIT.h. + inline void **nativeMap(bool constructing); + inline void *maybeNativeCodeForPC(bool constructing, jsbytecode *pc); + inline void *nativeCodeForPC(bool constructing, jsbytecode *pc); js::mjit::JITScript *getJIT(bool constructing) { return constructing ? jitCtor : jitNormal; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 37a97f13a407..cef97df52df9 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -590,7 +590,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp) JS_ASSERT(size_t(cursor - (uint8*)jit) == totalBytes); - script->setNativeMap(isConstructing, nmap); + jit->nmap = nmap; *jitp = jit; return Compile_Okay; diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 566d5cc1095d..f8e77216c662 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -835,7 +835,6 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) script->jitArityCheckNormal = NULL; cx->free(script->jitNormal); script->jitNormal = NULL; - script->nmapNormal = NULL; } if (script->jitCtor) { @@ -843,7 +842,6 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script) script->jitArityCheckCtor = NULL; cx->free(script->jitCtor); script->jitCtor = NULL; - script->nmapCtor = NULL; } } diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 31b01cdc18a9..eef8a81855f3 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -187,6 +187,7 @@ struct CallSite; struct JITScript { typedef JSC::MacroAssemblerCodeRef CodeRef; CodeRef code; /* pool & code addresses */ + void **nmap; /* pc -> JIT code map, sparse */ js::mjit::CallSite *callSites; uint32 nCallSites; @@ -268,6 +269,31 @@ struct CallSite } /* namespace js */ +inline void * +JSScript::maybeNativeCodeForPC(bool constructing, jsbytecode *pc) +{ + js::mjit::JITScript *jit = getJIT(constructing); + if (!jit) + return NULL; + JS_ASSERT(pc >= code && pc < code + length); + return jit->nmap[pc - code]; +} + +inline void ** +JSScript::nativeMap(bool constructing) +{ + return getJIT(constructing)->nmap; +} + +inline void * +JSScript::nativeCodeForPC(bool constructing, jsbytecode *pc) +{ + void **nmap = nativeMap(constructing); + JS_ASSERT(pc >= code && pc < code + length); + JS_ASSERT(nmap[pc - code]); + return nmap[pc - code]; +} + #ifdef _MSC_VER extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame); #else From 61df9ba9bc96b53f659b9073ac57f36dc30f9c27 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:47:12 -0700 Subject: [PATCH 222/284] bug 580128 - Define Error constructor in the sealed global for CTypes. r=jst/mrbkap/gal --- toolkit/components/ctypes/ctypes.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/toolkit/components/ctypes/ctypes.cpp b/toolkit/components/ctypes/ctypes.cpp index 932bd93dc756..6633dbc72d9d 100644 --- a/toolkit/components/ctypes/ctypes.cpp +++ b/toolkit/components/ctypes/ctypes.cpp @@ -123,11 +123,13 @@ InitAndSealCTypesClass(JSContext* cx, JSObject* global) !JS_SetCTypesCallbacks(cx, JSVAL_TO_OBJECT(ctypes), &sCallbacks)) return false; - // Seal up Object, Function, and Array and their prototypes. (This single - // object instance is shared amongst everyone who imports the ctypes module.) + // Seal up Object, Function, Array and Error and their prototypes. (This + // single object instance is shared amongst everyone who imports the ctypes + // module.) if (!SealObjectAndPrototype(cx, global, "Object") || !SealObjectAndPrototype(cx, global, "Function") || - !SealObjectAndPrototype(cx, global, "Array")) + !SealObjectAndPrototype(cx, global, "Array") || + !SealObjectAndPrototype(cx, global, "Error")) return false; // Finally, seal the global object, for good measure. (But not recursively; From dd733e03421dd10ce779053aee7e05a580b9fe8b Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:47:16 -0700 Subject: [PATCH 223/284] bug 580128 - Don't create multiple wrappers for global objects (like BackstagePass). r=mrbkap --- js/src/xpconnect/wrappers/WrapperFactory.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 08b7d579db56..db14f73bb96a 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -84,8 +84,9 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj // Now, our object is ready to be wrapped, but several objects (notably // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of // those objects in a security wrapper, then we need to hand back the - // wrapper for the new scope instead. So... - if (!IS_WN_WRAPPER(obj)) + // wrapper for the new scope instead. Also, global objects don't move + // between scopes so for those we also want to return the wrapper. So... + if (!IS_WN_WRAPPER(obj) || !obj->getParent()) return obj; XPCWrappedNative *wn = static_cast(xpc_GetJSPrivate(obj)); From 6ee87b1807919245d3a53e3859434a560245e4f9 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:47:19 -0700 Subject: [PATCH 224/284] bug 580128 - Don't create slim wrappers when crossing compartments. r=mrbkap --- js/src/xpconnect/src/xpcwrappednative.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 4d2dcec89a51..ad8041921254 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -3894,6 +3894,13 @@ ConstructSlimWrapper(XPCCallContext &ccx, return JS_FALSE; } + if(ccx.GetJSContext()->compartment != parent->compartment()) + { + SLIM_LOG_NOT_CREATED(ccx, identityObj, "wrong compartment"); + + return JS_FALSE; + } + JSAutoEnterCompartment ac; if(!ac.enter(ccx, parent)) { From b33c3bf7ef68f8d96c098286d22a51d561a30ae6 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:47:22 -0700 Subject: [PATCH 225/284] bug 580128 - Cross origin wrapper needs no waive xray flag. r=mrbkap --- js/src/jscompartment.cpp | 3 ++ js/src/jswrapper.cpp | 2 +- js/src/xpconnect/wrappers/WrapperFactory.cpp | 43 +++++++++++++++----- js/src/xpconnect/wrappers/WrapperFactory.h | 6 +++ js/src/xpconnect/wrappers/XrayWrapper.cpp | 27 +++++++----- 5 files changed, 59 insertions(+), 22 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 40a06d056eb7..a1f54e2068b5 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -177,6 +177,9 @@ JSCompartment::wrap(JSContext *cx, Value *vp) if (obj->getCompartment() == this) return true; } else { + if (cx->runtime->preWrapObjectCallback) + obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); + JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); vp->setObject(*obj); } diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 9669b7da87d3..7e30985e3c23 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -76,7 +76,7 @@ JSObject::unwrap(uintN *flagsp) { JSObject *wrapped = this; uintN flags = 0; - if (wrapped->isWrapper()) { + while (wrapped->isWrapper()) { flags |= static_cast(wrapped->getProxyHandler())->flags(); wrapped = wrapped->getProxyPrivate().toObjectOrNull(); } diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index db14f73bb96a..2cc653884035 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -64,14 +64,29 @@ JSWrapper WaiveXrayWrapperWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); // chrome, we wrap them into a special cross-compartment wrapper // that transitively extends the waiver to all properties we get // off it. -CrossOriginWrapper XrayWrapperWaivedWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); +CrossOriginWrapper CrossOriginWrapper::singleton(0); + +static JSObject * +DoubleWrap(JSContext *cx, JSObject *obj, uintN flags) +{ + if (flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG) { + js::SwitchToCompartment sc(cx, obj->compartment()); + return JSWrapper::New(cx, obj, NULL, obj->getParent(), + &WaiveXrayWrapperWrapper); + } + return obj; +} JSObject * WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags) { + // Don't unwrap an outer window, just double wrap it if needed. + if (obj->getClass()->ext.innerObject) + return DoubleWrap(cx, obj, flags); + // Here are the rules for wrapping: // We should never get a proxy here (the JS engine unwraps those for us). - JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); + JS_ASSERT(!obj->isWrapper()); // As soon as an object is wrapped in a security wrapper, it morphs to be // a fat wrapper. (see also: bug XXX). @@ -87,13 +102,13 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj // wrapper for the new scope instead. Also, global objects don't move // between scopes so for those we also want to return the wrapper. So... if (!IS_WN_WRAPPER(obj) || !obj->getParent()) - return obj; + return DoubleWrap(cx, obj, flags); XPCWrappedNative *wn = static_cast(xpc_GetJSPrivate(obj)); // We know that DOM objects only allow one object, we can return early. if (wn->GetProto()->ClassIsDOMObject()) - return obj; + return DoubleWrap(cx, obj, flags); XPCCallContext ccx(JS_CALLER, cx, obj); if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) { @@ -102,14 +117,14 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj JSObject *originalScope = scope; nsresult rv = wn->GetScriptableInfo()->GetCallback()-> PreCreate(wn->Native(), cx, scope, &scope); - NS_ENSURE_SUCCESS(rv, obj); + NS_ENSURE_SUCCESS(rv, DoubleWrap(cx, obj, flags)); // If the handed back scope differs from the passed-in scope and is in // a separate compartment, then this object is explicitly requesting // that we don't create a second JS object for it: create a security // wrapper. if (originalScope->getCompartment() != scope->getCompartment()) - return obj; + return DoubleWrap(cx, obj, flags); // Note: this penalizes objects that only have one wrapper, but are // being accessed across compartments. We would really prefer to @@ -122,21 +137,27 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj // possibly-new object. JSAutoEnterCompartment ac; if (!ac.enter(cx, scope)) - return obj; + return nsnull; + jsval v; nsresult rv = nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, scope, wn->Native(), nsnull, &NS_GET_IID(nsISupports), PR_FALSE, &v, nsnull); - NS_ENSURE_SUCCESS(rv, obj); - return JSVAL_TO_OBJECT(v); + if (NS_SUCCEEDED(rv)) + obj = JSVAL_TO_OBJECT(v); + + return DoubleWrap(cx, obj, flags); } JSObject * WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, uintN flags) { - NS_ASSERTION(!obj->isWrapper() || obj->getClass()->ext.innerObject, + NS_ASSERTION(!obj->isWrapper() || + (obj->isWrapper() && + obj->getProxyHandler() == &WaiveXrayWrapperWrapper) || + obj->getClass()->ext.innerObject, "wrapped object passed to rewrap"); NS_ASSERTION(JS_GET_CLASS(cx, obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); @@ -162,7 +183,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO } else if (flags & WAIVE_XRAY_WRAPPER_FLAG) { // If we waived the X-ray wrapper for this object, wrap it into a // special wrapper to transitively maintain the X-ray waiver. - wrapper = &XrayWrapperWaivedWrapper; + wrapper = &CrossOriginWrapper::singleton; } else { // Native objects must be wrapped into an X-ray wrapper. if (!obj->getGlobal()->isSystem() && diff --git a/js/src/xpconnect/wrappers/WrapperFactory.h b/js/src/xpconnect/wrappers/WrapperFactory.h index ab4103568042..44c30d11bffc 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.h +++ b/js/src/xpconnect/wrappers/WrapperFactory.h @@ -64,6 +64,10 @@ class WrapperFactory { return HasWrapperFlag(wrapper, PARTIALLY_TRANSPARENT); } + static bool HasWaiveXrayFlag(JSObject *wrapper) { + return HasWrapperFlag(wrapper, WAIVE_XRAY_WRAPPER_FLAG); + } + // Prepare a given object for wrapping in a new compartment. static JSObject *PrepareForWrapping(JSContext *cx, JSObject *scope, @@ -84,4 +88,6 @@ class WrapperFactory { static JSObject *WrapLocationObject(JSContext *cx, JSObject *obj); }; +extern JSWrapper WaiveXrayWrapperWrapper; + } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 8d6cd4b9b73a..162d006f7abe 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -341,8 +341,6 @@ holder_enumerate(JSContext *cx, JSObject *holder) return true; } -extern CrossOriginWrapper XrayWrapperWaivedWrapper; - static JSBool wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) { @@ -359,11 +357,17 @@ wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) OBJ_TO_OUTER_OBJECT(cx, wn); if (!wn) return false; - JSObject *obj = JSWrapper::New(cx, wn, NULL, holder->getParent(), &XrayWrapperWaivedWrapper); - if (!obj) - return false; + + JSObject *obj; + { + SwitchToCompartment sc(cx, wn->compartment()); + obj = JSWrapper::New(cx, wn, NULL, holder->getParent(), &WaiveXrayWrapperWrapper); + if (!obj) + return false; + } *vp = OBJECT_TO_JSVAL(obj); - return true; + + return JS_WrapValue(cx, vp); } static JSBool @@ -432,6 +436,9 @@ class AutoLeaveHelper static bool Transparent(JSContext *cx, JSObject *wrapper) { + if (WrapperFactory::HasWaiveXrayFlag(wrapper)) + return true; + if (!WrapperFactory::IsPartiallyTransparent(wrapper)) return false; @@ -527,7 +534,7 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe if (!Policy::enter(cx, wrapper, &id, set ? JSWrapper::SET : JSWrapper::GET, &priv)) return false; - // Redirect access straight to the wrapper if we are transparent. + // Redirect access straight to the wrapper if we should be transparent. if (Transparent(cx, wrapper)) { Policy::leave(cx, wrapper, priv); @@ -588,7 +595,7 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid JSObject *holder = GetHolder(wrapper); JSPropertyDescriptor *jsdesc = Jsvalify(desc); - // Redirect access straight to the wrapper if UniversalXPConnect is enabled. + // Redirect access straight to the wrapper if we should be transparent. if (Transparent(cx, wrapper)) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); @@ -628,7 +635,7 @@ XrayWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, { JSObject *holder = GetHolder(wrapper); - // Redirect access straight to the wrapper if UniversalXPConnect is enabled. + // Redirect access straight to the wrapper if we should be transparent. if (Transparent(cx, wrapper)) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); @@ -676,7 +683,7 @@ XrayWrapper::enumerate(JSContext *cx, JSObject *wrapper, js::AutoI { JSObject *holder = GetHolder(wrapper); - // Redirect access straight to the wrapper if we are transparent. + // Redirect access straight to the wrapper if we should be transparent. if (Transparent(cx, wrapper)) { JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); From 1d2520f0eaf534de1f7f546cbdaa34535367d74e Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:47:48 -0700 Subject: [PATCH 226/284] bug 580128 - Reparent proxies when we pull them out of the map in order to try to keep their parents sembling something current. r=jst --- js/src/jscompartment.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index a1f54e2068b5..275e09f3110a 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -196,6 +196,8 @@ JSCompartment::wrap(JSContext *cx, Value *vp) /* If we already have a wrapper for this value, use it. */ if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) { *vp = p->value; + if (vp->isObject()) + vp->toObject().setParent(global); return true; } From 5be445d6b27d3bf064d97ff0b825419a339106b4 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:47:55 -0700 Subject: [PATCH 227/284] bug 580128 - Outerize inner windows. r=jst --- js/src/xpconnect/src/xpcconvert.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 2a35b8b40844..658edac1fc44 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -1383,6 +1383,13 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, flat = locationWrapper; } + else + { + OBJ_TO_OUTER_OBJECT(cx, flat); + NS_ASSERTION(flat, "bad outer object hook!"); + NS_ASSERTION(flat->getCompartment() == cx->compartment, + "bad compartment"); + } } *d = OBJECT_TO_JSVAL(flat); From 9db3ea9f81366675ef49a453caa4ad4e0ea6ac93 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:48:10 -0700 Subject: [PATCH 228/284] Bug 580128. Disable reftests that don't play well with brain transplants yet. r=mrbkap@gmail.com --- layout/reftests/bugs/reftest.list | 3 ++- layout/reftests/xul-document-load/reftest.list | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 8123e976fc08..502534ced498 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -858,7 +858,8 @@ random-if(cocoaWidget) HTTP(..) == 404149-1.xul 404149-1-ref.xul # HTTP for font == 404301-1.html 404301-1-ref.html == 404309-1a.html 404309-1-ref.html == 404309-1b.html 404309-1-ref.html -!= data:application/xml, data:text/plain, # ensure we pretty-print this XML instead of letting it appear blank (test for bug 404419) +# Disabled due to compartments for now. +#!= data:application/xml, data:text/plain, # ensure we pretty-print this XML instead of letting it appear blank (test for bug 404419) == 404553-1.html 404553-1-ref.html # assertion test, also tests that marquee binding is applied correctly == 404666-1.html 404666-1-ref.html == 404666-2.html 404666-2-ref.html diff --git a/layout/reftests/xul-document-load/reftest.list b/layout/reftests/xul-document-load/reftest.list index a20fae6787ce..a726e216f307 100644 --- a/layout/reftests/xul-document-load/reftest.list +++ b/layout/reftests/xul-document-load/reftest.list @@ -12,7 +12,8 @@ == test012.xul reference-green-window.xul == test013.xul reference-green-window.xul == test014.xul reference-green-window.xul -== test015.xul reference-green-window.xul +# Disabled due to compartments for now. +#== test015.xul reference-green-window.xul == test016.xul reference-green-window.xul == test017.xul reference-green-window.xul == test018.xul reference-green-window.xul From e7f82068e186867b58e291379b72edb23bf0548c Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:48:13 -0700 Subject: [PATCH 229/284] bug 580128 - Fix test_printpreview.xul. r=mrbkap --- layout/base/tests/chrome/printpreview_helper.xul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/base/tests/chrome/printpreview_helper.xul b/layout/base/tests/chrome/printpreview_helper.xul index fe5332ea727c..12f89b65af95 100644 --- a/layout/base/tests/chrome/printpreview_helper.xul +++ b/layout/base/tests/chrome/printpreview_helper.xul @@ -104,7 +104,7 @@ function startTest1() { // Note this timeout is needed so that we can check that timers run // after print preview, but not during it. - window.frames[0].counter = counter; + window.frames[0].wrappedJSObject.counter = counter; window.frames[0].counterTimeout = "document.body.firstChild.nextSibling.innerHTML = ++counter + ' timers';" + "window.setTimeout(counterTimeout, 0);"; window.frames[0].setTimeout(window.frames[0].counterTimeout, 0); From 25a8fe2c0972db0548b7ab78bd45a3ce1da7766a Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:48:29 -0700 Subject: [PATCH 230/284] bug 580128 - Implement deep wrapping for .wrappedJSObject. r=mrbkap --- .../xpconnect/wrappers/CrossOriginWrapper.cpp | 29 +++++++++++++++++-- .../xpconnect/wrappers/CrossOriginWrapper.h | 7 +++++ js/src/xpconnect/wrappers/WrapperFactory.cpp | 25 ++++++++++++++++ js/src/xpconnect/wrappers/WrapperFactory.h | 3 ++ js/src/xpconnect/wrappers/XrayWrapper.cpp | 27 ++--------------- 5 files changed, 65 insertions(+), 26 deletions(-) diff --git a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp index 9e949945bf16..75158eb57789 100644 --- a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp +++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp @@ -37,12 +37,13 @@ * * ***** END LICENSE BLOCK ***** */ -#include "CrossOriginWrapper.h" - #include "nsJSPrincipals.h" #include "XPCWrapper.h" +#include "CrossOriginWrapper.h" +#include "WrapperFactory.h" + namespace xpc { CrossOriginWrapper::CrossOriginWrapper(uintN flags) : JSCrossCompartmentWrapper(flags) @@ -59,6 +60,30 @@ GetCompartmentPrincipal(JSCompartment *compartment) return static_cast(compartment->principals)->nsIPrincipalPtr; } +bool +CrossOriginWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, js::PropertyDescriptor *desc) +{ + return JSCrossCompartmentWrapper::getPropertyDescriptor(cx, wrapper, id, set, desc) && + WrapperFactory::WaiveXrayAndWrap(cx, js::Jsvalify(&desc->value)); +} + +bool +CrossOriginWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, js::PropertyDescriptor *desc) +{ + return JSCrossCompartmentWrapper::getOwnPropertyDescriptor(cx, wrapper, id, set, desc) && + WrapperFactory::WaiveXrayAndWrap(cx, js::Jsvalify(&desc->value)); +} + +bool +CrossOriginWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp) +{ + return JSCrossCompartmentWrapper::get(cx, wrapper, receiver, id, vp) && + WrapperFactory::WaiveXrayAndWrap(cx, js::Jsvalify(vp)); +} + bool CrossOriginWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act) { diff --git a/js/src/xpconnect/wrappers/CrossOriginWrapper.h b/js/src/xpconnect/wrappers/CrossOriginWrapper.h index d61476f4a33a..9bf2c4acb8ec 100644 --- a/js/src/xpconnect/wrappers/CrossOriginWrapper.h +++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.h @@ -50,6 +50,13 @@ class CrossOriginWrapper : public JSCrossCompartmentWrapper { CrossOriginWrapper(uintN flags); virtual ~CrossOriginWrapper(); + virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, js::PropertyDescriptor *desc); + virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, js::PropertyDescriptor *desc); + virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, + js::Value *vp); + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act); virtual void leave(JSContext *cx, JSObject *wrapper); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 2cc653884035..992515194693 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -282,4 +282,29 @@ WrapperFactory::WrapLocationObject(JSContext *cx, JSObject *obj) return wrapperObj; } +bool +WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp) +{ + if (!JSVAL_IS_OBJECT(*vp)) + return true; + + JSObject *obj = JSVAL_TO_OBJECT(*vp)->unwrap(); + + // We have to make sure that if we're wrapping an outer window, that + // the .wrappedJSObject also wraps the outer window. + OBJ_TO_OUTER_OBJECT(cx, obj); + if (!obj) + return false; + + { + js::SwitchToCompartment sc(cx, obj->compartment()); + obj = JSWrapper::New(cx, obj, NULL, obj->getParent(), &WaiveXrayWrapperWrapper); + if (!obj) + return false; + } + + *vp = OBJECT_TO_JSVAL(obj); + return JS_WrapValue(cx, vp); +} + } diff --git a/js/src/xpconnect/wrappers/WrapperFactory.h b/js/src/xpconnect/wrappers/WrapperFactory.h index 44c30d11bffc..275721870be4 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.h +++ b/js/src/xpconnect/wrappers/WrapperFactory.h @@ -86,6 +86,9 @@ class WrapperFactory { // Wrap a location object. static JSObject *WrapLocationObject(JSContext *cx, JSObject *obj); + + // Wrap wrapped object into a waiver wrapper and then re-wrap it. + static bool WaiveXrayAndWrap(JSContext *cx, jsval *vp); }; extern JSWrapper WaiveXrayWrapperWrapper; diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 162d006f7abe..66a6b60b5774 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -342,32 +342,11 @@ holder_enumerate(JSContext *cx, JSObject *holder) } static JSBool -wrappedJSObject_getter(JSContext *cx, JSObject *holder, jsid id, jsval *vp) +wrappedJSObject_getter(JSContext *cx, JSObject *wrapper, jsid id, jsval *vp) { - if (holder->isWrapper()) - holder = GetHolder(holder); + *vp = OBJECT_TO_JSVAL(wrapper); - // If the caller intentionally waives the X-ray wrapper we usually - // apply for wrapped natives, use a special wrapper to make sure the - // membrane will not automatically apply an X-ray wrapper. - JSObject *wn = GetWrappedNativeObjectFromHolder(cx, holder); - - // We have to make sure that if we're wrapping an outer window, that - // the .wrappedJSObject also wraps the outer window. - OBJ_TO_OUTER_OBJECT(cx, wn); - if (!wn) - return false; - - JSObject *obj; - { - SwitchToCompartment sc(cx, wn->compartment()); - obj = JSWrapper::New(cx, wn, NULL, holder->getParent(), &WaiveXrayWrapperWrapper); - if (!obj) - return false; - } - *vp = OBJECT_TO_JSVAL(obj); - - return JS_WrapValue(cx, vp); + return WrapperFactory::WaiveXrayAndWrap(cx, vp); } static JSBool From 814da522b1a4cf6be94f107b7315ffa7913db6cb Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:48:35 -0700 Subject: [PATCH 231/284] bug 580128 - Allow unwrapping windows via JSObject::unwrap. r=jst --- js/src/jswrapper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 7e30985e3c23..9f6d9ddc9e63 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -79,6 +79,8 @@ JSObject::unwrap(uintN *flagsp) while (wrapped->isWrapper()) { flags |= static_cast(wrapped->getProxyHandler())->flags(); wrapped = wrapped->getProxyPrivate().toObjectOrNull(); + if (wrapped->getClass()->ext.innerObject) + break; } if (flagsp) *flagsp = flags; From 1866028dc87ed362370a39a4085966496129d988 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:48:39 -0700 Subject: [PATCH 232/284] Bug 580128. Remove .wrappedJSObject gets that don't work with the new wrappers. r=mrbkap@gmail.com --- browser/base/content/test/browser_tab_dragdrop2.js | 3 +-- .../components/sessionstore/test/browser/browser_393716.js | 4 ++-- .../components/sessionstore/test/browser/browser_394759.js | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/browser/base/content/test/browser_tab_dragdrop2.js b/browser/base/content/test/browser_tab_dragdrop2.js index 21ed9deaa330..e2f293e85814 100644 --- a/browser/base/content/test/browser_tab_dragdrop2.js +++ b/browser/base/content/test/browser_tab_dragdrop2.js @@ -34,8 +34,7 @@ function test() window_B.close(); var doc = window_C.gBrowser.getBrowserForTab(window_C.gBrowser.tabs[0]) - .docShell.contentViewer.DOMDocument.wrappedJSObject; - var elems = document.documentElement.childNodes; + .docShell.contentViewer.DOMDocument; var calls = doc.defaultView.test_panels(); window_C.close(); finish(); diff --git a/browser/components/sessionstore/test/browser/browser_393716.js b/browser/components/sessionstore/test/browser/browser_393716.js index e00d5b48c715..83bc8ff3f592 100644 --- a/browser/components/sessionstore/test/browser/browser_393716.js +++ b/browser/components/sessionstore/test/browser/browser_393716.js @@ -59,7 +59,7 @@ function test() { // add text data let textbox = this.contentDocument.getElementById("textbox"); - textbox.wrappedJSObject.value = value3; + textbox.value = value3; // duplicate the tab let duplicateTab = ss.duplicateTab(window, tab2); @@ -71,7 +71,7 @@ function test() { ok(ss.getTabValue(duplicateTab, key2) == value2 && this.currentURI.spec == testURL, "correctly duplicated the tab's state"); let textbox = this.contentDocument.getElementById("textbox"); - is(textbox.wrappedJSObject.value, value3, "also duplicated text data"); + is(textbox.value, value3, "also duplicated text data"); // clean up tabbrowser.removeTab(duplicateTab); diff --git a/browser/components/sessionstore/test/browser/browser_394759.js b/browser/components/sessionstore/test/browser/browser_394759.js index ba28c195d3c7..418b0de81ff2 100644 --- a/browser/components/sessionstore/test/browser/browser_394759.js +++ b/browser/components/sessionstore/test/browser/browser_394759.js @@ -80,7 +80,7 @@ function test() { // mark the window with some unique data to be restored later on ss.setWindowValue(newWin, uniqueKey, uniqueValue); let textbox = newWin.content.document.getElementById("textbox"); - textbox.wrappedJSObject.value = uniqueText; + textbox.value = uniqueText; newWin.close(); @@ -108,7 +108,7 @@ function test() { "The window correctly restored the URL"); let textbox = newWin2.content.document.getElementById("textbox"); - is(textbox.wrappedJSObject.value, uniqueText, + is(textbox.value, uniqueText, "The window correctly restored the form"); is(ss.getWindowValue(newWin2, uniqueKey), uniqueValue, "The window correctly restored the data associated with it"); From 19e3f64dd68dac3e104a635425bff1234b5e7322 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:48:42 -0700 Subject: [PATCH 233/284] Bug 580128. Disable tests that don't play well with brain transplants yet. r=mrbkap@gmail.com --- browser/base/content/test/Makefile.in | 6 ++++-- browser/base/content/test/tabview/Makefile.in | 4 +++- .../console/hudservice/tests/browser/Makefile.in | 8 +++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in index 37f4632c0396..81de5e2f8c0f 100644 --- a/browser/base/content/test/Makefile.in +++ b/browser/base/content/test/Makefile.in @@ -179,7 +179,6 @@ _BROWSER_FILES = \ browser_pinnedTabs.js \ browser_plainTextLinks.js \ browser_pluginnotification.js \ - browser_popupUI.js \ browser_relatedTabs.js \ browser_sanitize-passwordDisabledHosts.js \ browser_sanitize-sitepermissions.js \ @@ -187,7 +186,6 @@ _BROWSER_FILES = \ browser_sanitizeDialog.js \ browser_scope.js \ browser_selectTabAtIndex.js \ - browser_tab_dragdrop.js \ browser_tab_dragdrop2.js \ browser_tab_dragdrop2_frame1.xul \ browser_tabfocus.js \ @@ -219,6 +217,10 @@ _BROWSER_FILES = \ browser_aboutHome.js \ $(NULL) +# compartment-disabled +# browser_popupUI.js \ +# browser_tab_dragdrop.js \ + ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT)) _BROWSER_FILES += \ browser_bug462289.js \ diff --git a/browser/base/content/test/tabview/Makefile.in b/browser/base/content/test/tabview/Makefile.in index 871c1fe8010d..94f368692bdf 100644 --- a/browser/base/content/test/tabview/Makefile.in +++ b/browser/base/content/test/tabview/Makefile.in @@ -58,7 +58,6 @@ _BROWSER_FILES = \ browser_tabview_exit_button.js \ browser_tabview_group.js \ browser_tabview_launch.js \ - browser_tabview_multiwindow_search.js \ browser_tabview_orphaned_tabs.js \ browser_tabview_search.js \ browser_tabview_snapping.js \ @@ -66,5 +65,8 @@ _BROWSER_FILES = \ browser_tabview_firstrun_pref.js \ $(NULL) +# compartments: test disabled +# browser_tabview_multiwindow_search.js \ + libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) diff --git a/toolkit/components/console/hudservice/tests/browser/Makefile.in b/toolkit/components/console/hudservice/tests/browser/Makefile.in index 2a92079db316..001805922543 100644 --- a/toolkit/components/console/hudservice/tests/browser/Makefile.in +++ b/toolkit/components/console/hudservice/tests/browser/Makefile.in @@ -47,7 +47,6 @@ include $(topsrcdir)/config/rules.mk _BROWSER_TEST_FILES = \ - browser_HUDServiceTestsAll.js \ browser_warn_user_about_replaced_api.js \ browser_webconsole_bug_585237_line_limit.js \ browser_webconsole_bug_586142_insert_newlines.js \ @@ -55,14 +54,17 @@ _BROWSER_TEST_FILES = \ browser_webconsole_bug_588967_input_expansion.js \ browser_webconsole_bug_580454_timestamp_l10n.js \ browser_webconsole_netlogging.js \ - browser_webconsole_bug_593003_iframe_wrong_hud.js \ browser_webconsole_bug_581231_close_button.js \ browser_webconsole_bug_595350_multiple_windows_and_tabs.js \ - browser_webconsole_consoleonpage.js \ browser_webconsole_bug_587617_output_copy.js \ browser_webconsole_bug_588342_document_focus.js \ $(NULL) +# compartment-disabled +# browser_HUDServiceTestsAll.js \ +# browser_webconsole_consoleonpage.js \ +# browser_webconsole_bug_593003_iframe_wrong_hud.js \ + _BROWSER_TEST_PAGES = \ test-console.html \ test-network.html \ From a592a50e703dd72be6db690eff980087924e680f Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:48:55 -0700 Subject: [PATCH 234/284] bug 580128 - Create SOWs same and cross compartment. r=jst --- js/src/xpconnect/src/xpcconvert.cpp | 18 ++++++++++ js/src/xpconnect/wrappers/AccessCheck.cpp | 3 ++ .../xpconnect/wrappers/FilteringWrapper.cpp | 6 +++- js/src/xpconnect/wrappers/WrapperFactory.cpp | 33 +++++++++++-------- js/src/xpconnect/wrappers/WrapperFactory.h | 6 +++- 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 658edac1fc44..831f0e82a0f1 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -49,6 +49,7 @@ #include "nsJSPrincipals.h" #include "nsWrapperCache.h" #include "WrapperFactory.h" +#include "AccessCheck.h" //#define STRICT_CHECK_OF_UNICODE #ifdef STRICT_CHECK_OF_UNICODE @@ -1383,6 +1384,23 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, flat = locationWrapper; } + else if(wrapper->NeedsSOW() && + !xpc::AccessCheck::isChrome(cx->compartment)) + { + JSObject *sowWrapper = wrapper->GetWrapper(); + if(!sowWrapper) + { + sowWrapper = xpc::WrapperFactory::WrapSOWObject(cx, flat); + if(!sowWrapper) + return JS_FALSE; + + // Cache the sow wrapper to ensure that we maintain + // the identity of this node. + wrapper->SetWrapper(sowWrapper); + } + + flat = sowWrapper; + } else { OBJ_TO_OUTER_OBJECT(cx, flat); diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 46e8b0c68541..b219ae2dc00d 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -329,6 +329,9 @@ AccessCheck::isScriptAccessOnly(JSContext *cx, JSObject *wrapper) // If the wrapper indicates script-only access, we are done. if (flags & WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG) { + if (flags & WrapperFactory::SOW_FLAG) + return !isSystemOnlyAccessPermitted(cx); + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) return true; diff --git a/js/src/xpconnect/wrappers/FilteringWrapper.cpp b/js/src/xpconnect/wrappers/FilteringWrapper.cpp index 1622408d3839..c5bed2efa5ba 100644 --- a/js/src/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/src/xpconnect/wrappers/FilteringWrapper.cpp @@ -150,6 +150,7 @@ FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, } #define SOW FilteringWrapper +#define SCSOW FilteringWrapper #define COW FilteringWrapper #define XOW FilteringWrapper, \ CrossOriginAccessiblePropertiesOnly> @@ -159,7 +160,10 @@ FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, #define XLW FilteringWrapper, \ SameOriginOrCrossOriginAccessiblePropertiesOnly> -template<> SOW SOW::singleton(0); +template<> SOW SOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG | + WrapperFactory::SOW_FLAG); +template<> SCSOW SCSOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG | + WrapperFactory::SOW_FLAG); template<> COW COW::singleton(0); template<> XOW XOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG | WrapperFactory::PARTIALLY_TRANSPARENT); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 992515194693..52464ed6ee60 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -198,22 +198,16 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO } } } else if (AccessCheck::isChrome(origin)) { - // If an object that needs a system only wrapper crosses into content - // from chrome, we have to wrap it into a system only wrapper on the - // fly. In this case we don't need to restrict to exposed properties - // since only privileged content will be allowed to touch it anyway. + wrapper = &FilteringWrapper::singleton; + } else if (AccessCheck::isSameOrigin(origin, target)) { + // Same origin we use a transparent wrapper, unless the compartment asks + // for an Xray or the wrapper needs a SOW. if (AccessCheck::needsSystemOnlyWrapper(obj)) { wrapper = &FilteringWrapper::singleton; - } else { - wrapper = &FilteringWrapper::singleton; - } - } else if (AccessCheck::isSameOrigin(origin, target)) { - // Same origin we use a transparent wrapper, unless the compartment asks - // for an Xray. - if (static_cast(target->data)->preferXrays && - IS_WN_WRAPPER(obj)) { + } else if (static_cast(target->data)->preferXrays && + IS_WN_WRAPPER(obj)) { typedef XrayWrapper Xray; wrapper = &Xray::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); @@ -223,6 +217,9 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO wrapper = &JSCrossCompartmentWrapper::singleton; } } else { + NS_ASSERTION(!AccessCheck::needsSystemOnlyWrapper(obj), + "bad object exposed across origins"); + // Cross origin we want to disallow scripting and limit access to // a predefined set of properties. XrayWrapper adds a property // (.wrappedJSObject) which allows bypassing the XrayWrapper, but @@ -307,4 +304,14 @@ WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp) return JS_WrapValue(cx, vp); } +JSObject * +WrapperFactory::WrapSOWObject(JSContext *cx, JSObject *obj) +{ + JSObject *wrapperObj = + JSWrapper::New(cx, obj, obj->getProto(), NULL, + &FilteringWrapper::singleton); + return wrapperObj; +} + } diff --git a/js/src/xpconnect/wrappers/WrapperFactory.h b/js/src/xpconnect/wrappers/WrapperFactory.h index 275721870be4..ceb356f08c3b 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.h +++ b/js/src/xpconnect/wrappers/WrapperFactory.h @@ -47,7 +47,8 @@ class WrapperFactory { enum { WAIVE_XRAY_WRAPPER_FLAG = (1<<0), IS_XRAY_WRAPPER_FLAG = (1<<1), SCRIPT_ACCESS_ONLY_FLAG = (1<<2), - PARTIALLY_TRANSPARENT = (1<<3) }; + PARTIALLY_TRANSPARENT = (1<<3), + SOW_FLAG = (1<<4) }; // Return true if any of any of the nested wrappers have the flag set. static bool HasWrapperFlag(JSObject *wrapper, uintN flag) { @@ -89,6 +90,9 @@ class WrapperFactory { // Wrap wrapped object into a waiver wrapper and then re-wrap it. static bool WaiveXrayAndWrap(JSContext *cx, jsval *vp); + + // Wrap a (same compartment) object in a SOW. + static JSObject *WrapSOWObject(JSContext *cx, JSObject *obj); }; extern JSWrapper WaiveXrayWrapperWrapper; From d1f7d8a54fd76ce9e9f03c14886e229db263d0d2 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:49:08 -0700 Subject: [PATCH 235/284] bug 580128 - By default properties created by property assignment are enumerable. r=gal --- js/src/jsproxy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 166771525de5..cb7a47bec5f0 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -180,7 +180,7 @@ JSProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, } desc.obj = proxy; desc.value = *vp; - desc.attrs = 0; + desc.attrs = JSPROP_ENUMERATE; desc.getter = NULL; desc.setter = NULL; desc.shortid = 0; From 1e201c2e7139d05207b5c439459ce68880c76629 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 10 Oct 2010 15:49:13 -0700 Subject: [PATCH 236/284] bug 580128 - split expando properties onto a separate object and implement enumeration. r=mrbkap --- js/src/xpconnect/wrappers/XrayWrapper.cpp | 171 +++++++++++++++------- js/src/xpconnect/wrappers/XrayWrapper.h | 3 + 2 files changed, 120 insertions(+), 54 deletions(-) diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 66a6b60b5774..70081936e65b 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -55,6 +55,7 @@ using namespace js; static const uint32 JSSLOT_WN_OBJ = JSSLOT_PRIVATE; static const uint32 JSSLOT_RESOLVING = JSSLOT_PRIVATE + 1; +static const uint32 JSSLOT_EXPANDO = JSSLOT_PRIVATE + 2; class ResolvingId { @@ -100,16 +101,13 @@ holder_get(JSContext *cx, JSObject *holder, jsid id, jsval *vp); static JSBool holder_set(JSContext *cx, JSObject *holder, jsid id, jsval *vp); -static JSBool -holder_enumerate(JSContext *cx, JSObject *holder); - namespace XrayUtils { JSClass HolderClass = { "NativePropertyHolder", - JSCLASS_HAS_RESERVED_SLOTS(2), + JSCLASS_HAS_RESERVED_SLOTS(3), JS_PropertyStub, JS_PropertyStub, holder_get, holder_set, - holder_enumerate, JS_ResolveStub, JS_ConvertStub, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; @@ -140,6 +138,17 @@ GetWrappedNativeObjectFromHolder(JSContext *cx, JSObject *holder) return wrappedObj; } +static JSObject * +GetExpandoObject(JSContext *cx, JSObject *holder) +{ + JSObject *expando = holder->getSlot(JSSLOT_EXPANDO).toObjectOrNull(); + if (!expando) { + expando = JS_NewObjectWithGivenProto(cx, nsnull, nsnull, holder->getParent()); + holder->setSlot(JSSLOT_EXPANDO, ObjectValue(*expando)); + } + return expando; +} + // Some DOM objects have shared properties that don't have an explicit // getter/setter and rely on the class getter/setter. We install a // class getter/setter on the holder object to trigger them. @@ -224,8 +233,14 @@ ResolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid i return false; } - if (pobj) - return JS_GetPropertyDescriptorById(cx, pobj, id, flags, desc); + if (pobj) { +#ifdef DEBUG + JSBool hasProp; + NS_ASSERTION(JS_HasPropertyById(cx, holder, id, &hasProp) && + hasProp, "id got defined somewhere else?"); +#endif + return JS_GetPropertyDescriptorById(cx, holder, id, flags, desc); + } } // There are no native numeric properties, so we can shortcut here. We will not @@ -312,35 +327,6 @@ ResolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid i desc->getter, desc->setter, desc->attrs); } -static JSBool -holder_enumerate(JSContext *cx, JSObject *holder) -{ - // Ask the native wrapper for all its ids - JSIdArray *ida; - { - JSObject *wrappednative = GetWrappedNativeObjectFromHolder(cx, holder); - JSAutoEnterCompartment ac; - if (!ac.enter(cx, wrappednative)) - return false; - ida = JS_Enumerate(cx, wrappednative); - } - - if (!ida) - return false; - - JSObject *wrapper = &holder->getSlot(JSSLOT_PROXY_OBJ).toObject(); - - // Resolve the underlying native properties onto the holder object - jsid *idp = ida->vector; - size_t length = ida->length; - while (length-- > 0) { - JSPropertyDescriptor dummy; - if (!ResolveNativeProperty(cx, wrapper, holder, *idp++, false, &dummy)) - return false; - } - return true; -} - static JSBool wrappedJSObject_getter(JSContext *cx, JSObject *wrapper, jsid id, jsval *vp) { @@ -476,8 +462,9 @@ Transparent(JSContext *cx, JSObject *wrapper) template bool -XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - bool set, PropertyDescriptor *desc_in) +XrayWrapper::resolveWrappedJSObject(JSContext *cx, JSObject *wrapper, + jsid id, bool set, + PropertyDescriptor *desc_in) { JSPropertyDescriptor *desc = Jsvalify(desc_in); @@ -496,6 +483,23 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe return true; } + desc->obj = NULL; + return true; +} + +template +bool +XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, + bool set, PropertyDescriptor *desc_in) +{ + JSPropertyDescriptor *desc = Jsvalify(desc_in); + + if (!resolveWrappedJSObject(cx, wrapper, id, set, desc_in)) + return false; + + if (desc->obj) + return true; + JSObject *holder = GetHolder(wrapper); if (IsResolving(holder, id)) { desc->obj = NULL; @@ -553,17 +557,31 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe return true; } - return JS_GetPropertyDescriptorById(cx, holder, id, - (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED, - desc); + return getOwnPropertyDescriptor(cx, wrapper, id, set, desc_in); + } template bool XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, - bool set, PropertyDescriptor *desc) + bool set, PropertyDescriptor *desc_in) { - return getPropertyDescriptor(cx, wrapper, id, set, desc); + JSPropertyDescriptor *desc = Jsvalify(desc_in); + + if (!resolveWrappedJSObject(cx, wrapper, id, false, desc_in)) + return false; + + if (desc->obj) + return true; + + JSObject *holder = GetHolder(wrapper); + JSObject *expando = GetExpandoObject(cx, holder); + if (!expando) + return false; + + return JS_GetPropertyDescriptorById(cx, expando, id, + (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED, + desc); } template @@ -596,14 +614,23 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid if (existing_desc.obj && (existing_desc.attrs & JSPROP_PERMANENT)) return true; // silently ignore attempt to overwrite native property - if (!(jsdesc->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { - if (!desc->getter) - jsdesc->getter = holder_get; - if (!desc->setter) - jsdesc->setter = holder_set; + if (IsResolving(holder, id)) { + if (!(jsdesc->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { + if (!desc->getter) + jsdesc->getter = holder_get; + if (!desc->setter) + jsdesc->setter = holder_set; + } + + return JS_DefinePropertyById(cx, holder, id, jsdesc->value, jsdesc->getter, jsdesc->setter, + jsdesc->attrs); } - return JS_DefinePropertyById(cx, holder, id, jsdesc->value, jsdesc->getter, jsdesc->setter, + JSObject *expando = GetExpandoObject(cx, holder); + if (!expando) + return false; + + return JS_DefinePropertyById(cx, expando, id, jsdesc->value, jsdesc->getter, jsdesc->setter, jsdesc->attrs); } @@ -625,7 +652,11 @@ XrayWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, return js::GetPropertyNames(cx, wnObject, JSITER_OWNONLY | JSITER_HIDDEN, &props); } - return js::GetPropertyNames(cx, holder, JSITER_OWNONLY | JSITER_HIDDEN, &props); + JSObject *expando = GetExpandoObject(cx, holder); + if (!expando) + return false; + + return js::GetPropertyNames(cx, expando, JSITER_OWNONLY | JSITER_HIDDEN, &props); } template @@ -650,7 +681,11 @@ XrayWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bo return true; } - if (!JS_DeletePropertyById2(cx, holder, id, &v) || !JS_ValueToBoolean(cx, v, &b)) + JSObject *expando = GetExpandoObject(cx, holder); + if (!expando) + return false; + + if (!JS_DeletePropertyById2(cx, expando, id, &v) || !JS_ValueToBoolean(cx, v, &b)) return false; *bp = !!b; return true; @@ -662,10 +697,10 @@ XrayWrapper::enumerate(JSContext *cx, JSObject *wrapper, js::AutoI { JSObject *holder = GetHolder(wrapper); + JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); + // Redirect access straight to the wrapper if we should be transparent. if (Transparent(cx, wrapper)) { - JSObject *wnObject = GetWrappedNativeObjectFromHolder(cx, holder); - JSAutoEnterCompartment ac; if (!ac.enter(cx, wnObject)) return false; @@ -673,7 +708,34 @@ XrayWrapper::enumerate(JSContext *cx, JSObject *wrapper, js::AutoI return js::GetPropertyNames(cx, wnObject, 0, &props); } - return js::GetPropertyNames(cx, holder, 0, &props); + // Enumerate expando properties first. + JSObject *expando = GetExpandoObject(cx, holder); + if (!expando) + return false; + + if (!js::GetPropertyNames(cx, expando, JSITER_OWNONLY, &props)) + return false; + + // Force all native properties to be materialized onto the wrapped native. + js::AutoIdVector wnProps(cx); + { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, wnObject)) + return false; + if (!js::GetPropertyNames(cx, wnObject, 0, &wnProps)) + return false; + } + + // Go through the properties we got and enumerate all native ones. + for (size_t n = 0; n < wnProps.length(); ++n) { + jsid id = wnProps[n]; + JSPropertyDescriptor dummy; + if (!ResolveNativeProperty(cx, wrapper, holder, id, false, &dummy)) + return false; + if (dummy.obj) + props.append(id); + } + return true; } template @@ -746,6 +808,7 @@ XrayWrapper::createHolder(JSContext *cx, JSObject *wrappedNative, wrappedNative->getClass()->ext.innerObject); holder->setSlot(JSSLOT_WN_OBJ, ObjectValue(*wrappedNative)); holder->setSlot(JSSLOT_RESOLVING, PrivateValue(NULL)); + holder->setSlot(JSSLOT_EXPANDO, NullValue()); return holder; } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.h b/js/src/xpconnect/wrappers/XrayWrapper.h index 751c2ee5e44b..8f60e3987663 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.h +++ b/js/src/xpconnect/wrappers/XrayWrapper.h @@ -61,6 +61,9 @@ class XrayWrapper : public Base { XrayWrapper(uintN flags); virtual ~XrayWrapper(); + bool resolveWrappedJSObject(JSContext *cx, JSObject *wrapper, jsid id, + bool set, js::PropertyDescriptor *desc); + /* Fundamental proxy traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, js::PropertyDescriptor *desc); From ab0c961743c0fb48c91e7a64f3304ca5a6d530ef Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:49:30 -0700 Subject: [PATCH 237/284] bug 580128 - Allow chrome://global/ scripts to unwrap SOWs going into C++. r=peterv/jst --- js/src/xpconnect/wrappers/AccessCheck.cpp | 3 +++ js/src/xpconnect/wrappers/XrayWrapper.cpp | 10 ++++++++++ js/src/xpconnect/wrappers/XrayWrapper.h | 3 +++ 3 files changed, 16 insertions(+) diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index b219ae2dc00d..ea989b82fda5 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -332,6 +332,9 @@ AccessCheck::isScriptAccessOnly(JSContext *cx, JSObject *wrapper) if (flags & WrapperFactory::SOW_FLAG) return !isSystemOnlyAccessPermitted(cx); + if (flags & WrapperFactory::PARTIALLY_TRANSPARENT) + return !XrayUtils::IsTransparent(cx, wrapper); + nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) return true; diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 70081936e65b..1e1a76d903ea 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -460,6 +460,16 @@ Transparent(JSContext *cx, JSObject *wrapper) return false; } +namespace XrayUtils { + +bool +IsTransparent(JSContext *cx, JSObject *wrapper) +{ + return Transparent(cx, wrapper); +} + +} + template bool XrayWrapper::resolveWrappedJSObject(JSContext *cx, JSObject *wrapper, diff --git a/js/src/xpconnect/wrappers/XrayWrapper.h b/js/src/xpconnect/wrappers/XrayWrapper.h index 8f60e3987663..7f60a2719d70 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.h +++ b/js/src/xpconnect/wrappers/XrayWrapper.h @@ -52,6 +52,9 @@ namespace XrayUtils { extern JSClass HolderClass; +bool +IsTransparent(JSContext *cx, JSObject *wrapper); + } // NB: Base *must* derive from JSProxyHandler From c914f1d5dd91558e0e410afe17e8651a646b1854 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Sun, 10 Oct 2010 15:49:38 -0700 Subject: [PATCH 238/284] Bug 580128. Remove cross origin string wrappers from hash when the wrapped string dies. r=mrbkap. As a note: this bug took peterv and mrbkap 7 hours to track down using replay debugging after spending hours trying to even catch it on a replay debugging box. --- js/src/jscompartment.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 275e09f3110a..0b7e253db326 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -334,8 +334,13 @@ JSCompartment::sweep(JSContext *cx) chunk = NULL; /* Remove dead wrappers from the table. */ for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { - if (IsAboutToBeFinalized(e.front().value.toGCThing())) + JS_ASSERT_IF(IsAboutToBeFinalized(e.front().key.toGCThing()) && + !IsAboutToBeFinalized(e.front().value.toGCThing()), + e.front().key.isString()); + if (IsAboutToBeFinalized(e.front().key.toGCThing()) || + IsAboutToBeFinalized(e.front().value.toGCThing())) { e.removeFront(); + } } #if defined JS_METHODJIT && defined JS_MONOIC From f45ee82f021d2a3259140c96baa683790f6328df Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Sun, 10 Oct 2010 15:49:51 -0700 Subject: [PATCH 239/284] Bug 580128 - Deal with the outer object hook failing a little more gracefully. r=jst --- js/src/xpconnect/wrappers/WrapperFactory.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 52464ed6ee60..de471cd29dd7 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -95,6 +95,8 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj // We only hand out outer objects to script. OBJ_TO_OUTER_OBJECT(cx, obj); + if (!obj) + return nsnull; // Now, our object is ready to be wrapped, but several objects (notably // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of From 0619f2a877f3852e4161740127e7ef49f27d2e59 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:50:08 -0700 Subject: [PATCH 240/284] Bug 580128 - Make nsJSNPRuntime compartment safe (r=gal). --- modules/plugin/base/src/nsJSNPRuntime.cpp | 37 +++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/modules/plugin/base/src/nsJSNPRuntime.cpp b/modules/plugin/base/src/nsJSNPRuntime.cpp index 81aade70ca45..2638a358b6fc 100644 --- a/modules/plugin/base/src/nsJSNPRuntime.cpp +++ b/modules/plugin/base/src/nsJSNPRuntime.cpp @@ -637,6 +637,11 @@ nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier id) AutoCXPusher pusher(cx); JSAutoRequest ar(cx); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, npjsobj->mJSObj)) + return PR_FALSE; + AutoJSExceptionReporter reporter(cx); jsval v; @@ -671,6 +676,11 @@ doInvoke(NPObject *npobj, NPIdentifier method, const NPVariant *args, AutoCXPusher pusher(cx); JSAutoRequest ar(cx); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, npjsobj->mJSObj)) + return PR_FALSE; + AutoJSExceptionReporter reporter(cx); if (method != NPIdentifier_VOID) { @@ -783,6 +793,10 @@ nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier id) AutoCXPusher pusher(cx); JSAutoRequest ar(cx); AutoJSExceptionReporter reporter(cx); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, npjsobj->mJSObj)) + return PR_FALSE; if (NPIdentifierIsString(id)) { JSString *str = NPIdentifierToString(id); @@ -822,6 +836,10 @@ nsJSObjWrapper::NP_GetProperty(NPObject *npobj, NPIdentifier id, AutoCXPusher pusher(cx); JSAutoRequest ar(cx); AutoJSExceptionReporter reporter(cx); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, npjsobj->mJSObj)) + return PR_FALSE; jsval v; return (GetProperty(cx, npjsobj->mJSObj, id, &v) && @@ -853,6 +871,10 @@ nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier id, AutoCXPusher pusher(cx); JSAutoRequest ar(cx); AutoJSExceptionReporter reporter(cx); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, npjsobj->mJSObj)) + return PR_FALSE; jsval v = NPVariantToJSVal(npp, cx, value); js::AutoValueRooter tvr(cx, v); @@ -898,6 +920,10 @@ nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier id) JSAutoRequest ar(cx); AutoJSExceptionReporter reporter(cx); jsval deleted = JSVAL_FALSE; + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, npjsobj->mJSObj)) + return PR_FALSE; if (NPIdentifierIsString(id)) { JSString *str = NPIdentifierToString(id); @@ -973,6 +999,10 @@ nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray, AutoCXPusher pusher(cx); JSAutoRequest ar(cx); AutoJSExceptionReporter reporter(cx); + JSAutoEnterCompartment ac; + + if (!ac.enter(cx, npjsobj->mJSObj)) + return PR_FALSE; JSIdArray *ida = ::JS_Enumerate(cx, npjsobj->mJSObj); if (!ida) { @@ -1077,13 +1107,16 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JSObject *obj) cx = GetJSContext(npp); if (!cx) { - NS_ERROR("Unable to find a JSContext in " - "nsJSObjWrapper::GetNewOrUsed()!"); + NS_ERROR("Unable to find a JSContext in nsJSObjWrapper::GetNewOrUsed()!"); return nsnull; } } + // No need to enter the right compartment here as we only get the + // class and private from the JSObject, neither of which cares about + // compartments. + JSClass *clazz = JS_GET_CLASS(cx, obj); if (clazz == &sNPObjectJSWrapperClass) { From eadf8a8150804ebeb15fc33292cc73f5e7e8c6c7 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Sun, 10 Oct 2010 15:50:11 -0700 Subject: [PATCH 241/284] Bug 580128 - No need to assert about commpartment mismatches in JS_Get/SetPrivate (r=gal). --- js/src/jsapi.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index f7965d8c6a78..89367e3d3c09 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2904,14 +2904,12 @@ JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) JS_PUBLIC_API(void *) JS_GetPrivate(JSContext *cx, JSObject *obj) { - assertSameCompartment(cx, obj); return obj->getPrivate(); } JS_PUBLIC_API(JSBool) JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) { - assertSameCompartment(cx, obj); obj->setPrivate(data); return true; } From 4194fc4e6817500abdd0631a3362f103a5035572 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Sun, 10 Oct 2010 21:11:45 -0700 Subject: [PATCH 242/284] Warning fixes (one left over from 602621, the other from 584789). --- js/src/jsscript.cpp | 6 ++++-- js/src/methodjit/StubCalls.cpp | 5 +---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 44874583d86c..55bc32a88b39 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1647,8 +1647,10 @@ class DisablePrincipalsTranscoding { JSPrincipalsTranscoder temp; public: - DisablePrincipalsTranscoding(JSContext *cx) { - callbacks = JS_GetRuntimeSecurityCallbacks(cx->runtime); + DisablePrincipalsTranscoding(JSContext *cx) + : callbacks(JS_GetRuntimeSecurityCallbacks(cx->runtime)), + temp(NULL) + { if (callbacks) { temp = callbacks->principalsTranscoder; callbacks->principalsTranscoder = NULL; diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index c39d29685c6d..26450a156578 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -914,9 +914,6 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) */ JSObject *parent = &fp->varobj(cx); - uint32 old; - bool doSet; - /* * Check for a const property of the same name -- or any kind of property * if executing with the strict option. We check here at runtime as well @@ -940,7 +937,7 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) * getters and setters that update the value of the property in the stack * frame. See bug 467495. */ - doSet = false; + bool doSet = false; if (prop) { JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT))); JS_ASSERT((attrs == JSPROP_ENUMERATE) == fp->isEvalFrame()); From d4d1a6d7d849ea157edbcfa5d934781edeec7355 Mon Sep 17 00:00:00 2001 From: Ginn Chen Date: Mon, 11 Oct 2010 18:23:55 +0800 Subject: [PATCH 243/284] Bug 601835 Add JS_STATIC_ASSERT back for Solaris Studio, r=brendan --- js/src/jsutil.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 31587781ad0f..60cc0bd7406a 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -91,14 +91,15 @@ JS_Assert(const char *s, const char *file, JSIntn ln); * allowed. */ +#ifdef __SUNPRO_CC /* * Sun Studio C++ compiler has a bug * "sizeof expression not accepted as size of array parameter" + * It happens when js_static_assert() function is declared inside functions. * The bug number is 6688515. It is not public yet. - * Turn off this assert for Sun Studio until this bug is fixed. + * Therefore, for Sun Studio, declare js_static_assert as an array instead. */ -#ifdef __SUNPRO_CC -#define JS_STATIC_ASSERT(cond) +#define JS_STATIC_ASSERT(cond) extern char js_static_assert[(cond) ? 1 : -1] #else #ifdef __COUNTER__ #define JS_STATIC_ASSERT_GLUE1(x,y) x##y From bb009a8dfe33be44f91a1e0446fc5aa7470652b0 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Wed, 6 Oct 2010 14:58:28 -0700 Subject: [PATCH 244/284] JSOPTION_ANONFUNFIX should be set in js shell (5595555, r=cdleary). --- js/src/shell/js.cpp | 5 +++-- js/src/trace-test/tests/basic/bug528644.js | 4 ++-- js/src/trace-test/tests/basic/math-trace-tests.js | 4 ++-- js/src/trace-test/tests/basic/testBug579602.js | 2 +- js/src/trace-test/tests/closures/bug540136.js | 4 ++-- js/src/trace-test/tests/closures/bug540242.js | 4 ++-- js/src/trace-test/tests/closures/bug540243.js | 4 ++-- js/src/trace-test/tests/closures/bug541239.js | 4 ++-- .../trace-test/tests/closures/setname-inner-heavy.js | 4 ++-- js/src/trace-test/tests/jaeger/bug555543.js | 2 +- js/src/trace-test/tests/jaeger/bug573433.js | 2 +- js/src/trace-test/tests/jaeger/bug580884.js | 4 ++-- js/src/trace-test/tests/jaeger/bug583158.js | 2 +- js/src/trace-test/tests/jaeger/bug588338.js | 2 +- js/src/trace-test/tests/pic/bug558099.js | 10 +++++----- 15 files changed, 29 insertions(+), 28 deletions(-) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 3fbcf8360e58..6be9a2832560 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5248,7 +5248,7 @@ NewGlobalObject(JSContext *cx) } int -shell(JSContext *cx, int argc, char **argv, char **envp) +Shell(JSContext *cx, int argc, char **argv, char **envp) { JSAutoRequest ar(cx); @@ -5411,9 +5411,10 @@ main(int argc, char **argv, char **envp) if (!cx) return 1; + JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_ANONFUNFIX); JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024); - result = shell(cx, argc, argv, envp); + result = Shell(cx, argc, argv, envp); DestroyContext(cx, true); diff --git a/js/src/trace-test/tests/basic/bug528644.js b/js/src/trace-test/tests/basic/bug528644.js index d09caaf11569..260e0d995904 100644 --- a/js/src/trace-test/tests/basic/bug528644.js +++ b/js/src/trace-test/tests/basic/bug528644.js @@ -6,11 +6,11 @@ function g(foo) { } var makegen = eval("\n\ - function(b) {\n\ + (function(b) {\n\ var h = \n\ eval(\"new function() { yield print(b) }\" ); \n\ return h\n\ - }\n\ + })\n\ "); g(makegen()); diff --git a/js/src/trace-test/tests/basic/math-trace-tests.js b/js/src/trace-test/tests/basic/math-trace-tests.js index dc52917dfe9e..418210fe15b8 100644 --- a/js/src/trace-test/tests/basic/math-trace-tests.js +++ b/js/src/trace-test/tests/basic/math-trace-tests.js @@ -23,10 +23,10 @@ function testmath(funcname, args, expected) { // Unless we eval here, the call to funcname won't get traced. // FUNCNAME="Infinity/Math.abs" and cases like that happen to // parse, too, in a twisted way. - var mapfunc = eval("function(a) {\n" + var mapfunc = eval("(function(a) {\n" + " for (var i = 0; i < a.length; i++)\n" + " a[i] = " + funcname + "(" + actuals +");\n" - + " }\n"); + + " })\n"); // To prevent the compiler from doing constant folding, produce an // array to pass to mapfunc that contains enough dummy diff --git a/js/src/trace-test/tests/basic/testBug579602.js b/js/src/trace-test/tests/basic/testBug579602.js index 581e620e06a5..5871c24d46e3 100644 --- a/js/src/trace-test/tests/basic/testBug579602.js +++ b/js/src/trace-test/tests/basic/testBug579602.js @@ -4,7 +4,7 @@ f = function() { x = yield } rv = f() -for (a in rv) function() {} +for (a in rv) (function() {}) x = Proxy.create((function() { return { defineProperty: gc diff --git a/js/src/trace-test/tests/closures/bug540136.js b/js/src/trace-test/tests/closures/bug540136.js index 79d0569706b0..bf815fcf5f05 100644 --- a/js/src/trace-test/tests/closures/bug540136.js +++ b/js/src/trace-test/tests/closures/bug540136.js @@ -1,7 +1,7 @@ // |trace-test| error: TypeError (eval("\ - function () {\ + (function () {\ for (var[x] = function(){} in \ (function m(a) {\ if (a < 1) {\ @@ -13,5 +13,5 @@ (eval(\"\"))\ )\ ([])\ - }\ + })\ "))() diff --git a/js/src/trace-test/tests/closures/bug540242.js b/js/src/trace-test/tests/closures/bug540242.js index 1ee85a9d26ea..56c1a5a31436 100644 --- a/js/src/trace-test/tests/closures/bug540242.js +++ b/js/src/trace-test/tests/closures/bug540242.js @@ -1,6 +1,6 @@ for (j = 0; j < 1; j++) { var f = eval("\ - function() {\ + (function() {\ for (var a = 0; a < 8; ++a) {\ if (a % 3 == 2) {\ eval(\"\ @@ -11,7 +11,7 @@ for (j = 0; j < 1; j++) { }\ gc()\ }\ - }\ + })\ "); f() } diff --git a/js/src/trace-test/tests/closures/bug540243.js b/js/src/trace-test/tests/closures/bug540243.js index 34f4b1e201de..07faa26b6e75 100644 --- a/js/src/trace-test/tests/closures/bug540243.js +++ b/js/src/trace-test/tests/closures/bug540243.js @@ -1,10 +1,10 @@ for (a in (eval("\ - function() {\ + (function() {\ return function() {\ yield ((function() {\ return d\ })())\ } ();\ var d = []\ - }\ + })\ "))()); diff --git a/js/src/trace-test/tests/closures/bug541239.js b/js/src/trace-test/tests/closures/bug541239.js index 2ae3052af0fc..32e3af1565b4 100644 --- a/js/src/trace-test/tests/closures/bug541239.js +++ b/js/src/trace-test/tests/closures/bug541239.js @@ -2,14 +2,14 @@ function m() { var d = 73; return (eval("\n\ - function() {\n\ + (function() {\n\ return function() {\n\ yield ((function() {\n\ print(d);\n\ return d\n\ })())\n\ } ();\n\ - }\n\ + })\n\ "))(); } diff --git a/js/src/trace-test/tests/closures/setname-inner-heavy.js b/js/src/trace-test/tests/closures/setname-inner-heavy.js index 1a9a358b4b9b..9c1919dc2f35 100644 --- a/js/src/trace-test/tests/closures/setname-inner-heavy.js +++ b/js/src/trace-test/tests/closures/setname-inner-heavy.js @@ -3,11 +3,11 @@ expected = 'undefined,'; function f() { (eval("\ - function () {\ + (function () {\ for (var z = 0; z < 2; ++z) {\ x = ''\ }\ - }\ + })\ "))(); } __defineSetter__("x", eval) diff --git a/js/src/trace-test/tests/jaeger/bug555543.js b/js/src/trace-test/tests/jaeger/bug555543.js index 29bb2912257c..b641c1b5832f 100644 --- a/js/src/trace-test/tests/jaeger/bug555543.js +++ b/js/src/trace-test/tests/jaeger/bug555543.js @@ -1,6 +1,6 @@ (function() { for each(let z in [new String(''), new String('q'), new String('')]) { - if (uneval() < z) function(){} + if (uneval() < z) (function(){}) } })() diff --git a/js/src/trace-test/tests/jaeger/bug573433.js b/js/src/trace-test/tests/jaeger/bug573433.js index 26a14e987d47..bf50585890cd 100644 --- a/js/src/trace-test/tests/jaeger/bug573433.js +++ b/js/src/trace-test/tests/jaeger/bug573433.js @@ -1,6 +1,6 @@ // |trace-test| error: TypeError function f() { - eval("function() \n{\nfor(x in[])\n{}\n}"); + eval("(function() \n{\nfor(x in[])\n{}\n})"); ("")() } f() diff --git a/js/src/trace-test/tests/jaeger/bug580884.js b/js/src/trace-test/tests/jaeger/bug580884.js index 5579afcd1a46..d05ccf680185 100644 --- a/js/src/trace-test/tests/jaeger/bug580884.js +++ b/js/src/trace-test/tests/jaeger/bug580884.js @@ -2,7 +2,7 @@ for (let a in [0]) a = e for (let a in [0]) -function () { +(function () { a -} +}) diff --git a/js/src/trace-test/tests/jaeger/bug583158.js b/js/src/trace-test/tests/jaeger/bug583158.js index 0524cff7276f..ec533bfb3782 100644 --- a/js/src/trace-test/tests/jaeger/bug583158.js +++ b/js/src/trace-test/tests/jaeger/bug583158.js @@ -3,7 +3,7 @@ function g() { var rv = (function() { this << 1 })() - if (a) function() {} + if (a) (function() {}) } g() diff --git a/js/src/trace-test/tests/jaeger/bug588338.js b/js/src/trace-test/tests/jaeger/bug588338.js index 5275e7056a19..dee86ae8cd4d 100644 --- a/js/src/trace-test/tests/jaeger/bug588338.js +++ b/js/src/trace-test/tests/jaeger/bug588338.js @@ -1,5 +1,5 @@ // |trace-test| error: is not a function -function() { (e) +function f() { (e) } (x = Proxy.createFunction((function(x) { return { get: function(r, b) { diff --git a/js/src/trace-test/tests/pic/bug558099.js b/js/src/trace-test/tests/pic/bug558099.js index 8b4694a91f77..5d8c68fa590c 100644 --- a/js/src/trace-test/tests/pic/bug558099.js +++ b/js/src/trace-test/tests/pic/bug558099.js @@ -1,4 +1,4 @@ -function()[function() function() function() function() function() function() {}] +(function()[function() function() function() function() function() function() {}]); foo = [{ text: "(function(){if(d){(1)}})", s: function() {}, @@ -46,9 +46,9 @@ foo = [{ } } ()); s = [function() function() function() function() function() function() {}] -[function() function() function() function() {}] -function() { [function() function() {}] } -function() {} +[function() function() function() function() {}]; +(function() { [function() function() {}] }); +(function() {}); (eval("\ (function(){\ for each(d in[\ @@ -57,4 +57,4 @@ function() {} [].filter(new Function,gczeal(2))\ }\ })\ -"))() +"))(); From bfac56a8fb4f98abfd2e9991ebfa80c0d6468d61 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 12 Oct 2010 11:50:02 -0700 Subject: [PATCH 245/284] Bug 514570 - 1 - Convert primitive-this-of-type function flags into a single primitive-this flag, to pave way for strict mode unadulterated-this passing. r=jorendorff --- js/src/jsapi.h | 8 +-- js/src/jsbool.cpp | 42 ++++++++-------- js/src/jsfun.h | 2 + js/src/jsinterp.cpp | 56 +++++++++++++++------ js/src/jsinterp.h | 37 +++++++------- js/src/jsinterpinlines.h | 61 +++++++++++++++++++++++ js/src/jsnum.cpp | 66 ++++++++++++------------- js/src/jsstr.cpp | 92 ++++++++++++++++++----------------- js/src/methodjit/Compiler.cpp | 2 +- js/src/shell/js.cpp | 4 +- 10 files changed, 228 insertions(+), 142 deletions(-) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 0ba0c614c49b..4af9d694f83f 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -491,13 +491,7 @@ extern JS_PUBLIC_DATA(jsid) JSID_EMPTY; #define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT) -#define JSFUN_THISP_FLAGS(f) (f) -#define JSFUN_THISP_TEST(f,t) ((f) & t) - -#define JSFUN_THISP_STRING 0x0100 /* |this| may be a primitive string */ -#define JSFUN_THISP_NUMBER 0x0200 /* |this| may be a primitive number */ -#define JSFUN_THISP_BOOLEAN 0x0400 /* |this| may be a primitive boolean */ -#define JSFUN_THISP_PRIMITIVE 0x0700 /* |this| may be any primitive value */ +#define JSFUN_PRIMITIVE_THIS 0x0100 /* |this| may be a primitive value */ #define JSFUN_FLAGS_MASK 0x07fa /* overlay JSFUN_* attributes -- bits 12-15 are used internally to diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp index 63c2c2f17516..08fe963aa78e 100644 --- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -54,6 +54,7 @@ #include "jsstr.h" #include "jsvector.h" +#include "jsinterpinlines.h" #include "jsobjinlines.h" using namespace js; @@ -77,28 +78,28 @@ Class js_BooleanClass = { static JSBool bool_toSource(JSContext *cx, uintN argc, Value *vp) { - const Value *primp; - if (!js_GetPrimitiveThis(cx, vp, &js_BooleanClass, &primp)) - return JS_FALSE; + bool b; + if (!GetPrimitiveThis(cx, vp, &b)) + return false; + char buf[32]; - JS_snprintf(buf, sizeof buf, "(new %s(%s))", - js_BooleanClass.name, - JS_BOOLEAN_STR(primp->toBoolean())); + JS_snprintf(buf, sizeof buf, "(new Boolean(%s))", JS_BOOLEAN_STR(b)); JSString *str = JS_NewStringCopyZ(cx, buf); if (!str) - return JS_FALSE; + return false; vp->setString(str); - return JS_TRUE; + return true; } #endif static JSBool bool_toString(JSContext *cx, uintN argc, Value *vp) { - const Value *primp; - if (!js_GetPrimitiveThis(cx, vp, &js_BooleanClass, &primp)) - return JS_FALSE; - JSAtom *atom = cx->runtime->atomState.booleanAtoms[primp->toBoolean() ? 1 : 0]; + bool b; + if (!GetPrimitiveThis(cx, vp, &b)) + return false; + + JSAtom *atom = cx->runtime->atomState.booleanAtoms[b ? 1 : 0]; JSString *str = ATOM_TO_STRING(atom); if (!str) return JS_FALSE; @@ -109,20 +110,21 @@ bool_toString(JSContext *cx, uintN argc, Value *vp) static JSBool bool_valueOf(JSContext *cx, uintN argc, Value *vp) { - const Value *primp; - if (!js_GetPrimitiveThis(cx, vp, &js_BooleanClass, &primp)) - return JS_FALSE; - *vp = *primp; + bool b; + if (!GetPrimitiveThis(cx, vp, &b)) + return false; + + vp->setBoolean(b); return JS_TRUE; } static JSFunctionSpec boolean_methods[] = { #if JS_HAS_TOSOURCE - JS_FN(js_toSource_str, bool_toSource, 0, JSFUN_THISP_BOOLEAN), + JS_FN(js_toSource_str, bool_toSource, 0, JSFUN_PRIMITIVE_THIS), #endif - JS_FN(js_toString_str, bool_toString, 0, JSFUN_THISP_BOOLEAN), - JS_FN(js_valueOf_str, bool_valueOf, 0, JSFUN_THISP_BOOLEAN), - JS_FN(js_toJSON_str, bool_valueOf, 0, JSFUN_THISP_BOOLEAN), + JS_FN(js_toString_str, bool_toString, 0, JSFUN_PRIMITIVE_THIS), + JS_FN(js_valueOf_str, bool_valueOf, 0, JSFUN_PRIMITIVE_THIS), + JS_FN(js_toJSON_str, bool_valueOf, 0, JSFUN_PRIMITIVE_THIS), JS_FS_END }; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 8fc6d487fecf..b0787aa70c79 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -178,6 +178,8 @@ struct JSFunction : public JSObject inline bool inStrictMode() const; + bool acceptsPrimitiveThis() const { return flags & JSFUN_PRIMITIVE_THIS; } + uintN countVars() const { JS_ASSERT(FUN_INTERPRETED(this)); return u.i.nvars; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index cab81834ddc5..7eca4a97d7c7 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -410,21 +410,6 @@ js_GetScopeChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen) return js_GetScopeChainFull(cx, fp, js_GetBlockChainFast(cx, fp, op, oplen)); } -JSBool -js_GetPrimitiveThis(JSContext *cx, Value *vp, Class *clasp, const Value **vpp) -{ - const Value *p = &vp[1]; - if (p->isObjectOrNull()) { - JSObject *obj = ComputeThisFromVp(cx, vp); - if (!InstanceOf(cx, obj, clasp, vp + 2)) - return JS_FALSE; - *vpp = &obj->getPrimitiveThis(); - } else { - *vpp = p; - } - return JS_TRUE; -} - /* Some objects (e.g., With) delegate 'this' to another object. */ static inline JSObject * CallThisObjectHook(JSContext *cx, JSObject *obj, Value *argv) @@ -463,6 +448,47 @@ ComputeGlobalThis(JSContext *cx, Value *argv) namespace js { +bool +ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp) +{ + Value &thisv = vp[1]; + +#ifdef DEBUG + if (thisv.isObject()) { + JS_ASSERT(thisv.toObject().getClass() != clasp); + } else if (thisv.isString()) { + JS_ASSERT(clasp != &js_StringClass); + } else if (thisv.isNumber()) { + JS_ASSERT(clasp != &js_NumberClass); + } else if (thisv.isBoolean()) { + JS_ASSERT(clasp != &js_BooleanClass); + } else { + JS_ASSERT(thisv.isUndefined() || thisv.isNull()); + } +#endif + + if (JSFunction *fun = js_ValueToFunction(cx, &vp[0], 0)) { + const char *name = thisv.isObject() + ? thisv.toObject().getClass()->name + : thisv.isString() + ? "string" + : thisv.isNumber() + ? "number" + : thisv.isBoolean() + ? "boolean" + : thisv.isNull() + ? js_null_str + : thisv.isUndefined() + ? js_undefined_str + : "value"; + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + clasp->name, JS_GetFunctionName(fun), + name); + } + return false; +} + bool ComputeThisFromArgv(JSContext *cx, Value *argv) { diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index d819616f363d..c07ee1232574 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -799,21 +799,26 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp); extern JSObject * js_GetScopeChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen); -/* - * Given a context and a vector of [callee, this, args...] for a function that - * was specified with a JSFUN_THISP_PRIMITIVE flag, get the primitive value of - * |this| into *thisvp. In doing so, if |this| is an object, insist it is an - * instance of clasp and extract its private slot value to return via *thisvp. - * - * NB: this function loads and uses *vp before storing *thisvp, so the two may - * alias the same Value. - */ -extern JSBool -js_GetPrimitiveThis(JSContext *cx, js::Value *vp, js::Class *clasp, - const js::Value **vpp); - namespace js { +/* + * Report an error that the this value passed as |this| in the given arguments + * vector is not compatible with the specified class. + */ +bool +ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp); + +/* + * Given a context and a vector of [callee, this, args...] for a function + * whose JSFUN_PRIMITIVE_THIS flag is set, set |*v| to the primitive value + * of |this|. If |this| is an object, insist that it be an instance of the + * appropriate wrapper class for T, and set |*v| to its private slot value. + * If |this| is a primitive, unbox it into |*v| if it's of the required + * type, and throw an error otherwise. + */ +template +bool GetPrimitiveThis(JSContext *cx, Value *vp, T *v); + inline void PutActivationObjects(JSContext *cx, JSStackFrame *fp); @@ -840,13 +845,11 @@ ComputeThisFromVpInPlace(JSContext *cx, js::Value *vp) return ComputeThisFromArgv(cx, vp + 2); } +/* Return true if |fun| would accept |v| as its |this|, without being wrapped. */ JS_ALWAYS_INLINE bool PrimitiveThisTest(JSFunction *fun, const Value &v) { - uint16 flags = fun->flags; - return (v.isString() && !!(flags & JSFUN_THISP_STRING)) || - (v.isNumber() && !!(flags & JSFUN_THISP_NUMBER)) || - (v.isBoolean() && !!(flags & JSFUN_THISP_BOOLEAN)); + return !v.isPrimitive() || fun->acceptsPrimitiveThis(); } /* diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index b5bd787a3583..5740cfa187df 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -40,7 +40,12 @@ #ifndef jsinterpinlines_h__ #define jsinterpinlines_h__ +#include "jsapi.h" +#include "jsbool.h" +#include "jsinterp.h" +#include "jsnum.h" #include "jsprobes.h" +#include "jsstr.h" #include "methodjit/MethodJIT.h" inline void @@ -563,6 +568,62 @@ InvokeSessionGuard::invoke(JSContext *cx) const return ok; } +namespace detail { + +template class PrimitiveBehavior { }; + +template<> +class PrimitiveBehavior { + public: + static inline bool isType(const Value &v) { return v.isString(); } + static inline JSString *extract(const Value &v) { return v.toString(); } + static inline Class *getClass() { return &js_StringClass; } +}; + +template<> +class PrimitiveBehavior { + public: + static inline bool isType(const Value &v) { return v.isBoolean(); } + static inline bool extract(const Value &v) { return v.toBoolean(); } + static inline Class *getClass() { return &js_BooleanClass; } +}; + +template<> +class PrimitiveBehavior { + public: + static inline bool isType(const Value &v) { return v.isNumber(); } + static inline double extract(const Value &v) { return v.toNumber(); } + static inline Class *getClass() { return &js_NumberClass; } +}; + +} // namespace detail + +template +bool +GetPrimitiveThis(JSContext *cx, Value *vp, T *v) +{ + typedef detail::PrimitiveBehavior Behavior; + + const Value &thisv = vp[1]; + if (Behavior::isType(thisv)) { + *v = Behavior::extract(thisv); + return true; + } + + if (thisv.isObjectOrNull()) { + JSObject *obj = thisv.toObjectOrNull(); + if (!obj || obj->getClass() != Behavior::getClass()) { + obj = ComputeThisFromVp(cx, vp); + if (!InstanceOf(cx, obj, Behavior::getClass(), vp + 2)) + return false; + } + *v = Behavior::extract(thisv.toObject().getPrimitiveThis()); + return true; + } + + return ReportIncompatibleMethod(cx, vp, Behavior::getClass()); +} + } #endif /* jsinterpinlines_h__ */ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 1056c9ef7010..270a5b68528d 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -71,6 +71,7 @@ #include "jstracer.h" #include "jsvector.h" +#include "jsinterpinlines.h" #include "jsobjinlines.h" #include "jsstrinlines.h" @@ -559,25 +560,24 @@ Number(JSContext *cx, uintN argc, Value *vp) static JSBool num_toSource(JSContext *cx, uintN argc, Value *vp) { - char buf[64]; - JSString *str; + double d; + if (!GetPrimitiveThis(cx, vp, &d)) + return false; - const Value *primp; - if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &primp)) - return JS_FALSE; - double d = primp->toNumber(); ToCStringBuf cbuf; char *numStr = NumberToCString(cx, &cbuf, d); if (!numStr) { JS_ReportOutOfMemory(cx); - return JS_FALSE; + return false; } + + char buf[64]; JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); - str = js_NewStringCopyZ(cx, buf); + JSString *str = js_NewStringCopyZ(cx, buf); if (!str) - return JS_FALSE; + return false; vp->setString(str); - return JS_TRUE; + return true; } #endif @@ -645,10 +645,10 @@ js_NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base); static JSBool num_toString(JSContext *cx, uintN argc, Value *vp) { - const Value *primp; - if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &primp)) - return JS_FALSE; - double d = primp->toNumber(); + double d; + if (!GetPrimitiveThis(cx, vp, &d)) + return false; + int32_t base = 10; if (argc != 0 && !vp[2].isUndefined()) { if (!ValueToECMAInt32(cx, vp[2], &base)) @@ -782,15 +782,12 @@ num_toLocaleString(JSContext *cx, uintN argc, Value *vp) static JSBool num_valueOf(JSContext *cx, uintN argc, Value *vp) { - if (vp[1].isNumber()) { - *vp = vp[1]; - return JS_TRUE; - } - JSObject *obj = ComputeThisFromVp(cx, vp); - if (!InstanceOf(cx, obj, &js_NumberClass, vp + 2)) - return JS_FALSE; - *vp = obj->getPrimitiveThis(); - return JS_TRUE; + double d; + if (!GetPrimitiveThis(cx, vp, &d)) + return false; + + vp->setNumber(d); + return true; } @@ -805,10 +802,9 @@ num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)]; char *numStr; - const Value *primp; - if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &primp)) - return JS_FALSE; - double d = primp->toNumber(); + double d; + if (!GetPrimitiveThis(cx, vp, &d)) + return false; double precision; if (argc == 0) { @@ -879,15 +875,15 @@ JS_DEFINE_TRCINFO_2(num_toString, static JSFunctionSpec number_methods[] = { #if JS_HAS_TOSOURCE - JS_FN(js_toSource_str, num_toSource, 0,JSFUN_THISP_NUMBER), + JS_FN(js_toSource_str, num_toSource, 0, JSFUN_PRIMITIVE_THIS), #endif - JS_TN(js_toString_str, num_toString, 1,JSFUN_THISP_NUMBER, &num_toString_trcinfo), - JS_FN(js_toLocaleString_str, num_toLocaleString, 0,JSFUN_THISP_NUMBER), - JS_FN(js_valueOf_str, num_valueOf, 0,JSFUN_THISP_NUMBER), - JS_FN(js_toJSON_str, num_valueOf, 0,JSFUN_THISP_NUMBER), - JS_FN("toFixed", num_toFixed, 1,JSFUN_THISP_NUMBER), - JS_FN("toExponential", num_toExponential, 1,JSFUN_THISP_NUMBER), - JS_FN("toPrecision", num_toPrecision, 1,JSFUN_THISP_NUMBER), + JS_TN(js_toString_str, num_toString, 1, JSFUN_PRIMITIVE_THIS, &num_toString_trcinfo), + JS_FN(js_toLocaleString_str, num_toLocaleString, 0, JSFUN_PRIMITIVE_THIS), + JS_FN(js_valueOf_str, num_valueOf, 0, JSFUN_PRIMITIVE_THIS), + JS_FN(js_toJSON_str, num_valueOf, 0, JSFUN_PRIMITIVE_THIS), + JS_FN("toFixed", num_toFixed, 1, JSFUN_PRIMITIVE_THIS), + JS_FN("toExponential", num_toExponential, 1, JSFUN_PRIMITIVE_THIS), + JS_FN("toPrecision", num_toPrecision, 1, JSFUN_PRIMITIVE_THIS), JS_FS_END }; diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 4b3639b9a386..6faa966181a7 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -79,9 +79,8 @@ #include "jscntxtinlines.h" #include "jsinterpinlines.h" #include "jsobjinlines.h" -#include "jsstrinlines.h" #include "jsregexpinlines.h" -#include "jscntxtinlines.h" +#include "jsstrinlines.h" using namespace js; using namespace js::gc; @@ -861,10 +860,13 @@ NormalizeThis(JSContext *cx, Value *vp) return NULL; /* - * js_GetPrimitiveThis seems to do a bunch of work (like calls to - * JS_THIS_OBJECT) which we don't need in the common case (where - * vp[1] is a String object) here. Note that vp[1] can still be a - * primitive value at this point. + * String.prototype.{toString,toSource,valueOf} throw a TypeError if the + * this-argument is not a string or a String object. So those methods use + * js::GetPrimitiveThis which provides that behavior. + * + * By standard, the rest of the String methods must ToString the + * this-argument rather than throw a TypeError. So those methods use + * NORMALIZE_THIS (and thus NormalizeThis) instead. */ if (vp[1].isObject()) { JSObject *obj = &vp[1].toObject(); @@ -904,23 +906,26 @@ static JSBool str_toSource(JSContext *cx, uintN argc, Value *vp) { JSString *str; - size_t i, j, k, n; - char buf[16]; - const jschar *s; - jschar *t; + if (!GetPrimitiveThis(cx, vp, &str)) + return false; - const Value *primp; - if (!js_GetPrimitiveThis(cx, vp, &js_StringClass, &primp)) - return JS_FALSE; - str = js_QuoteString(cx, primp->toString(), '"'); + str = js_QuoteString(cx, str, '"'); if (!str) - return JS_FALSE; - j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name); + return false; + + char buf[16]; + size_t j = JS_snprintf(buf, sizeof buf, "(new String("); + + const jschar *s; + size_t k; str->getCharsAndLength(s, k); - n = j + k + 2; - t = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + + size_t n = j + k + 2; + jschar *t = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); if (!t) - return JS_FALSE; + return false; + + size_t i; for (i = 0; i < j; i++) t[i] = buf[i]; for (j = 0; j < k; i++, j++) @@ -928,13 +933,14 @@ str_toSource(JSContext *cx, uintN argc, Value *vp) t[i++] = ')'; t[i++] = ')'; t[i] = 0; + str = js_NewString(cx, t, n); if (!str) { cx->free(t); - return JS_FALSE; + return false; } vp->setString(str); - return JS_TRUE; + return true; } #endif /* JS_HAS_TOSOURCE */ @@ -942,10 +948,10 @@ str_toSource(JSContext *cx, uintN argc, Value *vp) JSBool js_str_toString(JSContext *cx, uintN argc, Value *vp) { - const Value *primp; - if (!js_GetPrimitiveThis(cx, vp, &js_StringClass, &primp)) + JSString *str; + if (!GetPrimitiveThis(cx, vp, &str)) return false; - *vp = *primp; + vp->setString(str); return true; } @@ -3022,20 +3028,18 @@ JS_DEFINE_TRCINFO_1(str_concat, (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING, 1, nanojit::ACCSET_NONE))) -#define GENERIC JSFUN_GENERIC_NATIVE -#define PRIMITIVE JSFUN_THISP_PRIMITIVE -#define GENERIC_PRIMITIVE (GENERIC | PRIMITIVE) +static const uint16 GENERIC_PRIMITIVE = JSFUN_GENERIC_NATIVE | JSFUN_PRIMITIVE_THIS; static JSFunctionSpec string_methods[] = { #if JS_HAS_TOSOURCE JS_FN("quote", str_quote, 0,GENERIC_PRIMITIVE), - JS_FN(js_toSource_str, str_toSource, 0,JSFUN_THISP_STRING), + JS_FN(js_toSource_str, str_toSource, 0,JSFUN_PRIMITIVE_THIS), #endif /* Java-like methods. */ - JS_FN(js_toString_str, js_str_toString, 0,JSFUN_THISP_STRING), - JS_FN(js_valueOf_str, js_str_toString, 0,JSFUN_THISP_STRING), - JS_FN(js_toJSON_str, js_str_toString, 0,JSFUN_THISP_STRING), + JS_FN(js_toString_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS), + JS_FN(js_valueOf_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS), + JS_FN(js_toJSON_str, js_str_toString, 0,JSFUN_PRIMITIVE_THIS), JS_FN("substring", str_substring, 2,GENERIC_PRIMITIVE), JS_FN("toLowerCase", str_toLowerCase, 0,GENERIC_PRIMITIVE), JS_FN("toUpperCase", str_toUpperCase, 0,GENERIC_PRIMITIVE), @@ -3065,19 +3069,19 @@ static JSFunctionSpec string_methods[] = { /* HTML string methods. */ #if JS_HAS_STR_HTML_HELPERS - JS_FN("bold", str_bold, 0,PRIMITIVE), - JS_FN("italics", str_italics, 0,PRIMITIVE), - JS_FN("fixed", str_fixed, 0,PRIMITIVE), - JS_FN("fontsize", str_fontsize, 1,PRIMITIVE), - JS_FN("fontcolor", str_fontcolor, 1,PRIMITIVE), - JS_FN("link", str_link, 1,PRIMITIVE), - JS_FN("anchor", str_anchor, 1,PRIMITIVE), - JS_FN("strike", str_strike, 0,PRIMITIVE), - JS_FN("small", str_small, 0,PRIMITIVE), - JS_FN("big", str_big, 0,PRIMITIVE), - JS_FN("blink", str_blink, 0,PRIMITIVE), - JS_FN("sup", str_sup, 0,PRIMITIVE), - JS_FN("sub", str_sub, 0,PRIMITIVE), + JS_FN("bold", str_bold, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("italics", str_italics, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("fixed", str_fixed, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("fontsize", str_fontsize, 1,JSFUN_PRIMITIVE_THIS), + JS_FN("fontcolor", str_fontcolor, 1,JSFUN_PRIMITIVE_THIS), + JS_FN("link", str_link, 1,JSFUN_PRIMITIVE_THIS), + JS_FN("anchor", str_anchor, 1,JSFUN_PRIMITIVE_THIS), + JS_FN("strike", str_strike, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("small", str_small, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("big", str_big, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("blink", str_blink, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("sup", str_sup, 0,JSFUN_PRIMITIVE_THIS), + JS_FN("sub", str_sub, 0,JSFUN_PRIMITIVE_THIS), #endif JS_FS_END diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index cef97df52df9..44177542b09d 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2845,7 +2845,7 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom) masm.loadFunctionPrivate(funReg, temp); masm.load16(Address(temp, offsetof(JSFunction, flags)), temp); - Jump noPrim = masm.branchTest32(Assembler::Zero, temp, Imm32(JSFUN_THISP_STRING)); + Jump noPrim = masm.branchTest32(Assembler::Zero, temp, Imm32(JSFUN_PRIMITIVE_THIS)); { stubcc.linkExit(noPrim, Uses(2)); stubcc.leave(); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 6be9a2832560..babacf5f9549 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1940,9 +1940,7 @@ DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive) SHOW_FLAG(LAMBDA); SHOW_FLAG(HEAVYWEIGHT); - SHOW_FLAG(THISP_STRING); - SHOW_FLAG(THISP_NUMBER); - SHOW_FLAG(THISP_BOOLEAN); + SHOW_FLAG(PRIMITIVE_THIS); SHOW_FLAG(EXPR_CLOSURE); SHOW_FLAG(TRCINFO); From 8db4fe218aa689e28c5f99fe31c2fe0fa4490493 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 12 Oct 2010 11:50:03 -0700 Subject: [PATCH 246/284] Bug 514570 - 2 - Push |undefined| rather than |null| when calling functions without a specified |this| value, per ES5. r=jorendorff --- js/src/jsarray.cpp | 15 ++++++---- js/src/jsemit.cpp | 20 +++++-------- js/src/jsfun.cpp | 45 +++++++++++++++++++----------- js/src/jsinterp.cpp | 32 +++++++++++---------- js/src/jsobj.cpp | 8 ++++-- js/src/jsopcode.cpp | 4 +-- js/src/jsstr.cpp | 6 ++-- js/src/jstracer.cpp | 24 ++++++---------- js/src/methodjit/Compiler.cpp | 16 +++++------ js/src/methodjit/FrameState-inl.h | 9 ++++++ js/src/methodjit/FrameState.h | 20 ++++++++----- js/src/methodjit/NunboxAssembler.h | 8 ++++++ js/src/methodjit/PunboxAssembler.h | 9 ++++++ js/src/methodjit/StubCalls.cpp | 11 ++++---- 14 files changed, 134 insertions(+), 93 deletions(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index f0247e813052..fae9a38c7c59 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2816,12 +2816,17 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) Value thisv; if (argc > 1 && !REDUCE_MODE(mode)) { - JSObject *thisp; - if (!js_ValueToObjectOrNull(cx, argv[1], &thisp)) - return JS_FALSE; - thisv.setObjectOrNull(thisp); + if (argv[1].isNullOrUndefined()) { + thisv.setUndefined(); + } else { + JSObject *thisObj; + if (!js_ValueToObjectOrNull(cx, argv[1], &thisObj)) + return JS_FALSE; + JS_ASSERT(thisObj); + thisv.setObject(*thisObj); + } } else { - thisv.setNull(); + thisv.setUndefined(); } /* diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 81c24f2787e3..032139fcebce 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -6525,12 +6525,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * interpose the lambda-initialized method read barrier -- see the code * in jsinterp.cpp for JSOP_LAMBDA followed by JSOP_{SET,INIT}PROP. * - * Then (or in a call case that has no explicit reference-base object) - * we emit JSOP_NULL as a placeholder local GC root to hold the |this| - * parameter: in the operator new case, the newborn instance; in the - * base-less call case, a cookie meaning "use the global object as the - * |this| value" (or in ES5 strict mode, "use undefined", so we should - * use JSOP_PUSH instead of JSOP_NULL -- see bug 514570). + * Then (or in a call case that has no explicit reference-base + * object) we emit JSOP_PUSH to produce the |this| slot required + * for calls (which non-strict mode functions will box into the + * global object). */ pn2 = pn->pn_head; switch (pn2->pn_type) { @@ -6552,22 +6550,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (pn2->pn_op == JSOP_XMLNAME) { if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, cg)) return JS_FALSE; - callop = true; /* suppress JSOP_NULL after */ + callop = true; /* suppress JSOP_PUSH after */ break; } #endif /* FALL THROUGH */ default: - /* - * Push null as a placeholder for the global object, per ECMA-262 - * 11.2.3 step 6. - */ if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; - callop = false; /* trigger JSOP_NULL after */ + callop = false; /* trigger JSOP_PUSH after */ break; } - if (!callop && js_Emit1(cx, cg, JSOP_NULL) < 0) + if (!callop && js_Emit1(cx, cg, JSOP_PUSH) < 0) return JS_FALSE; /* Remember start of callable-object bytecode for decompilation hint. */ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 04cdef40d8a5..a3e59e7e7415 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2258,15 +2258,20 @@ js_fun_call(JSContext *cx, uintN argc, Value *vp) } Value *argv = vp + 2; + Value thisv; if (argc == 0) { /* Call fun with its global object as the 'this' param if no args. */ - obj = NULL; + thisv.setUndefined(); } else { /* Otherwise convert the first arg to 'this' and skip over it. */ - if (argv[0].isObject()) - obj = &argv[0].toObject(); - else if (!js_ValueToObjectOrNull(cx, argv[0], &obj)) - return JS_FALSE; + if (argv[0].isNullOrUndefined()) { + thisv.setUndefined(); + } else { + if (!js_ValueToObjectOrNull(cx, argv[0], &obj)) + return JS_FALSE; + JS_ASSERT(obj); + thisv.setObject(*obj); + } argc--; argv++; } @@ -2276,9 +2281,9 @@ js_fun_call(JSContext *cx, uintN argc, Value *vp) if (!cx->stack().pushInvokeArgs(cx, argc, &args)) return JS_FALSE; - /* Push fval, obj, and the args. */ + /* Push fval, thisv, and the args. */ args.callee() = fval; - args.thisv().setObjectOrNull(obj); + args.thisv() = thisv; memcpy(args.argv(), argv, argc * sizeof *argv); bool ok = Invoke(cx, args, 0); @@ -2360,11 +2365,17 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp) } } + /* Convert the first arg to 'this' and skip over it. */ - if (vp[2].isObject()) - obj = &vp[2].toObject(); - else if (!js_ValueToObjectOrNull(cx, vp[2], &obj)) - return JS_FALSE; + Value thisv; + if (vp[2].isNullOrUndefined()) { + thisv.setUndefined(); + } else { + if (!js_ValueToObjectOrNull(cx, vp[2], &obj)) + return JS_FALSE; + JS_ASSERT(obj); + thisv.setObject(*obj); + } LeaveTrace(cx); @@ -2377,7 +2388,7 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp) /* Push fval, obj, and aobj's elements as args. */ args.callee() = fval; - args.thisv().setObjectOrNull(obj); + args.thisv() = thisv; /* Steps 7-8. */ if (aobj && aobj->isArguments() && !aobj->isArgsLengthOverridden()) { @@ -2525,15 +2536,15 @@ CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp) * very shortly when this-boxing only occurs for non-strict * functions, callee-side, in bug 514570. */ - JSObject *boundThisObj; - if (boundThis.isObjectOrNull()) { - boundThisObj = boundThis.toObjectOrNull(); + if (boundThis.isNullOrUndefined()) { + args.thisv().setUndefined(); } else { + JSObject *boundThisObj; if (!js_ValueToObjectOrNull(cx, boundThis, &boundThisObj)) return false; + JS_ASSERT(boundThisObj); + args.thisv().setObject(*boundThisObj); } - - args.thisv() = ObjectOrNullValue(boundThisObj); } if (constructing ? !InvokeConstructor(cx, args) : !Invoke(cx, args, 0)) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 7eca4a97d7c7..47a84e133ccd 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -498,7 +498,7 @@ ComputeThisFromArgv(JSContext *cx, Value *argv) */ JS_ASSERT(!argv[-1].isMagic()); - if (argv[-1].isNull()) + if (argv[-1].isNullOrUndefined()) return ComputeGlobalThis(cx, argv); if (!argv[-1].isObject()) @@ -678,7 +678,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) JSFunction *fun = callee.getFunctionPrivate(); JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !fun->isConstructor()); if (fun->isNative()) { - JS_ASSERT(args.thisv().isObjectOrNull() || PrimitiveThisTest(fun, args.thisv())); + JS_ASSERT(args.thisv().isObject() || args.thisv().isUndefined() || PrimitiveThisTest(fun, args.thisv())); return CallJSNative(cx, fun->u.n.native, args.argc(), args.base()); } @@ -4239,7 +4239,7 @@ BEGIN_CASE(JSOP_CALLPROP) /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */ JSObject *funobj; if (!IsFunctionObject(rval, &funobj) || - !PrimitiveThisTest(GET_FUNCTION_PRIVATE(cx, funobj), lval)) { + !PrimitiveThisTest(funobj->getFunctionPrivate(), lval)) { if (!js_PrimitiveToObject(cx, ®s.sp[-1])) goto error; } @@ -4721,7 +4721,7 @@ BEGIN_CASE(JSOP_APPLY) DO_OP(); } - JS_ASSERT(vp[1].isObjectOrNull() || PrimitiveThisTest(newfun, vp[1])); + JS_ASSERT(vp[1].isObject() || vp[1].isUndefined() || PrimitiveThisTest(newfun, vp[1])); Probes::enterJSFun(cx, newfun); JSBool ok = CallJSNative(cx, newfun->u.n.native, argc, vp); @@ -4766,15 +4766,17 @@ END_CASE(JSOP_SETCALL) (clasp = thisp->getClass()) == &js_CallClass || \ clasp == &js_BlockClass || \ clasp == &js_DeclEnvClass) { \ - /* Normal case: thisp is global or an activation record. */ \ - /* Callee determines |this|. */ \ - thisp = NULL; \ + /* Push the ImplicitThisValue for the Environment Record */ \ + /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \ + /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \ + /* (Function Calls). */ \ + PUSH_UNDEFINED(); \ } else { \ thisp = thisp->thisObject(cx); \ if (!thisp) \ goto error; \ + PUSH_OBJECT(*thisp); \ } \ - PUSH_OBJECT_OR_NULL(thisp); \ JS_END_MACRO BEGIN_CASE(JSOP_GETGNAME) @@ -4819,7 +4821,7 @@ BEGIN_CASE(JSOP_CALLNAME) clasp == &js_DeclEnvClass); #endif if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - PUSH_NULL(); + PUSH_UNDEFINED(); len = JSOP_NAME_LENGTH; DO_NEXT_OP(len); } @@ -5180,7 +5182,7 @@ BEGIN_CASE(JSOP_CALLARG) METER_SLOT_OP(op, slot); PUSH_COPY(argv[slot]); if (op == JSOP_CALLARG) - PUSH_NULL(); + PUSH_UNDEFINED(); } END_CASE(JSOP_GETARG) @@ -5206,7 +5208,7 @@ BEGIN_CASE(JSOP_CALLLOCAL) uint32 slot = GET_SLOTNO(regs.pc); JS_ASSERT(slot < script->nslots); PUSH_COPY(regs.fp->slots()[slot]); - PUSH_NULL(); + PUSH_UNDEFINED(); } END_CASE(JSOP_CALLLOCAL) @@ -5230,7 +5232,7 @@ BEGIN_CASE(JSOP_CALLUPVAR) PUSH_COPY(rval); if (op == JSOP_CALLUPVAR) - PUSH_NULL(); + PUSH_UNDEFINED(); } END_CASE(JSOP_GETUPVAR) @@ -5272,7 +5274,7 @@ BEGIN_CASE(JSOP_CALLUPVAR_DBG) goto error; if (op == JSOP_CALLUPVAR_DBG) - PUSH_NULL(); + PUSH_UNDEFINED(); } END_CASE(JSOP_GETUPVAR_DBG) @@ -5286,7 +5288,7 @@ BEGIN_CASE(JSOP_CALLFCSLOT) JS_ASSERT(index < obj->getFunctionPrivate()->u.i.nupvars); PUSH_COPY(obj->getFlatClosureUpvar(index)); if (op == JSOP_CALLFCSLOT) - PUSH_NULL(); + PUSH_UNDEFINED(); } END_CASE(JSOP_GETFCSLOT) @@ -5299,7 +5301,7 @@ BEGIN_CASE(JSOP_CALLGLOBAL) JS_ASSERT(obj->containsSlot(slot)); PUSH_COPY(obj->getSlot(slot)); if (op == JSOP_CALLGLOBAL) - PUSH_NULL(); + PUSH_UNDEFINED(); } END_CASE(JSOP_GETGLOBAL) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 2cfd02b6c6f0..d36965d67423 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -5926,12 +5926,14 @@ js_PrimitiveToObject(JSContext *cx, Value *vp) JS_ASSERT(v.isPrimitive()); Class *clasp; - if (v.isNumber()) + if (v.isNumber()) { clasp = &js_NumberClass; - else if (v.isString()) + } else if (v.isString()) { clasp = &js_StringClass; - else + } else { + JS_ASSERT(v.isBoolean()); clasp = &js_BooleanClass; + } JSObject *obj = NewBuiltinClassInstance(cx, clasp); if (!obj) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index efbb1ac9d8fa..9de6599212b4 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -4117,8 +4117,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) } else { JS_NOT_REACHED("should see block chain operation"); } - LOCAL_ASSERT(*pc == JSOP_NULL); - pc += JSOP_NULL_LENGTH; + LOCAL_ASSERT(*pc == JSOP_PUSH); + pc += JSOP_PUSH_LENGTH; LOCAL_ASSERT(*pc == JSOP_CALL); LOCAL_ASSERT(GET_ARGC(pc) == 0); len = JSOP_CALL_LENGTH; diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 6faa966181a7..fa9434e7081f 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -856,7 +856,7 @@ Class js_StringClass = { static JSString * NormalizeThis(JSContext *cx, Value *vp) { - if (vp[1].isNull() && (!ComputeThisFromVp(cx, vp) || vp[1].isNull())) + if (vp[1].isNullOrUndefined() && !ComputeThisFromVp(cx, vp)) return NULL; /* @@ -2100,7 +2100,7 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t InvokeSessionGuard &session = rdata.session; if (!session.started()) { Value lambdav = ObjectValue(*lambda); - if (!rdata.session.start(cx, lambdav, NullValue(), argc)) + if (!session.start(cx, lambdav, UndefinedValue(), argc)) return false; } @@ -2409,7 +2409,7 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata CallArgs &args = rdata.singleShot; args.callee().setObject(*rdata.lambda); - args.thisv().setNull(); + args.thisv().setUndefined(); Value *sp = args.argv(); sp[0].setString(matchStr); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index fd2151f8e3c0..1fad8e9cc550 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -7063,12 +7063,6 @@ LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr) JS_ASSERT(state.eos == state.stackBase + MAX_NATIVE_STACK_SLOTS); JSObject* globalObj = outermostTree->globalObj; FlushNativeGlobalFrame(cx, globalObj, state.eos, ngslots, gslots, globalTypeMap); -#ifdef DEBUG - /* Verify that our state restoration worked. */ - for (JSStackFrame* fp = cx->fp(); fp; fp = fp->prev()) { - JS_ASSERT_IF(fp->isFunctionFrame(), fp->thisValue().isObjectOrNull()); - } -#endif #ifdef JS_JIT_SPEW if (innermost->exitType != TIMEOUT_EXIT) @@ -10063,10 +10057,10 @@ TraceRecorder::getThis(LIns*& this_ins) JS_ASSERT(fp->callee().getGlobal() == globalObj); const Value& thisv = fp->thisValue(); - if (!thisv.isNull()) { + if (!thisv.isUndefined()) { /* * fp->argv[-1] has already been computed. Since the type-specialization - * of traces distinguishes between null and objects, the same will be + * of traces distinguishes between |undefined| and objects, the same will be * true at run time (or we won't get this far). */ this_ins = get(&fp->thisValue()); @@ -13139,7 +13133,7 @@ TraceRecorder::record_JSOP_CALLNAME() NameResult nr; CHECK_STATUS_A(scopeChainProp(obj, vp, ins, nr)); stack(0, ins); - stack(1, INS_NULL()); + stack(1, INS_UNDEFINED()); return ARECORD_CONTINUE; } @@ -13153,7 +13147,7 @@ TraceRecorder::record_JSOP_CALLNAME() RETURN_STOP_A("callee is not an object"); stack(0, INS_CONSTOBJ(&pcval.toFunObj())); - stack(1, INS_NULL()); + stack(1, INS_UNDEFINED()); return ARECORD_CONTINUE; } @@ -13282,7 +13276,7 @@ JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_CALLUPVAR() { CHECK_STATUS_A(record_JSOP_GETUPVAR()); - stack(1, INS_NULL()); + stack(1, INS_UNDEFINED()); return ARECORD_CONTINUE; } @@ -13306,7 +13300,7 @@ JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_CALLFCSLOT() { CHECK_STATUS_A(record_JSOP_GETFCSLOT()); - stack(1, INS_NULL()); + stack(1, INS_UNDEFINED()); return ARECORD_CONTINUE; } @@ -15954,7 +15948,7 @@ TraceRecorder::record_JSOP_CALLLOCAL() { uintN slot = GET_SLOTNO(cx->regs->pc); stack(0, var(slot)); - stack(1, INS_NULL()); + stack(1, INS_UNDEFINED()); return ARECORD_CONTINUE; } @@ -15963,7 +15957,7 @@ TraceRecorder::record_JSOP_CALLARG() { uintN slot = GET_ARGNO(cx->regs->pc); stack(0, arg(slot)); - stack(1, INS_NULL()); + stack(1, INS_UNDEFINED()); return ARECORD_CONTINUE; } @@ -16167,7 +16161,7 @@ TraceRecorder::record_JSOP_CALLGLOBAL() Value &v = globalObj->getSlotRef(slot); stack(0, get(&v)); - stack(1, INS_NULL()); + stack(1, INS_UNDEFINED()); return ARECORD_CONTINUE; } diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 44177542b09d..baccbf2070e6 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -1208,7 +1208,7 @@ mjit::Compiler::generateMethod() { jsop_getarg(GET_SLOTNO(PC)); if (op == JSOP_CALLARG) - frame.push(NullValue()); + frame.push(UndefinedValue()); } END_CASE(JSOP_GETARG) @@ -1497,7 +1497,7 @@ mjit::Compiler::generateMethod() frame.freeReg(reg); frame.push(Address(reg, index * sizeof(Value))); if (op == JSOP_CALLFCSLOT) - frame.push(NullValue()); + frame.push(UndefinedValue()); } END_CASE(JSOP_CALLFCSLOT) @@ -1536,7 +1536,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_CALLGNAME) jsop_getgname(fullAtomIndex(PC)); if (op == JSOP_CALLGNAME) - frame.push(NullValue()); + frame.push(UndefinedValue()); END_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_SETGNAME) @@ -1571,7 +1571,7 @@ mjit::Compiler::generateMethod() stubCall(stubs::GetUpvar); frame.pushSynced(); if (op == JSOP_CALLUPVAR) - frame.push(NullValue()); + frame.push(UndefinedValue()); } END_CASE(JSOP_CALLUPVAR) @@ -1607,7 +1607,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_CALLLOCAL) frame.pushLocal(GET_SLOTNO(PC)); - frame.push(NullValue()); + frame.push(UndefinedValue()); END_CASE(JSOP_CALLLOCAL) BEGIN_CASE(JSOP_INT8) @@ -1682,7 +1682,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_CALLGLOBAL) jsop_getglobal(GET_SLOTNO(PC)); if (op == JSOP_CALLGLOBAL) - frame.push(NullValue()); + frame.push(UndefinedValue()); END_CASE(JSOP_GETGLOBAL) BEGIN_CASE(JSOP_SETGLOBAL) @@ -3314,7 +3314,7 @@ mjit::Compiler::jsop_this() { Address thisvAddr(JSFrameReg, JSStackFrame::offsetOfThis(fun)); if (0 && !script->strictModeCode) { - Jump null = masm.testNull(Assembler::Equal, thisvAddr); + Jump null = masm.testUndefined(Assembler::Equal, thisvAddr); stubcc.linkExit(null, Uses(1)); stubcc.leave(); stubcc.call(stubs::ComputeThis); @@ -3325,7 +3325,7 @@ mjit::Compiler::jsop_this() frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); } else { frame.push(thisvAddr); - Jump null = frame.testNull(Assembler::Equal, frame.peek(-1)); + Jump null = frame.testUndefined(Assembler::Equal, frame.peek(-1)); stubcc.linkExit(null, Uses(1)); stubcc.leave(); stubcc.call(stubs::This); diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index 036c24aa717a..54fcb45585d9 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -567,6 +567,15 @@ FrameState::testNull(Assembler::Condition cond, FrameEntry *fe) return masm.testNull(cond, tempRegForType(fe)); } +inline JSC::MacroAssembler::Jump +FrameState::testUndefined(Assembler::Condition cond, FrameEntry *fe) +{ + JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + if (shouldAvoidTypeRemat(fe)) + return masm.testUndefined(cond, addressOf(fe)); + return masm.testUndefined(cond, tempRegForType(fe)); +} + inline JSC::MacroAssembler::Jump FrameState::testInt32(Assembler::Condition cond, FrameEntry *fe) { diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index bca9568d3b7d..7612662880b7 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -660,43 +660,49 @@ class FrameState void discardFe(FrameEntry *fe); /* - * Helper function. Tests if a slot's type is null. Condition should + * Helper function. Tests if a slot's type is null. Condition must * be Equal or NotEqual. */ inline Jump testNull(Assembler::Condition cond, FrameEntry *fe); /* - * Helper function. Tests if a slot's type is an integer. Condition should + * Helper function. Tests if a slot's type is undefined. Condition must + * be Equal or NotEqual. + */ + inline Jump testUndefined(Assembler::Condition cond, FrameEntry *fe); + + /* + * Helper function. Tests if a slot's type is an integer. Condition must * be Equal or NotEqual. */ inline Jump testInt32(Assembler::Condition cond, FrameEntry *fe); /* - * Helper function. Tests if a slot's type is a double. Condition should + * Helper function. Tests if a slot's type is a double. Condition must * be Equal or Not Equal. */ inline Jump testDouble(Assembler::Condition cond, FrameEntry *fe); /* - * Helper function. Tests if a slot's type is a boolean. Condition should + * Helper function. Tests if a slot's type is a boolean. Condition must * be Equal or NotEqual. */ inline Jump testBoolean(Assembler::Condition cond, FrameEntry *fe); /* - * Helper function. Tests if a slot's type is a string. Condition should + * Helper function. Tests if a slot's type is a string. Condition must * be Equal or NotEqual. */ inline Jump testString(Assembler::Condition cond, FrameEntry *fe); /* - * Helper function. Tests if a slot's type is a non-funobj. Condition should + * Helper function. Tests if a slot's type is a non-funobj. Condition must * be Equal or NotEqual. */ inline Jump testObject(Assembler::Condition cond, FrameEntry *fe); /* - * Helper function. Tests if a slot's type is primitve. Condition should + * Helper function. Tests if a slot's type is primitive. Condition must * be Equal or NotEqual. */ inline Jump testPrimitive(Assembler::Condition cond, FrameEntry *fe); diff --git a/js/src/methodjit/NunboxAssembler.h b/js/src/methodjit/NunboxAssembler.h index 1303361f8220..28a606dd0c20 100644 --- a/js/src/methodjit/NunboxAssembler.h +++ b/js/src/methodjit/NunboxAssembler.h @@ -215,6 +215,14 @@ class Assembler : public BaseAssembler return branch32(cond, tagOf(address), ImmTag(JSVAL_TAG_NULL)); } + Jump testUndefined(Assembler::Condition cond, RegisterID reg) { + return branch32(cond, reg, ImmTag(JSVAL_TAG_UNDEFINED)); + } + + Jump testUndefined(Assembler::Condition cond, Address address) { + return branch32(cond, tagOf(address), ImmTag(JSVAL_TAG_UNDEFINED)); + } + Jump testInt32(Assembler::Condition cond, RegisterID reg) { return branch32(cond, reg, ImmTag(JSVAL_TAG_INT32)); } diff --git a/js/src/methodjit/PunboxAssembler.h b/js/src/methodjit/PunboxAssembler.h index 1bdaaa5b80fa..0fae0464db09 100644 --- a/js/src/methodjit/PunboxAssembler.h +++ b/js/src/methodjit/PunboxAssembler.h @@ -236,6 +236,15 @@ class Assembler : public BaseAssembler return branchPtr(cond, Registers::ValueReg, Imm64(JSVAL_BITS(JSVAL_NULL))); } + Jump testUndefined(Assembler::Condition cond, RegisterID reg) { + return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_UNDEFINED)); + } + + Jump testUndefined(Assembler::Condition cond, Address address) { + loadValue(address, Registers::ValueReg); + return branchPtr(cond, Registers::ValueReg, Imm64(JSVAL_BITS(JSVAL_VOID))); + } + Jump testInt32(Assembler::Condition cond, RegisterID reg) { return branchPtr(cond, reg, ImmTag(JSVAL_SHIFTED_TAG_INT32)); } diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 26450a156578..52db615d2343 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -371,7 +371,7 @@ NameOp(VMFrame &f, JSObject *obj, bool callname = false) #endif if (callname) { f.regs.sp++; - f.regs.sp[-1].setNull(); + f.regs.sp[-1].setUndefined(); } return obj; } @@ -416,14 +416,15 @@ NameOp(VMFrame &f, JSObject *obj, bool callname = false) (clasp = thisp->getClass()) == &js_CallClass || clasp == &js_BlockClass || clasp == &js_DeclEnvClass) { - thisp = NULL; + f.regs.sp++; + f.regs.sp[-1].setUndefined(); } else { thisp = thisp->thisObject(cx); if (!thisp) return NULL; + f.regs.sp++; + f.regs.sp[-1].setObject(*thisp); } - f.regs.sp++; - f.regs.sp[-1].setObjectOrNull(thisp); } return obj; } @@ -2161,7 +2162,7 @@ stubs::CallProp(VMFrame &f, JSAtom *origAtom) /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */ JSObject *funobj; if (!IsFunctionObject(rval, &funobj) || - !PrimitiveThisTest(GET_FUNCTION_PRIVATE(cx, funobj), lval)) { + !PrimitiveThisTest(funobj->getFunctionPrivate(), lval)) { if (!js_PrimitiveToObject(cx, ®s.sp[-1])) THROW(); } From 1b66d40016a17abe097e0980ad366b2cbba8c5b4 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 12 Oct 2010 11:50:03 -0700 Subject: [PATCH 247/284] Bug 514570 - 3 - Don't box |this| for strict mode functions. r=jorendorff --- js/src/jsapi-tests/testDebugger.cpp | 79 ++++++++- js/src/jsapi.h | 1 + js/src/jsarray.cpp | 15 +- js/src/jsdbgapi.cpp | 11 +- js/src/jsdbgapi.h | 4 +- js/src/jsfun.cpp | 44 +---- js/src/jsinterp.cpp | 117 ++++++-------- js/src/jsinterp.h | 2 +- js/src/jsinterpinlines.h | 56 ++++++- js/src/jsobj.cpp | 8 + js/src/jsparse.cpp | 3 + js/src/jstracer.cpp | 51 ++++-- js/src/jsval.h | 2 + js/src/jsvalue.h | 12 +- js/src/methodjit/Compiler.cpp | 44 ++--- js/src/methodjit/PolyIC.cpp | 68 ++++---- js/src/methodjit/StubCalls.cpp | 123 ++++++-------- js/src/methodjit/StubCalls.h | 2 - js/src/tests/ecma_5/Boolean/15.6.4.2.js | 17 ++ js/src/tests/ecma_5/Boolean/browser.js | 0 js/src/tests/ecma_5/Boolean/jstests.list | 2 + js/src/tests/ecma_5/Boolean/shell.js | 0 js/src/tests/ecma_5/Date/toJSON-01.js | 40 +++-- js/src/tests/ecma_5/Function/10.2.1.1.6.js | 35 ++++ js/src/tests/ecma_5/Function/function-bind.js | 22 ++- js/src/tests/ecma_5/Function/jstests.list | 1 + js/src/tests/ecma_5/Number/15.7.4.2.js | 17 ++ js/src/tests/ecma_5/Number/browser.js | 0 js/src/tests/ecma_5/Number/jstests.list | 2 + js/src/tests/ecma_5/Number/shell.js | 0 js/src/tests/ecma_5/String/15.5.4.2.js | 14 ++ js/src/tests/ecma_5/String/jstests.list | 1 + .../ecma_5/extensions/Boolean-toSource.js | 17 ++ .../ecma_5/extensions/Number-toSource.js | 17 ++ .../ecma_5/extensions/String-toSource.js | 14 ++ js/src/tests/ecma_5/extensions/jstests.list | 4 + .../tests/ecma_5/extensions/proxy-strict.js | 12 ++ js/src/tests/ecma_5/jstests.list | 2 + js/src/tests/ecma_5/misc/jstests.list | 1 + .../ecma_5/misc/unwrapped-no-such-method.js | 13 ++ js/src/tests/ecma_5/shell.js | 153 +++++++++++++----- js/src/tests/ecma_5/strict/10.4.2.js | 17 ++ js/src/tests/ecma_5/strict/10.4.3.js | 58 +++++++ js/src/tests/ecma_5/strict/15.3.4.5.js | 26 +++ js/src/tests/ecma_5/strict/jstests.list | 4 + .../strict/primitive-this-no-writeback.js | 20 +++ js/src/tests/ecma_5/strict/shell.js | 118 -------------- js/src/tests/ecma_5/strict/unbrand-this.js | 45 ++++++ .../tests/js1_8/extensions/regress-394709.js | 9 +- .../tests/basic/wrap-primitive-this.js | 39 +++++ 50 files changed, 865 insertions(+), 497 deletions(-) create mode 100644 js/src/tests/ecma_5/Boolean/15.6.4.2.js create mode 100644 js/src/tests/ecma_5/Boolean/browser.js create mode 100644 js/src/tests/ecma_5/Boolean/jstests.list create mode 100644 js/src/tests/ecma_5/Boolean/shell.js create mode 100644 js/src/tests/ecma_5/Function/10.2.1.1.6.js create mode 100644 js/src/tests/ecma_5/Number/15.7.4.2.js create mode 100644 js/src/tests/ecma_5/Number/browser.js create mode 100644 js/src/tests/ecma_5/Number/jstests.list create mode 100644 js/src/tests/ecma_5/Number/shell.js create mode 100644 js/src/tests/ecma_5/String/15.5.4.2.js create mode 100644 js/src/tests/ecma_5/extensions/Boolean-toSource.js create mode 100644 js/src/tests/ecma_5/extensions/Number-toSource.js create mode 100644 js/src/tests/ecma_5/extensions/String-toSource.js create mode 100644 js/src/tests/ecma_5/extensions/proxy-strict.js create mode 100644 js/src/tests/ecma_5/misc/unwrapped-no-such-method.js create mode 100644 js/src/tests/ecma_5/strict/10.4.3.js create mode 100644 js/src/tests/ecma_5/strict/15.3.4.5.js create mode 100644 js/src/tests/ecma_5/strict/primitive-this-no-writeback.js create mode 100644 js/src/tests/ecma_5/strict/unbrand-this.js create mode 100644 js/src/trace-test/tests/basic/wrap-primitive-this.js diff --git a/js/src/jsapi-tests/testDebugger.cpp b/js/src/jsapi-tests/testDebugger.cpp index edde4eb38fc6..035e515be8f8 100644 --- a/js/src/jsapi-tests/testDebugger.cpp +++ b/js/src/jsapi-tests/testDebugger.cpp @@ -8,16 +8,19 @@ static int callCount[2] = {0, 0}; static void * -callHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure) +callCountHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure) { callCount[before]++; - JS_GetFrameThis(cx, fp); // assert if fp is incomplete + + jsval thisv; + JS_GetFrameThis(cx, fp, &thisv); // assert if fp is incomplete + return cx; // any non-null value causes the hook to be called again after } BEGIN_TEST(testDebugger_bug519719) { - JS_SetCallHook(rt, callHook, NULL); + JS_SetCallHook(rt, callCountHook, NULL); EXEC("function call(fn) { fn(0); }\n" "function f(g) { for (var i = 0; i < 9; i++) call(g); }\n" "f(Math.sin);\n" // record loop, starting in f @@ -27,3 +30,73 @@ BEGIN_TEST(testDebugger_bug519719) return true; } END_TEST(testDebugger_bug519719) + +static void * +nonStrictThisHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure) +{ + if (before) { + bool *allWrapped = (bool *) closure; + jsval thisv; + JS_GetFrameThis(cx, fp, &thisv); + *allWrapped = *allWrapped && !JSVAL_IS_PRIMITIVE(thisv); + } + return NULL; +} + +BEGIN_TEST(testDebugger_getThisNonStrict) +{ + bool allWrapped = true; + JS_SetCallHook(rt, nonStrictThisHook, (void *) &allWrapped); + EXEC("function nonstrict() { }\n" + "Boolean.prototype.nonstrict = nonstrict;\n" + "String.prototype.nonstrict = nonstrict;\n" + "Number.prototype.nonstrict = nonstrict;\n" + "Object.prototype.nonstrict = nonstrict;\n" + "nonstrict.call(true);\n" + "true.nonstrict();\n" + "nonstrict.call('');\n" + "''.nonstrict();\n" + "nonstrict.call(42);\n" + "(42).nonstrict();\n" + // The below don't really get 'wrapped', but it's okay. + "nonstrict.call(undefined);\n" + "nonstrict.call(null);\n" + "nonstrict.call({});\n" + "({}).nonstrict();\n"); + CHECK(allWrapped); + return true; +} +END_TEST(testDebugger_getThisNonStrict) + +static void * +strictThisHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure) +{ + if (before) { + bool *anyWrapped = (bool *) closure; + jsval thisv; + JS_GetFrameThis(cx, fp, &thisv); + *anyWrapped = *anyWrapped || !JSVAL_IS_PRIMITIVE(thisv); + } + return NULL; +} + +BEGIN_TEST(testDebugger_getThisStrict) +{ + bool anyWrapped = false; + JS_SetCallHook(rt, strictThisHook, (void *) &anyWrapped); + EXEC("function strict() { 'use strict'; }\n" + "Boolean.prototype.strict = strict;\n" + "String.prototype.strict = strict;\n" + "Number.prototype.strict = strict;\n" + "strict.call(true);\n" + "true.strict();\n" + "strict.call('');\n" + "''.strict();\n" + "strict.call(42);\n" + "(42).strict();\n" + "strict.call(undefined);\n" + "strict.call(null);\n"); + CHECK(!anyWrapped); + return true; +} +END_TEST(testDebugger_getThisStrict) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 4af9d694f83f..1f1030b25224 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -334,6 +334,7 @@ static JS_ALWAYS_INLINE jsid INTERNED_STRING_TO_JSID(JSString *str) { jsid id; + JS_ASSERT(str); JS_ASSERT(JS_StringHasBeenInterned(str)); JS_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0); JSID_BITS(id) = (size_t)str; diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index fae9a38c7c59..69ad24e65084 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2814,20 +2814,7 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) if (length == 0) return JS_TRUE; - Value thisv; - if (argc > 1 && !REDUCE_MODE(mode)) { - if (argv[1].isNullOrUndefined()) { - thisv.setUndefined(); - } else { - JSObject *thisObj; - if (!js_ValueToObjectOrNull(cx, argv[1], &thisObj)) - return JS_FALSE; - JS_ASSERT(thisObj); - thisv.setObject(*thisObj); - } - } else { - thisv.setUndefined(); - } + Value thisv = (argc > 1 && !REDUCE_MODE(mode)) ? argv[1] : UndefinedValue(); /* * For all but REDUCE, we call with 3 args (value, index, array). REDUCE diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 4d6a40be8050..016ca1ef3b9c 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -1233,12 +1233,15 @@ JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp) return js_GetCallObject(cx, fp); } -JS_PUBLIC_API(JSObject *) -JS_GetFrameThis(JSContext *cx, JSStackFrame *fp) +JS_PUBLIC_API(JSBool) +JS_GetFrameThis(JSContext *cx, JSStackFrame *fp, jsval *thisv) { if (fp->isDummyFrame()) - return NULL; - return fp->computeThisObject(cx); + return false; + if (!fp->computeThis(cx)) + return false; + *thisv = Jsvalify(fp->thisValue()); + return true; } JS_PUBLIC_API(JSFunction *) diff --git a/js/src/jsdbgapi.h b/js/src/jsdbgapi.h index 03028145714b..0472e04759f1 100644 --- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -251,8 +251,8 @@ JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSObject *) JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp); -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameThis(JSContext *cx, JSStackFrame *fp); +extern JS_PUBLIC_API(JSBool) +JS_GetFrameThis(JSContext *cx, JSStackFrame *fp, jsval *thisv); extern JS_PUBLIC_API(JSFunction *) JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index a3e59e7e7415..27ff9256c1a6 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2260,18 +2260,10 @@ js_fun_call(JSContext *cx, uintN argc, Value *vp) Value *argv = vp + 2; Value thisv; if (argc == 0) { - /* Call fun with its global object as the 'this' param if no args. */ thisv.setUndefined(); } else { - /* Otherwise convert the first arg to 'this' and skip over it. */ - if (argv[0].isNullOrUndefined()) { - thisv.setUndefined(); - } else { - if (!js_ValueToObjectOrNull(cx, argv[0], &obj)) - return JS_FALSE; - JS_ASSERT(obj); - thisv.setObject(*obj); - } + thisv = argv[0]; + argc--; argv++; } @@ -2365,18 +2357,6 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp) } } - - /* Convert the first arg to 'this' and skip over it. */ - Value thisv; - if (vp[2].isNullOrUndefined()) { - thisv.setUndefined(); - } else { - if (!js_ValueToObjectOrNull(cx, vp[2], &obj)) - return JS_FALSE; - JS_ASSERT(obj); - thisv.setObject(*obj); - } - LeaveTrace(cx); /* Step 6. */ @@ -2388,7 +2368,7 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp) /* Push fval, obj, and aobj's elements as args. */ args.callee() = fval; - args.thisv() = thisv; + args.thisv() = vp[2]; /* Steps 7-8. */ if (aobj && aobj->isArguments() && !aobj->isArgsLengthOverridden()) { @@ -2530,22 +2510,8 @@ CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp) /* 15.3.4.5.1, 15.3.4.5.2 step 5. */ args.callee().setObject(*target); - if (!constructing) { - /* - * FIXME Pass boundThis directly without boxing! This will go away - * very shortly when this-boxing only occurs for non-strict - * functions, callee-side, in bug 514570. - */ - if (boundThis.isNullOrUndefined()) { - args.thisv().setUndefined(); - } else { - JSObject *boundThisObj; - if (!js_ValueToObjectOrNull(cx, boundThis, &boundThisObj)) - return false; - JS_ASSERT(boundThisObj); - args.thisv().setObject(*boundThisObj); - } - } + if (!constructing) + args.thisv() = boundThis; if (constructing ? !InvokeConstructor(cx, args) : !Invoke(cx, args, 0)) return false; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 47a84e133ccd..279c7312905f 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -677,10 +677,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags) /* Invoke native functions. */ JSFunction *fun = callee.getFunctionPrivate(); JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !fun->isConstructor()); - if (fun->isNative()) { - JS_ASSERT(args.thisv().isObject() || args.thisv().isUndefined() || PrimitiveThisTest(fun, args.thisv())); + if (fun->isNative()) return CallJSNative(cx, fun->u.n.native, args.argc(), args.base()); - } /* Handle the empty-script special case. */ JSScript *script = fun->script(); @@ -4016,18 +4014,21 @@ BEGIN_CASE(JSOP_LOCALINC) } BEGIN_CASE(JSOP_THIS) - if (!regs.fp->computeThisObject(cx)) + if (!regs.fp->computeThis(cx)) goto error; PUSH_COPY(regs.fp->thisValue()); END_CASE(JSOP_THIS) BEGIN_CASE(JSOP_UNBRANDTHIS) { - JSObject *obj = regs.fp->computeThisObject(cx); - if (!obj) - goto error; - if (obj->isNative() && !obj->unbrand(cx)) + if (!regs.fp->computeThis(cx)) goto error; + Value &thisv = regs.fp->thisValue(); + if (thisv.isObject()) { + JSObject *obj = &thisv.toObject(); + if (obj->isNative() && !obj->unbrand(cx)) + goto error; + } } END_CASE(JSOP_UNBRANDTHIS) @@ -4037,12 +4038,11 @@ END_CASE(JSOP_UNBRANDTHIS) jsint i; BEGIN_CASE(JSOP_GETTHISPROP) - obj = regs.fp->computeThisObject(cx); - if (!obj) + if (!regs.fp->computeThis(cx)) goto error; i = 0; - PUSH_NULL(); - goto do_getprop_with_obj; + PUSH_COPY(regs.fp->thisValue()); + goto do_getprop_body; BEGIN_CASE(JSOP_GETARGPROP) { @@ -4072,7 +4072,6 @@ BEGIN_CASE(JSOP_GETXPROP) do_getprop_with_lval: VALUE_TO_OBJECT(cx, vp, obj); - do_getprop_with_obj: { Value rval; do { @@ -4199,53 +4198,40 @@ BEGIN_CASE(JSOP_CALLPROP) regs.sp[-1] = rval; assertSameCompartment(cx, regs.sp[-1]); PUSH_COPY(lval); - goto end_callprop; - } - - /* - * Cache miss: use the immediate atom that was loaded for us under - * PropertyCache::test. - */ - jsid id; - id = ATOM_TO_JSID(atom); - - PUSH_NULL(); - if (lval.isObject()) { - if (!js_GetMethod(cx, &objv.toObject(), id, - JS_LIKELY(!aobj->getOps()->getProperty) - ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER - : JSGET_NO_METHOD_BARRIER, - &rval)) { - goto error; - } - regs.sp[-1] = objv; - regs.sp[-2] = rval; - assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]); } else { - JS_ASSERT(!objv.toObject().getOps()->getProperty); - if (!js_GetPropertyHelper(cx, &objv.toObject(), id, - JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, - &rval)) { - goto error; - } - regs.sp[-1] = lval; - regs.sp[-2] = rval; - assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]); - } + /* + * Cache miss: use the immediate atom that was loaded for us under + * PropertyCache::test. + */ + jsid id; + id = ATOM_TO_JSID(atom); - end_callprop: - /* Wrap primitive lval in object clothing if necessary. */ - if (lval.isPrimitive()) { - /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */ - JSObject *funobj; - if (!IsFunctionObject(rval, &funobj) || - !PrimitiveThisTest(funobj->getFunctionPrivate(), lval)) { - if (!js_PrimitiveToObject(cx, ®s.sp[-1])) + PUSH_NULL(); + if (lval.isObject()) { + if (!js_GetMethod(cx, &objv.toObject(), id, + JS_LIKELY(!aobj->getOps()->getProperty) + ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER + : JSGET_NO_METHOD_BARRIER, + &rval)) { goto error; + } + regs.sp[-1] = objv; + regs.sp[-2] = rval; + assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]); + } else { + JS_ASSERT(!objv.toObject().getOps()->getProperty); + if (!js_GetPropertyHelper(cx, &objv.toObject(), id, + JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, + &rval)) { + goto error; + } + regs.sp[-1] = lval; + regs.sp[-2] = rval; + assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]); } } #if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(rval.isUndefined())) { + if (JS_UNLIKELY(rval.isUndefined()) && regs.sp[-1].isObject()) { LOAD_ATOM(0, atom); regs.sp[-2].setString(ATOM_TO_STRING(atom)); if (!js_OnUnknownMethod(cx, regs.sp - 2)) @@ -4508,28 +4494,31 @@ END_CASE(JSOP_GETELEM) BEGIN_CASE(JSOP_CALLELEM) { - /* Fetch the left part and resolve it to a non-null object. */ - JSObject *obj; - FETCH_OBJECT(cx, -2, obj); + /* Find the object on which to look for |this|'s properties. */ + Value thisv = regs.sp[-2]; + JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2); + if (!thisObj) + goto error; /* Fetch index and convert it to id suitable for use with obj. */ jsid id; - FETCH_ELEMENT_ID(obj, -1, id); + FETCH_ELEMENT_ID(thisObj, -1, id); - /* Get or set the element. */ - if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, ®s.sp[-2])) + /* Get the method. */ + if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, ®s.sp[-2])) goto error; #if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(regs.sp[-2].isUndefined())) { + if (JS_UNLIKELY(regs.sp[-2].isUndefined()) && thisv.isObject()) { + /* For js_OnUnknownMethod, sp[-2] is the index, and sp[-1] is the object missing it. */ regs.sp[-2] = regs.sp[-1]; - regs.sp[-1].setObject(*obj); + regs.sp[-1].setObject(*thisObj); if (!js_OnUnknownMethod(cx, regs.sp - 2)) goto error; } else #endif { - regs.sp[-1].setObject(*obj); + regs.sp[-1] = thisv; } } END_CASE(JSOP_CALLELEM) @@ -4721,8 +4710,6 @@ BEGIN_CASE(JSOP_APPLY) DO_OP(); } - JS_ASSERT(vp[1].isObject() || vp[1].isUndefined() || PrimitiveThisTest(newfun, vp[1])); - Probes::enterJSFun(cx, newfun); JSBool ok = CallJSNative(cx, newfun->u.n.native, argc, vp); Probes::exitJSFun(cx, newfun); diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index c07ee1232574..0edead61202c 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -423,7 +423,7 @@ struct JSStackFrame return formalArgs()[-1]; } - inline JSObject *computeThisObject(JSContext *cx); + inline bool computeThis(JSContext *cx); /* * Callee diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 5740cfa187df..013509a3f93f 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -320,12 +320,24 @@ JSStackFrame::clearMissingArgs() SetValueRangeToUndefined(formalArgs() + numActualArgs(), formalArgsEnd()); } -inline JSObject * -JSStackFrame::computeThisObject(JSContext *cx) +inline bool +JSStackFrame::computeThis(JSContext *cx) { js::Value &thisv = thisValue(); - if (JS_LIKELY(!thisv.isPrimitive())) - return &thisv.toObject(); + if (thisv.isObject()) + return true; + if (isFunctionFrame()) { + if (fun()->acceptsPrimitiveThis()) + return true; + /* + * Eval function frames have their own |this| slot, which is a copy of the function's + * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the + * eval's frame will get the wrapper, but the function's frame will not. To prevent + * this, we always wrap a function's |this| before pushing an eval frame, and should + * thus never see an unwrapped primitive in a non-strict eval function frame. + */ + JS_ASSERT(!isEvalFrame()); + } if (!js::ComputeThisFromArgv(cx, &thisv + 1)) return NULL; JS_ASSERT(IsSaneThisObject(thisv.toObject())); @@ -624,6 +636,42 @@ GetPrimitiveThis(JSContext *cx, Value *vp, T *v) return ReportIncompatibleMethod(cx, vp, Behavior::getClass()); } +/* + * Return an object on which we should look for the properties of |value|. + * This helps us implement the custom [[Get]] method that ES5's GetValue + * algorithm uses for primitive values, without actually constructing the + * temporary object that the specification does. + * + * For objects, return the object itself. For string, boolean, and number + * primitive values, return the appropriate constructor's prototype. For + * undefined and null, throw an error and return NULL, attributing the + * problem to the value at |spindex| on the stack. + */ +JS_ALWAYS_INLINE JSObject * +ValuePropertyBearer(JSContext *cx, const Value &v, int spindex) +{ + if (v.isObject()) + return &v.toObject(); + + JSProtoKey protoKey; + if (v.isString()) { + protoKey = JSProto_String; + } else if (v.isNumber()) { + protoKey = JSProto_Number; + } else if (v.isBoolean()) { + protoKey = JSProto_Boolean; + } else { + JS_ASSERT(v.isNull() || v.isUndefined()); + js_ReportIsNullOrUndefined(cx, spindex, v, NULL); + return NULL; + } + + JSObject *pobj; + if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj)) + return NULL; + return pobj; +} + } #endif /* jsinterpinlines_h__ */ diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index d36965d67423..a65c9fe44613 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1103,6 +1103,14 @@ obj_eval(JSContext *cx, uintN argc, Value *vp) if (directCall && caller->scopeChain().compartment() != vp[0].toObject().compartment()) directCall = false; + /* + * Direct calls to eval are supposed to see the caller's |this|. If we + * haven't wrapped that yet, do so now, before we make a copy of it for + * the eval code to use. + */ + if (!caller->computeThis(cx)) + return false; + Value *argv = JS_ARGV(cx, vp); if (!argv[0].isString()) { *vp = argv[0]; diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 176ac0f38f9e..718bbd0efd05 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -3163,6 +3163,9 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda) if (!LeaveFunction(pn, &funtc, funAtom, lambda)) return NULL; + if (funtc.inStrictMode()) + fun->flags |= JSFUN_PRIMITIVE_THIS; + /* If the surrounding function is not strict code, reset the lexer. */ if (!outertc->inStrictMode()) tokenStream.setStrictMode(false); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 1fad8e9cc550..c939160a8158 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -10055,29 +10055,44 @@ TraceRecorder::getThis(LIns*& this_ins) } JS_ASSERT(fp->callee().getGlobal() == globalObj); - const Value& thisv = fp->thisValue(); + Value& thisv = fp->thisValue(); - if (!thisv.isUndefined()) { + if (fp->fun()->inStrictMode() || thisv.isObject()) { /* - * fp->argv[-1] has already been computed. Since the type-specialization - * of traces distinguishes between |undefined| and objects, the same will be - * true at run time (or we won't get this far). + * fp->thisValue() has already been computed. Since the + * type-specialization of traces distinguishes between computed and + * uncomputed |this|, the same will be true at run time (or we + * won't get this far). */ this_ins = get(&fp->thisValue()); return RECORD_CONTINUE; } + /* Don't bother tracing calls on wrapped primitive |this| values. */ + if (!thisv.isNullOrUndefined()) + RETURN_STOP("wrapping primitive |this|"); + /* - * Compute 'this' now. The result is globalObj->thisObject(), - * which is trace-constant. getThisObject writes back to fp->argv[-1], - * so do the same on trace. + * Compute 'this' now. The result is globalObj->thisObject(), which is + * trace-constant. getThisObject writes back to fp->thisValue(), so do + * the same on trace. */ - JSObject *obj = fp->computeThisObject(cx); - if (!obj) - RETURN_ERROR("getThisObject failed"); - JS_ASSERT(&fp->thisValue().toObject() == obj); - this_ins = INS_CONSTOBJ(obj); - set(&fp->thisValue(), this_ins); + if (!fp->computeThis(cx)) + RETURN_ERROR("computeThis failed"); + + /* thisv is a reference, so it'll see the newly computed |this|. */ +#ifdef DEBUG + /* + * Check that thisv is our global. It could be a XPCCrossOriginWrapper around an outer + * window object whose inner object is our global, so follow all the links as needed. + */ + JS_ASSERT(thisv.isObject()); + JSObject *thisObj = thisv.toObject().wrappedObject(cx); + OBJ_TO_INNER_OBJECT(cx, thisObj); + JS_ASSERT(thisObj == globalObj); +#endif + this_ins = INS_CONSTOBJ(globalObj); + set(&thisv, this_ins); return RECORD_CONTINUE; } @@ -15903,10 +15918,14 @@ TraceRecorder::record_JSOP_GETTHISPROP() CHECK_STATUS_A(getThis(this_ins)); /* - * It's safe to just use cx->fp->thisv here because getThis() returns + * It's safe to just use cx->fp->thisValue() here because getThis() returns * ARECORD_STOP or ARECORD_ERROR if thisv is not available. */ - CHECK_STATUS_A(getProp(&cx->fp()->thisValue().toObject(), this_ins)); + const Value &thisv = cx->fp()->thisValue(); + if (!thisv.isObject()) + RETURN_STOP_A("primitive this for GETTHISPROP"); + + CHECK_STATUS_A(getProp(&thisv.toObject(), this_ins)); return ARECORD_CONTINUE; } diff --git a/js/src/jsval.h b/js/src/jsval.h index 3f6f473da9af..eb25e44e73bd 100644 --- a/js/src/jsval.h +++ b/js/src/jsval.h @@ -405,6 +405,7 @@ static JS_ALWAYS_INLINE jsval_layout STRING_TO_JSVAL_IMPL(JSString *str) { jsval_layout l; + JS_ASSERT(str); l.s.tag = JSVAL_TAG_STRING; l.s.payload.str = str; return l; @@ -594,6 +595,7 @@ JSVAL_IS_STRING_IMPL(jsval_layout l) static JS_ALWAYS_INLINE jsval_layout STRING_TO_JSVAL_IMPL(JSString *str) { + JS_ASSERT(str); jsval_layout l; uint64 strBits = (uint64)str; JS_ASSERT((strBits >> JSVAL_TAG_SHIFT) == 0); diff --git a/js/src/jsvalue.h b/js/src/jsvalue.h index 42b0ce7edf62..f56105589915 100644 --- a/js/src/jsvalue.h +++ b/js/src/jsvalue.h @@ -202,7 +202,13 @@ BOX_NON_DOUBLE_JSVAL(JSValueType type, uint64 *slot) { jsval_layout l; JS_ASSERT(type > JSVAL_TYPE_DOUBLE && type <= JSVAL_UPPER_INCL_TYPE_OF_BOXABLE_SET); + JS_ASSERT_IF(type == JSVAL_TYPE_STRING || + type == JSVAL_TYPE_OBJECT || + type == JSVAL_TYPE_NONFUNOBJ || + type == JSVAL_TYPE_FUNOBJ, + *(uint32 *)slot != 0); l.s.tag = JSVAL_TYPE_TO_TAG(type & 0xF); + /* A 32-bit value in a 64-bit slot always occupies the low-addressed end. */ l.s.payload.u32 = *(uint32 *)slot; return l; } @@ -300,6 +306,11 @@ BOX_NON_DOUBLE_JSVAL(JSValueType type, uint64 *slot) uint32 shift = isI32 * 32; uint64 mask = ((uint64)-1) >> shift; uint64 payload = *slot & mask; + JS_ASSERT_IF(type == JSVAL_TYPE_STRING || + type == JSVAL_TYPE_OBJECT || + type == JSVAL_TYPE_NONFUNOBJ || + type == JSVAL_TYPE_FUNOBJ, + payload != 0); l.asBits = payload | JSVAL_TYPE_TO_SHIFTED_TAG(type & 0xF); return l; } @@ -366,7 +377,6 @@ class Value JS_ALWAYS_INLINE void setObject(JSObject &obj) { - JS_ASSERT(&obj != NULL); data = OBJECT_TO_JSVAL_IMPL(&obj); } diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index baccbf2070e6..0caea46c6123 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2838,27 +2838,6 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom) frame.pushTypedPayload(JSVAL_TYPE_STRING, strReg); frame.forgetType(frame.peek(-1)); - RegisterID temp = frame.allocReg(); - RegisterID funReg = frame.copyDataIntoReg(funFe); - Jump notFun1 = frame.testObject(Assembler::NotEqual, funFe); - Jump notFun2 = masm.testFunction(Assembler::NotEqual, funReg); - - masm.loadFunctionPrivate(funReg, temp); - masm.load16(Address(temp, offsetof(JSFunction, flags)), temp); - Jump noPrim = masm.branchTest32(Assembler::Zero, temp, Imm32(JSFUN_PRIMITIVE_THIS)); - { - stubcc.linkExit(noPrim, Uses(2)); - stubcc.leave(); - stubcc.call(stubs::WrapPrimitiveThis); - } - - frame.freeReg(funReg); - frame.freeReg(temp); - notFun2.linkTo(masm.label(), &masm); - notFun1.linkTo(masm.label(), &masm); - - stubcc.rejoin(Changes(1)); - return true; } @@ -3313,20 +3292,15 @@ void mjit::Compiler::jsop_this() { Address thisvAddr(JSFrameReg, JSStackFrame::offsetOfThis(fun)); - if (0 && !script->strictModeCode) { - Jump null = masm.testUndefined(Assembler::Equal, thisvAddr); - stubcc.linkExit(null, Uses(1)); - stubcc.leave(); - stubcc.call(stubs::ComputeThis); - stubcc.rejoin(Changes(1)); - - RegisterID reg = frame.allocReg(); - masm.loadPayload(thisvAddr, reg); - frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); - } else { - frame.push(thisvAddr); - Jump null = frame.testUndefined(Assembler::Equal, frame.peek(-1)); - stubcc.linkExit(null, Uses(1)); + frame.push(thisvAddr); + /* + * In strict mode code, we don't wrap 'this'. + * In direct-call eval code, we wrapped 'this' before entering the eval. + * In global code, 'this' is always an object. + */ + if (fun && !script->strictModeCode) { + Jump notObj = frame.testObject(Assembler::NotEqual, frame.peek(-1)); + stubcc.linkExit(notObj, Uses(1)); stubcc.leave(); stubcc.call(stubs::This); stubcc.rejoin(Changes(1)); diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index f869437c20f7..13c8d4e09cdd 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -2228,49 +2228,35 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic) regs.sp++; regs.sp[-2] = rval; regs.sp[-1] = lval; - goto end_callprop; - } - - /* - * Cache miss: use the immediate atom that was loaded for us under - * PropertyCache::test. - */ - jsid id; - id = ATOM_TO_JSID(pic->atom); - - regs.sp++; - regs.sp[-1].setNull(); - if (lval.isObject()) { - if (!js_GetMethod(cx, &objv.toObject(), id, - JS_LIKELY(!objv.toObject().getOps()->getProperty) - ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER - : JSGET_NO_METHOD_BARRIER, - &rval)) { - THROW(); - } - regs.sp[-1] = objv; - regs.sp[-2] = rval; } else { - JS_ASSERT(!objv.toObject().getOps()->getProperty); - if (!js_GetPropertyHelper(cx, &objv.toObject(), id, - JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, - &rval)) { - THROW(); - } - regs.sp[-1] = lval; - regs.sp[-2] = rval; - } + /* + * Cache miss: use the immediate atom that was loaded for us under + * PropertyCache::test. + */ + jsid id; + id = ATOM_TO_JSID(pic->atom); - end_callprop: - /* Wrap primitive lval in object clothing if necessary. */ - if (lval.isPrimitive()) { - /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */ - JSObject *funobj; - if (!IsFunctionObject(rval, &funobj) || - !PrimitiveThisTest(GET_FUNCTION_PRIVATE(cx, funobj), lval)) { - if (!js_PrimitiveToObject(cx, ®s.sp[-1])) + regs.sp++; + regs.sp[-1].setNull(); + if (lval.isObject()) { + if (!js_GetMethod(cx, &objv.toObject(), id, + JS_LIKELY(!objv.toObject().getOps()->getProperty) + ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER + : JSGET_NO_METHOD_BARRIER, + &rval)) { THROW(); - usePIC = false; + } + regs.sp[-1] = objv; + regs.sp[-2] = rval; + } else { + JS_ASSERT(!objv.toObject().getOps()->getProperty); + if (!js_GetPropertyHelper(cx, &objv.toObject(), id, + JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, + &rval)) { + THROW(); + } + regs.sp[-1] = lval; + regs.sp[-2] = rval; } } @@ -2294,7 +2280,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic) } #if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(rval.isUndefined())) { + if (JS_UNLIKELY(rval.isUndefined()) && regs.sp[-1].isObject()) { regs.sp[-2].setString(ATOM_TO_STRING(pic->atom)); if (!js_OnUnknownMethod(cx, regs.sp - 2)) THROW(); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 52db615d2343..c1db93499e55 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -64,6 +64,8 @@ #include "jscntxtinlines.h" #include "jsatominlines.h" #include "StubCalls-inl.h" +#include "jsfuninlines.h" + #ifdef XP_WIN # include "jswin.h" #endif @@ -581,30 +583,31 @@ stubs::CallElem(VMFrame &f) JSContext *cx = f.cx; JSFrameRegs ®s = f.regs; - /* Fetch the left part and resolve it to a non-null object. */ - JSObject *obj = ValueToObject(cx, ®s.sp[-2]); - if (!obj) + /* Find the object on which to look for |this|'s properties. */ + Value thisv = regs.sp[-2]; + JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2); + if (!thisObj) THROW(); - /* Fetch index and convert it to id suitable for use with obj. */ + /* Fetch index and convert it to id suitable for use with thisObj. */ jsid id; - if (!FetchElementId(f, obj, regs.sp[-1], id, ®s.sp[-2])) + if (!FetchElementId(f, thisObj, regs.sp[-1], id, ®s.sp[-2])) THROW(); /* Get or set the element. */ - if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, ®s.sp[-2])) + if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, ®s.sp[-2])) THROW(); #if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(regs.sp[-2].isUndefined())) { + if (JS_UNLIKELY(regs.sp[-2].isUndefined()) && thisv.isObject()) { regs.sp[-2] = regs.sp[-1]; - regs.sp[-1].setObject(*obj); + regs.sp[-1].setObject(*thisObj); if (!js_OnUnknownMethod(cx, regs.sp - 2)) THROW(); } else #endif { - regs.sp[-1].setObject(*obj); + regs.sp[-1] = thisv; } } @@ -1417,17 +1420,9 @@ stubs::Trap(VMFrame &f, jsbytecode *pc) void JS_FASTCALL stubs::This(VMFrame &f) { - JSObject *obj = f.fp()->computeThisObject(f.cx); - if (!obj) - THROW(); - f.regs.sp[-1].setObject(*obj); -} - -void JS_FASTCALL -stubs::ComputeThis(VMFrame &f) -{ - if (!f.fp()->computeThisObject(f.cx)) + if (!f.fp()->computeThis(f.cx)) THROW(); + f.regs.sp[-1] = f.fp()->thisValue(); } void JS_FASTCALL @@ -2123,52 +2118,39 @@ stubs::CallProp(VMFrame &f, JSAtom *origAtom) regs.sp++; regs.sp[-2] = rval; regs.sp[-1] = lval; - goto end_callprop; - } - - /* - * Cache miss: use the immediate atom that was loaded for us under - * PropertyCache::test. - */ - jsid id; - id = ATOM_TO_JSID(origAtom); - - regs.sp++; - regs.sp[-1].setNull(); - if (lval.isObject()) { - if (!js_GetMethod(cx, &objv.toObject(), id, - JS_LIKELY(!aobj->getOps()->getProperty) - ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER - : JSGET_NO_METHOD_BARRIER, - &rval)) { - THROW(); - } - regs.sp[-1] = objv; - regs.sp[-2] = rval; } else { - JS_ASSERT(!objv.toObject().getOps()->getProperty); - if (!js_GetPropertyHelper(cx, &objv.toObject(), id, - JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, - &rval)) { - THROW(); - } - regs.sp[-1] = lval; - regs.sp[-2] = rval; - } + /* + * Cache miss: use the immediate atom that was loaded for us under + * PropertyCache::test. + */ + jsid id; + id = ATOM_TO_JSID(origAtom); - end_callprop: - /* Wrap primitive lval in object clothing if necessary. */ - if (lval.isPrimitive()) { - /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */ - JSObject *funobj; - if (!IsFunctionObject(rval, &funobj) || - !PrimitiveThisTest(funobj->getFunctionPrivate(), lval)) { - if (!js_PrimitiveToObject(cx, ®s.sp[-1])) + regs.sp++; + regs.sp[-1].setNull(); + if (lval.isObject()) { + if (!js_GetMethod(cx, &objv.toObject(), id, + JS_LIKELY(!aobj->getOps()->getProperty) + ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER + : JSGET_NO_METHOD_BARRIER, + &rval)) { THROW(); + } + regs.sp[-1] = objv; + regs.sp[-2] = rval; + } else { + JS_ASSERT(!objv.toObject().getOps()->getProperty); + if (!js_GetPropertyHelper(cx, &objv.toObject(), id, + JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, + &rval)) { + THROW(); + } + regs.sp[-1] = lval; + regs.sp[-2] = rval; } } #if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(rval.isUndefined())) { + if (JS_UNLIKELY(rval.isUndefined()) && regs.sp[-1].isObject()) { regs.sp[-2].setString(ATOM_TO_STRING(origAtom)); if (!js_OnUnknownMethod(cx, regs.sp - 2)) THROW(); @@ -2176,24 +2158,6 @@ stubs::CallProp(VMFrame &f, JSAtom *origAtom) #endif } -void JS_FASTCALL -stubs::WrapPrimitiveThis(VMFrame &f) -{ - JSContext *cx = f.cx; - const Value &funv = f.regs.sp[-2]; - const Value &thisv = f.regs.sp[-1]; - - JS_ASSERT(thisv.isPrimitive()); - - /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */ - JSObject *funobj; - if (!IsFunctionObject(funv, &funobj) || - !PrimitiveThisTest(GET_FUNCTION_PRIVATE(cx, funobj), thisv)) { - if (!js_PrimitiveToObject(cx, &f.regs.sp[-1])) - THROW(); - } -} - void JS_FASTCALL stubs::Length(VMFrame &f) { @@ -2652,7 +2616,10 @@ finally: void JS_FASTCALL stubs::Unbrand(VMFrame &f) { - JSObject *obj = &f.regs.sp[-1].toObject(); + const Value &thisv = f.regs.sp[-1]; + if (!thisv.isObject()) + return; + JSObject *obj = &thisv.toObject(); if (obj->isNative() && !obj->unbrand(f.cx)) THROW(); } diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index da104367cbcb..e70484669d8a 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -48,7 +48,6 @@ namespace mjit { namespace stubs { void JS_FASTCALL This(VMFrame &f); -void JS_FASTCALL ComputeThis(VMFrame &f); JSObject * JS_FASTCALL NewInitArray(VMFrame &f); JSObject * JS_FASTCALL NewInitObject(VMFrame &f); JSObject * JS_FASTCALL NewArray(VMFrame &f, uint32 len); @@ -103,7 +102,6 @@ void JS_FASTCALL Throw(VMFrame &f); void JS_FASTCALL PutCallObject(VMFrame &f); void JS_FASTCALL PutActivationObjects(VMFrame &f); void JS_FASTCALL GetCallObject(VMFrame &f); -void JS_FASTCALL WrapPrimitiveThis(VMFrame &f); #if JS_MONOIC void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::MICInfo *mic); #else diff --git a/js/src/tests/ecma_5/Boolean/15.6.4.2.js b/js/src/tests/ecma_5/Boolean/15.6.4.2.js new file mode 100644 index 000000000000..3e10afa38807 --- /dev/null +++ b/js/src/tests/ecma_5/Boolean/15.6.4.2.js @@ -0,0 +1,17 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +assertEq(raisesException(TypeError)('Boolean.prototype.toString.call(42)'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toString.call("")'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toString.call({})'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toString.call(null)'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toString.call([])'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toString.call(undefined)'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toString.call(new String())'), true); + +assertEq(completesNormally('Boolean.prototype.toString.call(true)'), true); +assertEq(completesNormally('Boolean.prototype.toString.call(new Boolean(true))'), true); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/Boolean/browser.js b/js/src/tests/ecma_5/Boolean/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_5/Boolean/jstests.list b/js/src/tests/ecma_5/Boolean/jstests.list new file mode 100644 index 000000000000..17d4f458a18c --- /dev/null +++ b/js/src/tests/ecma_5/Boolean/jstests.list @@ -0,0 +1,2 @@ +url-prefix ../../jsreftest.html?test=ecma_5/Boolean/ +script 15.6.4.2.js diff --git a/js/src/tests/ecma_5/Boolean/shell.js b/js/src/tests/ecma_5/Boolean/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_5/Date/toJSON-01.js b/js/src/tests/ecma_5/Date/toJSON-01.js index 41be395f9a89..4a756aab54e7 100644 --- a/js/src/tests/ecma_5/Date/toJSON-01.js +++ b/js/src/tests/ecma_5/Date/toJSON-01.js @@ -31,30 +31,26 @@ assertEq(dateToJSON.length, 1); * 1. Let O be the result of calling ToObject, giving it the this value as its * argument. */ -function strictThis() { "use strict"; return this; } -if (strictThis.call(null) === null) +try { - try - { - dateToJSON.call(null); - throw new Error("should have thrown a TypeError"); - } - catch (e) - { - assertEq(e instanceof TypeError, true, - "ToObject throws TypeError for null/undefined"); - } + dateToJSON.call(null); + throw new Error("should have thrown a TypeError"); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "ToObject throws TypeError for null/undefined"); +} - try - { - dateToJSON.call(undefined); - throw new Error("should have thrown a TypeError"); - } - catch (e) - { - assertEq(e instanceof TypeError, true, - "ToObject throws TypeError for null/undefined"); - } +try +{ + dateToJSON.call(undefined); + throw new Error("should have thrown a TypeError"); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "ToObject throws TypeError for null/undefined"); } diff --git a/js/src/tests/ecma_5/Function/10.2.1.1.6.js b/js/src/tests/ecma_5/Function/10.2.1.1.6.js new file mode 100644 index 000000000000..69613386cd8d --- /dev/null +++ b/js/src/tests/ecma_5/Function/10.2.1.1.6.js @@ -0,0 +1,35 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +function strictThis() { 'use strict'; return this; } + +/* Check that calls of flat closure slots get the right |this|. */ +function flat(g) { + function h() { return g(); } + return h; +} +assertEq(flat(strictThis)(), undefined); + +/* Check that calls up upvars get the right |this|. */ +function upvar(f) { + function h() { + return f(); + } + return h(); +} +assertEq(upvar(strictThis), undefined); + +/* Check that calls to with-object properties get an appropriate 'this'. */ +var obj = { f: strictThis }; +with (obj) { + /* + * The method won't compile anything containing a 'with', but it can + * compile 'g'. + */ + function g() { return f(); } + assertEq(g(), obj); +} + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/Function/function-bind.js b/js/src/tests/ecma_5/Function/function-bind.js index b645b49019af..78f59c90e544 100644 --- a/js/src/tests/ecma_5/Function/function-bind.js +++ b/js/src/tests/ecma_5/Function/function-bind.js @@ -21,24 +21,20 @@ var bind = Function.prototype.bind; assertEq(bind.length, 1); -// The if here shouldn't be necessary when this-boxing doesn't happen in strict -// mode. var strictReturnThis = function() { "use strict"; return this; }; -if (strictReturnThis() === undefined) -{ - assertEq(strictReturnThis.bind(undefined)(), undefined); - assertEq(strictReturnThis.bind(null)(), undefined); - var obj = {}; - assertEq(strictReturnThis.bind(obj)(), obj); +assertEq(strictReturnThis.bind(undefined)(), undefined); +assertEq(strictReturnThis.bind(null)(), null); - assertEq(strictReturnThis.bind(NaN)(), NaN); +var obj = {}; +assertEq(strictReturnThis.bind(obj)(), obj); - assertEq(strictReturnThis.bind(true)(), true); - assertEq(strictReturnThis.bind(false)(), false); +assertEq(strictReturnThis.bind(NaN)(), NaN); - assertEq(strictReturnThis.bind("foopy")(), "foopy"); -} +assertEq(strictReturnThis.bind(true)(), true); +assertEq(strictReturnThis.bind(false)(), false); + +assertEq(strictReturnThis.bind("foopy")(), "foopy"); // rigorous, step-by-step testing diff --git a/js/src/tests/ecma_5/Function/jstests.list b/js/src/tests/ecma_5/Function/jstests.list index 8b1e06824776..eff3602c14da 100644 --- a/js/src/tests/ecma_5/Function/jstests.list +++ b/js/src/tests/ecma_5/Function/jstests.list @@ -1,4 +1,5 @@ url-prefix ../../jsreftest.html?test=ecma_5/Function/ +script 10.2.1.1.6.js script 15.3.4.3-01.js script arguments-caller-callee.js script function-caller.js diff --git a/js/src/tests/ecma_5/Number/15.7.4.2.js b/js/src/tests/ecma_5/Number/15.7.4.2.js new file mode 100644 index 000000000000..7a24474cdaf3 --- /dev/null +++ b/js/src/tests/ecma_5/Number/15.7.4.2.js @@ -0,0 +1,17 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +assertEq(raisesException(TypeError)('Number.prototype.toString.call(true)'), true); +assertEq(raisesException(TypeError)('Number.prototype.toString.call("")'), true); +assertEq(raisesException(TypeError)('Number.prototype.toString.call({})'), true); +assertEq(raisesException(TypeError)('Number.prototype.toString.call(null)'), true); +assertEq(raisesException(TypeError)('Number.prototype.toString.call([])'), true); +assertEq(raisesException(TypeError)('Number.prototype.toString.call(undefined)'), true); +assertEq(raisesException(TypeError)('Number.prototype.toString.call(new Boolean(true))'), true); + +assertEq(completesNormally('Number.prototype.toString.call(42)'), true); +assertEq(completesNormally('Number.prototype.toString.call(new Number(42))'), true); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/Number/browser.js b/js/src/tests/ecma_5/Number/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_5/Number/jstests.list b/js/src/tests/ecma_5/Number/jstests.list new file mode 100644 index 000000000000..b08bee3790ee --- /dev/null +++ b/js/src/tests/ecma_5/Number/jstests.list @@ -0,0 +1,2 @@ +url-prefix ../../jsreftest.html?test=ecma_5/Number/ +script 15.7.4.2.js diff --git a/js/src/tests/ecma_5/Number/shell.js b/js/src/tests/ecma_5/Number/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_5/String/15.5.4.2.js b/js/src/tests/ecma_5/String/15.5.4.2.js new file mode 100644 index 000000000000..46c7ccb69c37 --- /dev/null +++ b/js/src/tests/ecma_5/String/15.5.4.2.js @@ -0,0 +1,14 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +assertEq(raisesException(TypeError)('String.prototype.toString.call(42)'), true); +assertEq(raisesException(TypeError)('String.prototype.toString.call(true)'), true); +assertEq(raisesException(TypeError)('String.prototype.toString.call({})'), true); +assertEq(raisesException(TypeError)('String.prototype.toString.call(null)'), true); +assertEq(raisesException(TypeError)('String.prototype.toString.call([])'), true); +assertEq(raisesException(TypeError)('String.prototype.toString.call(undefined)'), true); +assertEq(completesNormally('String.prototype.toString.call("")'), true); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/String/jstests.list b/js/src/tests/ecma_5/String/jstests.list index c1d93c71be98..adf6ba074a68 100644 --- a/js/src/tests/ecma_5/String/jstests.list +++ b/js/src/tests/ecma_5/String/jstests.list @@ -1,2 +1,3 @@ url-prefix ../../jsreftest.html?test=ecma_5/String/ +script 15.5.4.2.js script 15.5.4.11-01.js diff --git a/js/src/tests/ecma_5/extensions/Boolean-toSource.js b/js/src/tests/ecma_5/extensions/Boolean-toSource.js new file mode 100644 index 000000000000..8d135a9fd79c --- /dev/null +++ b/js/src/tests/ecma_5/extensions/Boolean-toSource.js @@ -0,0 +1,17 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +assertEq(raisesException(TypeError)('Boolean.prototype.toSource.call(42)'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toSource.call("")'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toSource.call({})'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toSource.call(null)'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toSource.call([])'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toSource.call(undefined)'), true); +assertEq(raisesException(TypeError)('Boolean.prototype.toSource.call(new String())'), true); + +assertEq(completesNormally('Boolean.prototype.toSource.call(true)'), true); +assertEq(completesNormally('Boolean.prototype.toSource.call(new Boolean(true))'), true); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/Number-toSource.js b/js/src/tests/ecma_5/extensions/Number-toSource.js new file mode 100644 index 000000000000..c2e306f53159 --- /dev/null +++ b/js/src/tests/ecma_5/extensions/Number-toSource.js @@ -0,0 +1,17 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +assertEq(raisesException(TypeError)('Number.prototype.toSource.call("")'), true); +assertEq(raisesException(TypeError)('Number.prototype.toSource.call(true)'), true); +assertEq(raisesException(TypeError)('Number.prototype.toSource.call({})'), true); +assertEq(raisesException(TypeError)('Number.prototype.toSource.call(null)'), true); +assertEq(raisesException(TypeError)('Number.prototype.toSource.call([])'), true); +assertEq(raisesException(TypeError)('Number.prototype.toSource.call(undefined)'), true); +assertEq(raisesException(TypeError)('Number.prototype.toSource.call(new Boolean(true))'), true); + +assertEq(completesNormally('Number.prototype.toSource.call(42)'), true); +assertEq(completesNormally('Number.prototype.toSource.call(new Number(42))'), true); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/String-toSource.js b/js/src/tests/ecma_5/extensions/String-toSource.js new file mode 100644 index 000000000000..0614e574cbe6 --- /dev/null +++ b/js/src/tests/ecma_5/extensions/String-toSource.js @@ -0,0 +1,14 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +assertEq(raisesException(TypeError)('String.prototype.toSource.call(42)'), true); +assertEq(raisesException(TypeError)('String.prototype.toSource.call(true)'), true); +assertEq(raisesException(TypeError)('String.prototype.toSource.call({})'), true); +assertEq(raisesException(TypeError)('String.prototype.toSource.call(null)'), true); +assertEq(raisesException(TypeError)('String.prototype.toSource.call([])'), true); +assertEq(raisesException(TypeError)('String.prototype.toSource.call(undefined)'), true); +assertEq(completesNormally('String.prototype.toSource.call("")'), true); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/jstests.list b/js/src/tests/ecma_5/extensions/jstests.list index 2e43388eedd6..cedb1b068acf 100644 --- a/js/src/tests/ecma_5/extensions/jstests.list +++ b/js/src/tests/ecma_5/extensions/jstests.list @@ -1,5 +1,9 @@ url-prefix ../../jsreftest.html?test=ecma_5/extensions/ script 15.4.4.11.js script 8.12.5-01.js +script Boolean-toSource.js +script Number-toSource.js +script String-toSource.js +script proxy-strict.js script regress-bug567606.js script string-literal-getter-setter-decompilation.js diff --git a/js/src/tests/ecma_5/extensions/proxy-strict.js b/js/src/tests/ecma_5/extensions/proxy-strict.js new file mode 100644 index 000000000000..30e5779d5362 --- /dev/null +++ b/js/src/tests/ecma_5/extensions/proxy-strict.js @@ -0,0 +1,12 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var s = "grape"; +function f() { "use strict"; return this; } +var p = Proxy.createFunction(f, f); +String.prototype.p = p; +assertEq(s.p(), "grape"); + +reportCompare(true,true); diff --git a/js/src/tests/ecma_5/jstests.list b/js/src/tests/ecma_5/jstests.list index 141295cfda81..4a06bfad321e 100644 --- a/js/src/tests/ecma_5/jstests.list +++ b/js/src/tests/ecma_5/jstests.list @@ -1,9 +1,11 @@ include Array/jstests.list +include Boolean/jstests.list include Date/jstests.list include Expressions/jstests.list include Function/jstests.list include Global/jstests.list include JSON/jstests.list +include Number/jstests.list include Object/jstests.list include RegExp/jstests.list include String/jstests.list diff --git a/js/src/tests/ecma_5/misc/jstests.list b/js/src/tests/ecma_5/misc/jstests.list index 136f00db5816..7350a13cea4e 100644 --- a/js/src/tests/ecma_5/misc/jstests.list +++ b/js/src/tests/ecma_5/misc/jstests.list @@ -2,3 +2,4 @@ url-prefix ../../jsreftest.html?test=ecma_5/misc/ script global-numeric-properties.js script redeclare-var-non-writable-property.js script enumerate-undefined.js +script unwrapped-no-such-method.js diff --git a/js/src/tests/ecma_5/misc/unwrapped-no-such-method.js b/js/src/tests/ecma_5/misc/unwrapped-no-such-method.js new file mode 100644 index 000000000000..d17406e7bb9c --- /dev/null +++ b/js/src/tests/ecma_5/misc/unwrapped-no-such-method.js @@ -0,0 +1,13 @@ +// Our __noSuchMethod__ handling should only be called when |this| is an object. + +var x = ""; +// Reached from interpreter's JSOP_CALLPROP, and js::mjit::ic::CallProp. +try { x.i(); } catch (ex) { } + +// Reached from interpreter's JSOP_CALLELEM, and js::mjit::stubs::CallElem. +try { x[x](); } catch (ex) { } + +// Reached from js::mjit::stubs::CallProp: +try { true.i(); } catch(ex) { } + +reportCompare(true,true); diff --git a/js/src/tests/ecma_5/shell.js b/js/src/tests/ecma_5/shell.js index 0931be6d0b46..e528aff81977 100644 --- a/js/src/tests/ecma_5/shell.js +++ b/js/src/tests/ecma_5/shell.js @@ -1,41 +1,120 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + + +/* + * Return true if both of these return true: + * - LENIENT_PRED applied to CODE + * - STRICT_PRED applied to CODE with a use strict directive added to the front * - * ***** 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 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 ***** */ + * Run STRICT_PRED first, for testing code that affects the global environment + * in loose mode, but fails in strict mode. + */ +function testLenientAndStrict(code, lenient_pred, strict_pred) { + return (strict_pred("'use strict'; " + code) && + lenient_pred(code)); +} + +/* + * completesNormally(CODE) returns true if evaluating CODE (as eval + * code) completes normally (rather than throwing an exception). + */ +function completesNormally(code) { + try { + eval(code); + return true; + } catch (exception) { + return false; + } +} + +/* + * returns(VALUE)(CODE) returns true if evaluating CODE (as eval code) + * completes normally (rather than throwing an exception), yielding a value + * strictly equal to VALUE. + */ +function returns(value) { + return function(code) { + try { + return eval(code) === value; + } catch (exception) { + return false; + } + } +} + +/* + * returnsCopyOf(VALUE)(CODE) returns true if evaluating CODE (as eval code) + * completes normally (rather than throwing an exception), yielding a value + * that is deepEqual to VALUE. + */ +function returnsCopyOf(value) { + return function(code) { + try { + return deepEqual(eval(code), value); + } catch (exception) { + return false; + } + } +} + +/* + * raisesException(EXCEPTION)(CODE) returns true if evaluating CODE (as + * eval code) throws an exception object that is an instance of EXCEPTION, + * and returns false if it throws any other error or evaluates + * successfully. For example: raises(TypeError)("0()") == true. + */ +function raisesException(exception) { + return function (code) { + try { + eval(code); + return false; + } catch (actual) { + return actual instanceof exception; + } + }; +}; + +/* + * parsesSuccessfully(CODE) returns true if CODE parses as function + * code without an error. + */ +function parsesSuccessfully(code) { + try { + Function(code); + return true; + } catch (exception) { + return false; + } +}; + +/* + * parseRaisesException(EXCEPTION)(CODE) returns true if parsing CODE + * as function code raises EXCEPTION. + */ +function parseRaisesException(exception) { + return function (code) { + try { + Function(code); + return false; + } catch (actual) { + return actual instanceof exception; + } + }; +}; + +/* + * Return the result of applying uneval to VAL, and replacing all runs + * of whitespace with a single horizontal space (poor man's + * tokenization). + */ +function clean_uneval(val) { + return uneval(val).replace(/\s+/g, ' '); +} /* * Return true if A is equal to B, where equality on arrays and objects diff --git a/js/src/tests/ecma_5/strict/10.4.2.js b/js/src/tests/ecma_5/strict/10.4.2.js index 1ddbe3535f3e..5be91906434e 100644 --- a/js/src/tests/ecma_5/strict/10.4.2.js +++ b/js/src/tests/ecma_5/strict/10.4.2.js @@ -35,4 +35,21 @@ assertEq(completesNormally("Function('010')"), assertEq(raisesException(SyntaxError)("Function('\"use strict\"; 010')"), true); +/* + * If 'eval' causes a frame's primitive |this| to become wrapped, the frame should see the same + * wrapper object as the eval code. + */ +var call_this, eval_this; +function f(code) { + /* + * At this point, a primitive |this| has not yet been wrapped. A + * reference to |this| from the eval call should wrap it, and the wrapper + * should be stored where the call frame can see it. + */ + eval(code); + call_this = this; +} +f.call(true, 'eval_this = this'); +assertEq(call_this, eval_this); + reportCompare(true, true); diff --git a/js/src/tests/ecma_5/strict/10.4.3.js b/js/src/tests/ecma_5/strict/10.4.3.js new file mode 100644 index 000000000000..514b1ca0fb8e --- /dev/null +++ b/js/src/tests/ecma_5/strict/10.4.3.js @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var obj = {} + +function strict() { "use strict"; return this; } +assertEq(strict.call(""), ""); +assertEq(strict.call(true), true); +assertEq(strict.call(42), 42); +assertEq(strict.call(null), null); +assertEq(strict.call(undefined), undefined); +assertEq(strict.call(obj), obj); +assertEq(new strict() instanceof Object, true); + +/* + * The compiler internally converts x['foo'] to x.foo. Writing x[s] where + * s='foo' is enough to throw it off the scent for now. + */ +var strictString = 'strict'; + +Boolean.prototype.strict = strict; +assertEq(true.strict(), true); +assertEq(true[strictString](), true); + +Number.prototype.strict = strict; +assertEq((42).strict(), 42); +assertEq(42[strictString](), 42); + +String.prototype.strict = strict; +assertEq("".strict(), ""); +assertEq(""[strictString](), ""); + +function lenient() { return this; } +assertEq(lenient.call("") instanceof String, true); +assertEq(lenient.call(true) instanceof Boolean, true); +assertEq(lenient.call(42) instanceof Number, true); +assertEq(lenient.call(null), this); +assertEq(lenient.call(undefined), this); +assertEq(lenient.call(obj), obj); +assertEq(new lenient() instanceof Object, true); + +var lenientString = 'lenient'; + +Boolean.prototype.lenient = lenient; +assertEq(true.lenient() instanceof Boolean, true); +assertEq(true[lenientString]() instanceof Boolean, true); + +Number.prototype.lenient = lenient; +assertEq(42[lenientString]() instanceof Number, true); + +String.prototype.lenient = lenient; +assertEq(""[lenientString]() instanceof String, true); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/strict/15.3.4.5.js b/js/src/tests/ecma_5/strict/15.3.4.5.js new file mode 100644 index 000000000000..9a2eddfa2b8d --- /dev/null +++ b/js/src/tests/ecma_5/strict/15.3.4.5.js @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +function strict() { 'use strict'; return this; } +function lenient() { return this; } +var obj = {}; + +assertEq(strict.bind(true)(), true); +assertEq(strict.bind(42)(), 42); +assertEq(strict.bind("")(), ""); +assertEq(strict.bind(null)(), null); +assertEq(strict.bind(undefined)(), undefined); +assertEq(strict.bind(obj)(), obj); + +assertEq(lenient.bind(true)() instanceof Boolean, true); +assertEq(lenient.bind(42)() instanceof Number, true); +assertEq(lenient.bind("")() instanceof String, true); +assertEq(lenient.bind(null)(), this); +assertEq(lenient.bind(undefined)(), this); +assertEq(lenient.bind(obj)(), obj); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/strict/jstests.list b/js/src/tests/ecma_5/strict/jstests.list index 4e1f247e77e8..66269b9d34e6 100644 --- a/js/src/tests/ecma_5/strict/jstests.list +++ b/js/src/tests/ecma_5/strict/jstests.list @@ -3,6 +3,7 @@ script 8.7.2.js script 8.12.5.js script 8.12.7.js script 10.4.2.js +script 10.4.3.js script 10.6.js script 11.1.5.js script 11.3.1.js @@ -16,6 +17,7 @@ script 12.2.1.js script 12.10.1.js script 12.14.1.js script 13.1.js +script 15.3.4.5.js script 15.3.5.1.js script 15.3.5.2.js script 15.4.4.6.js @@ -30,5 +32,7 @@ script 15.10.7.js script B.1.1.js script B.1.2.js script function-name-arity.js +script primitive-this-no-writeback.js script regress-532254.js script regress-532041.js +script unbrand-this.js diff --git a/js/src/tests/ecma_5/strict/primitive-this-no-writeback.js b/js/src/tests/ecma_5/strict/primitive-this-no-writeback.js new file mode 100644 index 000000000000..d7356852b5b7 --- /dev/null +++ b/js/src/tests/ecma_5/strict/primitive-this-no-writeback.js @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +/* Verify that GETTHISPROP does not update the frame's |this| slot. */ + +var f = String.prototype.m = function () { + "use strict"; + assertEq(this, "s"); + // The GETTHISPROP should not cause |this| to become wrapped. + return [this.m, this]; +}; +var a = "s".m(); +assertEq(a[0], f); +assertEq(a[1], "s"); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_5/strict/shell.js b/js/src/tests/ecma_5/strict/shell.js index e60411647718..e69de29bb2d1 100644 --- a/js/src/tests/ecma_5/strict/shell.js +++ b/js/src/tests/ecma_5/strict/shell.js @@ -1,118 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ - -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - - -/* - * Return true if both of these return true: - * - LENIENT_PRED applied to CODE - * - STRICT_PRED applied to CODE with a use strict directive added to the front - * - * Run STRICT_PRED first, for testing code that affects the global environment - * in loose mode, but fails in strict mode. - */ -function testLenientAndStrict(code, lenient_pred, strict_pred) { - return (strict_pred("'use strict'; " + code) && - lenient_pred(code)); -} - -/* - * completesNormally(CODE) returns true if evaluating CODE (as eval - * code) completes normally (rather than throwing an exception). - */ -function completesNormally(code) { - try { - eval(code); - return true; - } catch (exception) { - return false; - } -} - -/* - * returns(VALUE)(CODE) returns true if evaluating CODE (as eval code) - * completes normally (rather than throwing an exception), yielding a value - * strictly equal to VALUE. - */ -function returns(value) { - return function(code) { - try { - return eval(code) === value; - } catch (exception) { - return false; - } - } -} - -/* - * returnsCopyOf(VALUE)(CODE) returns true if evaluating CODE (as eval code) - * completes normally (rather than throwing an exception), yielding a value - * that is deepEqual to VALUE. - */ -function returnsCopyOf(value) { - return function(code) { - try { - return deepEqual(eval(code), value); - } catch (exception) { - return false; - } - } -} - -/* - * raisesException(EXCEPTION)(CODE) returns true if evaluating CODE (as eval - * code) throws an exception object whose prototype is - * EXCEPTION.prototype, and returns false if it throws any other error - * or evaluates successfully. For example: raises(TypeError)("0()") == - * true. - */ -function raisesException(exception) { - return function (code) { - try { - eval(code); - return false; - } catch (actual) { - return exception.prototype.isPrototypeOf(actual); - } - }; -}; - -/* - * parsesSuccessfully(CODE) returns true if CODE parses as function - * code without an error. - */ -function parsesSuccessfully(code) { - try { - Function(code); - return true; - } catch (exception) { - return false; - } -}; - -/* - * parseRaisesException(EXCEPTION)(CODE) returns true if parsing CODE - * as function code raises EXCEPTION. - */ -function parseRaisesException(exception) { - return function (code) { - try { - Function(code); - return false; - } catch (actual) { - return exception.prototype.isPrototypeOf(actual); - } - }; -}; - -/* - * Return the result of applying uneval to VAL, and replacing all runs - * of whitespace with a single horizontal space (poor man's - * tokenization). - */ -function clean_uneval(val) { - return uneval(val).replace(/\s+/g, ' '); -} diff --git a/js/src/tests/ecma_5/strict/unbrand-this.js b/js/src/tests/ecma_5/strict/unbrand-this.js new file mode 100644 index 000000000000..15f428df381b --- /dev/null +++ b/js/src/tests/ecma_5/strict/unbrand-this.js @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +/* Test JSOP_UNBRANDTHIS's behavior on object and non-object |this| values. */ + +function strict() { + "use strict"; + this.insert = function(){ bar(); }; + function bar() {} +} + +var exception; + +// Try 'undefined' as a |this| value. +exception = null; +try { + strict.call(undefined); +} catch (x) { + exception = x; +} +assertEq(exception instanceof TypeError, true); + +// Try 'null' as a |this| value. +exception = null; +try { + strict.call(null); +} catch (x) { + exception = x; +} +assertEq(exception instanceof TypeError, true); + +// An object as a |this| value should be fine. +exception = null; +try { + strict.call({}); +} catch (x) { + exception = x; +} +assertEq(exception, null); + +reportCompare(true, true); diff --git a/js/src/tests/js1_8/extensions/regress-394709.js b/js/src/tests/js1_8/extensions/regress-394709.js index 1f9f44cdae80..a8557bc5255e 100644 --- a/js/src/tests/js1_8/extensions/regress-394709.js +++ b/js/src/tests/js1_8/extensions/regress-394709.js @@ -65,10 +65,15 @@ function test() runtest(); gc(); - var counter = countHeap(); + var count1 = countHeap(); runtest(); gc(); - if (counter != countHeap()) + var count2 = countHeap(); + runtest(); + gc(); + var count3 = countHeap(); + /* Try to be tolerant of conservative GC noise: we want a steady leak. */ + if (count1 < count2 && count2 < count3) throw "A leaky watch point is detected"; function runtest () { diff --git a/js/src/trace-test/tests/basic/wrap-primitive-this.js b/js/src/trace-test/tests/basic/wrap-primitive-this.js new file mode 100644 index 000000000000..5c9db8085c4a --- /dev/null +++ b/js/src/trace-test/tests/basic/wrap-primitive-this.js @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +/* + * If the tracer fails to notice that computeThis() will produce a wrapped + * primitive, then we may get: + * + * Assertion failure: thisObj == globalObj + */ + +var HOTLOOP = this.tracemonkey ? tracemonkey.HOTLOOP : 8; +var a; +function f(n) { + for (var i = 0; i < HOTLOOP; i++) + if (i == HOTLOOP - 2) + a = this; +} + +/* + * Various sorts of events can cause the global to be reshaped, which + * resets our loop counts. Furthermore, we don't record a branch off a + * trace until it has been taken HOTEXIT times. So simply calling the + * function twice may not be enough to ensure that the 'a = this' branch + * gets recorded. This is probably excessive, but it'll work. + */ +f.call("s", 1); +f.call("s", 1); +f.call("s", 1); +f.call("s", 1); +f.call("s", 1); +f.call("s", 1); +f.call("s", 1); +f.call("s", 1); +assertEq(typeof a, "object"); +assertEq("" + a, "s"); From eb02fa2270d3d5599104a3f18370e5847052fd1f Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 12 Oct 2010 11:50:03 -0700 Subject: [PATCH 248/284] Bug 575522 - Object.prototype.toString should return "[object Undefined]" and "[object Null]" when called with |this === undefined| or |this === null|, respectively. r=jorendorff,brendan --- js/src/jsatom.cpp | 2 + js/src/jsatom.h | 2 + js/src/jsobj.cpp | 25 ++++++++-- js/src/tests/ecma_5/Object/jstests.list | 1 + .../tests/ecma_5/Object/object-toString-01.js | 46 +++++++++++++++++++ 5 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 js/src/tests/ecma_5/Object/object-toString-01.js diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 3d6007bcd810..bd583d7e92df 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -158,6 +158,8 @@ const char *const js_common_atom_names[] = { js_name_str, /* nameAtom */ js_next_str, /* nextAtom */ js_noSuchMethod_str, /* noSuchMethodAtom */ + "[object Null]", /* objectNullAtom */ + "[object Undefined]", /* objectUndefinedAtom */ js_proto_str, /* protoAtom */ js_set_str, /* setAtom */ js_source_str, /* sourceAtom */ diff --git a/js/src/jsatom.h b/js/src/jsatom.h index 77963f0cff9a..77b30e6c04a0 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -351,6 +351,8 @@ struct JSAtomState JSAtom *nameAtom; JSAtom *nextAtom; JSAtom *noSuchMethodAtom; + JSAtom *objectNullAtom; + JSAtom *objectUndefinedAtom; JSAtom *protoAtom; JSAtom *setAtom; JSAtom *sourceAtom; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index a65c9fe44613..55c24bb70ca8 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -835,17 +835,32 @@ obj_toStringHelper(JSContext *cx, JSObject *obj) } +/* ES5 15.2.4.2. Note steps 1 and 2 are errata. */ static JSBool obj_toString(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ComputeThisFromVp(cx, vp); - if (!obj) + Value &thisv = vp[1]; + + /* ES5 15.2.4.2 step 1. */ + if (thisv.isUndefined()) { + vp->setString(ATOM_TO_STRING(cx->runtime->atomState.objectUndefinedAtom)); + return true; + } + + /* ES5 15.2.4.2 step 2. */ + if (thisv.isNull()) { + vp->setString(ATOM_TO_STRING(cx->runtime->atomState.objectNullAtom)); + return true; + } + + /* ES5 15.2.4.2 step 3. */ + if (!thisv.isObject() && !js_PrimitiveToObject(cx, &thisv)) return false; - JSString *str = js::obj_toStringHelper(cx, obj); + /* ES5 15.2.4.2 steps 4-5. */ + JSString *str = js::obj_toStringHelper(cx, &thisv.toObject()); if (!str) return false; - vp->setString(str); return true; } @@ -2692,7 +2707,7 @@ static JSFunctionSpec object_methods[] = { #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, obj_toSource, 0,0), #endif - JS_FN(js_toString_str, obj_toString, 0,0), + JS_FN(js_toString_str, obj_toString, 0,JSFUN_PRIMITIVE_THIS), JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0), JS_FN(js_valueOf_str, obj_valueOf, 0,0), #if JS_HAS_OBJ_WATCHPOINT diff --git a/js/src/tests/ecma_5/Object/jstests.list b/js/src/tests/ecma_5/Object/jstests.list index 6ef4350a4dc1..628ea88b493e 100644 --- a/js/src/tests/ecma_5/Object/jstests.list +++ b/js/src/tests/ecma_5/Object/jstests.list @@ -37,4 +37,5 @@ skip-if(!xulRuntime.shell) script 15.2.3.6-dictionary-redefinition-7-of-8.js # u skip-if(!xulRuntime.shell) script 15.2.3.6-dictionary-redefinition-8-of-8.js # uses shell load() function script 15.2.3.6-define-over-method.js script mutation-prevention-methods.js +script object-toString-01.js script vacuous-accessor-unqualified-name.js diff --git a/js/src/tests/ecma_5/Object/object-toString-01.js b/js/src/tests/ecma_5/Object/object-toString-01.js new file mode 100644 index 000000000000..6a8c4e86a91a --- /dev/null +++ b/js/src/tests/ecma_5/Object/object-toString-01.js @@ -0,0 +1,46 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden + */ + +var gTestfile = 'object-toString-01.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 575522; +var summary = '({}).toString.call(null) == "[object Null]", ' + + '({}).toString.call(undefined) == "[object Undefined]", '; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var toString = Object.prototype.toString; + +assertEq(toString.call(null), "[object Null]"); +assertEq(toString.call(undefined), "[object Undefined]"); + +assertEq(toString.call(true), "[object Boolean]"); +assertEq(toString.call(false), "[object Boolean]"); + +assertEq(toString.call(0), "[object Number]"); +assertEq(toString.call(-0), "[object Number]"); +assertEq(toString.call(1), "[object Number]"); +assertEq(toString.call(-1), "[object Number]"); +assertEq(toString.call(NaN), "[object Number]"); +assertEq(toString.call(Infinity), "[object Number]"); +assertEq(toString.call(-Infinity), "[object Number]"); + +assertEq(toString.call("foopy"), "[object String]"); + +assertEq(toString.call({}), "[object Object]"); + + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); From e54a804e93db9313b112bfa4db64a93ee1bf4987 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Tue, 12 Oct 2010 11:50:03 -0700 Subject: [PATCH 249/284] Bug 514570: Adapt jsd to new JS_GetFrameThis arguments. r=brendan It used to be: JSObject *JS_GetFrameThis(JSContext *, JSStackFrame *); Now it is: JSBool JS_GetFrameThis(JSContext *, JSStackFrame *, jsval *); (In strict mode code, |this| values that are primitives don't get wrapped.) --- js/jsd/jsd_stak.c | 11 +++++++---- js/jsd/jsd_step.c | 35 ++++++++++++++++++----------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/js/jsd/jsd_stak.c b/js/jsd/jsd_stak.c index 9b1ba2c12434..aed3ba913618 100644 --- a/js/jsd/jsd_stak.c +++ b/js/jsd/jsd_stak.c @@ -125,13 +125,14 @@ jsd_NewThreadState(JSDContext* jsdc, JSContext *cx ) { JSScript* script = JS_GetFrameScript(cx, fp); jsuword pc = (jsuword) JS_GetFramePC(cx, fp); + jsval dummyThis; /* * don't construct a JSDStackFrame for dummy frames (those without a * |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES * isn't set. */ - if (JS_GetFrameThis(cx, fp) && + if (JS_GetFrameThis(cx, fp, &dummyThis) && ((jsdc->flags & JSD_INCLUDE_NATIVE_FRAMES) || JS_IsScriptFrame(cx, fp))) { @@ -342,11 +343,13 @@ jsd_GetThisForStackFrame(JSDContext* jsdc, if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) ) { + JSBool ok; + jsval thisval; JS_BeginRequest(jsdthreadstate->context); - obj = JS_GetFrameThis(jsdthreadstate->context, jsdframe->fp); + ok = JS_GetFrameThis(jsdthreadstate->context, jsdframe->fp, &thisval); JS_EndRequest(jsdthreadstate->context); - if(obj) - jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj)); + if(ok) + jsdval = JSD_NewValue(jsdc, thisval); } JSD_UNLOCK_THREADSTATES(jsdc); diff --git a/js/jsd/jsd_step.c b/js/jsd/jsd_step.c index 99e9c42649b5..4844398bf97e 100644 --- a/js/jsd/jsd_step.c +++ b/js/jsd/jsd_step.c @@ -69,7 +69,6 @@ _interpreterTrace(JSDContext* jsdc, JSContext *cx, JSStackFrame *fp, JSDScript* jsdscript = NULL; JSScript * script; static indent = 0; - char* buf; const char* funName = NULL; script = JS_GetFrameScript(cx, fp); @@ -86,25 +85,23 @@ _interpreterTrace(JSDContext* jsdc, JSContext *cx, JSStackFrame *fp, if(before) { - buf = JS_smprintf("%sentering %s %s this: %0x\n", - _indentSpaces(indent++), - funName, - JS_IsConstructorFrame(cx, fp) ? "constructing":"", - (int)JS_GetFrameThis(cx, fp)); + jsval thisVal; + + printf("%sentering %s %s this: ", + _indentSpaces(indent++), + funName, + JS_IsConstructorFrame(cx, fp) ? "constructing":""); + + if (JS_GetFrameThis(cx, fp, &thisVal)) + printf("0x%0llx\n", (JSUword) thisVal); + else + puts(""); } else { - buf = JS_smprintf("%sleaving %s\n", - _indentSpaces(--indent), - funName); + printf("%sleaving %s\n", _indentSpaces(--indent), funName); } JS_ASSERT(indent >= 0); - - if(!buf) - return; - - printf(buf); - free(buf); } #endif @@ -127,8 +124,12 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before, return hookresult; } - if (before && JS_IsConstructorFrame(cx, fp)) - jsd_Constructing(jsdc, cx, JS_GetFrameThis(cx, fp), fp); + if (before && JS_IsConstructorFrame(cx, fp)) { + jsval newObj; + if (!JS_GetFrameThis(cx, fp, &newObj)) + return JS_FALSE; + jsd_Constructing(jsdc, cx, JSVAL_TO_OBJECT(newObj), fp); + } jsscript = JS_GetFrameScript(cx, fp); if (jsscript) From a2716375951fce2f8775c5feed98806b5ab82bf4 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Tue, 12 Oct 2010 11:50:03 -0700 Subject: [PATCH 250/284] Bug 514570: Adapt XPConnect to new JS_GetFrameThis arguments. r=jorendorff It used to be: JSObject *JS_GetFrameThis(JSContext *, JSStackFrame *); Now it is: JSBool JS_GetFrameThis(JSContext *, JSStackFrame *, jsval *); (In strict mode code, |this| values that are primitives don't get wrapped.) --- js/src/xpconnect/src/xpcdebug.cpp | 39 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/js/src/xpconnect/src/xpcdebug.cpp b/js/src/xpconnect/src/xpcdebug.cpp index 51fbcf624241..43ade8dfa5f9 100644 --- a/js/src/xpconnect/src/xpcdebug.cpp +++ b/js/src/xpconnect/src/xpcdebug.cpp @@ -70,7 +70,8 @@ static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp, { JSPropertyDescArray callProps = {0, nsnull}; JSPropertyDescArray thisProps = {0, nsnull}; - JSObject* thisObj = nsnull; + JSBool gotThisVal; + jsval thisVal; JSObject* callObj = nsnull; const char* funname = nsnull; const char* filename = nsnull; @@ -108,12 +109,14 @@ static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp, callProps.array = nsnull; // just to be sure } - thisObj = JS_GetFrameThis(cx, fp); - if(showThisProps) + gotThisVal = JS_GetFrameThis(cx, fp, &thisVal); + if (!gotThisVal || + !showThisProps || + JSVAL_IS_PRIMITIVE(thisVal) || + !JS_GetPropertyDescArray(cx, JSVAL_TO_OBJECT(thisVal), + &thisProps)) { - if(thisObj) - if(!JS_GetPropertyDescArray(cx, thisObj, &thisProps)) - thisProps.array = nsnull; // just to be sure + thisProps.array = nsnull; // just to be sure } } @@ -219,21 +222,25 @@ static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp, // print the value of 'this' - if(showLocals && thisObj) + if(showLocals) { - jsval thisJSVal = OBJECT_TO_JSVAL(thisObj); - JSString* thisValStr; - char* thisVal; - - if(nsnull != (thisValStr = JS_ValueToString(cx, thisJSVal)) && - nsnull != (thisVal = JS_GetStringBytes(thisValStr))) + if(gotThisVal) { - buf = JS_sprintf_append(buf, TAB "this = %s\n", thisVal); - if(!buf) goto out; + JSString* thisValStr; + char* thisValChars; + + if(nsnull != (thisValStr = JS_ValueToString(cx, thisVal)) && + nsnull != (thisValChars = JS_GetStringBytes(thisValStr))) + { + buf = JS_sprintf_append(buf, TAB "this = %s\n", thisValChars); + if(!buf) goto out; + } } + else + buf = JS_sprintf_append(buf, TAB "\n"); } - // print the properties of 'this' + // print the properties of 'this', if it is an object if(showThisProps && thisProps.array) { From 142983dfa4d9bf93be0444b3342e877ad9ac2183 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Tue, 12 Oct 2010 11:50:03 -0700 Subject: [PATCH 251/284] Bug 601168: Allow nsHTMLPluginObjElementSH::Call to pass through non-Object |this| values to the plugin. r=bz,gal This introduces some new JSAPI C++ entry points, one of which allows arbitrary jsvals to be passed as the |this| value to a call; this means we avoid a JSVAL_TO_OBJECT call in the caller, and its corresponding OBJECT_TO_JSVAL call in the callee. --- dom/base/nsDOMClassInfo.cpp | 3 +-- js/src/jsapi.cpp | 16 ++++++++++++++++ js/src/jsapi.h | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 30018934c687..0355be9bc495 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -9698,8 +9698,7 @@ nsHTMLPluginObjElementSH::Call(nsIXPConnectWrappedNative *wrapper, // not the 'this' parameter that the JS engine passes in. Pass in // the real this parameter from JS (argv[-1]) here. JSAutoRequest ar(cx); - *_retval = ::JS_CallFunctionValue(cx, JSVAL_TO_OBJECT(argv[-1]), - OBJECT_TO_JSVAL(pi_obj), argc, argv, vp); + *_retval = ::JS::Call(cx, argv[-1], pi_obj, argc, argv, vp); return NS_OK; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 89367e3d3c09..250400faf643 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4956,6 +4956,22 @@ JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval return ok; } +namespace JS { + +JS_PUBLIC_API(bool) +Call(JSContext *cx, jsval thisv, jsval fval, uintN argc, jsval *argv, jsval *rval) +{ + JSBool ok; + + CHECK_REQUEST(cx); + assertSameCompartment(cx, thisv, fval, JSValueArray(argv, argc)); + ok = ExternalInvoke(cx, Valueify(thisv), Valueify(fval), argc, Valueify(argv), Valueify(rval)); + LAST_FRAME_CHECKS(cx, ok); + return ok; +} + +} // namespace JS + JS_PUBLIC_API(JSObject *) JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 1f1030b25224..686a34f00242 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2596,6 +2596,39 @@ extern JS_PUBLIC_API(JSBool) JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, jsval *rval); +#ifdef __cplusplus +JS_END_EXTERN_C + +namespace JS { + +static inline bool +Call(JSContext *cx, JSObject *thisObj, JSFunction *fun, uintN argc, jsval *argv, jsval *rval) { + return !!JS_CallFunction(cx, thisObj, fun, argc, argv, rval); +} + +static inline bool +Call(JSContext *cx, JSObject *thisObj, const char *name, uintN argc, jsval *argv, jsval *rval) { + return !!JS_CallFunctionName(cx, thisObj, name, argc, argv, rval); +} + +static inline bool +Call(JSContext *cx, JSObject *thisObj, jsval fun, uintN argc, jsval *argv, jsval *rval) { + return !!JS_CallFunctionValue(cx, thisObj, fun, argc, argv, rval); +} + +extern JS_PUBLIC_API(bool) +Call(JSContext *cx, jsval thisv, jsval fun, uintN argc, jsval *argv, jsval *rval); + +static inline bool +Call(JSContext *cx, jsval thisv, JSObject *funObj, uintN argc, jsval *argv, jsval *rval) { + return Call(cx, thisv, OBJECT_TO_JSVAL(funObj), argc, argv, rval); +} + +} // namespace JS + +JS_BEGIN_EXTERN_C +#endif // __cplusplus + /* * These functions allow setting an operation callback that will be called * from the thread the context is associated with some time after any thread From b646386f2f382fd115af946fb392670b1632c543 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Tue, 12 Oct 2010 11:59:28 -0700 Subject: [PATCH 252/284] Bug 603533 - We need to hold a strong reference when forwarding to the outer window. r=jst --HG-- extra : rebase_source : e885ff63003bd52d94bb3d2bc7ea0e1c631884b6 --- dom/base/nsGlobalWindow.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 4ab748f3e896..5347e293acd9 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -280,7 +280,7 @@ static PRBool gDOMWindowDumpEnabled = PR_FALSE; #define FORWARD_TO_OUTER(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ - nsGlobalWindow *outer = GetOuterWindowInternal(); \ + nsRefPtr outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return err_rval; \ @@ -292,7 +292,7 @@ static PRBool gDOMWindowDumpEnabled = PR_FALSE; #define FORWARD_TO_OUTER_VOID(method, args) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ - nsGlobalWindow *outer = GetOuterWindowInternal(); \ + nsRefPtr outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return; \ @@ -305,12 +305,12 @@ static PRBool gDOMWindowDumpEnabled = PR_FALSE; #define FORWARD_TO_OUTER_CHROME(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ - nsGlobalWindow *outer = GetOuterWindowInternal(); \ + nsRefPtr outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return err_rval; \ } \ - return ((nsGlobalChromeWindow *)outer)->method args; \ + return ((nsGlobalChromeWindow *)outer.get())->method args; \ } \ PR_END_MACRO @@ -328,12 +328,12 @@ static PRBool gDOMWindowDumpEnabled = PR_FALSE; #define FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ - nsGlobalWindow *outer = GetOuterWindowInternal(); \ + nsRefPtr outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return err_rval; \ } \ - return ((nsGlobalModalWindow *)outer)->method args; \ + return ((nsGlobalModalWindow *)outer.get())->method args; \ } \ PR_END_MACRO @@ -1657,8 +1657,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, Thaw(); } - // XXX Brain transplant outer window JSObject and create new one! - NS_ASSERTION(!GetCurrentInnerWindow() || GetCurrentInnerWindow()->GetExtantDocument() == mDocument, "Uh, mDocument doesn't match the current inner window " From 2e8c14c928b453dd4108d1701a92e74245e5fc43 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 12 Oct 2010 14:40:05 -0500 Subject: [PATCH 253/284] Fix anyname_finalize to look in the right compartment, to avoid dangling pointer. Likely fix for bug 603270. r=mrbkap over IRC. --HG-- extra : rebase_source : 234c1412ce5543e32222cfd292ee905634ef6741 --- js/src/jsxml.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index cfaefd084d27..e172b64ef0d1 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -339,8 +339,8 @@ static void anyname_finalize(JSContext* cx, JSObject* obj) { /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ - if (cx->compartment->anynameObject == obj) - cx->compartment->anynameObject = NULL; + if (obj->compartment()->anynameObject == obj) + obj->compartment()->anynameObject = NULL; } static JSBool From 7f548c52f51c7c926d867f9d112aa72aaa5d0426 Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Tue, 12 Oct 2010 12:43:38 -0700 Subject: [PATCH 254/284] Bug 567873 - Android packaging should use standard packaging code, r=ted a=blocking-fennec --- embedding/Makefile.in | 2 +- embedding/android/Makefile.in | 74 ++------------------------- toolkit/mozapps/installer/packager.mk | 67 +++++++++++++++++++++++- 3 files changed, 72 insertions(+), 71 deletions(-) diff --git a/embedding/Makefile.in b/embedding/Makefile.in index 0b20db9e491d..7e289dfb829c 100644 --- a/embedding/Makefile.in +++ b/embedding/Makefile.in @@ -54,7 +54,7 @@ DIRS += test endif ifeq ($(OS_TARGET),Android) -TOOL_DIRS = android +DIRS += android endif include $(topsrcdir)/config/rules.mk diff --git a/embedding/android/Makefile.in b/embedding/android/Makefile.in index bed5b76329f8..153a13613274 100644 --- a/embedding/android/Makefile.in +++ b/embedding/android/Makefile.in @@ -76,16 +76,13 @@ DEFINES += \ GARBAGE += \ AndroidManifest.xml \ classes.dex \ - $(MOZ_APP_NAME).apk \ $(PROCESSEDJAVAFILES) \ gecko.ap_ \ - gecko-unaligned.apk \ - gecko-unsigned-unaligned.apk \ res/values/strings.xml \ R.java \ $(NULL) -GARBAGE_DIRS += res libs dist classes +GARBAGE_DIRS += classes DIRS = utils @@ -103,36 +100,6 @@ RES_LAYOUT = \ res/layout/notification_progress_text.xml $(NULL) -NATIVE_LIBS = $(shell cat $(DIST)/bin/dependentlibs.list) libxpcom.so libnssckbi.so libfreebl3.so libmozutils.so -FULL_LIBS = $(addprefix libs/armeabi/,$(NATIVE_LIBS)) - -# We'll strip all the libs by default, due to size, but we might -# want to have a non-stripped version for debugging. We should -# really pull out debuginfo and stuff. -ifdef NO_STRIP -DO_STRIP=echo not stripping -else -DO_STRIP=$(STRIP) -endif - -# The set of files/directories from the dist/bin dir that we'll make available in the apk -# some directories not listed due to special handling -DIST_LINK_FILES = \ - modules \ - res \ - application.ini \ - platform.ini \ - greprefs.js \ - browserconfig.properties \ - blocklist.xml \ - chrome.manifest \ - extensions \ - $(NULL) - -ifdef MOZ_IPC -DIST_LINK_FILES += $(MOZ_CHILD_PROCESS_NAME) -endif - JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar DEFAULT_BRANDPATH = $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/en-US/brand.dtd @@ -143,8 +110,6 @@ include $(topsrcdir)/config/rules.mk # Override the Java settings with some specific android settings include $(topsrcdir)/config/android-common.mk -tools:: $(MOZ_APP_NAME).apk - # Note that we're going to set up a dependency directly between embed_android.dex and the java files # Instead of on the .class files, since more than one .class file might be produced per .java file classes.dex: $(JAVAFILES) $(PROCESSEDJAVAFILES) R.java @@ -182,39 +147,6 @@ R.java: $(MOZ_APP_ICON) $(RES_LAYOUT) $(RES_DRAWABLE) res/values/strings.xml $(L gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_DRAWABLE) res/values/strings.xml $(LOCALIZED_STRINGS_XML) $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@ -libs/armeabi/%: $(DIST)/lib/% - @$(NSINSTALL) -D libs/armeabi - @cp -L -v $< $@ - @$(DO_STRIP) $@ - -# Bug 567873 - Android packaging should use standard packaging code -dist: FORCE - $(NSINSTALL) -D dist/components - rm -rf dist/components/* - @(for f in $(DIST)/bin/components/* ; do $(NSINSTALL) $$f dist/components ; done) - $(NSINSTALL) -D dist/chrome - rm -rf dist/chrome/* - @(for f in $(DIST)/bin/chrome/* ; do $(NSINSTALL) $$f dist/chrome ; done) - $(NSINSTALL) -D dist/defaults - rm -rf dist/defaults/* - @(for f in $(DIST)/bin/defaults/* ; do $(NSINSTALL) $$f dist/defaults ; done ) - @(for f in $(DIST_LINK_FILES) ; do if [ -e $(DIST)/bin/$$f ] ; then echo $$f ; ln -sf ../$(DIST)/bin/$$f dist ; fi ; done) -ifdef MOZ_UPDATER - $(NSINSTALL) $(DIST)/bin/update.locale dist -endif - -gecko-unsigned-unaligned.apk: gecko.ap_ classes.dex dist $(FULL_LIBS) - $(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z gecko.ap_ -f classes.dex -nf `pwd`/libs -rf dist - -gecko-unaligned.apk: gecko-unsigned-unaligned.apk - cp gecko-unsigned-unaligned.apk $@ -ifdef JARSIGNER - $(JARSIGNER) $@ -endif - -$(MOZ_APP_NAME).apk: gecko-unaligned.apk - $(ZIPALIGN) -f -v 4 gecko-unaligned.apk $@ - res/values/strings.xml: FORCE mkdir -p res/values $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \ @@ -222,3 +154,7 @@ res/values/strings.xml: FORCE -DSTRINGSPATH="$(DEFAULT_STRINGSPATH)" \ $(srcdir)/strings.xml.in \ > $@ + +libs:: gecko.ap_ classes.dex + $(INSTALL) classes.dex $(FINAL_TARGET) + $(UNZIP) -o gecko.ap_ -d $(FINAL_TARGET) diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk index 75fcd4a69866..cb5f5a231964 100644 --- a/toolkit/mozapps/installer/packager.mk +++ b/toolkit/mozapps/installer/packager.mk @@ -55,7 +55,11 @@ else ifeq (,$(filter-out gtk2 qt, $(MOZ_WIDGET_TOOLKIT))) MOZ_PKG_FORMAT = BZ2 else - MOZ_PKG_FORMAT = TGZ + ifeq (Android,$(OS_TARGET)) + MOZ_PKG_FORMAT = APK + else + MOZ_PKG_FORMAT = TGZ + endif endif endif endif @@ -147,6 +151,67 @@ INNER_MAKE_PACKAGE = rm -f app.7z && \ INNER_UNMAKE_PACKAGE = $(CYGWIN_WRAPPER) 7z x $(UNPACKAGE) && \ mv core $(MOZ_PKG_DIR) endif +ifeq ($(MOZ_PKG_FORMAT),APK) + +# we have custom stuff for Android +MOZ_OMNIJAR = + +JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar +include $(topsrcdir)/config/android-common.mk + +JARSIGNER ?= echo + +DIST_FILES = \ + resources.arsc \ + AndroidManifest.xml \ + chrome \ + components \ + defaults \ + modules \ + res \ + lib \ + extensions \ + application.ini \ + platform.ini \ + greprefs.js \ + browserconfig.properties \ + blocklist.xml \ + chrome.manifest \ + update.locale \ + $(NULL) + +NON_DIST_FILES = \ + classes.dex \ + $(NULL) + +UPLOAD_EXTRA_FILES += gecko-unsigned-unaligned.apk + +include $(topsrcdir)/ipc/app/defs.mk + +ifdef MOZ_IPC +DIST_FILES += $(MOZ_CHILD_PROCESS_NAME) +endif + +PKG_SUFFIX = .apk +INNER_MAKE_PACKAGE = \ + rm -f $(_ABS_DIST)/gecko.ap_ && \ + ( cd $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH) && \ + rm -rf lib && \ + mkdir -p lib/armeabi && \ + cp lib*.so lib/armeabi && \ + $(ZIP) -r9D $(_ABS_DIST)/gecko.ap_ $(DIST_FILES) -x $(NON_DIST_FILES) ) && \ + rm -f $(_ABS_DIST)/gecko.apk && \ + $(APKBUILDER) $(_ABS_DIST)/gecko.apk -v $(APKBUILDER_FLAGS) -z $(_ABS_DIST)/gecko.ap_ -f $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex && \ + cp $(_ABS_DIST)/gecko.apk $(_ABS_DIST)/gecko-unsigned-unaligned.apk && \ + $(JARSIGNER) $(_ABS_DIST)/gecko.apk && \ + $(ZIPALIGN) -f -v 4 $(_ABS_DIST)/gecko.apk $(PACKAGE) +INNER_UNMAKE_PACKAGE = \ + mkdir $(MOZ_PKG_DIR) && \ + cd $(MOZ_PKG_DIR) && \ + $(UNZIP) $(UNPACKAGE) && \ + mv lib/armeabi/*.so . && \ + rm -rf lib +endif ifeq ($(MOZ_PKG_FORMAT),DMG) ifndef _APPNAME ifdef MOZ_DEBUG From b99ff9d5d94ff07de2ff801f8470902aed46e144 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 7 Oct 2010 15:30:36 -0700 Subject: [PATCH 255/284] Bug 602408 - nanojit: print all hex numbers in LIR dumps with an 0x prefix. r=rreitmai. --HG-- extra : convert_revision : 819b3d7e0ab682bf76da6c24de58434f5744e640 --- js/src/nanojit/Nativei386.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/nanojit/Nativei386.cpp b/js/src/nanojit/Nativei386.cpp index 58505907eedd..351b0a0521ec 100644 --- a/js/src/nanojit/Nativei386.cpp +++ b/js/src/nanojit/Nativei386.cpp @@ -344,7 +344,7 @@ namespace nanojit inline void Assembler::LDdm(R reg, I32 addr) { count_ld(); ALUdm(0x8b, reg, addr); - asm_output("mov %s,0(%lx)", gpn(reg), (unsigned long)addr); + asm_output("mov %s,0(%p)", gpn(reg), (void*)addr); } #define SIBIDX(n) "1248"[n] @@ -567,7 +567,7 @@ namespace nanojit IMM32(tt); *(--_nIns) = JMP32; asm_output("jmp %p", t); - verbose_only( verbose_outputf("%010lx:", (unsigned long)_nIns); ) + verbose_only( verbose_outputf("%p:", (void*)_nIns); ) } inline void Assembler::JMP_indirect(R r) { From bed62572db551d6600022610cd446227f3a78e40 Mon Sep 17 00:00:00 2001 From: Rick Reitmaier Date: Thu, 7 Oct 2010 19:22:35 -0700 Subject: [PATCH 256/284] Bug 596056 - nanojit: random number generator needed for many hardening algorithms (r+nnethercote,edwsmith) introduce noise object Assembler to use a noise object in which the actual mechanics of how noise is being produced in abstracted by the client. The noise object will often need to contain state so its convenient to derive from this class and build an object containing all the necessary state. --HG-- extra : convert_revision : c569f3ddd2d9b7a69b3c63f1fd139ea8d513fd9c --- js/src/nanojit/Assembler.cpp | 1 + js/src/nanojit/Assembler.h | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/js/src/nanojit/Assembler.cpp b/js/src/nanojit/Assembler.cpp index 0173d2ff0cbb..16e7ace531c0 100755 --- a/js/src/nanojit/Assembler.cpp +++ b/js/src/nanojit/Assembler.cpp @@ -78,6 +78,7 @@ namespace nanojit , _branchStateMap(alloc) , _patches(alloc) , _labels(alloc) + , _noise(NULL) #if NJ_USES_IMMD_POOL , _immDPool(alloc) #endif diff --git a/js/src/nanojit/Assembler.h b/js/src/nanojit/Assembler.h index 43dbf4b67e59..8ea93ce635fe 100644 --- a/js/src/nanojit/Assembler.h +++ b/js/src/nanojit/Assembler.h @@ -181,6 +181,13 @@ namespace nanojit #endif #endif + class Noise + { + public: + // produce a random number from 0-maxValue for the JIT to use in attack mitigation + virtual uint32_t getValue(uint32_t maxValue) = 0; + }; + // error codes enum AssmError { @@ -284,6 +291,8 @@ namespace nanojit void assemble(Fragment* frag, LirFilter* reader); void beginAssembly(Fragment *frag); + void setNoiseGenerator(Noise* noise) { _noise = noise; } // used for attack mitigation; setting to 0 disables all mitigations + void releaseRegisters(); void patch(GuardRecord *lr); void patch(SideExit *exit); @@ -373,6 +382,7 @@ namespace nanojit RegAllocMap _branchStateMap; NInsMap _patches; LabelStateMap _labels; + Noise* _noise; // object to generate random noise used when hardening enabled. #if NJ_USES_IMMD_POOL ImmDPoolMap _immDPool; #endif From 87af73a001580b377b6968427644444dae576b38 Mon Sep 17 00:00:00 2001 From: Rick Reitmaier Date: Thu, 7 Oct 2010 19:35:06 -0700 Subject: [PATCH 257/284] fix build breakage --HG-- extra : convert_revision : c900245f6488bbcca67f1b2136c4f30427a789aa --- js/src/nanojit/Assembler.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/nanojit/Assembler.h b/js/src/nanojit/Assembler.h index 8ea93ce635fe..3353b592dcc9 100644 --- a/js/src/nanojit/Assembler.h +++ b/js/src/nanojit/Assembler.h @@ -184,6 +184,8 @@ namespace nanojit class Noise { public: + virtual ~Noise() {} + // produce a random number from 0-maxValue for the JIT to use in attack mitigation virtual uint32_t getValue(uint32_t maxValue) = 0; }; From 3155e1c9c7325acf3d427cbe25657409fd99e0ae Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 7 Oct 2010 20:24:07 -0700 Subject: [PATCH 258/284] Bug 599247 - nanojit: in Nativei386.cpp, generate d[b + i<isop(LIR_addp)); + + *base = addp->oprnd1(); + LIns* rhs = addp->oprnd2(); + int k; + + if (rhs->opcode() == LIR_lshp && rhs->oprnd2()->isImmI() && + (k = rhs->oprnd2()->immI(), (1 <= k && k <= 3))) + { + *index = rhs->oprnd1(); + *scale = k; + } else { + *index = rhs; + *scale = 0; + } + } void Assembler::patch(GuardRecord *lr) { if (!lr->jmp) // the guard might have been eliminated as redundant diff --git a/js/src/nanojit/Assembler.h b/js/src/nanojit/Assembler.h index 3353b592dcc9..223fe9588954 100644 --- a/js/src/nanojit/Assembler.h +++ b/js/src/nanojit/Assembler.h @@ -359,6 +359,8 @@ namespace nanojit void evict(LIns* vic); RegisterMask hint(LIns* ins); + void getBaseIndexScale(LIns* addp, LIns** base, LIns** index, int* scale); + void codeAlloc(NIns *&start, NIns *&end, NIns *&eip verbose_only(, size_t &nBytes)); diff --git a/js/src/nanojit/Nativei386.cpp b/js/src/nanojit/Nativei386.cpp index 351b0a0521ec..3d6fe0c54f42 100644 --- a/js/src/nanojit/Nativei386.cpp +++ b/js/src/nanojit/Nativei386.cpp @@ -113,7 +113,7 @@ namespace nanojit } } - inline void Assembler::MODRMsib(R reg, R base, R index, I32 scale, I32 disp) { + inline void Assembler::MODRMsib(I32 reg, R base, R index, I32 scale, I32 disp) { if (disp != 0 || base == rEBP) { if (isS8(disp)) { *(--_nIns) = int8_t(disp); @@ -123,11 +123,11 @@ namespace nanojit } *(--_nIns) = uint8_t(scale << 6 | REGNUM(index) << 3 | REGNUM(base)); if (disp == 0 && base != rEBP) { - *(--_nIns) = uint8_t(REGNUM(reg) << 3 | 4); + *(--_nIns) = uint8_t(reg << 3 | 4); } else if (isS8(disp)) { - *(--_nIns) = uint8_t(1 << 6 | REGNUM(reg) << 3 | 4); + *(--_nIns) = uint8_t(1 << 6 | reg << 3 | 4); } else { - *(--_nIns) = uint8_t(2 << 6 | REGNUM(reg) << 3 | 4); + *(--_nIns) = uint8_t(2 << 6 | reg << 3 | 4); } } @@ -156,10 +156,17 @@ namespace nanojit inline void Assembler::ALUsib(I32 c, R r, R base, R index, I32 scale, I32 disp) { underrunProtect(7); - MODRMsib(r, base, index, scale, disp); + MODRMsib(REGNUM(r), base, index, scale, disp); *(--_nIns) = uint8_t(c); } + inline void Assembler::ALUsib16(I32 c, R r, R base, R index, I32 scale, I32 disp) { + underrunProtect(8); + MODRMsib(REGNUM(r), base, index, scale, disp); + *(--_nIns) = uint8_t(c); + *(--_nIns) = 0x66; + } + inline void Assembler::ALUm16(I32 c, I32 r, I32 d, R b) { underrunProtect(9); MODRMm(r, d, b); @@ -183,7 +190,7 @@ namespace nanojit inline void Assembler::ALU2sib(I32 c, Register r, R base, R index, I32 scale, I32 disp) { underrunProtect(8); - MODRMsib(r, base, index, scale, disp); + MODRMsib(REGNUM(r), base, index, scale, disp); *(--_nIns) = uint8_t(c); *(--_nIns) = uint8_t(c>>8); } @@ -454,18 +461,41 @@ namespace nanojit asm_output("mov8 %d(%s),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), gpn(reg)); } + // Quirk of x86-32: reg must be a/b/c/d for byte stores here. + inline void Assembler::ST8sib(I32 disp, R base, R index, I32 scale, R reg) { + count_st(); + NanoAssert(REGNUM(reg) < 4); + ALUsib(0x88, reg, base, index, scale, disp); + asm_output("mov8 %d(%s+%s*%c),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), + gpn(index), SIBIDX(scale), gpn(reg)); + } + inline void Assembler::ST16(R base, I32 disp, R reg) { count_st(); ALUm16(0x89, REGNUM(reg), disp, base); asm_output("mov16 %d(%s),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), gpn(reg)); } + inline void Assembler::ST16sib(I32 disp, R base, R index, I32 scale, R reg) { + count_st(); + ALUsib16(0x89, reg, base, index, scale, disp); + asm_output("mov16 %d(%s+%s*%c),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), + gpn(index), SIBIDX(scale), gpn(reg)); + } + inline void Assembler::ST(R base, I32 disp, R reg) { count_st(); ALUm(0x89, REGNUM(reg), disp, base); asm_output("mov %d(%s),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), gpn(reg)); } + inline void Assembler::STsib(I32 disp, R base, R index, I32 scale, R reg) { + count_st(); + ALUsib(0x89, reg, base, index, scale, disp); + asm_output("mov %d(%s+%s*%c),%s", disp, base==UnspecifiedReg ? "0" : gpn(base), + gpn(index), SIBIDX(scale), gpn(reg)); + } + inline void Assembler::ST8i(R base, I32 disp, I32 imm) { count_st(); underrunProtect(8); @@ -475,6 +505,15 @@ namespace nanojit asm_output("mov8 %d(%s),%d", disp, gpn(base), imm); } + inline void Assembler::ST8isib(I32 disp, R base, R index, I32 scale, I32 imm) { + count_st(); + underrunProtect(8); + IMM8(imm); + MODRMsib(0, base, index, scale, disp); + *(--_nIns) = 0xc6; + asm_output("mov8 %d(%s+%s*%c),%d", disp, gpn(base), gpn(index), SIBIDX(scale), imm); + } + inline void Assembler::ST16i(R base, I32 disp, I32 imm) { count_st(); underrunProtect(10); @@ -485,6 +524,16 @@ namespace nanojit asm_output("mov16 %d(%s),%d", disp, gpn(base), imm); } + inline void Assembler::ST16isib(I32 disp, R base, R index, I32 scale, I32 imm) { + count_st(); + underrunProtect(10); + IMM16(imm); + MODRMsib(0, base, index, scale, disp); + *(--_nIns) = 0xc7; + *(--_nIns) = 0x66; + asm_output("mov16 %d(%s+%s*%c),%d", disp, gpn(base), gpn(index), SIBIDX(scale), imm); + } + inline void Assembler::STi(R base, I32 disp, I32 imm) { count_st(); underrunProtect(11); @@ -494,6 +543,15 @@ namespace nanojit asm_output("mov %d(%s),%d", disp, gpn(base), imm); } + inline void Assembler::STisib(I32 disp, R base, R index, I32 scale, I32 imm) { + count_st(); + underrunProtect(11); + IMM32(imm); + MODRMsib(0, base, index, scale, disp); + *(--_nIns) = 0xc7; + asm_output("mov %d(%s+%s*%c),%d", disp, gpn(base), gpn(index), SIBIDX(scale), imm); + } + inline void Assembler::RET() { count_ret(); ALU0(0xc3); asm_output("ret"); } inline void Assembler::NOP() { count_alu(); ALU0(0x90); asm_output("nop"); } inline void Assembler::INT3() { ALU0(0xcc); asm_output("int3"); } @@ -865,7 +923,7 @@ namespace nanojit // new comparison operations being added. for (LOpcode op = LOpcode(0); op < LIR_sentinel; op = LOpcode(op+1)) if (isCmpOpcode(op)) - nHints[op] = AllowableFlagRegs; + nHints[op] = AllowableByteRegs; } void Assembler::nBeginAssembly() { @@ -1216,53 +1274,70 @@ namespace nanojit void Assembler::asm_store32(LOpcode op, LIns* value, int dr, LIns* base) { if (value->isImmI()) { - Register rb = getBaseReg(base, dr, GpRegs); - int c = value->immI(); - switch (op) { - case LIR_sti2c: - ST8i(rb, dr, c); - break; - case LIR_sti2s: - ST16i(rb, dr, c); - break; - case LIR_sti: - STi(rb, dr, c); - break; - default: - NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); - break; - } - } - else - { - // Quirk of x86-32: reg must be a/b/c/d for single-byte stores. - const RegisterMask SrcRegs = - (op == LIR_sti2c) ? - (1<opcode() == LIR_addp) { + LIns* index; + int scale; + getBaseIndexScale(base, &base, &index, &scale); - Register ra, rb; - if (base->isImmI()) { - // absolute address - rb = UnspecifiedReg; - dr += base->immI(); - ra = findRegFor(value, SrcRegs); + Register rb, ri; + getBaseReg2(GpRegs, index, ri, GpRegs, base, rb, dr); + + int c = value->immI(); + switch (op) { + case LIR_sti2c: ST8isib( dr, rb, ri, scale, c); break; + case LIR_sti2s: ST16isib(dr, rb, ri, scale, c); break; + case LIR_sti: STisib( dr, rb, ri, scale, c); break; + default: NanoAssert(0); break; + } } else { - getBaseReg2(SrcRegs, value, ra, GpRegs, base, rb, dr); + Register rb = getBaseReg(base, dr, GpRegs); + int c = value->immI(); + switch (op) { + case LIR_sti2c: ST8i( rb, dr, c); break; + case LIR_sti2s: ST16i(rb, dr, c); break; + case LIR_sti: STi( rb, dr, c); break; + default: NanoAssert(0); break; + } } - switch (op) { - case LIR_sti2c: - ST8(rb, dr, ra); - break; - case LIR_sti2s: - ST16(rb, dr, ra); - break; - case LIR_sti: - ST(rb, dr, ra); - break; - default: - NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); - break; + + } else { + // Quirk of x86-32: reg must be a/b/c/d for single-byte stores. + const RegisterMask SrcRegs = (op == LIR_sti2c) ? AllowableByteRegs : GpRegs; + + Register rv, rb; + if (base->opcode() == LIR_addp) { + LIns* index; + int scale; + getBaseIndexScale(base, &base, &index, &scale); + + Register rb, ri, rv; + getBaseReg2(SrcRegs, value, rv, GpRegs, base, rb, dr); + ri = (index == value) ? rv + : (index == base) ? rb + : findRegFor(index, GpRegs & ~(rmask(rb)|rmask(rv))); + + switch (op) { + case LIR_sti2c: ST8sib( dr, rb, ri, scale, rv); break; + case LIR_sti2s: ST16sib(dr, rb, ri, scale, rv); break; + case LIR_sti: STsib( dr, rb, ri, scale, rv); break; + default: NanoAssert(0); break; + } + + } else { + if (base->isImmI()) { + // absolute address + rb = UnspecifiedReg; + dr += base->immI(); + rv = findRegFor(value, SrcRegs); + } else { + getBaseReg2(SrcRegs, value, rv, GpRegs, base, rb, dr); + } + switch (op) { + case LIR_sti2c: ST8( rb, dr, rv); break; + case LIR_sti2s: ST16(rb, dr, rv); break; + case LIR_sti: ST( rb, dr, rv); break; + default: NanoAssert(0); break; + } } } } @@ -1586,7 +1661,7 @@ namespace nanojit void Assembler::asm_condd(LIns* ins) { LOpcode opcode = ins->opcode(); - Register r = prepareResultReg(ins, AllowableFlagRegs); + Register r = prepareResultReg(ins, AllowableByteRegs); // SETcc only sets low 8 bits, so extend MOVZX8(r,r); @@ -1616,7 +1691,7 @@ namespace nanojit { LOpcode op = ins->opcode(); - Register r = prepareResultReg(ins, AllowableFlagRegs); + Register r = prepareResultReg(ins, AllowableByteRegs); // SETcc only sets low 8 bits, so extend MOVZX8(r,r); @@ -1874,126 +1949,64 @@ namespace nanojit intptr_t addr = base->immI(); addr += d; switch (op) { - case LIR_lduc2ui: - LD8Zdm(rr, addr); - break; - case LIR_ldc2i: - LD8Sdm(rr, addr); - break; - case LIR_ldus2ui: - LD16Zdm(rr, addr); - break; - case LIR_lds2i: - LD16Sdm(rr, addr); - break; - case LIR_ldi: - LDdm(rr, addr); - break; - default: - NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); - break; + case LIR_lduc2ui: LD8Zdm( rr, addr); break; + case LIR_ldc2i: LD8Sdm( rr, addr); break; + case LIR_ldus2ui: LD16Zdm(rr, addr); break; + case LIR_lds2i: LD16Sdm(rr, addr); break; + case LIR_ldi: LDdm( rr, addr); break; + default: NanoAssert(0); break; } freeResourcesOf(ins); } else if (base->opcode() == LIR_addp) { // Search for add(X,Y). - LIns *lhs = base->oprnd1(); - LIns *rhs = base->oprnd2(); - - // If we have this: - // - // W = ld (add(X, shl(Y, Z)))[d] , where int(1) <= Z <= int(3) - // - // we assign lhs=X, rhs=Y, scale=Z, and generate this: - // - // mov rW, [rX+rY*(2^rZ)] - // - // Otherwise, we must have this: - // - // W = ld (add(X, Y))[d] - // - // which we treat like this: - // - // W = ld (add(X, shl(Y, 0)))[d] - // + LIns* index; int scale; - if (rhs->opcode() == LIR_lshp && rhs->oprnd2()->isImmI()) { - scale = rhs->oprnd2()->immI(); - if (scale >= 1 && scale <= 3) - rhs = rhs->oprnd1(); - else - scale = 0; - } else { - scale = 0; - } + getBaseIndexScale(base, &base, &index, &scale); - // If 'lhs' isn't in a register, it can be clobbered by 'ins'. - // Likewise for 'rhs', but we try it with 'lhs' first. - Register ra, rb; - // @todo -- If LHS and/or RHS is const, we could eliminate a register use. - if (!lhs->isInReg()) { - ra = rr; - rb = findRegFor(rhs, GpRegs & ~(rmask(ra))); + // If 'base' isn't in a register, it can be clobbered by 'ins'. + // Likewise for 'rhs', but we try it with 'base' first. + Register rb, ri; + // @todo -- If base and/or index is const, we could eliminate a register use. + if (!base->isInReg()) { + rb = rr; + ri = findRegFor(index, GpRegs & ~(rmask(rb))); } else { - ra = lhs->getReg(); - NanoAssert(ra != rr); - rb = rhs->isInReg() ? findRegFor(rhs, GpRegs & ~(rmask(ra))) : rr; + rb = base->getReg(); + NanoAssert(rb != rr); + ri = index->isInReg() ? findRegFor(index, GpRegs & ~(rmask(rb))) : rr; } switch (op) { - case LIR_lduc2ui: - LD8Zsib(rr, d, ra, rb, scale); - break; - case LIR_ldc2i: - LD8Ssib(rr, d, ra, rb, scale); - break; - case LIR_ldus2ui: - LD16Zsib(rr, d, ra, rb, scale); - break; - case LIR_lds2i: - LD16Ssib(rr, d, ra, rb, scale); - break; - case LIR_ldi: - LDsib(rr, d, ra, rb, scale); - break; - default: - NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); - break; + case LIR_lduc2ui: LD8Zsib( rr, d, rb, ri, scale); break; + case LIR_ldc2i: LD8Ssib( rr, d, rb, ri, scale); break; + case LIR_ldus2ui: LD16Zsib(rr, d, rb, ri, scale); break; + case LIR_lds2i: LD16Ssib(rr, d, rb, ri, scale); break; + case LIR_ldi: LDsib( rr, d, rb, ri, scale); break; + default: NanoAssert(0); break; } freeResourcesOf(ins); - if (!lhs->isInReg()) { - NanoAssert(ra == rr); - findSpecificRegForUnallocated(lhs, ra); - } else if (!rhs->isInReg()) { + if (!base->isInReg()) { NanoAssert(rb == rr); - findSpecificRegForUnallocated(rhs, rb); + findSpecificRegForUnallocated(base, rb); + } else if (!index->isInReg()) { + NanoAssert(ri == rr); + findSpecificRegForUnallocated(index, ri); } } else { Register ra = getBaseReg(base, d, GpRegs); switch (op) { - case LIR_lduc2ui: - LD8Z(rr, d, ra); - break; - case LIR_ldc2i: - LD8S(rr, d, ra); - break; - case LIR_ldus2ui: - LD16Z(rr, d, ra); - break; - case LIR_lds2i: - LD16S(rr, d, ra); - break; - case LIR_ldi: - LD(rr, d, ra); - break; - default: - NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); - break; + case LIR_lduc2ui: LD8Z( rr, d, ra); break; + case LIR_ldc2i: LD8S( rr, d, ra); break; + case LIR_ldus2ui: LD16Z(rr, d, ra); break; + case LIR_lds2i: LD16S(rr, d, ra); break; + case LIR_ldi: LD( rr, d, ra); break; + default: NanoAssert(0); break; } freeResourcesOf(ins); diff --git a/js/src/nanojit/Nativei386.h b/js/src/nanojit/Nativei386.h index ecba115d97b4..fae9ced25f90 100644 --- a/js/src/nanojit/Nativei386.h +++ b/js/src/nanojit/Nativei386.h @@ -166,7 +166,7 @@ namespace nanojit static const RegisterMask ScratchRegs = 1< Date: Thu, 7 Oct 2010 23:14:10 -0700 Subject: [PATCH 259/284] These tests should have been added as part of the patch for bug 595728. --HG-- extra : convert_revision : f31ee612d65e224424ae24d1852491c1b1698388 --- js/src/lirasm/tests/64-bit/shq.in | 32 ++++++++++++++++++++++++++++++ js/src/lirasm/tests/64-bit/shq.out | 1 + js/src/lirasm/tests/shi.in | 32 ++++++++++++++++++++++++++++++ js/src/lirasm/tests/shi.out | 1 + 4 files changed, 66 insertions(+) create mode 100644 js/src/lirasm/tests/64-bit/shq.in create mode 100644 js/src/lirasm/tests/64-bit/shq.out create mode 100644 js/src/lirasm/tests/shi.in create mode 100644 js/src/lirasm/tests/shi.out diff --git a/js/src/lirasm/tests/64-bit/shq.in b/js/src/lirasm/tests/64-bit/shq.in new file mode 100644 index 000000000000..7e9686a03d58 --- /dev/null +++ b/js/src/lirasm/tests/64-bit/shq.in @@ -0,0 +1,32 @@ +; Only the bottom 6 bits of the shift amount in lshq/rshq/rshuq are used. + +two = immq 2 + +sh1 = immi 1 +sh2 = immi 65 ; 0100_0001b +sh3 = immi 268435393 ; 0000_1111_1111_1111_1111_1111_1100_0001b + +a1 = lshq two sh1 ; --> 4 +a2 = lshq two sh2 ; --> 4 +a3 = lshq two sh3 ; --> 4 + +b1 = rshq two sh1 ; --> 1 +b2 = rshq two sh2 ; --> 1 +b3 = rshq two sh3 ; --> 1 + +c1 = rshuq two sh1 ; --> 1 +c2 = rshuq two sh2 ; --> 1 +c3 = rshuq two sh3 ; --> 1 + +s0 = immq 0 +s1 = addq s0 a1 +s2 = addq s1 a2 +s3 = addq s2 a3 +s4 = addq s3 b1 +s5 = addq s4 b2 +s6 = addq s5 b3 +s7 = addq s6 c1 +s8 = addq s7 c2 +s9 = addq s8 c2 ; --> 18 + +retq s9 diff --git a/js/src/lirasm/tests/64-bit/shq.out b/js/src/lirasm/tests/64-bit/shq.out new file mode 100644 index 000000000000..5b7e7754b5d5 --- /dev/null +++ b/js/src/lirasm/tests/64-bit/shq.out @@ -0,0 +1 @@ +Output is: 18 diff --git a/js/src/lirasm/tests/shi.in b/js/src/lirasm/tests/shi.in new file mode 100644 index 000000000000..41bf05179951 --- /dev/null +++ b/js/src/lirasm/tests/shi.in @@ -0,0 +1,32 @@ +; Only the bottom 5 bits of the shift amount in lshi/rshi/rshui are used. + +two = immi 2 + +sh1 = immi 1 +sh2 = immi 33 ; 0010_0001b +sh3 = immi 268435425 ; 0000_1111_1111_1111_1111_1111_1110_0001b + +a1 = lshi two sh1 ; --> 4 +a2 = lshi two sh2 ; --> 4 +a3 = lshi two sh3 ; --> 4 + +b1 = rshi two sh1 ; --> 1 +b2 = rshi two sh2 ; --> 1 +b3 = rshi two sh3 ; --> 1 + +c1 = rshui two sh1 ; --> 1 +c2 = rshui two sh2 ; --> 1 +c3 = rshui two sh3 ; --> 1 + +s0 = immi 0 +s1 = addi s0 a1 +s2 = addi s1 a2 +s3 = addi s2 a3 +s4 = addi s3 b1 +s5 = addi s4 b2 +s6 = addi s5 b3 +s7 = addi s6 c1 +s8 = addi s7 c2 +s9 = addi s8 c2 ; --> 18 + +reti s9 diff --git a/js/src/lirasm/tests/shi.out b/js/src/lirasm/tests/shi.out new file mode 100644 index 000000000000..5b7e7754b5d5 --- /dev/null +++ b/js/src/lirasm/tests/shi.out @@ -0,0 +1 @@ +Output is: 18 From bbd245418ba7aa1a5a0a7ec301a5910a32d0bab5 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 12 Oct 2010 11:51:42 -0700 Subject: [PATCH 260/284] Fix for bug 603677 (Crash on startup due to GetProto on an XPCWrappedNative being null with Bugzilla Tweaks jetpack installed), compartments followup. r=mrbkap. --HG-- extra : rebase_source : 884494ce295e8e2222de4f3bafb02d4a96e2bcc2 --- js/src/xpconnect/wrappers/WrapperFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index de471cd29dd7..03214ff58c22 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -109,7 +109,7 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj XPCWrappedNative *wn = static_cast(xpc_GetJSPrivate(obj)); // We know that DOM objects only allow one object, we can return early. - if (wn->GetProto()->ClassIsDOMObject()) + if (wn->HasProto() && wn->GetProto()->ClassIsDOMObject()) return DoubleWrap(cx, obj, flags); XPCCallContext ccx(JS_CALLER, cx, obj); From 65783d923e3c2516c8f73fb42dc7841acac53220 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 12 Oct 2010 12:53:29 -0700 Subject: [PATCH 261/284] Update nanojit-import-rev stamp. --- js/src/nanojit-import-rev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/nanojit-import-rev b/js/src/nanojit-import-rev index be5dd344e42d..28adcdaf795e 100644 --- a/js/src/nanojit-import-rev +++ b/js/src/nanojit-import-rev @@ -1 +1 @@ -661718b83d398aa4c91f522f7d08ef5cea8e20a9 +f31ee612d65e224424ae24d1852491c1b1698388 From 97e5ca141a68bec787957973ea6573c28fba556c Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Tue, 12 Oct 2010 16:28:22 -0700 Subject: [PATCH 262/284] Bug 603816 - Don't assume that target->data is non-null. r=peterv --HG-- extra : rebase_source : 6a3e949376465cc9eac3d4ffa6b7fc3ba501c502 --- js/src/xpconnect/tests/chrome/test_wrappers.xul | 12 ++++++++++++ js/src/xpconnect/wrappers/WrapperFactory.cpp | 7 +++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/js/src/xpconnect/tests/chrome/test_wrappers.xul b/js/src/xpconnect/tests/chrome/test_wrappers.xul index ce30df6068aa..51c45cf53936 100644 --- a/js/src/xpconnect/tests/chrome/test_wrappers.xul +++ b/js/src/xpconnect/tests/chrome/test_wrappers.xul @@ -23,6 +23,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 /** Test for Bug 533596 **/ + function test() { + var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowUtils); + is(utils.getClassName(this), "Proxy", "this gets properly wrapped"); + } + function go() { var win = $('ifr').contentWindow; var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) @@ -34,6 +40,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 is(win.location.href, "http://example.org/tests/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html", "can still get strings out"); + { + let local = test; + eval(); + local(); + } + var unsafeWin = win.wrappedJSObject; is(utils.getClassName(unsafeWin), "Proxy", "can get a Proxy"); is(utils.getClassName(unsafeWin.location), "Proxy", "deep wrapping works"); diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 03214ff58c22..fcd2a97b49b0 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -168,12 +168,12 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO JSObject *xrayHolder = nsnull; JSWrapper *wrapper; + CompartmentPrivate *targetdata = static_cast(target->data); if (AccessCheck::isChrome(target)) { if (AccessCheck::isChrome(origin)) { // Same origin we use a transparent wrapper, unless the compartment asks // for an Xray. - if (static_cast(target->data)->preferXrays && - IS_WN_WRAPPER(obj)) { + if (targetdata && targetdata->preferXrays && IS_WN_WRAPPER(obj)) { typedef XrayWrapper Xray; wrapper = &Xray::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); @@ -208,8 +208,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO if (AccessCheck::needsSystemOnlyWrapper(obj)) { wrapper = &FilteringWrapper::singleton; - } else if (static_cast(target->data)->preferXrays && - IS_WN_WRAPPER(obj)) { + } else if (targetdata && targetdata->preferXrays && IS_WN_WRAPPER(obj)) { typedef XrayWrapper Xray; wrapper = &Xray::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); From 6bf32316faae4c8bf5bc6945842a8b01c2485337 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Tue, 12 Oct 2010 17:44:34 -0700 Subject: [PATCH 263/284] Revert unintended changes. --- js/src/xpconnect/tests/chrome/test_wrappers.xul | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/js/src/xpconnect/tests/chrome/test_wrappers.xul b/js/src/xpconnect/tests/chrome/test_wrappers.xul index 51c45cf53936..ce30df6068aa 100644 --- a/js/src/xpconnect/tests/chrome/test_wrappers.xul +++ b/js/src/xpconnect/tests/chrome/test_wrappers.xul @@ -23,12 +23,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 /** Test for Bug 533596 **/ - function test() { - var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindowUtils); - is(utils.getClassName(this), "Proxy", "this gets properly wrapped"); - } - function go() { var win = $('ifr').contentWindow; var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) @@ -40,12 +34,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=500931 is(win.location.href, "http://example.org/tests/js/src/xpconnect/tests/mochitest/chrome_wrappers_helper.html", "can still get strings out"); - { - let local = test; - eval(); - local(); - } - var unsafeWin = win.wrappedJSObject; is(utils.getClassName(unsafeWin), "Proxy", "can get a Proxy"); is(utils.getClassName(unsafeWin.location), "Proxy", "deep wrapping works"); From 920b80ef4eed9b934141a2d34bffb7bd2616a14b Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Tue, 12 Oct 2010 17:25:37 -0700 Subject: [PATCH 264/284] Bug 603558: add -m option to turn on method jit for xpcshell, r=dvander, a=shaver --HG-- extra : rebase_source : 9374b3ab212fa8cd41d33b3887e678b9888be972 --- js/src/xpconnect/shell/xpcshell.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/src/xpconnect/shell/xpcshell.cpp b/js/src/xpconnect/shell/xpcshell.cpp index 9601d20a2bbb..49e06b0a2fa3 100644 --- a/js/src/xpconnect/shell/xpcshell.cpp +++ b/js/src/xpconnect/shell/xpcshell.cpp @@ -1311,6 +1311,9 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) case 'j': JS_ToggleOptions(cx, JSOPTION_JIT); break; + case 'm': + JS_ToggleOptions(cx, JSOPTION_METHODJIT); + break; #ifdef MOZ_SHARK case 'k': JS_ConnectShark(); From 669cd09bcdb4ba57fc1c7a9d2a278e83a4d41d08 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Wed, 13 Oct 2010 09:22:05 -0700 Subject: [PATCH 265/284] Bug 603433 - TM: various GC cleanups, r=igor --- js/src/jsgc.h | 2 +- js/src/jsgcinlines.h | 23 +---------------------- js/src/jsgcstats.cpp | 2 +- 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d000cdb7d015..43cdca85e5f7 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1040,7 +1040,7 @@ NewCompartment(JSContext *cx, JSPrincipals *principals); inline JSCompartment * JSObject::getCompartment() const { - return ((Cell *)this)->compartment(); + return compartment(); } #endif /* jsgc_h___ */ diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 73702207b4d4..454c46690b69 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -103,7 +103,7 @@ js_NewGCString(JSContext *cx) inline JSShortString * js_NewGCShortString(JSContext *cx) { - return (JSShortString *) NewFinalizableGCThing(cx, js::gc::FINALIZE_SHORT_STRING); + return NewFinalizableGCThing(cx, js::gc::FINALIZE_SHORT_STRING); } inline JSString * @@ -204,21 +204,6 @@ MarkChildren(JSTracer *trc, JSObject *obj) (op ? op : js_TraceObject)(trc, obj); } -static inline void -MarkChildren(JSTracer *trc, JSFunction *fun) -{ - JSObject *obj = reinterpret_cast(fun); - if (!obj->map) - return; - if (JSObject *proto = obj->getProto()) - MarkObject(trc, *proto, "proto"); - - if (JSObject *parent = obj->getParent()) - MarkObject(trc, *parent, "parent"); - TraceOp op = obj->getOps()->trace; - (op ? op : js_TraceObject)(trc, obj); -} - static inline void MarkChildren(JSTracer *trc, JSString *str) { @@ -240,12 +225,6 @@ MarkChildren(JSTracer *trc, JSXML *xml) } #endif -#if JS_STACK_GROWTH_DIRECTION > 0 -# define JS_CHECK_STACK_SIZE(limit, lval) ((jsuword)(lval) < limit) -#else -# define JS_CHECK_STACK_SIZE(limit, lval) ((jsuword)(lval) > limit) -#endif - static inline bool RecursionTooDeep(GCMarker *gcmarker) { #ifdef JS_GC_ASSUME_LOW_C_STACK diff --git a/js/src/jsgcstats.cpp b/js/src/jsgcstats.cpp index 04588abdad42..000a4aa7a20b 100644 --- a/js/src/jsgcstats.cpp +++ b/js/src/jsgcstats.cpp @@ -371,7 +371,7 @@ GCTimer::finish(bool lastGC) { gcFile = fopen("gcTimer.dat", "a"); fprintf(gcFile, " AppTime, Total, Mark, Sweep, FinObj,"); - fprintf(gcFile, " FinStr, Destroy, newChunks, destoyChunks\n"); + fprintf(gcFile, " FinStr, Destroy, newChunks, destroyChunks\n"); } JS_ASSERT(gcFile); fprintf(gcFile, "%12.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %7.1f, ", From 146964085d9462af558e4b674f83fad1bf67ef94 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 12 Oct 2010 13:41:40 -0700 Subject: [PATCH 266/284] Bug 603017 - speed up js::DefaultValue on String builtin with void hint (r=brendan) --- js/src/jsobj.cpp | 122 +++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 55c24bb70ca8..156219f1c3bc 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -5588,6 +5588,51 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool str namespace js { +/* + * When we have an object of a builtin class, we don't quite know what its + * valueOf/toString methods are, since these methods may have been overwritten + * or shadowed. However, we can still do better than js_TryMethod by + * hard-coding the necessary properties for us to find the native we expect. + * + * TODO: a per-thread shape-based cache would be faster and simpler. + */ +static JS_ALWAYS_INLINE bool +StringMethodIsNative(JSContext *cx, JSObject *obj, jsid methodid, Native native) +{ + JS_ASSERT(obj->getClass() == &js_StringClass); + + JS_LOCK_OBJ(cx, obj); + JSObject *lockedobj = obj; + const Shape *shape = obj->nativeLookup(methodid); + JSObject *pobj = obj; + + if (!shape) { + pobj = obj->getProto(); + + if (pobj && pobj->getClass() == &js_StringClass) { + JS_UNLOCK_OBJ(cx, obj); + JS_LOCK_OBJ(cx, pobj); + lockedobj = pobj; + shape = pobj->nativeLookup(methodid); + } + } + + if (shape && shape->hasDefaultGetter() && pobj->containsSlot(shape->slot)) { + const Value &fval = pobj->lockedGetSlot(shape->slot); + + JSObject *funobj; + if (IsFunctionObject(fval, &funobj)) { + JSFunction *fun = funobj->getFunctionPrivate(); + if (fun->maybeNative() == native) { + JS_UNLOCK_OBJ(cx, lockedobj); + return true; + } + } + } + JS_UNLOCK_OBJ(cx, lockedobj); + return false; +} + JSBool DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) { @@ -5595,66 +5640,37 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) Value v = ObjectValue(*obj); if (hint == JSTYPE_STRING) { - /* - * Optimize for String objects with standard toString methods. Support - * new String(...) instances whether mutated to have their own scope or - * not, as well as direct String.prototype references. - */ - if (obj->getClass() == &js_StringClass) { - jsid toStringId = ATOM_TO_JSID(cx->runtime->atomState.toStringAtom); - - JS_LOCK_OBJ(cx, obj); - JSObject *lockedobj = obj; - const Shape *shape = obj->nativeLookup(toStringId); - JSObject *pobj = obj; - - if (!shape) { - pobj = obj->getProto(); - - if (pobj && pobj->getClass() == &js_StringClass) { - JS_UNLOCK_OBJ(cx, obj); - JS_LOCK_OBJ(cx, pobj); - lockedobj = pobj; - shape = pobj->nativeLookup(toStringId); - } - } - - if (shape && shape->hasDefaultGetter() && pobj->containsSlot(shape->slot)) { - const Value &fval = pobj->lockedGetSlot(shape->slot); - - JSObject *funobj; - if (IsFunctionObject(fval, &funobj)) { - JSFunction *fun = funobj->getFunctionPrivate(); - if (fun->maybeNative() == js_str_toString) { - JS_UNLOCK_OBJ(cx, lockedobj); - *vp = obj->getPrimitiveThis(); - return JS_TRUE; - } - } - } - JS_UNLOCK_OBJ(cx, lockedobj); - } - - /* - * Propagate the exception if js_TryMethod finds an appropriate - * method, and calling that method returned failure. - */ - if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, - 0, NULL, &v)) { - return JS_FALSE; + /* Optimize (new String(...)).toString(). */ + if (obj->getClass() == &js_StringClass && + StringMethodIsNative(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), + js_str_toString)) { + *vp = obj->getPrimitiveThis(); + return true; } + if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v)) + return false; if (!v.isPrimitive()) { if (!obj->getClass()->convert(cx, obj, hint, &v)) - return JS_FALSE; + return false; } } else { + /* Optimize (new String(...)).valueOf(). */ + if (obj->getClass() == &js_StringClass && + StringMethodIsNative(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), + js_str_toString)) { + *vp = obj->getPrimitiveThis(); + return true; + } + if (!obj->getClass()->convert(cx, obj, hint, &v)) - return JS_FALSE; + return false; if (v.isObject()) { JS_ASSERT(hint != TypeOfValue(cx, v)); if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v)) - return JS_FALSE; + return false; } } if (v.isObject()) { @@ -5663,7 +5679,7 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) if (hint == JSTYPE_STRING) { str = JS_InternString(cx, obj->getClass()->name); if (!str) - return JS_FALSE; + return false; } else { str = NULL; } @@ -5673,10 +5689,10 @@ DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp) (hint == JSTYPE_VOID) ? "primitive type" : JS_TYPE_STR(hint)); - return JS_FALSE; + return false; } *vp = v; - return JS_TRUE; + return true; } } /* namespace js */ From 2833d2263fd89591ae4b2319a4b173460467be75 Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Tue, 12 Oct 2010 17:39:30 -0400 Subject: [PATCH 267/284] Faster x64 Syncing. b=598839, r=dvander. --- js/src/methodjit/CodeGenIncludes.h | 2 +- js/src/methodjit/FastArithmetic.cpp | 4 +- js/src/methodjit/FastOps.cpp | 4 +- js/src/methodjit/FrameEntry.h | 1 + js/src/methodjit/FrameState-inl.h | 225 +++++++++++++++++++++++----- js/src/methodjit/FrameState.cpp | 204 +++++++++++-------------- js/src/methodjit/FrameState.h | 22 +-- js/src/methodjit/PunboxAssembler.h | 4 +- 8 files changed, 290 insertions(+), 176 deletions(-) diff --git a/js/src/methodjit/CodeGenIncludes.h b/js/src/methodjit/CodeGenIncludes.h index d014e6d8a1d7..4f90a78c546c 100644 --- a/js/src/methodjit/CodeGenIncludes.h +++ b/js/src/methodjit/CodeGenIncludes.h @@ -46,7 +46,7 @@ #elif defined JS_PUNBOX64 # include "PunboxAssembler.h" #else -# error "Neither JS_NUNBOX32 nor JS_PUNBOX32 is defined." +# error "Neither JS_NUNBOX32 nor JS_PUNBOX64 is defined." #endif /* Get a label for assertion purposes. Prevent #ifdef clutter. */ diff --git a/js/src/methodjit/FastArithmetic.cpp b/js/src/methodjit/FastArithmetic.cpp index b16cb4486517..ae38b05f5e1a 100644 --- a/js/src/methodjit/FastArithmetic.cpp +++ b/js/src/methodjit/FastArithmetic.cpp @@ -1008,8 +1008,8 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar JaegerSpew(JSpew_Insns, " ---- BEGIN STUB CALL CODE ---- \n"); /* The lhs/rhs need to be synced in the stub call path. */ - frame.syncEntry(stubcc.masm, lhs, lvr); - frame.syncEntry(stubcc.masm, rhs, rvr); + frame.ensureValueSynced(stubcc.masm, lhs, lvr); + frame.ensureValueSynced(stubcc.masm, rhs, rvr); /* Call the stub, adjusting for the two values just pushed. */ stubcc.call(stub, frame.stackDepth() + script->nfixed + 2); diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 7c835e006a7e..69fd8ead7d62 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1653,13 +1653,11 @@ mjit::Compiler::jsop_stricteq(JSOp op) masm.set32(cond, frame.tempRegForType(test), Imm32(mask), result); #elif defined JS_CPU_X64 RegisterID maskReg = frame.allocReg(); - frame.pinReg(maskReg); - masm.move(ImmTag(known->getKnownTag()), maskReg); + RegisterID r = frame.tempRegForType(test); masm.setPtr(cond, r, maskReg, result); - frame.unpinReg(maskReg); frame.freeReg(maskReg); #endif frame.popn(2); diff --git a/js/src/methodjit/FrameEntry.h b/js/src/methodjit/FrameEntry.h index fc189f05ea59..56a4dc72ac1a 100644 --- a/js/src/methodjit/FrameEntry.h +++ b/js/src/methodjit/FrameEntry.h @@ -195,6 +195,7 @@ class FrameEntry FrameEntry *copyOf() const { JS_ASSERT(isCopy()); + JS_ASSERT(copy < this); return copy; } diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index 54fcb45585d9..6a6b5917c284 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -126,20 +126,6 @@ FrameState::allocReg(FrameEntry *fe, RematInfo::RematType type) return reg; } -inline void -FrameState::emitLoadTypeTag(FrameEntry *fe, RegisterID reg) const -{ - emitLoadTypeTag(this->masm, fe, reg); -} - -inline void -FrameState::emitLoadTypeTag(Assembler &masm, FrameEntry *fe, RegisterID reg) const -{ - if (fe->isCopy()) - fe = fe->copyOf(); - masm.loadTypeTag(addressOf(fe), reg); -} - inline void FrameState::convertInt32ToDouble(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg) const { @@ -485,33 +471,188 @@ FrameState::shouldAvoidDataRemat(FrameEntry *fe) } inline void -FrameState::syncType(const FrameEntry *fe, Address to, Assembler &masm) const +FrameState::ensureFeSynced(const FrameEntry *fe, Assembler &masm) const { - JS_ASSERT_IF(fe->type.synced(), - fe->isCopied() && addressOf(fe).offset != to.offset); - JS_ASSERT(fe->type.inRegister() || fe->type.isConstant()); + Address to = addressOf(fe); + const FrameEntry *backing = fe; + if (fe->isCopy()) + backing = fe->copyOf(); - /* Store a double's type bits, even though !isTypeKnown(). */ - if (fe->isConstant()) - masm.storeTypeTag(ImmTag(fe->getKnownTag()), to); - else if (fe->isTypeKnown()) - masm.storeTypeTag(ImmType(fe->getKnownType()), to); - else - masm.storeTypeTag(fe->type.reg(), to); +#if defined JS_PUNBOX64 + /* If we can, sync the type and data in one go. */ + if (!fe->data.synced() && !fe->type.synced()) { + if (backing->isConstant()) + masm.storeValue(backing->getValue(), to); + else if (backing->isTypeKnown()) + masm.storeValueFromComponents(ImmType(backing->getKnownType()), backing->data.reg(), to); + else + masm.storeValueFromComponents(backing->type.reg(), backing->data.reg(), to); + return; + } +#endif + + /* + * On x86_64, only one of the following two calls will have output, + * and a load will only occur if necessary. + */ + ensureDataSynced(fe, masm); + ensureTypeSynced(fe, masm); } inline void -FrameState::syncData(const FrameEntry *fe, Address to, Assembler &masm) const +FrameState::ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const { - JS_ASSERT_IF(addressOf(fe).base == to.base && - addressOf(fe).offset == to.offset, - !fe->data.synced()); - JS_ASSERT(fe->data.inRegister() || fe->data.isConstant()); + if (fe->type.synced()) + return; - if (fe->data.isConstant()) - masm.storePayload(ImmPayload(fe->getPayload()), to); + Address to = addressOf(fe); + const FrameEntry *backing = fe; + if (fe->isCopy()) + backing = fe->copyOf(); + +#if defined JS_PUNBOX64 + /* Attempt to store the entire Value, to prevent a load. */ + if (backing->isConstant()) { + masm.storeValue(backing->getValue(), to); + return; + } + + if (backing->data.inRegister()) { + RegisterID dreg = backing->data.reg(); + if (backing->isTypeKnown()) + masm.storeValueFromComponents(ImmType(backing->getKnownType()), dreg, to); + else + masm.storeValueFromComponents(backing->type.reg(), dreg, to); + return; + } +#endif + + /* Store a double's type bits, even though !isTypeKnown(). */ + if (backing->isConstant()) + masm.storeTypeTag(ImmTag(backing->getKnownTag()), to); + else if (fe->isTypeKnown()) + masm.storeTypeTag(ImmType(backing->getKnownType()), to); else - masm.storePayload(fe->data.reg(), to); + masm.storeTypeTag(backing->type.reg(), to); +} + +inline void +FrameState::ensureDataSynced(const FrameEntry *fe, Assembler &masm) const +{ + if (fe->data.synced()) + return; + + Address to = addressOf(fe); + const FrameEntry *backing = fe; + if (fe->isCopy()) + backing = fe->copyOf(); + +#if defined JS_PUNBOX64 + if (backing->isConstant()) + masm.storeValue(backing->getValue(), to); + else if (backing->isTypeKnown()) + masm.storeValueFromComponents(ImmType(backing->getKnownType()), backing->data.reg(), to); + else if (backing->type.inRegister()) + masm.storeValueFromComponents(backing->type.reg(), backing->data.reg(), to); + else + masm.storePayload(backing->data.reg(), to); +#elif defined JS_NUNBOX32 + if (backing->isConstant()) + masm.storePayload(ImmPayload(backing->getPayload()), to); + else + masm.storePayload(backing->data.reg(), to); +#endif +} + +inline void +FrameState::syncFe(FrameEntry *fe) +{ + FrameEntry *backing = fe; + if (fe->isCopy()) + backing = fe->copyOf(); + + bool needTypeReg = !fe->type.synced() && backing->type.inMemory(); + bool needDataReg = !fe->data.synced() && backing->data.inMemory(); + +#if defined JS_NUNBOX32 + /* Determine an ordering that won't spill known regs. */ + if (needTypeReg && !needDataReg) { + syncData(fe); + syncType(fe); + } else { + syncType(fe); + syncData(fe); + } +#elif defined JS_PUNBOX64 + if (JS_UNLIKELY(needTypeReg && needDataReg)) { + /* Memory-to-memory moves can only occur for copies backed by memory. */ + JS_ASSERT(backing != fe); + + /* Use ValueReg to do a whole-Value mem-to-mem move. */ + masm.loadValue(addressOf(backing), Registers::ValueReg); + masm.storeValue(Registers::ValueReg, addressOf(fe)); + } else { + /* Store in case unpinning is necessary. */ + MaybeRegisterID pairReg; + + /* Get a register if necessary, without clobbering its pair. */ + if (needTypeReg) { + if (backing->data.inRegister()) { + pairReg = backing->data.reg(); + pinReg(backing->data.reg()); + } + tempRegForType(backing); + } else if (needDataReg) { + if (backing->type.inRegister()) { + pairReg = backing->type.reg(); + pinReg(backing->type.reg()); + } + tempRegForData(backing); + } + + ensureFeSynced(fe, masm); + + if (pairReg.isSet()) + unpinReg(pairReg.reg()); + } + + if (!fe->type.synced()) + fe->type.sync(); + if (!fe->data.synced()) + fe->data.sync(); +#endif +} + +inline void +FrameState::syncType(FrameEntry *fe) +{ + FrameEntry *backing = fe; + if (fe->isCopy()) + backing = fe->copyOf(); + + if (!fe->type.synced() && backing->type.inMemory()) + tempRegForType(backing); + + ensureTypeSynced(fe, masm); + + if (!fe->type.synced()) + fe->type.sync(); +} + +inline void +FrameState::syncData(FrameEntry *fe) +{ + FrameEntry *backing = fe; + if (fe->isCopy()) + backing = fe->copyOf(); + + if (!fe->data.synced() && backing->data.inMemory()) + tempRegForData(backing); + + ensureDataSynced(fe, masm); + + if (!fe->data.synced()) + fe->data.sync(); } inline void @@ -525,7 +666,17 @@ FrameState::forgetType(FrameEntry *fe) if (!fe->isTypeKnown()) return; - syncType(fe, addressOf(fe), masm); + /* + * Likewise, storeLocal() may have set this FE, with a known type, + * to be a copy of another FE, which has an unknown type. + * Just forget the type, since the backing is used in all cases. + */ + if (fe->isCopy()) { + fe->type.invalidate(); + return; + } + + ensureTypeSynced(fe, masm); fe->type.setMemory(); } @@ -852,11 +1003,7 @@ FrameState::loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) cons return; } - if (!fe->data.synced()) - syncData(fe, addressOf(fe), masm); - if (!fe->type.synced()) - syncType(fe, addressOf(fe), masm); - + ensureFeSynced(fe, masm); masm.loadDouble(addressOf(fe), fpReg); } diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 5e269810e6db..30d2f08d75e9 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -121,16 +121,10 @@ FrameState::evictReg(RegisterID reg) FrameEntry *fe = regstate[reg].fe(); if (regstate[reg].type() == RematInfo::TYPE) { - if (!fe->type.synced()) { - syncType(fe, addressOf(fe), masm); - fe->type.sync(); - } + ensureTypeSynced(fe, masm); fe->type.setMemory(); } else { - if (!fe->data.synced()) { - syncData(fe, addressOf(fe), masm); - fe->data.sync(); - } + ensureDataSynced(fe, masm); fe->data.setMemory(); } } @@ -441,23 +435,34 @@ FrameState::sync(Assembler &masm, Uses uses) const return; /* Sync all registers up-front. */ - for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) { - RegisterID reg = RegisterID(i); + Registers allRegs(Registers::AvailRegs); + while (!allRegs.empty()) { + RegisterID reg = allRegs.takeAnyReg(); FrameEntry *fe = regstate[reg].usedBy(); if (!fe) continue; JS_ASSERT(fe->isTracked()); +#if defined JS_PUNBOX64 + /* Sync entire FE to prevent loads. */ + ensureFeSynced(fe, masm); + + /* Take the other register in the pair, if one exists. */ + if (regstate[reg].type() == RematInfo::DATA && fe->type.inRegister()) + allRegs.takeReg(fe->type.reg()); + else if (regstate[reg].type() == RematInfo::TYPE && fe->data.inRegister()) + allRegs.takeReg(fe->data.reg()); +#elif defined JS_NUNBOX32 + /* Sync register if unsynced. */ if (regstate[reg].type() == RematInfo::DATA) { JS_ASSERT(fe->data.reg() == reg); - if (!fe->data.synced()) - syncData(fe, addressOf(fe), masm); + ensureDataSynced(fe, masm); } else { JS_ASSERT(fe->type.reg() == reg); - if (!fe->type.synced()) - syncType(fe, addressOf(fe), masm); + ensureTypeSynced(fe, masm); } +#endif } /* @@ -473,51 +478,37 @@ FrameState::sync(Assembler &masm, Uses uses) const if (!fe->isTracked()) continue; - Address address = addressOf(fe); + FrameEntry *backing = fe; if (!fe->isCopy()) { - /* - * If this |fe| has registers, track them as available. They've - * already been synced. Otherwise, see if a constant needs to be - * synced. - */ if (fe->data.inRegister()) avail.putReg(fe->data.reg()); - else if (!fe->data.synced()) - syncData(fe, address, masm); - if (fe->type.inRegister()) avail.putReg(fe->type.reg()); - else if (!fe->type.synced()) - syncType(fe, address, masm); } else { - FrameEntry *backing = fe->copyOf(); - JS_ASSERT(backing != fe); + backing = fe->copyOf(); JS_ASSERT(!backing->isConstant() && !fe->isConstant()); - /* - * If the copy is backed by something not in a register, fall back - * to a slower sync algorithm. - */ - if ((!fe->type.synced() && !backing->type.inRegister()) || - (!fe->data.synced() && !backing->data.inRegister())) { + /* Fall back to a slower sync algorithm if load required. */ + if ((!fe->type.synced() && backing->type.inMemory()) || + (!fe->data.synced() && backing->data.inMemory())) { syncFancy(masm, avail, fe, bottom); return; } - - if (!fe->type.synced()) { - /* :TODO: we can do better, the type is learned for all copies. */ - if (fe->isTypeKnown()) { - //JS_ASSERT(fe->getTypeTag() == backing->getTypeTag()); - masm.storeTypeTag(ImmType(fe->getKnownType()), address); - } else { - masm.storeTypeTag(backing->type.reg(), address); - } - } - - if (!fe->data.synced()) - masm.storePayload(backing->data.reg(), address); } + + /* If a part still needs syncing, it is either a copy or constant. */ +#if defined JS_PUNBOX64 + /* All register-backed FEs have been entirely synced up-front. */ + if (!fe->type.inRegister() && !fe->data.inRegister()) + ensureFeSynced(fe, masm); +#elif defined JS_NUNBOX32 + /* All components held in registers have been already synced. */ + if (!fe->data.inRegister()) + ensureDataSynced(fe, masm); + if (!fe->type.inRegister()) + ensureTypeSynced(fe, masm); +#endif } } @@ -536,19 +527,35 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) JS_ASSERT(fe->isTracked()); +#if defined JS_PUNBOX64 + /* Don't use syncFe(), since that may clobber more registers. */ + ensureFeSynced(fe, masm); + + if (!fe->type.synced()) + fe->type.sync(); + if (!fe->data.synced()) + fe->data.sync(); + + /* Take the other register in the pair, if one exists. */ if (regstate[reg].type() == RematInfo::DATA) { JS_ASSERT(fe->data.reg() == reg); - if (!fe->data.synced()) { - syncData(fe, addressOf(fe), masm); - fe->data.sync(); - } + if (fe->type.inRegister() && search.hasReg(fe->type.reg())) + search.takeReg(fe->type.reg()); } else { JS_ASSERT(fe->type.reg() == reg); - if (!fe->type.synced()) { - syncType(fe, addressOf(fe), masm); - fe->type.sync(); - } + if (fe->data.inRegister() && search.hasReg(fe->data.reg())) + search.takeReg(fe->data.reg()); } +#elif defined JS_NUNBOX32 + /* Sync this register. */ + if (regstate[reg].type() == RematInfo::DATA) { + JS_ASSERT(fe->data.reg() == reg); + syncData(fe); + } else { + JS_ASSERT(fe->type.reg() == reg); + syncType(fe); + } +#endif } uint32 maxvisits = tracker.nentries; @@ -563,31 +570,18 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) if (fe >= spStop) continue; - Address address = addressOf(fe); - FrameEntry *backing = fe; + syncFe(fe); - if (fe->isCopy()) - backing = fe->copyOf(); - - if (!fe->data.synced()) { - if (backing != fe && backing->data.inMemory()) - tempRegForData(backing); - syncData(backing, address, masm); - fe->data.sync(); - if (fe->data.inRegister() && kill.hasReg(fe->data.reg())) { - forgetReg(fe->data.reg()); - fe->data.setMemory(); - } + /* Forget registers. */ + if (fe->data.inRegister() && kill.hasReg(fe->data.reg()) && + !regstate[fe->data.reg()].isPinned()) { + forgetReg(fe->data.reg()); + fe->data.setMemory(); } - if (!fe->type.synced()) { - if (backing != fe && backing->type.inMemory()) - tempRegForType(backing); - syncType(backing, address, masm); - fe->type.sync(); - if (fe->type.inRegister() && kill.hasReg(fe->type.reg())) { - forgetReg(fe->type.reg()); - fe->type.setMemory(); - } + if (fe->type.inRegister() && kill.hasReg(fe->type.reg()) && + !regstate[fe->type.reg()].isPinned()) { + forgetReg(fe->type.reg()); + fe->type.setMemory(); } } @@ -669,8 +663,7 @@ FrameState::copyDataIntoReg(FrameEntry *fe, RegisterID hint) RegisterID reg = fe->data.reg(); if (reg == hint) { if (freeRegs.empty()) { - if (!fe->data.synced()) - syncData(fe, addressOf(fe), masm); + ensureDataSynced(fe, masm); fe->data.setMemory(); } else { reg = allocReg(); @@ -698,8 +691,7 @@ FrameState::copyDataIntoReg(Assembler &masm, FrameEntry *fe) if (fe->data.inRegister()) { RegisterID reg = fe->data.reg(); if (freeRegs.empty()) { - if (!fe->data.synced()) - syncData(fe, addressOf(fe), masm); + ensureDataSynced(fe, masm); fe->data.setMemory(); regstate[reg].forget(); } else { @@ -731,8 +723,7 @@ FrameState::copyTypeIntoReg(FrameEntry *fe) if (fe->type.inRegister()) { RegisterID reg = fe->type.reg(); if (freeRegs.empty()) { - if (!fe->type.synced()) - syncType(fe, addressOf(fe), masm); + ensureTypeSynced(fe, masm); fe->type.setMemory(); regstate[reg].forget(); } else { @@ -784,13 +775,9 @@ FrameState::copyEntryIntoFPReg(Assembler &masm, FrameEntry *fe, FPRegisterID fpr if (fe->isCopy()) fe = fe->copyOf(); - /* The entry must be synced to memory. */ - if (!fe->data.synced()) - syncData(fe, addressOf(fe), masm); - if (!fe->type.synced()) - syncType(fe, addressOf(fe), masm); - + ensureFeSynced(fe, masm); masm.loadDouble(addressOf(fe), fpreg); + return fpreg; } @@ -810,8 +797,7 @@ FrameState::ownRegForType(FrameEntry *fe) if (freeRegs.empty()) { /* For now... just steal the register that already exists. */ - if (!backing->type.synced()) - syncType(backing, addressOf(backing), masm); + ensureTypeSynced(backing, masm); reg = backing->type.reg(); backing->type.setMemory(); regstate[reg].forget(); @@ -854,8 +840,7 @@ FrameState::ownRegForData(FrameEntry *fe) if (freeRegs.empty()) { /* For now... just steal the register that already exists. */ - if (!backing->data.synced()) - syncData(backing, addressOf(backing), masm); + ensureDataSynced(backing, masm); reg = backing->data.reg(); backing->data.setMemory(); regstate[reg].forget(); @@ -1106,33 +1091,14 @@ FrameState::storeLocal(uint32 n, bool popGuaranteed, bool typeChange) return; /* Ensure that the local variable remains synced. */ - if (local->isCopy()) { - FrameEntry *backing = local->copyOf(); - if (!local->data.synced()) { - if (backing->data.inMemory()) - tempRegForData(backing); - syncData(backing, addressOf(local), masm); - } - if (!local->type.synced()) { - if (backing->type.inMemory()) - tempRegForType(backing); - syncType(backing, addressOf(local), masm); - } - } else { - if (!local->data.synced()) { - syncData(local, addressOf(local), masm); - local->data.sync(); - } - if (!local->type.synced()) { - syncType(local, addressOf(local), masm); - local->type.sync(); - } - if (closed) - forgetEntry(local); - } + syncFe(local); - if (closed) + if (closed) { + /* If the FE can have registers, free them before resetting. */ + if (!local->isCopy()) + forgetEntry(local); local->resetSynced(); + } } void @@ -1336,7 +1302,7 @@ FrameState::unpinEntry(const ValueRemat &vr) } void -FrameState::syncEntry(Assembler &masm, FrameEntry *fe, const ValueRemat &vr) +FrameState::ensureValueSynced(Assembler &masm, FrameEntry *fe, const ValueRemat &vr) { #if defined JS_PUNBOX64 if (!vr.isDataSynced || !vr.isTypeSynced) diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index 7612662880b7..a858fcf228e5 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -408,13 +408,6 @@ class FrameState */ inline RegisterID tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const; - /* - * Forcibly loads the type tag for the specified FrameEntry - * into a register already marked as owning the type. - */ - inline void emitLoadTypeTag(FrameEntry *fe, RegisterID reg) const; - inline void emitLoadTypeTag(Assembler &masm, FrameEntry *fe, RegisterID reg) const; - /* * Convert an integer to a double without applying * additional Register pressure. @@ -490,7 +483,7 @@ class FrameState void unpinEntry(const ValueRemat &vr); /* Syncs fe to memory, given its state as constructed by a call to pinEntry. */ - void syncEntry(Assembler &masm, FrameEntry *fe, const ValueRemat &vr); + void ensureValueSynced(Assembler &masm, FrameEntry *fe, const ValueRemat &vr); struct BinaryAlloc { MaybeRegisterID lhsType; @@ -798,8 +791,17 @@ class FrameState void evictReg(RegisterID reg); inline FrameEntry *rawPush(); inline void addToTracker(FrameEntry *fe); - inline void syncType(const FrameEntry *fe, Address to, Assembler &masm) const; - inline void syncData(const FrameEntry *fe, Address to, Assembler &masm) const; + + /* Guarantee sync, but do not set any sync flag. */ + inline void ensureFeSynced(const FrameEntry *fe, Assembler &masm) const; + inline void ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const; + inline void ensureDataSynced(const FrameEntry *fe, Assembler &masm) const; + + /* Guarantee sync, even if register allocation is required, and set sync. */ + inline void syncFe(FrameEntry *fe); + inline void syncType(FrameEntry *fe); + inline void syncData(FrameEntry *fe); + inline FrameEntry *getLocal(uint32 slot); inline void forgetAllRegs(FrameEntry *fe); inline void swapInTracker(FrameEntry *lhs, FrameEntry *rhs); diff --git a/js/src/methodjit/PunboxAssembler.h b/js/src/methodjit/PunboxAssembler.h index 0fae0464db09..3e8f29bc3ae1 100644 --- a/js/src/methodjit/PunboxAssembler.h +++ b/js/src/methodjit/PunboxAssembler.h @@ -131,8 +131,8 @@ class Assembler : public BaseAssembler } void loadValueAsComponents(const Value &val, RegisterID type, RegisterID payload) { - move(Imm64(val.asRawBits() & 0xFFFF800000000000), type); - move(Imm64(val.asRawBits() & 0x00007FFFFFFFFFFF), payload); + move(Imm64(val.asRawBits() & JSVAL_TAG_MASK), type); + move(Imm64(val.asRawBits() & JSVAL_PAYLOAD_MASK), payload); } template From d7f499f7a68326a79b42442627cd0ed0f461b13c Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 12 Oct 2010 15:03:15 -0700 Subject: [PATCH 268/284] Fix for bug 603531 ("ASSERTION: bad outer object hook" and crash [@ js::gc::Cell::compartment]), compartments followup. r=mrbkap. --- dom/base/nsGlobalWindow.cpp | 22 ++++++++++------------ dom/base/nsPIDOMWindow.h | 4 ++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 5347e293acd9..598ab686733e 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -280,7 +280,7 @@ static PRBool gDOMWindowDumpEnabled = PR_FALSE; #define FORWARD_TO_OUTER(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ - nsRefPtr outer = GetOuterWindowInternal(); \ + nsGlobalWindow *outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return err_rval; \ @@ -292,7 +292,7 @@ static PRBool gDOMWindowDumpEnabled = PR_FALSE; #define FORWARD_TO_OUTER_VOID(method, args) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ - nsRefPtr outer = GetOuterWindowInternal(); \ + nsGlobalWindow *outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return; \ @@ -305,12 +305,12 @@ static PRBool gDOMWindowDumpEnabled = PR_FALSE; #define FORWARD_TO_OUTER_CHROME(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ - nsRefPtr outer = GetOuterWindowInternal(); \ + nsGlobalWindow *outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return err_rval; \ } \ - return ((nsGlobalChromeWindow *)outer.get())->method args; \ + return ((nsGlobalChromeWindow *)outer)->method args; \ } \ PR_END_MACRO @@ -328,12 +328,12 @@ static PRBool gDOMWindowDumpEnabled = PR_FALSE; #define FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ - nsRefPtr outer = GetOuterWindowInternal(); \ + nsGlobalWindow *outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return err_rval; \ } \ - return ((nsGlobalModalWindow *)outer.get())->method args; \ + return ((nsGlobalModalWindow *)outer)->method args; \ } \ PR_END_MACRO @@ -833,7 +833,7 @@ nsGlobalWindow::~nsGlobalWindow() printf("--DOMWINDOW == %d (%p) [serial = %d] [outer = %p] [url = %s]\n", gRefCnt, static_cast(static_cast(this)), - mSerial, static_cast(mOuterWindow), url.get()); + mSerial, static_cast(mOuterWindow.get()), url.get()); #endif #ifdef PR_LOGGING @@ -864,10 +864,6 @@ nsGlobalWindow::~nsGlobalWindow() nsGlobalWindow *w; while ((w = (nsGlobalWindow *)PR_LIST_HEAD(this)) != this) { - NS_ASSERTION(w->mOuterWindow == this, "Uh, bad outer window pointer?"); - - w->mOuterWindow = nsnull; - PR_REMOVE_AND_INIT_LINK(w); } } else { @@ -1230,6 +1226,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgumentsLast) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mInnerWindowHolder) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOuterWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOpenerScriptPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mListenerManager) @@ -1266,6 +1263,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mArgumentsLast) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInnerWindowHolder) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOuterWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOpenerScriptPrincipal) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mListenerManager) @@ -7253,7 +7251,7 @@ nsGlobalWindow::SetChromeEventHandler(nsPIDOMEventTarget* aChromeEventHandler) // Need the cast to be able to call the protected method on a // superclass. We could make the method public instead, but it's really // better this way. - static_cast(mOuterWindow)-> + static_cast(mOuterWindow.get())-> SetChromeEventHandlerInternal(aChromeEventHandler); } } diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index 8d13bc5e6f12..eac97d46d41c 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -316,7 +316,7 @@ public: nsPIDOMWindow *GetOuterWindow() { - return mIsInnerWindow ? mOuterWindow : this; + return mIsInnerWindow ? mOuterWindow.get() : this; } nsPIDOMWindow *GetCurrentInnerWindow() const @@ -606,7 +606,7 @@ protected: // And these are the references between inner and outer windows. nsPIDOMWindow *mInnerWindow; - nsPIDOMWindow *mOuterWindow; + nsCOMPtr mOuterWindow; // the element within the document that is currently focused when this // window is active From 7824df9649126752d1f1ff136c682799bcddfc5d Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Tue, 12 Oct 2010 20:08:40 -0400 Subject: [PATCH 269/284] Eliminate ImmutableSync on x64. b=601066, r=dvander. --- js/src/methodjit/FrameState.cpp | 35 +++++++++++++++++++++++++++++- js/src/methodjit/FrameState.h | 4 ++++ js/src/methodjit/ImmutableSync.cpp | 5 +++++ js/src/methodjit/ImmutableSync.h | 2 +- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 30d2f08d75e9..831455c61ea0 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -47,7 +47,10 @@ using namespace js::mjit; JS_STATIC_ASSERT(sizeof(FrameEntry) % 8 == 0); FrameState::FrameState(JSContext *cx, JSScript *script, Assembler &masm) - : cx(cx), script(script), masm(masm), entries(NULL), reifier(cx, *this), + : cx(cx), script(script), masm(masm), entries(NULL), +#if defined JS_NUNBOX32 + reifier(cx, *this), +#endif inTryBlock(false) { } @@ -78,8 +81,10 @@ FrameState::init(uint32 nargs) if (!cursor) return false; +#if defined JS_NUNBOX32 if (!reifier.init(nslots)) return false; +#endif entries = (FrameEntry *)cursor; cursor += sizeof(FrameEntry) * nslots; @@ -414,6 +419,7 @@ FrameState::assertValidRegisterState() const } #endif +#if defined JS_NUNBOX32 void FrameState::syncFancy(Assembler &masm, Registers avail, FrameEntry *resumeAt, FrameEntry *bottom) const @@ -427,6 +433,7 @@ FrameState::syncFancy(Assembler &masm, Registers avail, FrameEntry *resumeAt, reifier.sync(fe); } } +#endif void FrameState::sync(Assembler &masm, Uses uses) const @@ -489,12 +496,38 @@ FrameState::sync(Assembler &masm, Uses uses) const backing = fe->copyOf(); JS_ASSERT(!backing->isConstant() && !fe->isConstant()); +#if defined JS_PUNBOX64 + if ((!fe->type.synced() && backing->type.inMemory()) || + (!fe->data.synced() && backing->data.inMemory())) { + + RegisterID syncReg = Registers::ValueReg; + + /* Load the entire Value into syncReg. */ + if (backing->type.synced() && backing->data.synced()) { + masm.loadValue(addressOf(backing), syncReg); + } else if (backing->type.inMemory()) { + masm.loadTypeTag(addressOf(backing), syncReg); + masm.orPtr(backing->data.reg(), syncReg); + } else { + JS_ASSERT(backing->data.inMemory()); + masm.loadPayload(addressOf(backing), syncReg); + if (backing->isTypeKnown()) + masm.orPtr(ImmType(backing->getKnownType()), syncReg); + else + masm.orPtr(backing->type.reg(), syncReg); + } + + masm.storeValue(syncReg, addressOf(fe)); + continue; + } +#elif defined JS_NUNBOX32 /* Fall back to a slower sync algorithm if load required. */ if ((!fe->type.synced() && backing->type.inMemory()) || (!fe->data.synced() && backing->data.inMemory())) { syncFancy(masm, avail, fe, bottom); return; } +#endif } /* If a part still needs syncing, it is either a copy or constant. */ diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index a858fcf228e5..6f9692380dd7 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -807,8 +807,10 @@ class FrameState inline void swapInTracker(FrameEntry *lhs, FrameEntry *rhs); inline uint32 localIndex(uint32 n); void pushCopyOf(uint32 index); +#if defined JS_NUNBOX32 void syncFancy(Assembler &masm, Registers avail, FrameEntry *resumeAt, FrameEntry *bottom) const; +#endif inline bool tryFastDoubleLoad(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const; void resetInternalState(); @@ -882,7 +884,9 @@ class FrameState */ RegisterState regstate[Assembler::TotalRegisters]; +#if defined JS_NUNBOX32 mutable ImmutableSync reifier; +#endif JSPackedBool *closedVars; bool eval; diff --git a/js/src/methodjit/ImmutableSync.cpp b/js/src/methodjit/ImmutableSync.cpp index 46260aef3029..04d26fdfb4f6 100644 --- a/js/src/methodjit/ImmutableSync.cpp +++ b/js/src/methodjit/ImmutableSync.cpp @@ -36,6 +36,9 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ + +#if defined JS_NUNBOX32 + #include "FrameEntry.h" #include "FrameState.h" #include "FrameState-inl.h" @@ -270,3 +273,5 @@ ImmutableSync::syncNormal(FrameEntry *fe) } } +#endif /* JS_NUNBOX32 */ + diff --git a/js/src/methodjit/ImmutableSync.h b/js/src/methodjit/ImmutableSync.h index 28234af2f6db..204dbab41891 100644 --- a/js/src/methodjit/ImmutableSync.h +++ b/js/src/methodjit/ImmutableSync.h @@ -37,7 +37,7 @@ * * ***** END LICENSE BLOCK ***** */ -#if !defined jsjaeger_imm_sync_h__ && defined JS_METHODJIT +#if !defined jsjaeger_imm_sync_h__ && defined JS_METHODJIT && defined JS_NUNBOX32 #define jsjaeger_imm_sync_h__ #include "methodjit/MachineRegs.h" From 5bb4dd3d8cbe79c68d132236da2933d47a6ab8fe Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 12 Oct 2010 17:25:34 -0700 Subject: [PATCH 270/284] Try to fix orange from d468bf98f7a1 (Fix for bug 603531 ("ASSERTION: bad outer object hook" and crash [@ js::gc::Cell::compartment]), compartments followup.) --HG-- extra : rebase_source : 26ed7548f57836136162e6830034d0e40bf0853b --- dom/base/nsGlobalWindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 598ab686733e..33783729b8ef 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2188,7 +2188,8 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell) for (nsRefPtr inner = (nsGlobalWindow *)PR_LIST_HEAD(this); inner != this; inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) { - NS_ASSERTION(inner->mOuterWindow == this, "bad outer window pointer"); + NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this, + "bad outer window pointer"); inner->FreeInnerObjects(PR_TRUE); } @@ -7244,7 +7245,8 @@ nsGlobalWindow::SetChromeEventHandler(nsPIDOMEventTarget* aChromeEventHandler) for (nsGlobalWindow *inner = (nsGlobalWindow *)PR_LIST_HEAD(this); inner != this; inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) { - NS_ASSERTION(inner->mOuterWindow == this, "bad outer window pointer"); + NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this, + "bad outer window pointer"); inner->SetChromeEventHandlerInternal(aChromeEventHandler); } } else if (mOuterWindow) { From 6d829f0b30b3597b87ba179f0e0a339a6ed7207f Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Tue, 12 Oct 2010 20:34:15 -0400 Subject: [PATCH 271/284] Fix typo in ensureTypeSynced(), fixing assert. b=598839. --- js/src/methodjit/FrameState-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index 6a6b5917c284..a74679c68cb0 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -530,7 +530,7 @@ FrameState::ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const /* Store a double's type bits, even though !isTypeKnown(). */ if (backing->isConstant()) masm.storeTypeTag(ImmTag(backing->getKnownTag()), to); - else if (fe->isTypeKnown()) + else if (backing->isTypeKnown()) masm.storeTypeTag(ImmType(backing->getKnownType()), to); else masm.storeTypeTag(backing->type.reg(), to); From 3e696d4587536e15e97cee1d8524d66d0ff7a1ce Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Wed, 13 Oct 2010 11:16:20 -0700 Subject: [PATCH 272/284] Backed out changeset a63b6fa0229c per request from sayrer due to a trace malloc alloc regression. --- js/src/methodjit/FrameState-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index a74679c68cb0..6a6b5917c284 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -530,7 +530,7 @@ FrameState::ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const /* Store a double's type bits, even though !isTypeKnown(). */ if (backing->isConstant()) masm.storeTypeTag(ImmTag(backing->getKnownTag()), to); - else if (backing->isTypeKnown()) + else if (fe->isTypeKnown()) masm.storeTypeTag(ImmType(backing->getKnownType()), to); else masm.storeTypeTag(backing->type.reg(), to); From 1ab7cb0265de60199591d824088bda8f840c73b1 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Wed, 13 Oct 2010 11:17:13 -0700 Subject: [PATCH 273/284] Backed out changeset ad0c80eacba7 per request from sayrer due to a trace malloc alloc regression. --- js/src/methodjit/FrameState.cpp | 35 +----------------------------- js/src/methodjit/FrameState.h | 4 ---- js/src/methodjit/ImmutableSync.cpp | 5 ----- js/src/methodjit/ImmutableSync.h | 2 +- 4 files changed, 2 insertions(+), 44 deletions(-) diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 831455c61ea0..30d2f08d75e9 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -47,10 +47,7 @@ using namespace js::mjit; JS_STATIC_ASSERT(sizeof(FrameEntry) % 8 == 0); FrameState::FrameState(JSContext *cx, JSScript *script, Assembler &masm) - : cx(cx), script(script), masm(masm), entries(NULL), -#if defined JS_NUNBOX32 - reifier(cx, *this), -#endif + : cx(cx), script(script), masm(masm), entries(NULL), reifier(cx, *this), inTryBlock(false) { } @@ -81,10 +78,8 @@ FrameState::init(uint32 nargs) if (!cursor) return false; -#if defined JS_NUNBOX32 if (!reifier.init(nslots)) return false; -#endif entries = (FrameEntry *)cursor; cursor += sizeof(FrameEntry) * nslots; @@ -419,7 +414,6 @@ FrameState::assertValidRegisterState() const } #endif -#if defined JS_NUNBOX32 void FrameState::syncFancy(Assembler &masm, Registers avail, FrameEntry *resumeAt, FrameEntry *bottom) const @@ -433,7 +427,6 @@ FrameState::syncFancy(Assembler &masm, Registers avail, FrameEntry *resumeAt, reifier.sync(fe); } } -#endif void FrameState::sync(Assembler &masm, Uses uses) const @@ -496,38 +489,12 @@ FrameState::sync(Assembler &masm, Uses uses) const backing = fe->copyOf(); JS_ASSERT(!backing->isConstant() && !fe->isConstant()); -#if defined JS_PUNBOX64 - if ((!fe->type.synced() && backing->type.inMemory()) || - (!fe->data.synced() && backing->data.inMemory())) { - - RegisterID syncReg = Registers::ValueReg; - - /* Load the entire Value into syncReg. */ - if (backing->type.synced() && backing->data.synced()) { - masm.loadValue(addressOf(backing), syncReg); - } else if (backing->type.inMemory()) { - masm.loadTypeTag(addressOf(backing), syncReg); - masm.orPtr(backing->data.reg(), syncReg); - } else { - JS_ASSERT(backing->data.inMemory()); - masm.loadPayload(addressOf(backing), syncReg); - if (backing->isTypeKnown()) - masm.orPtr(ImmType(backing->getKnownType()), syncReg); - else - masm.orPtr(backing->type.reg(), syncReg); - } - - masm.storeValue(syncReg, addressOf(fe)); - continue; - } -#elif defined JS_NUNBOX32 /* Fall back to a slower sync algorithm if load required. */ if ((!fe->type.synced() && backing->type.inMemory()) || (!fe->data.synced() && backing->data.inMemory())) { syncFancy(masm, avail, fe, bottom); return; } -#endif } /* If a part still needs syncing, it is either a copy or constant. */ diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index 6f9692380dd7..a858fcf228e5 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -807,10 +807,8 @@ class FrameState inline void swapInTracker(FrameEntry *lhs, FrameEntry *rhs); inline uint32 localIndex(uint32 n); void pushCopyOf(uint32 index); -#if defined JS_NUNBOX32 void syncFancy(Assembler &masm, Registers avail, FrameEntry *resumeAt, FrameEntry *bottom) const; -#endif inline bool tryFastDoubleLoad(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) const; void resetInternalState(); @@ -884,9 +882,7 @@ class FrameState */ RegisterState regstate[Assembler::TotalRegisters]; -#if defined JS_NUNBOX32 mutable ImmutableSync reifier; -#endif JSPackedBool *closedVars; bool eval; diff --git a/js/src/methodjit/ImmutableSync.cpp b/js/src/methodjit/ImmutableSync.cpp index 04d26fdfb4f6..46260aef3029 100644 --- a/js/src/methodjit/ImmutableSync.cpp +++ b/js/src/methodjit/ImmutableSync.cpp @@ -36,9 +36,6 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ - -#if defined JS_NUNBOX32 - #include "FrameEntry.h" #include "FrameState.h" #include "FrameState-inl.h" @@ -273,5 +270,3 @@ ImmutableSync::syncNormal(FrameEntry *fe) } } -#endif /* JS_NUNBOX32 */ - diff --git a/js/src/methodjit/ImmutableSync.h b/js/src/methodjit/ImmutableSync.h index 204dbab41891..28234af2f6db 100644 --- a/js/src/methodjit/ImmutableSync.h +++ b/js/src/methodjit/ImmutableSync.h @@ -37,7 +37,7 @@ * * ***** END LICENSE BLOCK ***** */ -#if !defined jsjaeger_imm_sync_h__ && defined JS_METHODJIT && defined JS_NUNBOX32 +#if !defined jsjaeger_imm_sync_h__ && defined JS_METHODJIT #define jsjaeger_imm_sync_h__ #include "methodjit/MachineRegs.h" From 4a9bb60db93bf64c8657536313ff9fd3ef2e3cc9 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Wed, 13 Oct 2010 11:17:51 -0700 Subject: [PATCH 274/284] Backed out changeset 08970767d83d per request from sayrer due to a trace malloc alloc regression. --- js/src/methodjit/CodeGenIncludes.h | 2 +- js/src/methodjit/FastArithmetic.cpp | 4 +- js/src/methodjit/FastOps.cpp | 4 +- js/src/methodjit/FrameEntry.h | 1 - js/src/methodjit/FrameState-inl.h | 219 +++++----------------------- js/src/methodjit/FrameState.cpp | 204 +++++++++++++++----------- js/src/methodjit/FrameState.h | 22 ++- js/src/methodjit/PunboxAssembler.h | 4 +- 8 files changed, 173 insertions(+), 287 deletions(-) diff --git a/js/src/methodjit/CodeGenIncludes.h b/js/src/methodjit/CodeGenIncludes.h index 4f90a78c546c..d014e6d8a1d7 100644 --- a/js/src/methodjit/CodeGenIncludes.h +++ b/js/src/methodjit/CodeGenIncludes.h @@ -46,7 +46,7 @@ #elif defined JS_PUNBOX64 # include "PunboxAssembler.h" #else -# error "Neither JS_NUNBOX32 nor JS_PUNBOX64 is defined." +# error "Neither JS_NUNBOX32 nor JS_PUNBOX32 is defined." #endif /* Get a label for assertion purposes. Prevent #ifdef clutter. */ diff --git a/js/src/methodjit/FastArithmetic.cpp b/js/src/methodjit/FastArithmetic.cpp index ae38b05f5e1a..b16cb4486517 100644 --- a/js/src/methodjit/FastArithmetic.cpp +++ b/js/src/methodjit/FastArithmetic.cpp @@ -1008,8 +1008,8 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar JaegerSpew(JSpew_Insns, " ---- BEGIN STUB CALL CODE ---- \n"); /* The lhs/rhs need to be synced in the stub call path. */ - frame.ensureValueSynced(stubcc.masm, lhs, lvr); - frame.ensureValueSynced(stubcc.masm, rhs, rvr); + frame.syncEntry(stubcc.masm, lhs, lvr); + frame.syncEntry(stubcc.masm, rhs, rvr); /* Call the stub, adjusting for the two values just pushed. */ stubcc.call(stub, frame.stackDepth() + script->nfixed + 2); diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 69fd8ead7d62..7c835e006a7e 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1653,11 +1653,13 @@ mjit::Compiler::jsop_stricteq(JSOp op) masm.set32(cond, frame.tempRegForType(test), Imm32(mask), result); #elif defined JS_CPU_X64 RegisterID maskReg = frame.allocReg(); - masm.move(ImmTag(known->getKnownTag()), maskReg); + frame.pinReg(maskReg); + masm.move(ImmTag(known->getKnownTag()), maskReg); RegisterID r = frame.tempRegForType(test); masm.setPtr(cond, r, maskReg, result); + frame.unpinReg(maskReg); frame.freeReg(maskReg); #endif frame.popn(2); diff --git a/js/src/methodjit/FrameEntry.h b/js/src/methodjit/FrameEntry.h index 56a4dc72ac1a..fc189f05ea59 100644 --- a/js/src/methodjit/FrameEntry.h +++ b/js/src/methodjit/FrameEntry.h @@ -195,7 +195,6 @@ class FrameEntry FrameEntry *copyOf() const { JS_ASSERT(isCopy()); - JS_ASSERT(copy < this); return copy; } diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index 6a6b5917c284..54fcb45585d9 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -126,6 +126,20 @@ FrameState::allocReg(FrameEntry *fe, RematInfo::RematType type) return reg; } +inline void +FrameState::emitLoadTypeTag(FrameEntry *fe, RegisterID reg) const +{ + emitLoadTypeTag(this->masm, fe, reg); +} + +inline void +FrameState::emitLoadTypeTag(Assembler &masm, FrameEntry *fe, RegisterID reg) const +{ + if (fe->isCopy()) + fe = fe->copyOf(); + masm.loadTypeTag(addressOf(fe), reg); +} + inline void FrameState::convertInt32ToDouble(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg) const { @@ -471,188 +485,33 @@ FrameState::shouldAvoidDataRemat(FrameEntry *fe) } inline void -FrameState::ensureFeSynced(const FrameEntry *fe, Assembler &masm) const +FrameState::syncType(const FrameEntry *fe, Address to, Assembler &masm) const { - Address to = addressOf(fe); - const FrameEntry *backing = fe; - if (fe->isCopy()) - backing = fe->copyOf(); - -#if defined JS_PUNBOX64 - /* If we can, sync the type and data in one go. */ - if (!fe->data.synced() && !fe->type.synced()) { - if (backing->isConstant()) - masm.storeValue(backing->getValue(), to); - else if (backing->isTypeKnown()) - masm.storeValueFromComponents(ImmType(backing->getKnownType()), backing->data.reg(), to); - else - masm.storeValueFromComponents(backing->type.reg(), backing->data.reg(), to); - return; - } -#endif - - /* - * On x86_64, only one of the following two calls will have output, - * and a load will only occur if necessary. - */ - ensureDataSynced(fe, masm); - ensureTypeSynced(fe, masm); -} - -inline void -FrameState::ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const -{ - if (fe->type.synced()) - return; - - Address to = addressOf(fe); - const FrameEntry *backing = fe; - if (fe->isCopy()) - backing = fe->copyOf(); - -#if defined JS_PUNBOX64 - /* Attempt to store the entire Value, to prevent a load. */ - if (backing->isConstant()) { - masm.storeValue(backing->getValue(), to); - return; - } - - if (backing->data.inRegister()) { - RegisterID dreg = backing->data.reg(); - if (backing->isTypeKnown()) - masm.storeValueFromComponents(ImmType(backing->getKnownType()), dreg, to); - else - masm.storeValueFromComponents(backing->type.reg(), dreg, to); - return; - } -#endif + JS_ASSERT_IF(fe->type.synced(), + fe->isCopied() && addressOf(fe).offset != to.offset); + JS_ASSERT(fe->type.inRegister() || fe->type.isConstant()); /* Store a double's type bits, even though !isTypeKnown(). */ - if (backing->isConstant()) - masm.storeTypeTag(ImmTag(backing->getKnownTag()), to); + if (fe->isConstant()) + masm.storeTypeTag(ImmTag(fe->getKnownTag()), to); else if (fe->isTypeKnown()) - masm.storeTypeTag(ImmType(backing->getKnownType()), to); + masm.storeTypeTag(ImmType(fe->getKnownType()), to); else - masm.storeTypeTag(backing->type.reg(), to); + masm.storeTypeTag(fe->type.reg(), to); } inline void -FrameState::ensureDataSynced(const FrameEntry *fe, Assembler &masm) const +FrameState::syncData(const FrameEntry *fe, Address to, Assembler &masm) const { - if (fe->data.synced()) - return; + JS_ASSERT_IF(addressOf(fe).base == to.base && + addressOf(fe).offset == to.offset, + !fe->data.synced()); + JS_ASSERT(fe->data.inRegister() || fe->data.isConstant()); - Address to = addressOf(fe); - const FrameEntry *backing = fe; - if (fe->isCopy()) - backing = fe->copyOf(); - -#if defined JS_PUNBOX64 - if (backing->isConstant()) - masm.storeValue(backing->getValue(), to); - else if (backing->isTypeKnown()) - masm.storeValueFromComponents(ImmType(backing->getKnownType()), backing->data.reg(), to); - else if (backing->type.inRegister()) - masm.storeValueFromComponents(backing->type.reg(), backing->data.reg(), to); + if (fe->data.isConstant()) + masm.storePayload(ImmPayload(fe->getPayload()), to); else - masm.storePayload(backing->data.reg(), to); -#elif defined JS_NUNBOX32 - if (backing->isConstant()) - masm.storePayload(ImmPayload(backing->getPayload()), to); - else - masm.storePayload(backing->data.reg(), to); -#endif -} - -inline void -FrameState::syncFe(FrameEntry *fe) -{ - FrameEntry *backing = fe; - if (fe->isCopy()) - backing = fe->copyOf(); - - bool needTypeReg = !fe->type.synced() && backing->type.inMemory(); - bool needDataReg = !fe->data.synced() && backing->data.inMemory(); - -#if defined JS_NUNBOX32 - /* Determine an ordering that won't spill known regs. */ - if (needTypeReg && !needDataReg) { - syncData(fe); - syncType(fe); - } else { - syncType(fe); - syncData(fe); - } -#elif defined JS_PUNBOX64 - if (JS_UNLIKELY(needTypeReg && needDataReg)) { - /* Memory-to-memory moves can only occur for copies backed by memory. */ - JS_ASSERT(backing != fe); - - /* Use ValueReg to do a whole-Value mem-to-mem move. */ - masm.loadValue(addressOf(backing), Registers::ValueReg); - masm.storeValue(Registers::ValueReg, addressOf(fe)); - } else { - /* Store in case unpinning is necessary. */ - MaybeRegisterID pairReg; - - /* Get a register if necessary, without clobbering its pair. */ - if (needTypeReg) { - if (backing->data.inRegister()) { - pairReg = backing->data.reg(); - pinReg(backing->data.reg()); - } - tempRegForType(backing); - } else if (needDataReg) { - if (backing->type.inRegister()) { - pairReg = backing->type.reg(); - pinReg(backing->type.reg()); - } - tempRegForData(backing); - } - - ensureFeSynced(fe, masm); - - if (pairReg.isSet()) - unpinReg(pairReg.reg()); - } - - if (!fe->type.synced()) - fe->type.sync(); - if (!fe->data.synced()) - fe->data.sync(); -#endif -} - -inline void -FrameState::syncType(FrameEntry *fe) -{ - FrameEntry *backing = fe; - if (fe->isCopy()) - backing = fe->copyOf(); - - if (!fe->type.synced() && backing->type.inMemory()) - tempRegForType(backing); - - ensureTypeSynced(fe, masm); - - if (!fe->type.synced()) - fe->type.sync(); -} - -inline void -FrameState::syncData(FrameEntry *fe) -{ - FrameEntry *backing = fe; - if (fe->isCopy()) - backing = fe->copyOf(); - - if (!fe->data.synced() && backing->data.inMemory()) - tempRegForData(backing); - - ensureDataSynced(fe, masm); - - if (!fe->data.synced()) - fe->data.sync(); + masm.storePayload(fe->data.reg(), to); } inline void @@ -666,17 +525,7 @@ FrameState::forgetType(FrameEntry *fe) if (!fe->isTypeKnown()) return; - /* - * Likewise, storeLocal() may have set this FE, with a known type, - * to be a copy of another FE, which has an unknown type. - * Just forget the type, since the backing is used in all cases. - */ - if (fe->isCopy()) { - fe->type.invalidate(); - return; - } - - ensureTypeSynced(fe, masm); + syncType(fe, addressOf(fe), masm); fe->type.setMemory(); } @@ -1003,7 +852,11 @@ FrameState::loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) cons return; } - ensureFeSynced(fe, masm); + if (!fe->data.synced()) + syncData(fe, addressOf(fe), masm); + if (!fe->type.synced()) + syncType(fe, addressOf(fe), masm); + masm.loadDouble(addressOf(fe), fpReg); } diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 30d2f08d75e9..5e269810e6db 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -121,10 +121,16 @@ FrameState::evictReg(RegisterID reg) FrameEntry *fe = regstate[reg].fe(); if (regstate[reg].type() == RematInfo::TYPE) { - ensureTypeSynced(fe, masm); + if (!fe->type.synced()) { + syncType(fe, addressOf(fe), masm); + fe->type.sync(); + } fe->type.setMemory(); } else { - ensureDataSynced(fe, masm); + if (!fe->data.synced()) { + syncData(fe, addressOf(fe), masm); + fe->data.sync(); + } fe->data.setMemory(); } } @@ -435,34 +441,23 @@ FrameState::sync(Assembler &masm, Uses uses) const return; /* Sync all registers up-front. */ - Registers allRegs(Registers::AvailRegs); - while (!allRegs.empty()) { - RegisterID reg = allRegs.takeAnyReg(); + for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) { + RegisterID reg = RegisterID(i); FrameEntry *fe = regstate[reg].usedBy(); if (!fe) continue; JS_ASSERT(fe->isTracked()); -#if defined JS_PUNBOX64 - /* Sync entire FE to prevent loads. */ - ensureFeSynced(fe, masm); - - /* Take the other register in the pair, if one exists. */ - if (regstate[reg].type() == RematInfo::DATA && fe->type.inRegister()) - allRegs.takeReg(fe->type.reg()); - else if (regstate[reg].type() == RematInfo::TYPE && fe->data.inRegister()) - allRegs.takeReg(fe->data.reg()); -#elif defined JS_NUNBOX32 - /* Sync register if unsynced. */ if (regstate[reg].type() == RematInfo::DATA) { JS_ASSERT(fe->data.reg() == reg); - ensureDataSynced(fe, masm); + if (!fe->data.synced()) + syncData(fe, addressOf(fe), masm); } else { JS_ASSERT(fe->type.reg() == reg); - ensureTypeSynced(fe, masm); + if (!fe->type.synced()) + syncType(fe, addressOf(fe), masm); } -#endif } /* @@ -478,37 +473,51 @@ FrameState::sync(Assembler &masm, Uses uses) const if (!fe->isTracked()) continue; - FrameEntry *backing = fe; + Address address = addressOf(fe); if (!fe->isCopy()) { + /* + * If this |fe| has registers, track them as available. They've + * already been synced. Otherwise, see if a constant needs to be + * synced. + */ if (fe->data.inRegister()) avail.putReg(fe->data.reg()); + else if (!fe->data.synced()) + syncData(fe, address, masm); + if (fe->type.inRegister()) avail.putReg(fe->type.reg()); + else if (!fe->type.synced()) + syncType(fe, address, masm); } else { - backing = fe->copyOf(); + FrameEntry *backing = fe->copyOf(); + JS_ASSERT(backing != fe); JS_ASSERT(!backing->isConstant() && !fe->isConstant()); - /* Fall back to a slower sync algorithm if load required. */ - if ((!fe->type.synced() && backing->type.inMemory()) || - (!fe->data.synced() && backing->data.inMemory())) { + /* + * If the copy is backed by something not in a register, fall back + * to a slower sync algorithm. + */ + if ((!fe->type.synced() && !backing->type.inRegister()) || + (!fe->data.synced() && !backing->data.inRegister())) { syncFancy(masm, avail, fe, bottom); return; } - } - /* If a part still needs syncing, it is either a copy or constant. */ -#if defined JS_PUNBOX64 - /* All register-backed FEs have been entirely synced up-front. */ - if (!fe->type.inRegister() && !fe->data.inRegister()) - ensureFeSynced(fe, masm); -#elif defined JS_NUNBOX32 - /* All components held in registers have been already synced. */ - if (!fe->data.inRegister()) - ensureDataSynced(fe, masm); - if (!fe->type.inRegister()) - ensureTypeSynced(fe, masm); -#endif + if (!fe->type.synced()) { + /* :TODO: we can do better, the type is learned for all copies. */ + if (fe->isTypeKnown()) { + //JS_ASSERT(fe->getTypeTag() == backing->getTypeTag()); + masm.storeTypeTag(ImmType(fe->getKnownType()), address); + } else { + masm.storeTypeTag(backing->type.reg(), address); + } + } + + if (!fe->data.synced()) + masm.storePayload(backing->data.reg(), address); + } } } @@ -527,35 +536,19 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) JS_ASSERT(fe->isTracked()); -#if defined JS_PUNBOX64 - /* Don't use syncFe(), since that may clobber more registers. */ - ensureFeSynced(fe, masm); - - if (!fe->type.synced()) - fe->type.sync(); - if (!fe->data.synced()) - fe->data.sync(); - - /* Take the other register in the pair, if one exists. */ if (regstate[reg].type() == RematInfo::DATA) { JS_ASSERT(fe->data.reg() == reg); - if (fe->type.inRegister() && search.hasReg(fe->type.reg())) - search.takeReg(fe->type.reg()); + if (!fe->data.synced()) { + syncData(fe, addressOf(fe), masm); + fe->data.sync(); + } } else { JS_ASSERT(fe->type.reg() == reg); - if (fe->data.inRegister() && search.hasReg(fe->data.reg())) - search.takeReg(fe->data.reg()); + if (!fe->type.synced()) { + syncType(fe, addressOf(fe), masm); + fe->type.sync(); + } } -#elif defined JS_NUNBOX32 - /* Sync this register. */ - if (regstate[reg].type() == RematInfo::DATA) { - JS_ASSERT(fe->data.reg() == reg); - syncData(fe); - } else { - JS_ASSERT(fe->type.reg() == reg); - syncType(fe); - } -#endif } uint32 maxvisits = tracker.nentries; @@ -570,18 +563,31 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore) if (fe >= spStop) continue; - syncFe(fe); + Address address = addressOf(fe); + FrameEntry *backing = fe; - /* Forget registers. */ - if (fe->data.inRegister() && kill.hasReg(fe->data.reg()) && - !regstate[fe->data.reg()].isPinned()) { - forgetReg(fe->data.reg()); - fe->data.setMemory(); + if (fe->isCopy()) + backing = fe->copyOf(); + + if (!fe->data.synced()) { + if (backing != fe && backing->data.inMemory()) + tempRegForData(backing); + syncData(backing, address, masm); + fe->data.sync(); + if (fe->data.inRegister() && kill.hasReg(fe->data.reg())) { + forgetReg(fe->data.reg()); + fe->data.setMemory(); + } } - if (fe->type.inRegister() && kill.hasReg(fe->type.reg()) && - !regstate[fe->type.reg()].isPinned()) { - forgetReg(fe->type.reg()); - fe->type.setMemory(); + if (!fe->type.synced()) { + if (backing != fe && backing->type.inMemory()) + tempRegForType(backing); + syncType(backing, address, masm); + fe->type.sync(); + if (fe->type.inRegister() && kill.hasReg(fe->type.reg())) { + forgetReg(fe->type.reg()); + fe->type.setMemory(); + } } } @@ -663,7 +669,8 @@ FrameState::copyDataIntoReg(FrameEntry *fe, RegisterID hint) RegisterID reg = fe->data.reg(); if (reg == hint) { if (freeRegs.empty()) { - ensureDataSynced(fe, masm); + if (!fe->data.synced()) + syncData(fe, addressOf(fe), masm); fe->data.setMemory(); } else { reg = allocReg(); @@ -691,7 +698,8 @@ FrameState::copyDataIntoReg(Assembler &masm, FrameEntry *fe) if (fe->data.inRegister()) { RegisterID reg = fe->data.reg(); if (freeRegs.empty()) { - ensureDataSynced(fe, masm); + if (!fe->data.synced()) + syncData(fe, addressOf(fe), masm); fe->data.setMemory(); regstate[reg].forget(); } else { @@ -723,7 +731,8 @@ FrameState::copyTypeIntoReg(FrameEntry *fe) if (fe->type.inRegister()) { RegisterID reg = fe->type.reg(); if (freeRegs.empty()) { - ensureTypeSynced(fe, masm); + if (!fe->type.synced()) + syncType(fe, addressOf(fe), masm); fe->type.setMemory(); regstate[reg].forget(); } else { @@ -775,9 +784,13 @@ FrameState::copyEntryIntoFPReg(Assembler &masm, FrameEntry *fe, FPRegisterID fpr if (fe->isCopy()) fe = fe->copyOf(); - ensureFeSynced(fe, masm); - masm.loadDouble(addressOf(fe), fpreg); + /* The entry must be synced to memory. */ + if (!fe->data.synced()) + syncData(fe, addressOf(fe), masm); + if (!fe->type.synced()) + syncType(fe, addressOf(fe), masm); + masm.loadDouble(addressOf(fe), fpreg); return fpreg; } @@ -797,7 +810,8 @@ FrameState::ownRegForType(FrameEntry *fe) if (freeRegs.empty()) { /* For now... just steal the register that already exists. */ - ensureTypeSynced(backing, masm); + if (!backing->type.synced()) + syncType(backing, addressOf(backing), masm); reg = backing->type.reg(); backing->type.setMemory(); regstate[reg].forget(); @@ -840,7 +854,8 @@ FrameState::ownRegForData(FrameEntry *fe) if (freeRegs.empty()) { /* For now... just steal the register that already exists. */ - ensureDataSynced(backing, masm); + if (!backing->data.synced()) + syncData(backing, addressOf(backing), masm); reg = backing->data.reg(); backing->data.setMemory(); regstate[reg].forget(); @@ -1091,14 +1106,33 @@ FrameState::storeLocal(uint32 n, bool popGuaranteed, bool typeChange) return; /* Ensure that the local variable remains synced. */ - syncFe(local); - - if (closed) { - /* If the FE can have registers, free them before resetting. */ - if (!local->isCopy()) + if (local->isCopy()) { + FrameEntry *backing = local->copyOf(); + if (!local->data.synced()) { + if (backing->data.inMemory()) + tempRegForData(backing); + syncData(backing, addressOf(local), masm); + } + if (!local->type.synced()) { + if (backing->type.inMemory()) + tempRegForType(backing); + syncType(backing, addressOf(local), masm); + } + } else { + if (!local->data.synced()) { + syncData(local, addressOf(local), masm); + local->data.sync(); + } + if (!local->type.synced()) { + syncType(local, addressOf(local), masm); + local->type.sync(); + } + if (closed) forgetEntry(local); - local->resetSynced(); } + + if (closed) + local->resetSynced(); } void @@ -1302,7 +1336,7 @@ FrameState::unpinEntry(const ValueRemat &vr) } void -FrameState::ensureValueSynced(Assembler &masm, FrameEntry *fe, const ValueRemat &vr) +FrameState::syncEntry(Assembler &masm, FrameEntry *fe, const ValueRemat &vr) { #if defined JS_PUNBOX64 if (!vr.isDataSynced || !vr.isTypeSynced) diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index a858fcf228e5..7612662880b7 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -408,6 +408,13 @@ class FrameState */ inline RegisterID tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const; + /* + * Forcibly loads the type tag for the specified FrameEntry + * into a register already marked as owning the type. + */ + inline void emitLoadTypeTag(FrameEntry *fe, RegisterID reg) const; + inline void emitLoadTypeTag(Assembler &masm, FrameEntry *fe, RegisterID reg) const; + /* * Convert an integer to a double without applying * additional Register pressure. @@ -483,7 +490,7 @@ class FrameState void unpinEntry(const ValueRemat &vr); /* Syncs fe to memory, given its state as constructed by a call to pinEntry. */ - void ensureValueSynced(Assembler &masm, FrameEntry *fe, const ValueRemat &vr); + void syncEntry(Assembler &masm, FrameEntry *fe, const ValueRemat &vr); struct BinaryAlloc { MaybeRegisterID lhsType; @@ -791,17 +798,8 @@ class FrameState void evictReg(RegisterID reg); inline FrameEntry *rawPush(); inline void addToTracker(FrameEntry *fe); - - /* Guarantee sync, but do not set any sync flag. */ - inline void ensureFeSynced(const FrameEntry *fe, Assembler &masm) const; - inline void ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const; - inline void ensureDataSynced(const FrameEntry *fe, Assembler &masm) const; - - /* Guarantee sync, even if register allocation is required, and set sync. */ - inline void syncFe(FrameEntry *fe); - inline void syncType(FrameEntry *fe); - inline void syncData(FrameEntry *fe); - + inline void syncType(const FrameEntry *fe, Address to, Assembler &masm) const; + inline void syncData(const FrameEntry *fe, Address to, Assembler &masm) const; inline FrameEntry *getLocal(uint32 slot); inline void forgetAllRegs(FrameEntry *fe); inline void swapInTracker(FrameEntry *lhs, FrameEntry *rhs); diff --git a/js/src/methodjit/PunboxAssembler.h b/js/src/methodjit/PunboxAssembler.h index 3e8f29bc3ae1..0fae0464db09 100644 --- a/js/src/methodjit/PunboxAssembler.h +++ b/js/src/methodjit/PunboxAssembler.h @@ -131,8 +131,8 @@ class Assembler : public BaseAssembler } void loadValueAsComponents(const Value &val, RegisterID type, RegisterID payload) { - move(Imm64(val.asRawBits() & JSVAL_TAG_MASK), type); - move(Imm64(val.asRawBits() & JSVAL_PAYLOAD_MASK), payload); + move(Imm64(val.asRawBits() & 0xFFFF800000000000), type); + move(Imm64(val.asRawBits() & 0x00007FFFFFFFFFFF), payload); } template From 699053914db77d1fcc720e448a2b14ff0b2c1b67 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Wed, 13 Oct 2010 11:17:21 -0700 Subject: [PATCH 275/284] Bug 604108 - Grab the right compartment to check if we're finalizing the functionNamespaceObject. r=peterv --- js/src/jsxml.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index e172b64ef0d1..8272fcf56709 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -232,8 +232,8 @@ DEFINE_GETTER(NameURI_getter, static void namespace_finalize(JSContext *cx, JSObject *obj) { - if (cx->compartment->functionNamespaceObject == obj) - cx->compartment->functionNamespaceObject = NULL; + if (obj->compartment()->functionNamespaceObject == obj) + obj->compartment()->functionNamespaceObject = NULL; } static JSBool From 31725d8b36fba9bb7810a3e6729a5c6c06de6b74 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Wed, 13 Oct 2010 11:37:25 -0700 Subject: [PATCH 276/284] Bug 603845 - Protect ourselves against compartments that have null principals. r=gal --- js/src/xpconnect/wrappers/AccessCheck.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index ea989b82fda5..e21da3dedba7 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -60,9 +60,17 @@ GetCompartmentPrincipal(JSCompartment *compartment) bool AccessCheck::isSameOrigin(JSCompartment *a, JSCompartment *b) { + nsIPrincipal *aprin = GetCompartmentPrincipal(a); + nsIPrincipal *bprin = GetCompartmentPrincipal(b); + + // If either a or b doesn't have principals, we don't have enough + // information to tell. Seeing as how this is Gecko, we are default-unsafe + // in this case. + if (!aprin || !bprin) + return true; + PRBool cond; - return NS_SUCCEEDED(GetCompartmentPrincipal(a)->Equals(GetCompartmentPrincipal(b), &cond)) && - cond; + return NS_SUCCEEDED(aprin->Equals(bprin, &cond)) && cond; } bool From b6bbf1729a92036dd526ed5a90e43207d4bc81d0 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Wed, 13 Oct 2010 11:49:22 -0700 Subject: [PATCH 277/284] Flexible length JSObject, bug 584917. r=brendan,igor --- js/src/jsapi-tests/testConservativeGC.cpp | 2 +- js/src/jsapi.cpp | 25 +- js/src/jsarray.cpp | 223 +++-------- js/src/jsarray.h | 9 - js/src/jsbuiltins.cpp | 7 +- js/src/jsbuiltins.h | 3 +- js/src/jscompartment.cpp | 26 +- js/src/jscompartment.h | 12 +- js/src/jsdate.cpp | 4 +- js/src/jsdbgapi.cpp | 10 +- js/src/jsemit.cpp | 18 +- js/src/jsfun.cpp | 106 ++--- js/src/jsfun.h | 15 +- js/src/jsgc.cpp | 352 ++++++---------- js/src/jsgc.h | 200 ++++++---- js/src/jsgcinlines.h | 76 +++- js/src/jsgcstats.cpp | 47 ++- js/src/jsinterp.cpp | 26 +- js/src/jsiter.cpp | 6 +- js/src/jslock.cpp | 3 +- js/src/jsobj.cpp | 291 +++++++------- js/src/jsobj.h | 263 ++++++------ js/src/jsobjinlines.h | 377 ++++++++++-------- js/src/jsopcode.cpp | 2 +- js/src/jsopcode.tbl | 2 +- js/src/jsproxy.cpp | 30 +- js/src/jsproxy.h | 10 +- js/src/jsregexp.h | 8 +- js/src/jsscope.cpp | 20 + js/src/jsscopeinlines.h | 40 +- js/src/jstracer.cpp | 173 ++++---- js/src/jstracer.h | 15 +- js/src/jsvalue.h | 12 + js/src/methodjit/BaseAssembler.h | 9 +- js/src/methodjit/Compiler.cpp | 30 +- js/src/methodjit/FastOps.cpp | 34 +- js/src/methodjit/MonoIC.cpp | 4 - js/src/methodjit/NunboxAssembler.h | 16 +- js/src/methodjit/PolyIC.cpp | 86 ++-- js/src/methodjit/PunboxAssembler.h | 16 +- js/src/methodjit/StubCalls.cpp | 16 +- js/src/methodjit/StubCalls.h | 4 +- js/src/shell/js.cpp | 2 +- .../trace-test/tests/basic/testBug597736.js | 6 +- .../tests/basic/testHoleInDenseArray.js | 3 +- .../jaeger/bug563000/eif-trap-typechange.js | 2 +- js/src/xpconnect/src/xpcprivate.h | 6 +- js/src/xpconnect/wrappers/XrayWrapper.cpp | 6 +- 48 files changed, 1286 insertions(+), 1367 deletions(-) diff --git a/js/src/jsapi-tests/testConservativeGC.cpp b/js/src/jsapi-tests/testConservativeGC.cpp index 3cce8b3bed12..45d785bba54e 100644 --- a/js/src/jsapi-tests/testConservativeGC.cpp +++ b/js/src/jsapi-tests/testConservativeGC.cpp @@ -52,7 +52,7 @@ bool checkObjectFields(JSObject *savedCopy, JSObject *obj) * doing memcmp. */ savedCopy->objShape = obj->objShape; - savedCopy->dslots = obj->dslots; + savedCopy->slots = obj->slots; CHECK(!memcmp(savedCopy, obj, sizeof(*obj))); return true; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 250400faf643..e5183b9adb25 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1246,7 +1246,8 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) // If the wrapper is in the same compartment as the destination, then // we know that we won't find wrapper in the destination's cross // compartment map and that the same object will continue to work. - wrapper->swap(target); + if (!wrapper->swap(cx, target)) + return NULL; return wrapper; } @@ -1261,7 +1262,8 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) // possibly security wrapper, innards). obj = &p->value.toObject(); map.remove(p); - obj->swap(target); + if (!obj->swap(cx, target)) + return NULL; } else { // Otherwise, this is going to be our outer window proxy in the new // compartment. @@ -1297,7 +1299,8 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) // entry in the compartment's wrapper map to point to the old // wrapper. JS_ASSERT(tobj != wobj); - wobj->swap(tobj); + if (!wobj->swap(cx, tobj)) + return NULL; pmap.put(targetv, ObjectValue(*wobj)); } } @@ -1308,7 +1311,8 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) JSObject *tobj = obj; if (!ac.enter(cx, wrapper) || !JS_WrapObject(cx, &tobj)) return NULL; - wrapper->swap(tobj); + if (!wrapper->swap(cx, tobj)) + return NULL; wrapper->getCompartment()->crossCompartmentWrappers.put(targetv, wrapperv); } @@ -3107,8 +3111,6 @@ JS_DeepFreezeObject(JSContext *cx, JSObject *obj) /* Walk slots in obj and if any value is a non-null object, seal it. */ for (uint32 i = 0, n = obj->slotSpan(); i < n; ++i) { const Value &v = obj->getSlot(i); - if (i == JSSLOT_PRIVATE && (obj->getClass()->flags & JSCLASS_HAS_PRIVATE)) - continue; if (v.isPrimitive()) continue; if (!JS_DeepFreezeObject(cx, &v.toObject())) @@ -3908,8 +3910,7 @@ JS_Enumerate(JSContext *cx, JSObject *obj) * + native case here uses a Shape *, but that iterates in reverse! * + so we make non-native match, by reverse-iterating after JS_Enumerating */ -const uint32 JSSLOT_ITER_INDEX = JSSLOT_PRIVATE + 1; -JS_STATIC_ASSERT(JSSLOT_ITER_INDEX < JS_INITIAL_NSLOTS); +const uint32 JSSLOT_ITER_INDEX = 0; static void prop_iter_finalize(JSContext *cx, JSObject *obj) @@ -3918,7 +3919,7 @@ prop_iter_finalize(JSContext *cx, JSObject *obj) if (!pdata) return; - if (obj->fslots[JSSLOT_ITER_INDEX].toInt32() >= 0) { + if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() >= 0) { /* Non-native case: destroy the ida enumerated when obj was created. */ JSIdArray *ida = (JSIdArray *) pdata; JS_DestroyIdArray(cx, ida); @@ -3932,7 +3933,7 @@ prop_iter_trace(JSTracer *trc, JSObject *obj) if (!pdata) return; - if (obj->fslots[JSSLOT_ITER_INDEX].toInt32() < 0) { + if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) { /* Native case: just mark the next property to visit. */ ((Shape *) pdata)->trace(trc); } else { @@ -3998,7 +3999,7 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj) /* iterobj cannot escape to other threads here. */ iterobj->setPrivate(const_cast(pdata)); - iterobj->fslots[JSSLOT_ITER_INDEX].setInt32(index); + iterobj->getSlotRef(JSSLOT_ITER_INDEX).setInt32(index); return iterobj; } @@ -4012,7 +4013,7 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) CHECK_REQUEST(cx); assertSameCompartment(cx, iterobj); - i = iterobj->fslots[JSSLOT_ITER_INDEX].toInt32(); + i = iterobj->getSlot(JSSLOT_ITER_INDEX).toInt32(); if (i < 0) { /* Native case: private data is a property tree node pointer. */ obj = iterobj->getParent(); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 69ad24e65084..dcb8c600ed8c 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -312,109 +312,6 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, return JS_TRUE; } -bool -JSObject::growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap) -{ - JS_ASSERT(isDenseArray()); - JS_ASSERT(newcap >= ARRAY_CAPACITY_MIN); - JS_ASSERT(newcap >= oldcap); - - if (newcap > MAX_DSLOTS_LENGTH32) { - if (!JS_ON_TRACE(cx)) - js_ReportAllocationOverflow(cx); - return JS_FALSE; - } - - /* dslots can be NULL during array creation. */ - Value *slots = dslots ? dslots - 1 : NULL; - Value *newslots = (Value *) cx->realloc(slots, (size_t(newcap) + 1) * sizeof(Value)); - if (!newslots) - return false; - - dslots = newslots + 1; - setDenseArrayCapacity(newcap); - - Value *base = addressOfDenseArrayElement(0); - for (Value *vp = base + oldcap, *end = base + newcap; vp < end; ++vp) - vp->setMagic(JS_ARRAY_HOLE); - - return true; -} - -bool -JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap) -{ - /* - * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to - * grow, double its capacity, to push() N elements in amortized O(N) time. - * - * Above this limit, grow by 12.5% each time. Speed is still amortized - * O(N), with a higher constant factor, and we waste less space. - */ - static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024; - - /* - * Round up all large allocations to a multiple of this (1MB), so as not - * to waste space if malloc gives us 1MB-sized chunks (as jemalloc does). - */ - static const size_t CAPACITY_CHUNK = 1024 * 1024 / sizeof(Value); - - uint32 oldcap = getDenseArrayCapacity(); - - if (newcap > oldcap) { - /* - * If this overflows uint32, newcap is very large. nextsize will end - * up being less than newcap, the code below will thus disregard it, - * and resizeDenseArrayElements() will fail. - * - * The way we use dslots[-1] forces a few +1s and -1s here. For - * example, (oldcap * 2 + 1) produces the sequence 7, 15, 31, 63, ... - * which makes the total allocation size (with dslots[-1]) a power - * of two. - */ - uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX) - ? oldcap * 2 + 1 - : oldcap + (oldcap >> 3); - - uint32 actualCapacity = JS_MAX(newcap, nextsize); - if (actualCapacity >= CAPACITY_CHUNK) - actualCapacity = JS_ROUNDUP(actualCapacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */ - else if (actualCapacity < ARRAY_CAPACITY_MIN) - actualCapacity = ARRAY_CAPACITY_MIN; - - if (!growDenseArrayElements(cx, oldcap, actualCapacity)) - return false; - } - return true; -} - -bool -JSObject::shrinkDenseArrayElements(JSContext *cx, uint32 newcap) -{ - JS_ASSERT(isDenseArray()); - JS_ASSERT(newcap < getDenseArrayCapacity()); - JS_ASSERT(dslots); - - uint32 fill = newcap; - - if (newcap < ARRAY_CAPACITY_MIN) - newcap = ARRAY_CAPACITY_MIN; - - Value *newslots = (Value *) cx->realloc(dslots - 1, (size_t(newcap) + 1) * sizeof(Value)); - if (!newslots) - return false; - - dslots = newslots + 1; - setDenseArrayCapacity(newcap); - - /* we refuse to shrink below a minimum value, so we have to clear the excess space */ - Value *base = addressOfDenseArrayElement(0); - while (fill < newcap) - base[fill++].setMagic(JS_ARRAY_HOLE); - - return true; -} - static bool ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp) { @@ -542,8 +439,8 @@ js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i) return true; if (INDEX_TOO_SPARSE(obj, u)) return false; - return obj->ensureDenseArrayElements(cx, u + 1); + return obj->ensureDenseArrayElements(cx, u + 1); } JS_DEFINE_CALLINFO_3(extern, BOOL, js_EnsureDenseArrayCapacity, CONTEXT, OBJECT, INT32, 0, nanojit::ACCSET_STORE_ANY) @@ -672,13 +569,13 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool str if (obj->isDenseArray()) { /* * Don't reallocate if we're not actually shrinking our slots. If we do - * shrink slots here, resizeDenseArrayElements will fill all slots to the + * shrink slots here, ensureDenseArrayElements will fill all slots to the * right of newlen with JS_ARRAY_HOLE. This permits us to disregard * length when reading from arrays as long we are within the capacity. */ jsuint oldcap = obj->getDenseArrayCapacity(); - if (oldcap > newlen && !obj->shrinkDenseArrayElements(cx, newlen)) - return false; + if (oldcap > newlen) + obj->shrinkDenseArrayElements(cx, newlen); obj->setArrayLength(newlen); } else if (oldlen - newlen < (1 << 24)) { do { @@ -986,20 +883,11 @@ array_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool return JS_TRUE; } -static void -array_finalize(JSContext *cx, JSObject *obj) -{ - obj->freeDenseArrayElements(cx); -} - static void array_trace(JSTracer *trc, JSObject *obj) { JS_ASSERT(obj->isDenseArray()); - if (!obj->dslots) - return; - size_t holes = 0; uint32 capacity = obj->getDenseArrayCapacity(); for (uint32 i = 0; i < capacity; i++) { @@ -1040,7 +928,7 @@ array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props) Class js_ArrayClass = { "Array", Class::NON_NATIVE | - JSCLASS_HAS_RESERVED_SLOTS(JSObject::DENSE_ARRAY_CLASS_RESERVED_SLOTS) | + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), PropertyStub, /* addProperty */ PropertyStub, /* delProperty */ @@ -1049,7 +937,7 @@ Class js_ArrayClass = { EnumerateStub, ResolveStub, js_TryValueOf, - array_finalize, + NULL, NULL, /* reserved0 */ NULL, /* checkAccess */ NULL, /* call */ @@ -1134,26 +1022,18 @@ JSObject::makeDenseArraySlow(JSContext *cx) * the same initial shape. */ JSObject *arrayProto = getProto(); - if (!InitScopeForObject(cx, this, &js_SlowArrayClass, arrayProto)) + if (!InitScopeForObject(cx, this, &js_SlowArrayClass, arrayProto, FINALIZE_OBJECT0)) return false; - uint32 capacity; + uint32 capacity = getDenseArrayCapacity(); - if (dslots) { - capacity = getDenseArrayCapacity(); - dslots[-1].setPrivateUint32(JS_INITIAL_NSLOTS + capacity); - } else { - /* - * Array.prototype is constructed as a dense array, but is immediately slowified before - * we have time to set capacity. - */ - capacity = 0; - } - - /* Begin with the length property to share more of the property tree. */ + /* + * Begin with the length property to share more of the property tree. + * The getter/setter here will directly access the object's private value. + */ if (!addProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), array_length_getter, NULL, - JSSLOT_ARRAY_LENGTH, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0)) { + SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0)) { setMap(oldMap); return false; } @@ -1171,24 +1051,12 @@ JSObject::makeDenseArraySlow(JSContext *cx) continue; } - /* Assert that the length covering i fits in the alloted bits. */ - JS_ASSERT(JS_INITIAL_NSLOTS + i + 1 < NSLOTS_LIMIT); - - if (!addDataProperty(cx, id, JS_INITIAL_NSLOTS + i, JSPROP_ENUMERATE)) { + if (!addDataProperty(cx, id, i, JSPROP_ENUMERATE)) { setMap(oldMap); return false; } } - /* - * Render our formerly-reserved non-private properties GC-safe. We do not - * need to make the length slot GC-safe because it is the private slot - * (this is statically asserted within JSObject) where the implementation - * can store an arbitrary value. - */ - JS_ASSERT(js_SlowArrayClass.flags & JSCLASS_HAS_PRIVATE); - voidDenseOnlyArraySlots(); - /* * Finally, update class. If |this| is Array.prototype, then js_InitClass * will create an emptyShape whose class is &js_SlowArrayClass, to ensure @@ -1530,7 +1398,6 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector JS_ASSERT(obj->isDenseArray()); obj->setArrayLength(length); - obj->setDenseArrayCapacity(0); if (!vector || !length) return true; if (!obj->ensureDenseArrayElements(cx, length)) @@ -2488,10 +2355,8 @@ array_concat(JSContext *cx, uintN argc, Value *vp) /* * Clone aobj but pass the minimum of its length and capacity, to * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In - * such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which - * will cause nobj to be over-allocated to 16. But in the normal case - * where length is <= capacity, nobj and aobj will have the same - * capacity. + * the normal case where length is <= capacity, nobj and aobj will have + * the same capacity. */ length = aobj->getArrayLength(); jsuint capacity = aobj->getDenseArrayCapacity(); @@ -3000,10 +2865,12 @@ static JSFunctionSpec array_static_methods[] = { JS_FS_END }; +/* The count here is a guess for the final capacity. */ static inline JSObject * -NewDenseArrayObject(JSContext *cx) +NewDenseArrayObject(JSContext *cx, jsuint count) { - return NewNonFunction(cx, &js_ArrayClass, NULL, NULL); + gc::FinalizeKind kind = GuessObjectGCKind(count, true); + return NewNonFunction(cx, &js_ArrayClass, NULL, NULL, kind); } JSBool @@ -3012,12 +2879,6 @@ js_Array(JSContext *cx, uintN argc, Value *vp) jsuint length; const Value *vector; - /* Whether called with 'new' or not, use a new Array object. */ - JSObject *obj = NewDenseArrayObject(cx); - if (!obj) - return JS_FALSE; - vp->setObject(*obj); - if (argc == 0) { length = 0; vector = NULL; @@ -3034,6 +2895,12 @@ js_Array(JSContext *cx, uintN argc, Value *vp) vector = NULL; } + /* Whether called with 'new' or not, use a new Array object. */ + JSObject *obj = NewDenseArrayObject(cx, length); + if (!obj) + return JS_FALSE; + vp->setObject(*obj); + return InitArrayObject(cx, obj, length, vector); } @@ -3045,15 +2912,15 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto, int32 len) JS_ASSERT(proto->isArray()); - JSObject* obj = js_NewGCObject(cx); + gc::FinalizeKind kind = GuessObjectGCKind(len, true); + JSObject* obj = js_NewGCObject(cx, kind); if (!obj) return NULL; - /* Initialize all fields, calling init before setting obj->map. */ - obj->init(&js_ArrayClass, proto, proto->getParent(), NullValue(), cx); + /* Initialize all fields of JSObject. */ + obj->init(cx, &js_ArrayClass, proto, proto->getParent(), + (void*) len, true); obj->setSharedNonNativeMap(); - obj->setArrayLength(len); - obj->setDenseArrayCapacity(0); return obj; } #ifdef JS_TRACER @@ -3067,7 +2934,7 @@ js_NewPreallocatedArray(JSContext* cx, JSObject* proto, int32 len) JSObject *obj = js_NewEmptyArray(cx, proto, len); if (!obj) return NULL; - if (!obj->growDenseArrayElements(cx, 0, JS_MAX(len, ARRAY_CAPACITY_MIN))) + if (!obj->ensureDenseArrayElements(cx, len)) return NULL; return obj; } @@ -3076,6 +2943,17 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewPreallocatedArray, CONTEXT, OBJECT, I 0, nanojit::ACCSET_STORE_ANY) #endif +JSObject* JS_FASTCALL +js_InitializerArray(JSContext* cx, int32 count) +{ + gc::FinalizeKind kind = GuessObjectGCKind(count, true); + return NewArrayWithKind(cx, kind); +} +#ifdef JS_TRACER +JS_DEFINE_CALLINFO_2(extern, OBJECT, js_InitializerArray, CONTEXT, INT32, 0, + nanojit::ACCSET_STORE_ANY) +#endif + JSObject * js_InitArrayClass(JSContext *cx, JSObject *obj) { @@ -3088,7 +2966,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) * Assert that js_InitClass used the correct (slow array, not dense array) * class for proto's emptyShape class. */ - JS_ASSERT(proto->emptyShape->getClass() == proto->getClass()); + JS_ASSERT(proto->emptyShapes && proto->emptyShapes[0]->getClass() == proto->getClass()); proto->setArrayLength(0); return proto; @@ -3097,7 +2975,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) JSObject * js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector) { - JSObject *obj = NewDenseArrayObject(cx); + JSObject *obj = NewDenseArrayObject(cx, length); if (!obj) return NULL; @@ -3220,7 +3098,7 @@ js_IsDensePrimitiveArray(JSObject *obj) jsuint capacity = obj->getDenseArrayCapacity(); for (jsuint i = 0; i < capacity; i++) { - if (obj->dslots[i].isObject()) + if (obj->getDenseArrayElement(i).isObject()) return JS_FALSE; } @@ -3244,10 +3122,9 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone) /* * Must use the minimum of original array's length and capacity, to handle - * |a = [1,2,3]; a.length = 10000| "dense" cases efficiently. In such a case - * we would use ARRAY_CAPACITY_MIN (not 3), which will cause the clone to be - * over-allocated. In the normal case where length is <= capacity the - * clone and original array will have the same capacity. + * |a = [1,2,3]; a.length = 10000| "dense" cases efficiently. In the normal + * case where length is <= capacity, the clone and original array will have + * the same capacity. */ jsuint jsvalCount = JS_MIN(obj->getDenseArrayCapacity(), length); @@ -3256,7 +3133,7 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone) return JS_FALSE; for (jsuint i = 0; i < jsvalCount; i++) { - const Value &val = obj->dslots[i]; + const Value &val = obj->getDenseArrayElement(i); if (val.isString()) { // Strings must be made immutable before being copied to a clone. diff --git a/js/src/jsarray.h b/js/src/jsarray.h index b878bd568346..5dfc685da955 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -46,8 +46,6 @@ #include "jspubtd.h" #include "jsobj.h" -#define ARRAY_CAPACITY_MIN 7 - extern JSBool js_StringIsIndex(JSString *str, jsuint *indexp); @@ -139,13 +137,6 @@ js_InitArrayClass(JSContext *cx, JSObject *obj); extern bool js_InitContextBusyArrayTable(JSContext *cx); -/* - * Creates a new array with the given length and proto (NB: NULL is not - * translated to Array.prototype), with len slots preallocated. - */ -extern JSObject * JS_FASTCALL -js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len); - extern JSObject * js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector); diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index ca2e9afd0235..eb328bcca18d 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -302,11 +302,14 @@ js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* pa JSFunction *fun = (JSFunction*) funobj; JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); - JSObject* closure = js_NewGCObject(cx); + JSObject* closure = js_NewGCObject(cx, gc::FINALIZE_OBJECT2); if (!closure) return NULL; - closure->initSharingEmptyShape(&js_FunctionClass, proto, parent, fun, cx); + if (!closure->initSharingEmptyShape(cx, &js_FunctionClass, proto, parent, + fun, gc::FINALIZE_OBJECT2)) { + return NULL; + } return closure; } JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index b959acbb8e3e..a7701af2644f 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -578,6 +578,7 @@ js_dmod(jsdouble a, jsdouble b); JS_DECLARE_CALLINFO(js_Array_dense_setelem_hole) JS_DECLARE_CALLINFO(js_NewEmptyArray) JS_DECLARE_CALLINFO(js_NewPreallocatedArray) +JS_DECLARE_CALLINFO(js_InitializerArray) JS_DECLARE_CALLINFO(js_ArrayCompPush_tn) JS_DECLARE_CALLINFO(js_EnsureDenseArrayCapacity) @@ -614,7 +615,7 @@ JS_DECLARE_CALLINFO(js_NumberToString) /* Defined in jsobj.cpp. */ JS_DECLARE_CALLINFO(js_Object_tn) JS_DECLARE_CALLINFO(js_CreateThisFromTrace) -JS_DECLARE_CALLINFO(js_NonEmptyObject) +JS_DECLARE_CALLINFO(js_InitializerObject) /* Defined in jsregexp.cpp. */ JS_DECLARE_CALLINFO(js_CloneRegExpObject) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 0b7e253db326..ce17a22a1549 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -66,15 +66,8 @@ bool JSCompartment::init() { chunk = NULL; - shortStringArena.init(); - stringArena.init(); - funArena.init(); -#if JS_HAS_XML_SUPPORT - xmlArena.init(); -#endif - objArena.init(); - for (unsigned i = 0; i < JS_EXTERNAL_STRING_LIMIT; i++) - externalStringArenas[i].init(); + for (unsigned i = 0; i < FINALIZE_LIMIT; i++) + arenas[i].init(); for (unsigned i = 0; i < FINALIZE_LIMIT; i++) freeLists.finalizables[i] = NULL; #ifdef JS_GCMETER @@ -86,21 +79,10 @@ JSCompartment::init() bool JSCompartment::arenaListsAreEmpty() { - bool empty = objArena.isEmpty() && - funArena.isEmpty() && -#if JS_HAS_XML_SUPPORT - xmlArena.isEmpty() && -#endif - shortStringArena.isEmpty() && - stringArena.isEmpty(); - if (!empty) - return false; - - for (unsigned i = 0; i < JS_EXTERNAL_STRING_LIMIT; i++) { - if (!externalStringArenas[i].isEmpty()) + for (unsigned i = 0; i < FINALIZE_LIMIT; i++) { + if (!arenas[i].isEmpty()) return false; } - return true; } diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index d2d17748841d..fcff8b620330 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -54,17 +54,9 @@ struct JS_FRIEND_API(JSCompartment) { JSPrincipals *principals; js::gc::Chunk *chunk; - js::gc::ArenaList objArena; - js::gc::ArenaList funArena; - js::gc::ArenaList shortStringArena; - js::gc::ArenaList stringArena; - js::gc::ArenaList externalStringArenas[js::gc::JS_EXTERNAL_STRING_LIMIT]; -#if JS_HAS_XML_SUPPORT - js::gc::ArenaList xmlArena; -#endif + js::gc::ArenaList arenas[js::gc::FINALIZE_LIMIT]; + js::gc::FreeLists freeLists; - js::gc::FreeLists freeLists; - #ifdef JS_GCMETER js::gc::JSGCArenaStats compartmentStats[js::gc::FINALIZE_LIMIT]; #endif diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 85bcc0086991..3e26e2c17a31 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -2563,7 +2563,9 @@ JS_FRIEND_API(JSObject *) js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) { JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass); - if (!obj || !SetUTCTime(cx, obj, msec_time)) + if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS)) + return NULL; + if (!SetUTCTime(cx, obj, msec_time)) return NULL; return obj; } diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 016ca1ef3b9c..a7df71e4068d 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -1667,15 +1667,7 @@ JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure) JS_PUBLIC_API(size_t) JS_GetObjectTotalSize(JSContext *cx, JSObject *obj) { - size_t nbytes = (obj->isFunction() && obj->getPrivate() == obj) - ? sizeof(JSFunction) - : sizeof *obj; - - if (obj->dslots) { - nbytes += (obj->dslots[-1].toPrivateUint32() - JS_INITIAL_NSLOTS + 1) - * sizeof obj->dslots[0]; - } - return nbytes; + return obj->slotsAndStructSize(); } static size_t diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 032139fcebce..ff7bd598d853 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -244,7 +244,7 @@ UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) JS_ASSERT(nuses == 0); blockObj = cg->objectList.lastbox->object; JS_ASSERT(blockObj->isStaticBlock()); - JS_ASSERT(blockObj->fslots[JSSLOT_BLOCK_DEPTH].isUndefined()); + JS_ASSERT(blockObj->getSlot(JSSLOT_BLOCK_DEPTH).isUndefined()); OBJ_SET_BLOCK_DEPTH(cx, blockObj, cg->stackDepth); ndefs = OBJ_BLOCK_COUNT(cx, blockObj); @@ -1650,9 +1650,8 @@ js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSStmtInfo *stmt JS_ASSERT(shape->hasShortID()); if (slotp) { - JS_ASSERT(obj->fslots[JSSLOT_BLOCK_DEPTH].isInt32()); - *slotp = obj->fslots[JSSLOT_BLOCK_DEPTH].toInt32() + - shape->shortid; + JS_ASSERT(obj->getSlot(JSSLOT_BLOCK_DEPTH).isInt32()); + *slotp = obj->getSlot(JSSLOT_BLOCK_DEPTH).toInt32() + shape->shortid; } return stmt; } @@ -4460,8 +4459,15 @@ EmitFunctionDefNop(JSContext *cx, JSCodeGenerator *cg, uintN index) static bool EmitNewInit(JSContext *cx, JSCodeGenerator *cg, JSProtoKey key, JSParseNode *pn, int sharpnum) { - if (js_Emit2(cx, cg, JSOP_NEWINIT, (jsbytecode) key) < 0) - return false; + /* + * Watch for overflow on the initializer size. This isn't problematic because + * (a) we'll be reporting an error for the initializer shortly, and (b) + * the count is only used as a hint for the interpreter and JITs, and does not + * need to be correct. + */ + uint16 count = (pn->pn_count >= JS_BIT(16)) ? JS_BIT(16) - 1 : pn->pn_count; + + EMIT_UINT16PAIR_IMM_OP(JSOP_NEWINIT, (uint16) key, count); #if JS_HAS_SHARP_VARS if (cg->hasSharps()) { if (pn->pn_count != 0) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 27ff9256c1a6..b40cd6b00674 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -183,7 +183,8 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee) if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) return NULL; - JSObject *argsobj = js_NewGCObject(cx); + JS_STATIC_ASSERT(JSObject::ARGS_CLASS_RESERVED_SLOTS == 2); + JSObject *argsobj = js_NewGCObject(cx, FINALIZE_OBJECT2); if (!argsobj) return NULL; @@ -194,10 +195,10 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee) SetValueRangeToUndefined(data->slots, argc); /* Can't fail from here on, so initialize everything in argsobj. */ - argsobj->init(callee.getFunctionPrivate()->inStrictMode() + argsobj->init(cx, callee.getFunctionPrivate()->inStrictMode() ? &StrictArgumentsClass : &js_ArgumentsClass, - proto, parent, NULL, cx); + proto, parent, NULL, false); argsobj->setMap(cx->runtime->emptyArgumentsShape); @@ -972,15 +973,20 @@ CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp) static JSObject * NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &callee) { - JSObject *callobj = js_NewGCObject(cx); + size_t vars = fun->countArgsAndVars(); + size_t slots = JSObject::CALL_RESERVED_SLOTS + vars; + gc::FinalizeKind kind = gc::GetGCObjectKind(slots); + + JSObject *callobj = js_NewGCObject(cx, kind); if (!callobj) return NULL; - callobj->init(&js_CallClass, NULL, &scopeChain, NULL, cx); + /* Init immediately to avoid GC seeing a half-init'ed object. */ + callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false); callobj->setMap(fun->u.i.names); /* This must come after callobj->lastProp has been set. */ - if (!callobj->ensureInstanceReservedSlots(cx, fun->countArgsAndVars())) + if (!callobj->ensureInstanceReservedSlots(cx, vars)) return NULL; #ifdef DEBUG @@ -1000,11 +1006,11 @@ NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &ca static inline JSObject * NewDeclEnvObject(JSContext *cx, JSStackFrame *fp) { - JSObject *envobj = js_NewGCObject(cx); + JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2); if (!envobj) return NULL; - envobj->init(&js_DeclEnvClass, NULL, &fp->scopeChain(), fp, cx); + envobj->init(cx, &js_DeclEnvClass, NULL, &fp->scopeChain(), fp, false); envobj->setMap(cx->runtime->emptyDeclEnvShape); return envobj; } @@ -1079,34 +1085,10 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTI inline static void CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, Value *slots) { - /* Copy however many args fit into fslots. */ - uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1; - JS_ASSERT(first <= JS_INITIAL_NSLOTS); - - Value *vp = &callobj.fslots[first]; - uintN len = Min(nargs, uintN(JS_INITIAL_NSLOTS) - first); - - memcpy(vp, argv, len * sizeof(Value)); - vp += len; - - nargs -= len; - if (nargs != 0) { - /* Copy any remaining args into dslots. */ - vp = callobj.dslots; - memcpy(vp, argv + len, nargs * sizeof(Value)); - vp += nargs; - } else { - /* Copy however many vars fit into any remaining fslots. */ - first += len; - len = JS_MIN(nvars, JS_INITIAL_NSLOTS - first); - memcpy(vp, slots, len * sizeof(Value)); - slots += len; - nvars -= len; - vp = callobj.dslots; - } - - /* Copy any remaining vars into dslots. */ - memcpy(vp, slots, nvars * sizeof(Value)); + JS_ASSERT(callobj.numSlots() >= JSObject::CALL_RESERVED_SLOTS + nargs + nvars); + Value *base = callobj.getSlots() + JSObject::CALL_RESERVED_SLOTS; + memcpy(base, argv, nargs * sizeof(Value)); + memcpy(base + nargs, slots, nvars * sizeof(Value)); } void @@ -1125,22 +1107,13 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) JS_ASSERT(fun == callobj.getCallObjCalleeFunction()); uintN n = fun->countArgsAndVars(); - /* - * Since for a call object all fixed slots happen to be taken, we can copy - * arguments and variables straight into JSObject.dslots. - */ - JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE == - 1 + JSObject::CALL_RESERVED_SLOTS); if (n != 0) { - JS_ASSERT(JSFunction::FIRST_FREE_SLOT + n <= callobj.numSlots()); + JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots()); uint32 nargs = fun->nargs; uint32 nvars = fun->u.i.nvars; - JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1); - JSScript *script = fun->u.i.script; - memcpy(callobj.dslots, fp->formalArgs(), nargs * sizeof(Value)); if (script->usesEval #ifdef JS_METHODJIT || script->debugMode @@ -1156,13 +1129,13 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) uint32 nclosed = script->nClosedArgs; for (uint32 i = 0; i < nclosed; i++) { uint32 e = script->getClosedArg(i); - callobj.dslots[e] = fp->formalArg(e); + callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e)); } nclosed = script->nClosedVars; for (uint32 i = 0; i < nclosed; i++) { uint32 e = script->getClosedVar(i); - callobj.dslots[nargs + e] = fp->slots()[e]; + callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]); } } } @@ -1255,16 +1228,13 @@ CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, Value *vp, } if (!fp) { + i += JSObject::CALL_RESERVED_SLOTS; if (kind == JSCPK_VAR) i += fun->nargs; else JS_ASSERT(kind == JSCPK_ARG); - const uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1; - JS_ASSERT(first == JSSLOT_FREE(&js_CallClass)); - JS_ASSERT(first <= JS_INITIAL_NSLOTS); - - array = (i < JS_INITIAL_NSLOTS - first) ? obj->fslots : obj->dslots; + array = obj->getSlots(); } else if (kind == JSCPK_ARG) { array = fp->formalArgs(); } else { @@ -1410,14 +1380,11 @@ call_trace(JSTracer *trc, JSObject *obj) * cycles involving Call objects whose frames are active without this * hiding hack. */ - uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1; - JS_ASSERT(first <= JS_INITIAL_NSLOTS); - + uintN first = JSObject::CALL_RESERVED_SLOTS; uintN count = fp->fun()->countArgsAndVars(); - uintN fixed = JS_MIN(count, JS_INITIAL_NSLOTS - first); - SetValueRangeToUndefined(&obj->fslots[first], fixed); - SetValueRangeToUndefined(obj->dslots, count - fixed); + JS_ASSERT(obj->numSlots() >= first + count); + SetValueRangeToUndefined(obj->getSlots() + first, count); } MaybeMarkGenerator(trc, obj); @@ -2418,8 +2385,8 @@ JSObject::initBoundFunction(JSContext *cx, const Value &thisArg, JS_ASSERT(isFunction()); flags |= JSObject::BOUND_FUNCTION; - fslots[JSSLOT_BOUND_FUNCTION_THIS] = thisArg; - fslots[JSSLOT_BOUND_FUNCTION_ARGS_COUNT].setPrivateUint32(argslen); + getSlotRef(JSSLOT_BOUND_FUNCTION_THIS) = thisArg; + getSlotRef(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).setPrivateUint32(argslen); if (argslen != 0) { /* FIXME? Burn memory on an empty scope whose shape covers the args slots. */ EmptyShape *empty = EmptyShape::create(cx, clasp); @@ -2432,9 +2399,8 @@ JSObject::initBoundFunction(JSContext *cx, const Value &thisArg, if (!ensureInstanceReservedSlots(cx, argslen)) return false; - JS_ASSERT(dslots); - JS_ASSERT(dslots[-1].toPrivateUint32() >= argslen); - memcpy(&dslots[0], args, argslen * sizeof(Value)); + JS_ASSERT(numSlots() >= argslen + FUN_CLASS_RESERVED_SLOTS); + memcpy(getSlots() + FUN_CLASS_RESERVED_SLOTS, args, argslen * sizeof(Value)); } return true; } @@ -2455,7 +2421,7 @@ JSObject::getBoundFunctionThis() const JS_ASSERT(isFunction()); JS_ASSERT(isBoundFunction()); - return fslots[JSSLOT_BOUND_FUNCTION_THIS]; + return getSlot(JSSLOT_BOUND_FUNCTION_THIS); } inline const js::Value * @@ -2464,10 +2430,10 @@ JSObject::getBoundFunctionArguments(uintN &argslen) const JS_ASSERT(isFunction()); JS_ASSERT(isBoundFunction()); - argslen = fslots[JSSLOT_BOUND_FUNCTION_ARGS_COUNT].toPrivateUint32(); - JS_ASSERT_IF(argslen > 0, dslots); - JS_ASSERT_IF(argslen > 0, dslots[-1].toPrivateUint32() >= argslen); - return &dslots[0]; + argslen = getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32(); + JS_ASSERT_IF(argslen > 0, numSlots() >= argslen); + + return getSlots() + FUN_CLASS_RESERVED_SLOTS; } namespace { @@ -3174,7 +3140,7 @@ JSFunction::addLocal(JSContext *cx, JSAtom *atom, JSLocalKind kind) uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED; uint16 *indexp; PropertyOp getter, setter; - uint32 slot = JSSLOT_START(&js_CallClass) + JSObject::CALL_RESERVED_SLOTS; + uint32 slot = JSObject::CALL_RESERVED_SLOTS; if (kind == JSLOCAL_ARG) { JS_ASSERT(u.i.nupvars == 0); diff --git a/js/src/jsfun.h b/js/src/jsfun.h index b0787aa70c79..e97f9bec758b 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -136,8 +136,10 @@ enum JSLocalKind { JSLOCAL_UPVAR }; -struct JSFunction : public JSObject +struct JSFunction : public JSObject_Slots2 { + /* Functions always have two fixed slots (FUN_CLASS_RESERVED_SLOTS). */ + uint16 nargs; /* maximum number of specified arguments, reflected as f.length/f.arity */ uint16 flags; /* flags, see JSFUN_* below and in jsapi.h */ @@ -277,7 +279,7 @@ struct JSFunction : public JSObject public: void setJoinable() { JS_ASSERT(FUN_INTERPRETED(this)); - fslots[METHOD_ATOM_SLOT].setNull(); + getSlotRef(METHOD_ATOM_SLOT).setNull(); flags |= JSFUN_JOINABLE; } @@ -287,14 +289,14 @@ struct JSFunction : public JSObject * flattened upvars. */ JSAtom *methodAtom() const { - return (joinable() && fslots[METHOD_ATOM_SLOT].isString()) - ? STRING_TO_ATOM(fslots[METHOD_ATOM_SLOT].toString()) + return (joinable() && getSlot(METHOD_ATOM_SLOT).isString()) + ? STRING_TO_ATOM(getSlot(METHOD_ATOM_SLOT).toString()) : NULL; } void setMethodAtom(JSAtom *atom) { JS_ASSERT(joinable()); - fslots[METHOD_ATOM_SLOT].setString(ATOM_TO_STRING(atom)); + getSlotRef(METHOD_ATOM_SLOT).setString(ATOM_TO_STRING(atom)); } js::Native maybeNative() const { @@ -306,9 +308,8 @@ struct JSFunction : public JSObject return u.i.script; } - /* Number of extra fixed function object slots besides JSSLOT_PRIVATE. */ + /* Number of extra fixed function object slots. */ static const uint32 CLASS_RESERVED_SLOTS = JSObject::FUN_CLASS_RESERVED_SLOTS; - static const uint32 FIRST_FREE_SLOT = JSSLOT_PRIVATE + CLASS_RESERVED_SLOTS + 1; }; /* diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index fed0e77e4361..635135ce451d 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -157,6 +157,17 @@ JS_STATIC_ASSERT(sizeof(Arena) == 4096); namespace js{ namespace gc{ +/* This array should be const, but that doesn't link right under GCC. */ +FinalizeKind slotsToThingKind[] = { + /* 0 */ FINALIZE_OBJECT0, FINALIZE_OBJECT2, FINALIZE_OBJECT2, FINALIZE_OBJECT4, + /* 4 */ FINALIZE_OBJECT4, FINALIZE_OBJECT8, FINALIZE_OBJECT8, FINALIZE_OBJECT8, + /* 8 */ FINALIZE_OBJECT8, FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12, + /* 12 */ FINALIZE_OBJECT12, FINALIZE_OBJECT16, FINALIZE_OBJECT16, FINALIZE_OBJECT16, + /* 16 */ FINALIZE_OBJECT16 +}; + +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT); + /* Initialize the arena and setup the free list. */ template void @@ -202,52 +213,12 @@ Arena::inFreeList(void *thing) const return false; } -template -inline T * -Arena::getAlignedThing(T *thing) -{ - jsuword start = reinterpret_cast(&t.things[0]); - jsuword offset = reinterpret_cast(thing) - start; - offset -= offset % aheader.thingSize; - return reinterpret_cast(start + offset); -} - -#ifdef DEBUG -template -bool -Arena::assureThingIsAligned(T *thing) -{ - return (getAlignedThing(thing) == thing); -} - -template -bool -Arena::assureThingIsAligned(JSObject *thing); - -template -bool -Arena::assureThingIsAligned(JSFunction *thing); - -template -bool -Arena::assureThingIsAligned(JSString *thing); - -template -bool -Arena::assureThingIsAligned(JSShortString *thing); - -#if JS_HAS_XML_SUPPORT -template -bool -Arena::assureThingIsAligned(JSXML *thing); -#endif - -#endif - template inline ConservativeGCTest Arena::mark(T *thing, JSTracer *trc) { + JS_ASSERT(sizeof(T) == aheader.thingSize); + thing = getAlignedThing(thing); if (thing > &t.things[ThingsPerArena-1].t || thing < &t.things[0].t) @@ -267,119 +238,35 @@ Arena::mark(T *thing, JSTracer *trc) #ifdef DEBUG bool checkArenaListsForThing(JSCompartment *comp, void *thing) { - if (comp->objArena.arenasContainThing(thing) || - comp->funArena.arenasContainThing(thing) || + if (comp->arenas[FINALIZE_OBJECT0].arenasContainThing(thing) || + comp->arenas[FINALIZE_OBJECT2].arenasContainThing(thing) || + comp->arenas[FINALIZE_OBJECT4].arenasContainThing(thing) || + comp->arenas[FINALIZE_OBJECT8].arenasContainThing(thing) || + comp->arenas[FINALIZE_OBJECT12].arenasContainThing(thing) || + comp->arenas[FINALIZE_OBJECT16].arenasContainThing(thing) || + comp->arenas[FINALIZE_FUNCTION].arenasContainThing(thing) || #if JS_HAS_XML_SUPPORT - comp->xmlArena.arenasContainThing(thing) || + comp->arenas[FINALIZE_XML].arenasContainThing(thing) || #endif - comp->shortStringArena.arenasContainThing(thing) || - comp->stringArena.arenasContainThing(thing)) { + comp->arenas[FINALIZE_SHORT_STRING].arenasContainThing(thing)) { return true; } - for (unsigned i = 0; i < JS_EXTERNAL_STRING_LIMIT; i++) { - if (comp->externalStringArenas[i].arenasContainThing(thing)) + for (unsigned i = FINALIZE_STRING; i <= FINALIZE_EXTERNAL_STRING_LAST; i++) { + if (comp->arenas[i].arenasContainThing(thing)) return true; } return false; } #endif -template -void -EmptyArenaLists::insert(Arena *arena) { - Arena *a = reinterpret_cast *>(arena); - a->header()->next = cellFreeList; - cellFreeList = a; -} - -template<> -void -EmptyArenaLists::insert(Arena *arena) { - arena->header()->next = objectFreeList; - objectFreeList = arena; -} - -template<> -void -EmptyArenaLists::insert(Arena *arena) { - arena->header()->next = functionFreeList; - functionFreeList = arena; -} - -template<> -void -EmptyArenaLists::insert(Arena *arena) { - arena->header()->next = stringFreeList; - stringFreeList = arena; -} - -template<> -void -EmptyArenaLists::insert(Arena *arena) { - arena->header()->next = shortStringFreeList; - shortStringFreeList = arena; -} - -template -Arena *EmptyArenaLists::getTypedFreeList() { - return NULL; -} - -template<> -Arena *EmptyArenaLists::getTypedFreeList() { - Arena *arena = objectFreeList; - if (arena) { - objectFreeList = arena->header()->next; - return arena; - } - return NULL; -} - -template<> -Arena *EmptyArenaLists::getTypedFreeList() { - Arena *arena = stringFreeList; - if (arena) { - stringFreeList = arena->header()->next; - return arena; - } - return NULL; -} - -template<> -Arena *EmptyArenaLists::getTypedFreeList() { - Arena *arena = shortStringFreeList; - if (arena) { - shortStringFreeList = arena->header()->next; - return arena; - } - return NULL; -} - -template<> -Arena *EmptyArenaLists::getTypedFreeList() { - Arena *arena = functionFreeList; - if (arena) { - functionFreeList = arena->header()->next; - return arena; - } - return NULL; -} - } /* namespace gc */ } /* namespace js */ void JSCompartment::finishArenaLists() { - objArena.releaseAll(); - funArena.releaseAll(); - shortStringArena.releaseAll(); - stringArena.releaseAll(); -#if JS_HAS_XML_SUPPORT - xmlArena.releaseAll(); -#endif - for (unsigned i = 0; i < 8; i++) - externalStringArenas[i].releaseAll(); + for (int i = 0; i < FINALIZE_LIMIT; i++) + arenas[i].releaseAll(); } void @@ -454,7 +341,7 @@ Chunk::releaseArena(Arena *arena) JS_ASSERT(rt->gcBytes >= sizeof(Arena)); rt->gcBytes -= sizeof(Arena); - info.emptyArenaLists.insert(arena); + info.emptyArenaLists.insert((Arena *)arena); arena->header()->isUsed = false; ++info.numFree; if (unused()) @@ -683,6 +570,13 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) namespace js { +template +static inline ConservativeGCTest +MarkCell(Cell *cell, JSTracer *trc) +{ + return GetArena(cell)->mark((T *)cell, trc); +} + /* * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets traceKind * accordingly. Otherwise returns the reason for rejection. @@ -732,7 +626,7 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w, uint32 &traceKind) if (!chunk->withinArenasRange(cell)) return CGCT_NOTARENA; - ArenaHeader *aheader = cell->arena()->header(); + ArenaHeader *aheader = cell->arena()->header(); if (!aheader->isUsed) return CGCT_FREEARENA; @@ -741,8 +635,23 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w, uint32 &traceKind) traceKind = aheader->thingKind; switch (traceKind) { - case FINALIZE_OBJECT: - test = GetArena(cell)->mark((JSObject *)cell, trc); + case FINALIZE_OBJECT0: + test = MarkCell(cell, trc); + break; + case FINALIZE_OBJECT2: + test = MarkCell(cell, trc); + break; + case FINALIZE_OBJECT4: + test = MarkCell(cell, trc); + break; + case FINALIZE_OBJECT8: + test = MarkCell(cell, trc); + break; + case FINALIZE_OBJECT12: + test = MarkCell(cell, trc); + break; + case FINALIZE_OBJECT16: + test = MarkCell(cell, trc); break; case FINALIZE_STRING: case FINALIZE_EXTERNAL_STRING0: @@ -753,17 +662,17 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w, uint32 &traceKind) case FINALIZE_EXTERNAL_STRING5: case FINALIZE_EXTERNAL_STRING6: case FINALIZE_EXTERNAL_STRING7: - test = GetArena(cell)->mark((JSString *)cell, trc); + test = MarkCell(cell, trc); break; case FINALIZE_SHORT_STRING: - test = GetArena(cell)->mark((JSShortString *)cell, trc); + test = MarkCell(cell, trc); break; case FINALIZE_FUNCTION: - test = GetArena(cell)->mark((JSFunction *)cell, trc); + test = MarkCell(cell, trc); break; #if JS_HAS_XML_SUPPORT case FINALIZE_XML: - test = GetArena(cell)->mark((JSXML *)cell, trc); + test = MarkCell(cell, trc); break; #endif default: @@ -1142,50 +1051,12 @@ IsGCThresholdReached(JSRuntime *rt) struct JSShortString; -template -ArenaList * -GetFinalizableArenaList(JSCompartment *c, unsigned thingKind); - -template <> -ArenaList * -GetFinalizableArenaList(JSCompartment *c, unsigned thingKind) { - JS_ASSERT(thingKind == FINALIZE_OBJECT); - return &c->objArena; +ArenaList * +GetFinalizableArenaList(JSCompartment *c, unsigned thingKind) { + JS_ASSERT(thingKind < FINALIZE_LIMIT); + return &c->arenas[thingKind]; } -template <> -ArenaList * -GetFinalizableArenaList(JSCompartment *c, unsigned thingKind) { - JS_ASSERT(thingKind >= FINALIZE_STRING && thingKind <= FINALIZE_EXTERNAL_STRING_LAST); - - if (JS_LIKELY(thingKind == FINALIZE_STRING)) - return &c->stringArena; - return &c->externalStringArenas[thingKind - FINALIZE_EXTERNAL_STRING0]; -} - -template <> -ArenaList * -GetFinalizableArenaList(JSCompartment *c, unsigned thingKind) { - JS_ASSERT(thingKind == FINALIZE_SHORT_STRING); - return &c->shortStringArena; -} - -template <> -ArenaList * -GetFinalizableArenaList(JSCompartment *c, unsigned thingKind) { - JS_ASSERT(thingKind == FINALIZE_FUNCTION); - return &c->funArena; -} - -#if JS_HAS_XML_SUPPORT -template <> -ArenaList * -GetFinalizableArenaList(JSCompartment *c, unsigned thingKind) { - JS_ASSERT(thingKind == FINALIZE_XML); - return &c->xmlArena; -} -#endif - #ifdef DEBUG bool CheckAllocation(JSContext *cx) @@ -1199,15 +1070,15 @@ CheckAllocation(JSContext *cx) #endif template -bool -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) +inline bool +RefillTypedFreeList(JSContext *cx, unsigned thingKind) { JSCompartment *compartment = cx->compartment; JS_ASSERT_IF(compartment->freeLists.finalizables[thingKind], !*compartment->freeLists.finalizables[thingKind]); JSRuntime *rt = cx->runtime; - ArenaList *arenaList; + ArenaList *arenaList; Arena *a; JS_ASSERT(!rt->gcRunning); @@ -1217,7 +1088,7 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota; bool doGC = canGC && IsGCThresholdReached(rt); - arenaList = GetFinalizableArenaList(cx->compartment, thingKind); + arenaList = GetFinalizableArenaList(cx->compartment, thingKind); do { if (doGC) { JS_ASSERT(!JS_ON_TRACE(cx)); @@ -1238,15 +1109,16 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) if (compartment->freeLists.finalizables[thingKind]) return true; } - if ((a = arenaList->getNextWithFreeList())) { + if ((a = (Arena *) arenaList->getNextWithFreeList())) { JS_ASSERT(a->header()->freeList); + JS_ASSERT(sizeof(T) == a->header()->thingSize); compartment->freeLists.populate(a, thingKind); return true; } a = AllocateArena(cx, thingKind); if (a) { compartment->freeLists.populate(a, thingKind); - arenaList->insert(a); + arenaList->insert((Arena *) a); a->getMarkingDelay()->init(); return true; } @@ -1259,27 +1131,45 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) } while (true); } -template bool -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); - -template -bool -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); - -template -bool -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); - -template -bool -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); - +RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) +{ + switch (thingKind) { + case FINALIZE_OBJECT0: + return RefillTypedFreeList(cx, thingKind); + case FINALIZE_OBJECT2: + return RefillTypedFreeList(cx, thingKind); + case FINALIZE_OBJECT4: + return RefillTypedFreeList(cx, thingKind); + case FINALIZE_OBJECT8: + return RefillTypedFreeList(cx, thingKind); + case FINALIZE_OBJECT12: + return RefillTypedFreeList(cx, thingKind); + case FINALIZE_OBJECT16: + return RefillTypedFreeList(cx, thingKind); + case FINALIZE_STRING: + case FINALIZE_EXTERNAL_STRING0: + case FINALIZE_EXTERNAL_STRING1: + case FINALIZE_EXTERNAL_STRING2: + case FINALIZE_EXTERNAL_STRING3: + case FINALIZE_EXTERNAL_STRING4: + case FINALIZE_EXTERNAL_STRING5: + case FINALIZE_EXTERNAL_STRING6: + case FINALIZE_EXTERNAL_STRING7: + return RefillTypedFreeList(cx, thingKind); + case FINALIZE_SHORT_STRING: + return RefillTypedFreeList(cx, thingKind); + case FINALIZE_FUNCTION: + return RefillTypedFreeList(cx, thingKind); #if JS_HAS_XML_SUPPORT -template -bool -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); + case FINALIZE_XML: + return RefillTypedFreeList(cx, thingKind); #endif + default: + JS_NOT_REACHED("bad finalize kind"); + return false; + } +} intN js_GetExternalStringGCType(JSString *str) { @@ -1455,9 +1345,24 @@ GCMarker::markDelayedChildren() #endif switch (a->header()->thingKind) { - case FINALIZE_OBJECT: + case FINALIZE_OBJECT0: reinterpret_cast *>(a)->markDelayedChildren(this); break; + case FINALIZE_OBJECT2: + reinterpret_cast *>(a)->markDelayedChildren(this); + break; + case FINALIZE_OBJECT4: + reinterpret_cast *>(a)->markDelayedChildren(this); + break; + case FINALIZE_OBJECT8: + reinterpret_cast *>(a)->markDelayedChildren(this); + break; + case FINALIZE_OBJECT12: + reinterpret_cast *>(a)->markDelayedChildren(this); + break; + case FINALIZE_OBJECT16: + reinterpret_cast *>(a)->markDelayedChildren(this); + break; case FINALIZE_STRING: case FINALIZE_EXTERNAL_STRING0: case FINALIZE_EXTERNAL_STRING1: @@ -1929,9 +1834,9 @@ static void FinalizeArenaList(JSCompartment *comp, JSContext *cx, unsigned thingKind) { JS_STATIC_ASSERT(!(sizeof(T) & Cell::CellMask)); - ArenaList *arenaList = GetFinalizableArenaList(comp, thingKind); - Arena **ap = &arenaList->head; - Arena *a = *ap; + ArenaList *arenaList = GetFinalizableArenaList(comp, thingKind); + Arena **ap = &arenaList->head; + Arena *a = (Arena *) *ap; if (!a) return; JS_ASSERT(sizeof(T) == arenaList->head->header()->thingSize); @@ -1940,7 +1845,7 @@ FinalizeArenaList(JSCompartment *comp, JSContext *cx, unsigned thingKind) uint32 nlivearenas = 0, nkilledarenas = 0, nthings = 0; #endif for (;;) { - ArenaHeader *header = a->header(); + ArenaHeader *header = a->header(); JS_ASSERT_IF(header->hasFreeThings, header->freeList); JS_ASSERT(header->thingKind == thingKind); JS_ASSERT(!a->getMarkingDelay()->link); @@ -2018,7 +1923,7 @@ FinalizeArenaList(JSCompartment *comp, JSContext *cx, unsigned thingKind) #endif *ap = (header->next); JS_ASSERT((T *)header->freeList == &a->t.things[0].t); - a->chunk()->releaseArena((Arena *)a); + a->chunk()->releaseArena(a); METER(nkilledarenas++); } else { JS_ASSERT(nfree < a->ThingsPerArena); @@ -2030,7 +1935,7 @@ FinalizeArenaList(JSCompartment *comp, JSContext *cx, unsigned thingKind) ap = &header->next; METER(nlivearenas++); } - if (!(a = *ap)) + if (!(a = (Arena *) *ap)) break; } arenaList->cursor = arenaList->head; @@ -2336,7 +2241,12 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM) */ for (JSCompartment **comp = rt->compartments.begin(); comp != rt->compartments.end(); comp++) { - FinalizeArenaList(*comp, cx, FINALIZE_OBJECT); + FinalizeArenaList(*comp, cx, FINALIZE_OBJECT0); + FinalizeArenaList(*comp, cx, FINALIZE_OBJECT2); + FinalizeArenaList(*comp, cx, FINALIZE_OBJECT4); + FinalizeArenaList(*comp, cx, FINALIZE_OBJECT8); + FinalizeArenaList(*comp, cx, FINALIZE_OBJECT12); + FinalizeArenaList(*comp, cx, FINALIZE_OBJECT16); FinalizeArenaList(*comp, cx, FINALIZE_FUNCTION); #if JS_HAS_XML_SUPPORT FinalizeArenaList(*comp, cx, FINALIZE_XML); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 43cdca85e5f7..d6a00df5c137 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -72,11 +72,43 @@ js_TraceXML(JSTracer *trc, JSXML* thing); namespace js { namespace gc { +/* + * The kind of GC thing with a finalizer. The external strings follow the + * ordinary string to simplify js_GetExternalStringGCType. + */ +enum FinalizeKind { + FINALIZE_OBJECT0, + FINALIZE_OBJECT2, + FINALIZE_OBJECT4, + FINALIZE_OBJECT8, + FINALIZE_OBJECT12, + FINALIZE_OBJECT16, + FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16, + FINALIZE_FUNCTION, +#if JS_HAS_XML_SUPPORT + FINALIZE_XML, +#endif + FINALIZE_SHORT_STRING, + FINALIZE_STRING, + FINALIZE_EXTERNAL_STRING0, + FINALIZE_EXTERNAL_STRING1, + FINALIZE_EXTERNAL_STRING2, + FINALIZE_EXTERNAL_STRING3, + FINALIZE_EXTERNAL_STRING4, + FINALIZE_EXTERNAL_STRING5, + FINALIZE_EXTERNAL_STRING6, + FINALIZE_EXTERNAL_STRING7, + FINALIZE_EXTERNAL_STRING_LAST = FINALIZE_EXTERNAL_STRING7, + FINALIZE_LIMIT +}; + +const uintN JS_FINALIZE_OBJECT_LIMIT = 6; +const uintN JS_EXTERNAL_STRING_LIMIT = 8; + /* Every arena has a header. */ -template struct ArenaHeader { JSCompartment *compartment; - Arena *next; + Arena *next; FreeCell *freeList; unsigned thingKind; bool isUsed; @@ -108,12 +140,12 @@ struct Arena { static const size_t ArenaSize = 4096; struct AlignedArenaHeader { - T align[(sizeof(ArenaHeader) + sizeof(T) - 1) / sizeof(T)]; + T align[(sizeof(ArenaHeader) + sizeof(T) - 1) / sizeof(T)]; }; /* We want things in the arena to be aligned, so align the header. */ union { - ArenaHeader aheader; + ArenaHeader aheader; AlignedArenaHeader align; }; @@ -124,7 +156,7 @@ struct Arena { inline Chunk *chunk() const; inline size_t arenaIndex() const; - inline ArenaHeader *header() { return &aheader; }; + inline ArenaHeader *header() { return &aheader; }; inline MarkingDelay *getMarkingDelay() const; inline ArenaBitmap *bitmap() const; @@ -132,9 +164,9 @@ struct Arena { inline ConservativeGCTest mark(T *thing, JSTracer *trc); void markDelayedChildren(JSTracer *trc); inline bool inFreeList(void *thing) const; - inline T *getAlignedThing(T *thing); + inline T *getAlignedThing(void *thing); #ifdef DEBUG - bool assureThingIsAligned(T *thing); + inline bool assureThingIsAligned(void *thing); #endif void init(JSCompartment *compartment, unsigned thingKind); @@ -198,55 +230,60 @@ struct MarkingDelay { }; struct EmptyArenaLists { - Arena *cellFreeList; - Arena *objectFreeList; - Arena *stringFreeList; - Arena *shortStringFreeList; - Arena *functionFreeList; + /* Arenas with no internal freelist prepared. */ + Arena *cellFreeList; + + /* Arenas with internal freelists prepared for a given finalize kind. */ + Arena *freeLists[FINALIZE_LIMIT]; void init() { - cellFreeList = NULL; - objectFreeList = NULL; - stringFreeList = NULL; - shortStringFreeList = NULL; - functionFreeList = NULL; + PodZero(this); } Arena *getOtherArena() { - Arena *arena = NULL; - if ((arena = (Arena *)cellFreeList)) { - cellFreeList = cellFreeList->header()->next; - return arena; - } else if ((arena = (Arena *)objectFreeList)) { - objectFreeList = objectFreeList->header()->next; - return arena; - } else if ((arena = (Arena *)stringFreeList)) { - stringFreeList = stringFreeList->header()->next; - return arena; - } else if ((arena = (Arena *)shortStringFreeList)) { - shortStringFreeList = shortStringFreeList->header()->next; - return arena; - } else { - JS_ASSERT(functionFreeList); - arena = (Arena *)functionFreeList; - functionFreeList = functionFreeList->header()->next; + Arena *arena = cellFreeList; + if (arena) { + cellFreeList = arena->header()->next; return arena; } + for (int i = 0; i < FINALIZE_LIMIT; i++) { + if ((arena = (Arena *) freeLists[i])) { + freeLists[i] = freeLists[i]->header()->next; + return arena; + } + } + JS_NOT_REACHED("No arena"); + return NULL; } template - Arena *getTypedFreeList(); + inline Arena *getTypedFreeList(unsigned thingKind); template - Arena *getNext(JSCompartment *comp, unsigned thingKind); + inline Arena *getNext(JSCompartment *comp, unsigned thingKind); template - void insert(Arena *arena); + inline void insert(Arena *arena); }; +template +inline Arena * +EmptyArenaLists::getTypedFreeList(unsigned thingKind) { + JS_ASSERT(thingKind < FINALIZE_LIMIT); + if (thingKind >= FINALIZE_EXTERNAL_STRING0) + thingKind = FINALIZE_STRING; + Arena *arena = (Arena*) freeLists[thingKind]; + if (arena) { + freeLists[thingKind] = freeLists[thingKind]->header()->next; + return arena; + } + return NULL; +} + template -Arena *EmptyArenaLists::getNext(JSCompartment *comp, unsigned thingKind) { - Arena *arena = getTypedFreeList(); +inline Arena * +EmptyArenaLists::getNext(JSCompartment *comp, unsigned thingKind) { + Arena *arena = getTypedFreeList(thingKind); if (arena) { JS_ASSERT(arena->header()->isUsed == false); JS_ASSERT(arena->header()->thingSize == sizeof(T)); @@ -261,6 +298,17 @@ Arena *EmptyArenaLists::getNext(JSCompartment *comp, unsigned thingKind) { return arena; } +template +inline void +EmptyArenaLists::insert(Arena *arena) { + unsigned thingKind = arena->header()->thingKind; + JS_ASSERT(thingKind < FINALIZE_LIMIT); + if (thingKind >= FINALIZE_EXTERNAL_STRING0) + thingKind = FINALIZE_STRING; + arena->header()->next = freeLists[thingKind]; + freeLists[thingKind] = (Arena *) arena; +} + /* The chunk header (located at the end of the chunk to preserve arena alignment). */ struct ChunkInfo { Chunk *link; @@ -366,6 +414,25 @@ Arena::bitmap() const return &chunk()->bitmaps[arenaIndex()]; } +template +inline T * +Arena::getAlignedThing(void *thing) +{ + jsuword start = reinterpret_cast(&t.things[0]); + jsuword offset = reinterpret_cast(thing) - start; + offset -= offset % aheader.thingSize; + return reinterpret_cast(start + offset); +} + +#ifdef DEBUG +template +inline bool +Arena::assureThingIsAligned(void *thing) +{ + return (getAlignedThing(thing) == thing); +} +#endif + static void AssertValidColor(const void *thing, uint32 color) { @@ -400,30 +467,6 @@ GetArena(Cell *cell) return reinterpret_cast *>(cell->arena()); } -/* - * The kind of GC thing with a finalizer. The external strings follow the - * ordinary string to simplify js_GetExternalStringGCType. - */ -enum JSFinalizeGCThingKind { - FINALIZE_OBJECT, - FINALIZE_FUNCTION, -#if JS_HAS_XML_SUPPORT - FINALIZE_XML, -#endif - FINALIZE_SHORT_STRING, - FINALIZE_STRING, - FINALIZE_EXTERNAL_STRING0, - FINALIZE_EXTERNAL_STRING1, - FINALIZE_EXTERNAL_STRING2, - FINALIZE_EXTERNAL_STRING3, - FINALIZE_EXTERNAL_STRING4, - FINALIZE_EXTERNAL_STRING5, - FINALIZE_EXTERNAL_STRING6, - FINALIZE_EXTERNAL_STRING7, - FINALIZE_EXTERNAL_STRING_LAST = FINALIZE_EXTERNAL_STRING7, - FINALIZE_LIMIT -}; - #define JSTRACE_XML 2 /* @@ -443,15 +486,18 @@ const size_t GC_ARENA_ALLOCATION_TRIGGER = 30 * js::GC_CHUNK_SIZE; */ const float GC_HEAP_GROWTH_FACTOR = 3; -const uintN JS_EXTERNAL_STRING_LIMIT = 8; - static inline size_t GetFinalizableTraceKind(size_t thingKind) { JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8); static const uint8 map[FINALIZE_LIMIT] = { - JSTRACE_OBJECT, /* FINALIZE_OBJECT */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT2 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT4 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT8 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT12 */ + JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */ JSTRACE_OBJECT, /* FINALIZE_FUNCTION */ #if JS_HAS_XML_SUPPORT /* FINALIZE_XML */ JSTRACE_XML, @@ -515,22 +561,22 @@ extern bool checkArenaListsForThing(JSCompartment *comp, jsuword thing); #endif -template +/* The arenas in a list have uniform kind. */ struct ArenaList { - Arena *head; /* list start */ - Arena *cursor; /* arena with free things */ + Arena *head; /* list start */ + Arena *cursor; /* arena with free things */ inline void init() { head = NULL; cursor = NULL; } - inline Arena *getNextWithFreeList() { - Arena *a; + inline Arena *getNextWithFreeList() { + Arena *a; while (cursor != NULL) { - ArenaHeader *aheader = cursor->header(); + ArenaHeader *aheader = cursor->header(); a = cursor; - cursor = (Arena *)aheader->next; + cursor = aheader->next; if (aheader->freeList) return a; } @@ -538,8 +584,9 @@ struct ArenaList { } #ifdef DEBUG + template bool arenasContainThing(void *thing) { - for (Arena *a = head; a; a = (Arena *)a->header()->next) { + for (Arena *a = (Arena *) head; a; a = (Arena *) a->header()->next) { JS_ASSERT(a->header()->isUsed); if (thing >= &a->t.things[0] && thing < &a->t.things[a->ThingsPerArena]) return true; @@ -548,14 +595,14 @@ struct ArenaList { } #endif - inline void insert(Arena *a) { + inline void insert(Arena *a) { a->header()->next = head; head = a; } void releaseAll() { while (head) { - Arena *next = head->header()->next; + Arena *next = head->header()->next; head->chunk()->releaseArena(head); head = next; } @@ -668,7 +715,6 @@ CheckGCFreeListLink(js::gc::FreeCell *cell) JS_ASSERT_IF(cell->link, cell < cell->link); } -template extern bool RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 454c46690b69..162aab29b721 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -55,6 +55,50 @@ # define METER_IF(condition, x) ((void) 0) #endif +namespace js { +namespace gc { + +/* Capacity for slotsToThingKind */ +const size_t SLOTS_TO_THING_KIND_LIMIT = 17; + +/* Get the best kind to use when making an object with the given slot count. */ +static inline FinalizeKind +GetGCObjectKind(size_t numSlots) +{ + extern FinalizeKind slotsToThingKind[]; + + if (numSlots >= SLOTS_TO_THING_KIND_LIMIT) + return FINALIZE_OBJECT0; + return slotsToThingKind[numSlots]; +} + +/* Get the number of fixed slots and initial capacity associated with a kind. */ +static inline size_t +GetGCKindSlots(FinalizeKind thingKind) +{ + /* Using a switch in hopes that thingKind will usually be a compile-time constant. */ + switch (thingKind) { + case FINALIZE_OBJECT0: + return 0; + case FINALIZE_OBJECT2: + return 2; + case FINALIZE_OBJECT4: + return 4; + case FINALIZE_OBJECT8: + return 8; + case FINALIZE_OBJECT12: + return 12; + case FINALIZE_OBJECT16: + return 16; + default: + JS_NOT_REACHED("Bad object finalize kind"); + return 0; + } +} + +} /* namespace gc */ +} /* namespace js */ + /* * Allocates a new GC thing. After a successful allocation the caller must * fully initialize the thing before calling any function that can potentially @@ -80,7 +124,7 @@ NewFinalizableGCThing(JSContext *cx, unsigned thingKind) CheckGCFreeListLink(cell); return (T *)cell; } - if (!RefillFinalizableFreeList(cx, thingKind)) + if (!RefillFinalizableFreeList(cx, thingKind)) return NULL; } while (true); } @@ -89,9 +133,13 @@ NewFinalizableGCThing(JSContext *cx, unsigned thingKind) #undef METER_IF inline JSObject * -js_NewGCObject(JSContext *cx) +js_NewGCObject(JSContext *cx, js::gc::FinalizeKind kind) { - return NewFinalizableGCThing(cx, js::gc::FINALIZE_OBJECT); + JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST); + JSObject *obj = NewFinalizableGCThing(cx, kind); + if (obj) + obj->capacity = js::gc::GetGCKindSlots(kind); + return obj; } inline JSString * @@ -117,7 +165,10 @@ js_NewGCExternalString(JSContext *cx, uintN type) inline JSFunction* js_NewGCFunction(JSContext *cx) { - return NewFinalizableGCThing(cx, js::gc::FINALIZE_FUNCTION); + JSFunction *fun = NewFinalizableGCThing(cx, js::gc::FINALIZE_FUNCTION); + if (fun) + fun->capacity = JSObject::FUN_CLASS_RESERVED_SLOTS; + return fun; } #if JS_HAS_XML_SUPPORT @@ -180,7 +231,12 @@ MarkObject(JSTracer *trc, JSObject &obj, const char *name) JS_ASSERT(&obj); JS_SET_TRACING_NAME(trc, name); JS_ASSERT(GetArena((Cell *)&obj)->assureThingIsAligned(&obj) || - GetArena((Cell *)&obj)->assureThingIsAligned((JSFunction *)&obj)); + GetArena((Cell *)&obj)->assureThingIsAligned(&obj) || + GetArena((Cell *)&obj)->assureThingIsAligned(&obj) || + GetArena((Cell *)&obj)->assureThingIsAligned(&obj) || + GetArena((Cell *)&obj)->assureThingIsAligned(&obj) || + GetArena((Cell *)&obj)->assureThingIsAligned(&obj) || + GetArena((Cell *)&obj)->assureThingIsAligned(&obj)); Mark(trc, &obj); } @@ -196,8 +252,14 @@ MarkChildren(JSTracer *trc, JSObject *obj) MarkObject(trc, *proto, "proto"); if (JSObject *parent = obj->getParent()) MarkObject(trc, *parent, "parent"); - if (obj->emptyShape) - obj->emptyShape->trace(trc); + + if (obj->emptyShapes) { + int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1; + for (int i = 0; i < count; i++) { + if (obj->emptyShapes[i]) + obj->emptyShapes[i]->trace(trc); + } + } /* Delegate to ops or the native marking op. */ TraceOp op = obj->getOps()->trace; diff --git a/js/src/jsgcstats.cpp b/js/src/jsgcstats.cpp index 000a4aa7a20b..c333cab649ae 100644 --- a/js/src/jsgcstats.cpp +++ b/js/src/jsgcstats.cpp @@ -111,7 +111,12 @@ UpdateCompartmentStats(JSCompartment *comp, unsigned thingKind, uint32 nlivearen } static const char *const GC_ARENA_NAMES[] = { - "object", + "object_0", + "object_2", + "object_4", + "object_8", + "object_12", + "object_16", "function", #if JS_HAS_XML_SUPPORT "xml", @@ -129,12 +134,34 @@ static const char *const GC_ARENA_NAMES[] = { }; JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GC_ARENA_NAMES) == FINALIZE_LIMIT); +template +static inline void +GetSizeAndThings(size_t &thingSize, size_t &thingsPerArena) +{ + thingSize = sizeof(T); + thingsPerArena = Arena::ThingsPerArena; +} + void GetSizeAndThingsPerArena(int thingKind, size_t &thingSize, size_t &thingsPerArena) { switch (thingKind) { - case FINALIZE_OBJECT: - thingSize = sizeof(JSObject); - thingsPerArena = Arena::ThingsPerArena; + case FINALIZE_OBJECT0: + GetSizeAndThings(thingSize, thingsPerArena); + break; + case FINALIZE_OBJECT2: + GetSizeAndThings(thingSize, thingsPerArena); + break; + case FINALIZE_OBJECT4: + GetSizeAndThings(thingSize, thingsPerArena); + break; + case FINALIZE_OBJECT8: + GetSizeAndThings(thingSize, thingsPerArena); + break; + case FINALIZE_OBJECT12: + GetSizeAndThings(thingSize, thingsPerArena); + break; + case FINALIZE_OBJECT16: + GetSizeAndThings(thingSize, thingsPerArena); break; case FINALIZE_STRING: case FINALIZE_EXTERNAL_STRING0: @@ -145,21 +172,17 @@ void GetSizeAndThingsPerArena(int thingKind, size_t &thingSize, size_t &thingsPe case FINALIZE_EXTERNAL_STRING5: case FINALIZE_EXTERNAL_STRING6: case FINALIZE_EXTERNAL_STRING7: - thingSize = sizeof(JSString); - thingsPerArena = Arena::ThingsPerArena; + GetSizeAndThings(thingSize, thingsPerArena); break; case FINALIZE_SHORT_STRING: - thingSize = sizeof(JSShortString); - thingsPerArena = Arena::ThingsPerArena; + GetSizeAndThings(thingSize, thingsPerArena); break; case FINALIZE_FUNCTION: - thingSize = sizeof(JSFunction); - thingsPerArena = Arena::ThingsPerArena; + GetSizeAndThings(thingSize, thingsPerArena); break; #if JS_HAS_XML_SUPPORT case FINALIZE_XML: - thingSize = sizeof(JSXML); - thingsPerArena = Arena::ThingsPerArena; + GetSizeAndThings(thingSize, thingsPerArena); break; #endif default: diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 279c7312905f..6105de381916 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -512,8 +512,8 @@ ComputeThisFromArgv(JSContext *cx, Value *argv) #if JS_HAS_NO_SUCH_METHOD -const uint32 JSSLOT_FOUND_FUNCTION = JSSLOT_PRIVATE; -const uint32 JSSLOT_SAVED_ID = JSSLOT_PRIVATE + 1; +const uint32 JSSLOT_FOUND_FUNCTION = 0; +const uint32 JSSLOT_SAVED_ID = 1; Class js_NoSuchMethodClass = { "NoSuchMethod", @@ -564,7 +564,7 @@ js_OnUnknownMethod(JSContext *cx, Value *vp) vp[0] = IdToValue(id); } #endif - obj = js_NewGCObject(cx); + obj = js_NewGCObject(cx, FINALIZE_OBJECT2); if (!obj) return false; @@ -575,8 +575,9 @@ js_OnUnknownMethod(JSContext *cx, Value *vp) * NoSuchMethod helper objects own no manually allocated resources. */ obj->map = NULL; - obj->init(&js_NoSuchMethodClass, NULL, NULL, tvr.value(), cx); - obj->fslots[JSSLOT_SAVED_ID] = vp[0]; + obj->init(cx, &js_NoSuchMethodClass, NULL, NULL, NULL, false); + obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value()); + obj->setSlot(JSSLOT_SAVED_ID, vp[0]); vp[0].setObject(*obj); } return true; @@ -594,9 +595,9 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags) JSObject *obj = &vp[0].toObject(); JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass); - args.callee() = obj->fslots[JSSLOT_FOUND_FUNCTION]; + args.callee() = obj->getSlot(JSSLOT_FOUND_FUNCTION); args.thisv() = vp[1]; - args[0] = obj->fslots[JSSLOT_SAVED_ID]; + args[0] = obj->getSlot(JSSLOT_SAVED_ID); JSObject *argsobj = js_NewArrayObject(cx, argc, vp + 2); if (!argsobj) return JS_FALSE; @@ -5890,15 +5891,20 @@ END_CASE(JSOP_NEWARRAY) BEGIN_CASE(JSOP_NEWINIT) { - jsint i = GET_INT8(regs.pc); + jsint i = GET_UINT16(regs.pc); + jsint count = GET_UINT16(regs.pc + UINT16_LEN); + JS_ASSERT(i == JSProto_Array || i == JSProto_Object); JSObject *obj; + + gc::FinalizeKind kind = GuessObjectGCKind(count, i == JSProto_Array); + if (i == JSProto_Array) { - obj = js_NewArrayObject(cx, 0, NULL); + obj = NewArrayWithKind(cx, kind); if (!obj) goto error; } else { - obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) goto error; } diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index b66ecd9cbe0c..e24ee6b58f2d 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -289,7 +289,7 @@ EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uint if (pobj->getArrayLength() > 0) { size_t capacity = pobj->getDenseArrayCapacity(); - Value *vp = pobj->dslots; + Value *vp = pobj->getDenseArrayElements(); for (size_t i = 0; i < capacity; ++i, ++vp) { if (!vp->isMagic(JS_ARRAY_HOLE)) { /* Dense arrays never get so large that i would not fit into an integer id. */ @@ -460,10 +460,10 @@ NewIteratorObject(JSContext *cx, uintN flags) * helper objects) expect it to have a non-null map pointer, so we * share an empty Enumerator scope in the runtime. */ - JSObject *obj = js_NewGCObject(cx); + JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT0); if (!obj) return false; - obj->init(&js_IteratorClass, NULL, NULL, NullValue(), cx); + obj->init(cx, &js_IteratorClass, NULL, NULL, NULL, false); obj->setMap(cx->runtime->emptyEnumeratorShape); return obj; } diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp index 807951ff86b1..4a7ebee04487 100644 --- a/js/src/jslock.cpp +++ b/js/src/jslock.cpp @@ -507,8 +507,7 @@ FinishSharingTitle(JSContext *cx, JSTitle *title) JSObject *obj = TITLE_TO_OBJECT(title); if (obj) { uint32 nslots = obj->slotSpan(); - JS_ASSERT(nslots >= JSSLOT_START(obj->getClass())); - for (uint32 i = JSSLOT_START(obj->getClass()); i != nslots; ++i) { + for (uint32 i = 0; i != nslots; ++i) { Value v = obj->getSlot(i); if (v.isString() && !js_MakeStringImmutable(cx, v.toString())) { diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 156219f1c3bc..9cc01a859837 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2758,7 +2758,8 @@ js_Object(JSContext *cx, uintN argc, Value *vp) if (!obj) { /* Make an object whether this was called with 'new' or not. */ JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined()); - obj = NewBuiltinClassInstance(cx, &js_ObjectClass); + gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) return JS_FALSE; } @@ -2784,13 +2785,15 @@ js_CreateThis(JSContext *cx, JSObject *callee) JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL; JSObject *parent = callee->getParent(); - return NewObject(cx, newclasp, proto, parent); + gc::FinalizeKind kind = NewObjectGCKind(cx, newclasp); + return NewObject(cx, newclasp, proto, parent, kind); } JSObject * js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto) { - return NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent()); + gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + return NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent(), kind); } JSObject * @@ -2810,15 +2813,17 @@ js_CreateThisForFunction(JSContext *cx, JSObject *callee) static JS_ALWAYS_INLINE JSObject* NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto, - const Value &privateSlotValue) + /*gc::FinalizeKind*/ unsigned _kind) { JS_ASSERT(clasp->isNative()); + gc::FinalizeKind kind = gc::FinalizeKind(_kind); - JSObject* obj = js_NewGCObject(cx); + JSObject* obj = js_NewGCObject(cx, kind); if (!obj) return NULL; - obj->initSharingEmptyShape(clasp, proto, proto->getParent(), privateSlotValue, cx); + if (!obj->initSharingEmptyShape(cx, clasp, proto, proto->getParent(), NULL, kind)) + return NULL; return obj; } @@ -2826,8 +2831,7 @@ JSObject* FASTCALL js_Object_tn(JSContext* cx, JSObject* proto) { JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE)); - - return NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue()); + return NewObjectWithClassProto(cx, &js_ObjectClass, proto, FINALIZE_OBJECT8); } JS_DEFINE_TRCINFO_1(js_Object, @@ -2835,22 +2839,24 @@ JS_DEFINE_TRCINFO_1(js_Object, nanojit::ACCSET_STORE_ANY))) JSObject* FASTCALL -js_NonEmptyObject(JSContext* cx, JSObject* proto) +js_InitializerObject(JSContext* cx, int32 count) { - JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE)); - - JSObject *obj = NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue()); - return (obj && obj->ensureClassReservedSlotsForEmptyObject(cx)) ? obj : NULL; + gc::FinalizeKind kind = GuessObjectGCKind(count, false); + return NewBuiltinClassInstance(cx, &js_ObjectClass, kind); } -JS_DEFINE_CALLINFO_2(extern, CONSTRUCTOR_RETRY, js_NonEmptyObject, CONTEXT, CALLEE_PROTOTYPE, 0, +JS_DEFINE_CALLINFO_2(extern, OBJECT, js_InitializerObject, CONTEXT, INT32, 0, nanojit::ACCSET_STORE_ANY) JSObject* FASTCALL js_String_tn(JSContext* cx, JSObject* proto, JSString* str) { JS_ASSERT(JS_ON_TRACE(cx)); - return NewObjectWithClassProto(cx, &js_StringClass, proto, StringValue(str)); + JSObject *obj = NewObjectWithClassProto(cx, &js_StringClass, proto, FINALIZE_OBJECT2); + if (!obj) + return NULL; + obj->setPrimitiveThis(StringValue(str)); + return obj; } JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0, nanojit::ACCSET_STORE_ANY) @@ -2904,7 +2910,8 @@ js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor) * FIXME: 561785 at least. Quasi-natives including XML objects prevent us * from easily or unconditionally calling NewNativeClassInstance here. */ - return NewNonFunction(cx, clasp, proto, parent); + gc::FinalizeKind kind = NewObjectGCKind(cx, clasp); + return NewNonFunction(cx, clasp, proto, parent, kind); } JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0, @@ -3126,11 +3133,13 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) { JSObject *obj; - obj = js_NewGCObject(cx); + obj = js_NewGCObject(cx, FINALIZE_OBJECT2); if (!obj) return NULL; - obj->init(&js_WithClass, proto, parent, js_FloatingFrameIfGenerator(cx, cx->fp()), cx); + JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp()); + + obj->init(cx, &js_WithClass, proto, parent, priv, false); obj->setMap(cx->runtime->emptyWithShape); OBJ_SET_BLOCK_DEPTH(cx, obj, depth); @@ -3152,11 +3161,11 @@ js_NewBlockObject(JSContext *cx) * Null obj's proto slot so that Object.prototype.* does not pollute block * scopes and to give the block object its own scope. */ - JSObject *blockObj = js_NewGCObject(cx); + JSObject *blockObj = js_NewGCObject(cx, FINALIZE_OBJECT2); if (!blockObj) return NULL; - blockObj->init(&js_BlockClass, NULL, NULL, NullValue(), cx); + blockObj->init(cx, &js_BlockClass, NULL, NULL, NULL, false); blockObj->setMap(cx->runtime->emptyBlockShape); return blockObj; } @@ -3166,20 +3175,24 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp) { JS_ASSERT(proto->isStaticBlock()); - JSObject *clone = js_NewGCObject(cx); + size_t count = OBJ_BLOCK_COUNT(cx, proto); + gc::FinalizeKind kind = gc::GetGCObjectKind(count + 1); + + JSObject *clone = js_NewGCObject(cx, kind); if (!clone) return NULL; JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, fp); /* The caller sets parent on its own. */ - clone->init(&js_BlockClass, proto, NULL, priv, cx); - clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH]; + clone->init(cx, &js_BlockClass, proto, NULL, priv, false); clone->setMap(proto->map); - if (!clone->ensureInstanceReservedSlots(cx, OBJ_BLOCK_COUNT(cx, proto))) + if (!clone->ensureInstanceReservedSlots(cx, count + 1)) return NULL; + clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH)); + JS_ASSERT(clone->isClonedBlock()); return clone; } @@ -3187,9 +3200,6 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp) JS_REQUIRES_STACK JSBool js_PutBlockObject(JSContext *cx, JSBool normalUnwind) { - /* Blocks have one fixed slot available for the first local.*/ - JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2); - JSStackFrame *const fp = cx->fp(); JSObject *obj = &fp->scopeChain(); JS_ASSERT(obj->isClonedBlock()); @@ -3197,7 +3207,7 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind) /* Block objects should have all reserved slots allocated early. */ uintN count = OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(obj->numSlots() == JSSLOT_BLOCK_DEPTH + 1 + count); + JS_ASSERT(obj->numSlots() >= JSSLOT_BLOCK_DEPTH + 1 + count); /* The block and its locals must be on the current stack for GC safety. */ uintN depth = OBJ_BLOCK_DEPTH(cx, obj); @@ -3209,15 +3219,8 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind) if (normalUnwind) { uintN slot = JSSLOT_BLOCK_FIRST_FREE_SLOT; - uintN flen = JS_MIN(count, JS_INITIAL_NSLOTS - slot); - uintN stop = slot + flen; - depth += fp->numFixed(); - while (slot < stop) - obj->fslots[slot++] = fp->slots()[depth++]; - count -= flen; - if (count != 0) - memcpy(obj->dslots, fp->slots() + depth, count * sizeof(Value)); + memcpy(obj->getSlots() + slot, fp->slots() + depth, count * sizeof(Value)); } /* We must clear the private slot even with errors. */ @@ -3298,7 +3301,7 @@ GetObjectSize(JSObject *obj) { return (obj->isFunction() && !obj->getPrivate()) ? sizeof(JSFunction) - : sizeof(JSObject); + : sizeof(JSObject) + sizeof(js::Value) * obj->numFixedSlots(); } /* @@ -3307,17 +3310,50 @@ GetObjectSize(JSObject *obj) * transitions are inherently not thread-safe. Don't perform a swap operation on objects * shared across threads or, or bad things will happen. You have been warned. */ -void -JSObject::swap(JSObject *other) +bool +JSObject::swap(JSContext *cx, JSObject *other) { size_t size = GetObjectSize(this); - JS_ASSERT(size == GetObjectSize(other)); + + if (size != GetObjectSize(other)) { + /* + * Objects with different numbers of fixed slots can be swapped only if they + * are both shapeless non-natives, to preserve the invariant that objects with the + * same shape have the same number of fixed slots. Use a dynamic array for both. + */ + JS_ASSERT(!isNative()); + JS_ASSERT(!other->isNative()); + size = sizeof(JSObject); + if (!hasSlotsArray()) { + if (!allocSlots(cx, numSlots())) + return false; + } + if (!other->hasSlotsArray()) { + if (!other->allocSlots(cx, other->numSlots())) + return false; + } + } + + bool thisInline = !hasSlotsArray(); + bool otherInline = !other->hasSlotsArray(); + + JS_STATIC_ASSERT(FINALIZE_OBJECT_LAST == FINALIZE_OBJECT16); + + char tmp[tl::Max::result]; + JS_ASSERT(size <= sizeof(tmp)); /* Trade the guts of the objects. */ - char tmp[tl::Max::result]; memcpy(tmp, this, size); memcpy(this, other, size); memcpy(other, tmp, size); + + /* Fixup pointers for inline slots on the objects. */ + if (thisInline) + other->slots = other->fixedSlots(); + if (otherInline) + this->slots = this->fixedSlots(); + + return true; } #if JS_HAS_XDR @@ -3503,13 +3539,12 @@ DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, const Shape *shape = obj->nativeLookup(id); if (!shape) { - uint32 index = 2 * JSProto_LIMIT + key; - if (!js_SetReservedSlot(cx, obj, index, v)) { + uint32 slot = 2 * JSProto_LIMIT + key; + if (!js_SetReservedSlot(cx, obj, slot, v)) { JS_UNLOCK_OBJ(cx, obj); return false; } - uint32 slot = JSSLOT_START(obj->getClass()) + index; shape = obj->addProperty(cx, id, PropertyStub, PropertyStub, slot, attrs, 0, 0); JS_UNLOCK_OBJ(cx, obj); @@ -3675,7 +3710,7 @@ js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, */ JS_ASSERT_IF(proto->clasp != clasp, clasp == &js_ArrayClass && proto->clasp == &js_SlowArrayClass); - if (!proto->getEmptyShape(cx, proto->clasp)) + if (!proto->getEmptyShape(cx, proto->clasp, FINALIZE_OBJECT0)) goto bad; /* If this is a standard class, cache its prototype. */ @@ -3693,111 +3728,103 @@ bad: } bool -JSObject::allocSlots(JSContext *cx, size_t nslots) +JSObject::allocSlots(JSContext *cx, size_t newcap) { - JS_ASSERT(!dslots); - JS_ASSERT(nslots > JS_INITIAL_NSLOTS); + uint32 oldcap = numSlots(); - size_t nwords = slotsToDynamicWords(nslots); - dslots = (Value*) cx->malloc(nwords * sizeof(Value)); - if (!dslots) + JS_ASSERT(newcap >= oldcap && !hasSlotsArray()); + + if (newcap > NSLOTS_LIMIT) { + if (!JS_ON_TRACE(cx)) + js_ReportAllocationOverflow(cx); return false; + } - dslots++; - dslots[-1].setPrivateUint32(nslots); - SetValueRangeToUndefined(dslots, nslots - JS_INITIAL_NSLOTS); + Value *tmpslots = (Value*) cx->malloc(newcap * sizeof(Value)); + if (!tmpslots) + return false; /* Leave slots at inline buffer. */ + slots = tmpslots; + capacity = newcap; + + /* Copy over anything from the inline buffer. */ + memcpy(slots, fixedSlots(), oldcap * sizeof(Value)); + ClearValueRange(slots + oldcap, newcap - oldcap, isDenseArray()); return true; } bool -JSObject::growSlots(JSContext *cx, size_t nslots) +JSObject::growSlots(JSContext *cx, size_t newcap) { /* - * Minimal number of dynamic slots to allocate. + * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to + * grow, double its capacity, to add N elements in amortized O(N) time. + * + * Above this limit, grow by 12.5% each time. Speed is still amortized + * O(N), with a higher constant factor, and we waste less space. */ - const size_t MIN_DYNAMIC_WORDS = 4; + static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024; + static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value); - /* - * The limit to switch to linear allocation strategy from the power of 2 - * growth no to waste too much memory. - */ - const size_t LINEAR_GROWTH_STEP = JS_BIT(16); + uint32 oldcap = numSlots(); + JS_ASSERT(oldcap < newcap); - /* If we are allocating fslots, there is nothing to do. */ - if (nslots <= JS_INITIAL_NSLOTS) - return true; + uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX) + ? oldcap * 2 + : oldcap + (oldcap >> 3); + + uint32 actualCapacity = JS_MAX(newcap, nextsize); + if (actualCapacity >= CAPACITY_CHUNK) + actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK); + else if (actualCapacity < SLOT_CAPACITY_MIN) + actualCapacity = SLOT_CAPACITY_MIN; /* Don't let nslots get close to wrapping around uint32. */ - if (nslots >= NSLOTS_LIMIT) { + if (actualCapacity >= NSLOTS_LIMIT) { JS_ReportOutOfMemory(cx); return false; } - size_t nwords = slotsToDynamicWords(nslots); + /* If nothing was allocated yet, treat it as initial allocation. */ + if (!hasSlotsArray()) + return allocSlots(cx, actualCapacity); - /* - * Round up nslots so the number of bytes in dslots array is power - * of 2 to ensure exponential grouth. - */ - uintN log; - if (nwords <= MIN_DYNAMIC_WORDS) { - nwords = MIN_DYNAMIC_WORDS; - } else if (nwords < LINEAR_GROWTH_STEP) { - JS_CEILING_LOG2(log, nwords); - nwords = JS_BIT(log); - } else { - nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP); - } - nslots = dynamicWordsToSlots(nwords); - - /* - * If nothing was allocated yet, treat it as initial allocation (but with - * the exponential growth algorithm applied). - */ - if (!dslots) - return allocSlots(cx, nslots); - - size_t oldnslots = dslots[-1].toPrivateUint32(); - - Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value)); - if (!tmpdslots) - return false; /* leave dslots at its old size */ - - dslots = tmpdslots; - dslots++; - dslots[-1].setPrivateUint32(nslots); + Value *tmpslots = (Value*) cx->realloc(slots, actualCapacity * sizeof(Value)); + if (!tmpslots) + return false; /* Leave dslots as its old size. */ + slots = tmpslots; + capacity = actualCapacity; /* Initialize the additional slots we added. */ - JS_ASSERT(nslots > oldnslots); - Value *beg = dslots + (oldnslots - JS_INITIAL_NSLOTS); - Value *end = dslots + (nslots - JS_INITIAL_NSLOTS); - SetValueRangeToUndefined(beg, end); - + ClearValueRange(slots + oldcap, actualCapacity - oldcap, isDenseArray()); return true; } void -JSObject::shrinkSlots(JSContext *cx, size_t nslots) +JSObject::shrinkSlots(JSContext *cx, size_t newcap) { - /* Nothing to shrink? */ - if (!dslots) + uint32 oldcap = numSlots(); + JS_ASSERT(newcap <= oldcap); + JS_ASSERT(newcap >= slotSpan()); + + if (oldcap <= SLOT_CAPACITY_MIN || !hasSlotsArray()) { + /* We won't shrink the slots any more. Clear excess holes. */ + ClearValueRange(slots + newcap, oldcap - newcap, isDenseArray()); return; + } - JS_ASSERT(dslots[-1].toPrivateUint32() > JS_INITIAL_NSLOTS); - JS_ASSERT(nslots <= dslots[-1].toPrivateUint32()); + uint32 fill = newcap; + if (newcap < SLOT_CAPACITY_MIN) + newcap = SLOT_CAPACITY_MIN; - if (nslots <= JS_INITIAL_NSLOTS) { - freeSlotsArray(cx); - dslots = NULL; - } else { - size_t nwords = slotsToDynamicWords(nslots); - Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value)); - if (!tmpdslots) - return; /* leave dslots at its old size */ + Value *tmpslots = (Value*) cx->realloc(slots, newcap * sizeof(Value)); + if (!tmpslots) + return; /* Leave slots at its old size. */ + slots = tmpslots; + capacity = newcap; - dslots = tmpdslots; - dslots++; - dslots[-1].setPrivateUint32(nslots); + if (fill < newcap) { + /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */ + ClearValueRange(slots + fill, newcap - fill, isDenseArray()); } } @@ -4888,10 +4915,12 @@ js_NativeGetInline(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *sh JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj)); slot = shape->slot; - if (slot != SHAPE_INVALID_SLOT) + if (slot != SHAPE_INVALID_SLOT) { *vp = pobj->lockedGetSlot(slot); - else + JS_ASSERT(!vp->isMagic()); + } else { vp->setUndefined(); + } if (shape->hasDefaultGetter()) return true; @@ -6173,7 +6202,6 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) JSObject *obj = (JSObject *)trc->debugPrintArg; uint32 slot = (uint32)trc->debugPrintIndex; - JS_ASSERT(slot >= JSSLOT_START(obj->getClass())); const Shape *shape; if (obj->isNative()) { @@ -6190,9 +6218,8 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) const char *slotname = NULL; Class *clasp = obj->getClass(); if (clasp->flags & JSCLASS_IS_GLOBAL) { - uint32 key = slot - JSSLOT_START(clasp); #define JS_PROTO(name,code,init) \ - if ((code) == key) { slotname = js_##name##_str; goto found; } + if ((code) == slot) { slotname = js_##name##_str; goto found; } #include "jsproto.tbl" #undef JS_PROTO } @@ -6220,7 +6247,7 @@ js_TraceObject(JSTracer *trc, JSObject *obj) JS_ASSERT(obj->isNative()); JSContext *cx = trc->context; - if (!obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) { + if (obj->hasSlotsArray() && !obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) { /* * Trim overlong dslots allocations from the GC, to avoid thrashing in * case of delete-happy code that settles down at a given population. @@ -6267,9 +6294,8 @@ js_TraceObject(JSTracer *trc, JSObject *obj) uint32 nslots = obj->numSlots(); if (!obj->nativeEmpty() && obj->slotSpan() < nslots) nslots = obj->slotSpan(); - JS_ASSERT(nslots >= JSSLOT_START(clasp)); - for (uint32 i = JSSLOT_START(clasp); i != nslots; ++i) { + for (uint32 i = 0; i != nslots; ++i) { const Value &v = obj->getSlot(i); JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i); MarkValueRaw(trc, v); @@ -6298,14 +6324,13 @@ js_ClearNative(JSContext *cx, JSObject *obj) } bool -js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, Value *vp) +js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, Value *vp) { if (!obj->isNative()) { vp->setUndefined(); return true; } - uint32 slot = JSSLOT_START(obj->getClass()) + index; JS_LOCK_OBJ(cx, obj); if (slot < obj->numSlots()) *vp = obj->getSlot(slot); @@ -6316,13 +6341,12 @@ js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, Value *vp) } bool -js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, const Value &v) +js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, const Value &v) { if (!obj->isNative()) return true; Class *clasp = obj->getClass(); - uint32 slot = JSSLOT_START(clasp) + index; JS_LOCK_OBJ(cx, obj); if (slot >= obj->numSlots()) { @@ -6595,11 +6619,8 @@ js_DumpObject(JSObject *obj) dumpValue(ObjectOrNullValue(obj->getParent())); fputc('\n', stderr); - i = JSSLOT_PRIVATE; - if (clasp->flags & JSCLASS_HAS_PRIVATE) { - i = JSSLOT_PRIVATE + 1; + if (clasp->flags & JSCLASS_HAS_PRIVATE) fprintf(stderr, "private %p\n", obj->getPrivate()); - } fprintf(stderr, "slots:\n"); reservedEnd = i + JSCLASS_RESERVED_SLOTS(clasp); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index a35a90b87874..6fc1fde07704 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -237,17 +237,6 @@ struct NativeIterator; } -const uint32 JS_INITIAL_NSLOTS = 3; - -/* - * The first available slot to store generic value. For JSCLASS_HAS_PRIVATE - * classes the slot stores a pointer to private data stuffed in a Value. - * Such pointer is stored as is without an overhead of PRIVATE_TO_JSVAL - * tagging and should be accessed using the (get|set)Private methods of - * JSObject. - */ -const uint32 JSSLOT_PRIVATE = 0; - struct JSFunction; /* @@ -276,9 +265,12 @@ struct JSFunction; * Both these flag bits are initially zero; they may be set or queried using * the (is|set)(Delegate|System) inline methods. * - * The dslots member is null or a pointer into a dynamically allocated vector - * of Values for reserved and dynamic slots. If dslots is not null, dslots[-1] - * records the number of available slots. + * The slots member is a pointer to the slot vector for the object. + * This can be either a fixed array allocated immediately after the object, + * or a dynamically allocated array. A dynamic array can be tested for with + * hasSlotsArray(). In all cases, capacity gives the number of usable slots. + * Two objects with the same shape have the same number of fixed slots, + * and either both have or neither have dynamically allocated slot arrays. */ struct JSObject : js::gc::Cell { /* @@ -346,18 +338,20 @@ struct JSObject : js::gc::Cell { uint32 flags; /* flags */ uint32 objShape; /* copy of lastProp->shape, or override if different */ - JSObject *proto; /* object's prototype */ - JSObject *parent; /* object's parent */ - js::Value *dslots; /* dynamically allocated slots */ - - /* Empty shape of kids if prototype, located here to align fslots on 32 bit targets. */ - js::EmptyShape *emptyShape; - - js::Value fslots[JS_INITIAL_NSLOTS]; /* small number of fixed slots */ #ifdef JS_THREADSAFE JSTitle title; #endif + /* If prototype, lazily filled array of empty shapes for each object size. */ + js::EmptyShape **emptyShapes; + + JSObject *proto; /* object's prototype */ + JSObject *parent; /* object's parent */ + void *privateData; /* private data */ + jsuword capacity; /* capacity of slots */ + js::Value *slots; /* dynamically allocated slots, + or pointer to fixedSlots() */ + /* * Return an immutable, shareable, empty shape with the same clasp as this * and the same slotSpan as this had when empty. @@ -366,7 +360,8 @@ struct JSObject : js::gc::Cell { * used as the scope of a new object whose prototype is |proto|. */ inline bool canProvideEmptyShape(js::Class *clasp); - inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp); + inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp, + /* gc::FinalizeKind */ unsigned kind); bool isNative() const { return map->isNative(); } @@ -529,29 +524,30 @@ struct JSObject : js::gc::Cell { inline bool hasPropertyTable() const; - uint32 numSlots(void) const { - return dslots ? dslots[-1].toPrivateUint32() : uint32(JS_INITIAL_NSLOTS); - } + uint32 numSlots() const { return capacity; } size_t slotsAndStructSize(uint32 nslots) const; size_t slotsAndStructSize() const { return slotsAndStructSize(numSlots()); } - private: - static size_t slotsToDynamicWords(size_t nslots) { - JS_ASSERT(nslots > JS_INITIAL_NSLOTS); - return nslots + 1 - JS_INITIAL_NSLOTS; - } + inline js::Value* fixedSlots() const; + inline size_t numFixedSlots() const; - static size_t dynamicWordsToSlots(size_t nwords) { - JS_ASSERT(nwords > 1); - return nwords - 1 + JS_INITIAL_NSLOTS; - } + static inline size_t getFixedSlotOffset(size_t slot); public: + /* Minimum size for dynamically allocated slots. */ + static const uint32 SLOT_CAPACITY_MIN = 8; + bool allocSlots(JSContext *cx, size_t nslots); bool growSlots(JSContext *cx, size_t nslots); void shrinkSlots(JSContext *cx, size_t nslots); + bool ensureSlots(JSContext *cx, size_t nslots) { + if (numSlots() < nslots) + return growSlots(cx, nslots); + return true; + } + /* * Ensure that the object has at least JSCLASS_RESERVED_SLOTS(clasp) + * nreserved slots. @@ -570,6 +566,14 @@ struct JSObject : js::gc::Cell { */ bool ensureInstanceReservedSlots(JSContext *cx, size_t nreserved); + /* + * Get a direct pointer to the object's slots. + * This can be reallocated if the object is modified, watch out! + */ + js::Value *getSlots() const { + return slots; + } + /* * NB: ensureClassReservedSlotsForEmptyObject asserts that nativeEmpty() * Use ensureClassReservedSlots for any object, either empty or already @@ -584,26 +588,18 @@ struct JSObject : js::gc::Cell { bool containsSlot(uint32 slot) const { return slot < slotSpan(); } js::Value& getSlotRef(uintN slot) { - return (slot < JS_INITIAL_NSLOTS) - ? fslots[slot] - : (JS_ASSERT(slot < dslots[-1].toPrivateUint32()), - dslots[slot - JS_INITIAL_NSLOTS]); + JS_ASSERT(slot < capacity); + return slots[slot]; } const js::Value &getSlot(uintN slot) const { - return (slot < JS_INITIAL_NSLOTS) - ? fslots[slot] - : (JS_ASSERT(slot < dslots[-1].toPrivateUint32()), - dslots[slot - JS_INITIAL_NSLOTS]); + JS_ASSERT(slot < capacity); + return slots[slot]; } void setSlot(uintN slot, const js::Value &value) { - if (slot < JS_INITIAL_NSLOTS) { - fslots[slot] = value; - } else { - JS_ASSERT(slot < dslots[-1].toPrivateUint32()); - dslots[slot - JS_INITIAL_NSLOTS] = value; - } + JS_ASSERT(slot < capacity); + slots[slot] = value; } inline const js::Value &lockedGetSlot(uintN slot) const; @@ -612,8 +608,7 @@ struct JSObject : js::gc::Cell { /* * These ones are for multi-threaded ("MT") objects. Use getSlot(), * getSlotRef(), setSlot() to directly manipulate slots in obj when only - * one thread can access obj, or when accessing read-only slots within - * JS_INITIAL_NSLOTS. + * one thread can access obj. */ inline js::Value getSlotMT(JSContext *cx, uintN slot); inline void setSlotMT(JSContext *cx, uintN slot, const js::Value &value); @@ -664,12 +659,12 @@ struct JSObject : js::gc::Cell { void *getPrivate() const { JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); - return *(void **)&fslots[JSSLOT_PRIVATE]; + return privateData; } void setPrivate(void *data) { JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); - *(void **)&fslots[JSSLOT_PRIVATE] = data; + privateData = data; } @@ -700,7 +695,7 @@ struct JSObject : js::gc::Cell { */ private: - static const uint32 JSSLOT_PRIMITIVE_THIS = JSSLOT_PRIVATE; + static const uint32 JSSLOT_PRIMITIVE_THIS = 0; public: inline const js::Value &getPrimitiveThis() const; @@ -710,36 +705,16 @@ struct JSObject : js::gc::Cell { * Array-specific getters and setters (for both dense and slow arrays). */ - // Used by dense and slow arrays. - static const uint32 JSSLOT_ARRAY_LENGTH = JSSLOT_PRIVATE; - - static const uint32 JSSLOT_DENSE_ARRAY_CAPACITY = JSSLOT_PRIVATE + 1; - - // This assertion must remain true; see comment in js_MakeArraySlow(). - // (Nb: This method is never called, it just contains a static assertion. - // The static assertion isn't inline because that doesn't work on Mac.) - inline void staticAssertArrayLengthIsInPrivateSlot(); - - public: - static const uint32 DENSE_ARRAY_CLASS_RESERVED_SLOTS = 3; - inline uint32 getArrayLength() const; inline void setArrayLength(uint32 length); - inline uint32 getDenseArrayCapacity() const; - inline void setDenseArrayCapacity(uint32 capacity); - - inline const js::Value &getDenseArrayElement(uint32 i) const; - inline js::Value *addressOfDenseArrayElement(uint32 i); - inline void setDenseArrayElement(uint32 i, const js::Value &v); - - inline js::Value *getDenseArrayElements() const; // returns pointer to the Array's elements array - bool growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap); - bool ensureDenseArrayElements(JSContext *cx, uint32 newcap); - bool shrinkDenseArrayElements(JSContext *cx, uint32 newcap); - inline void freeDenseArrayElements(JSContext *cx); - - inline void voidDenseOnlyArraySlots(); // used when converting a dense array to a slow array + inline uint32 getDenseArrayCapacity(); + inline js::Value* getDenseArrayElements(); + inline const js::Value &getDenseArrayElement(uintN idx); + inline js::Value* addressOfDenseArrayElement(uintN idx); + inline void setDenseArrayElement(uintN idx, const js::Value &val); + inline bool ensureDenseArrayElements(JSContext *cx, uintN cap); + inline void shrinkDenseArrayElements(JSContext *cx, uintN cap); JSBool makeDenseArraySlow(JSContext *cx); @@ -751,7 +726,7 @@ struct JSObject : js::gc::Cell { /* * Reserved slot structure for Arguments objects: * - * JSSLOT_PRIVATE - the function's stack frame until the function + * private - the function's stack frame until the function * returns; also, JS_ARGUMENTS_OBJECT_ON_TRACE if * arguments was created on trace * JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag @@ -769,13 +744,13 @@ struct JSObject : js::gc::Cell { * Argument index i is stored in ArgumentsData.slots[i], accessible via * {get,set}ArgsElement(). */ - static const uint32 JSSLOT_ARGS_DATA = JSSLOT_PRIVATE + 2; + static const uint32 JSSLOT_ARGS_DATA = 1; public: /* Number of extra fixed arguments object slots besides JSSLOT_PRIVATE. */ - static const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1; + static const uint32 JSSLOT_ARGS_LENGTH = 0; static const uint32 ARGS_CLASS_RESERVED_SLOTS = 2; - static const uint32 ARGS_FIRST_FREE_SLOT = JSSLOT_PRIVATE + ARGS_CLASS_RESERVED_SLOTS + 1; + static const uint32 ARGS_FIRST_FREE_SLOT = ARGS_CLASS_RESERVED_SLOTS + 1; /* Lower-order bit stolen from the length slot. */ static const uint32 ARGS_LENGTH_OVERRIDDEN_BIT = 0x1; @@ -802,19 +777,18 @@ struct JSObject : js::gc::Cell { inline void setArgsCallee(const js::Value &callee); inline const js::Value &getArgsElement(uint32 i) const; - inline js::Value *addressOfArgsElement(uint32 i) const; + inline js::Value *addressOfArgsElement(uint32 i); inline void setArgsElement(uint32 i, const js::Value &v); private: /* * Reserved slot structure for Arguments objects: - * */ - static const uint32 JSSLOT_CALL_CALLEE = JSSLOT_PRIVATE + 1; - static const uint32 JSSLOT_CALL_ARGUMENTS = JSSLOT_PRIVATE + 2; + static const uint32 JSSLOT_CALL_CALLEE = 0; + static const uint32 JSSLOT_CALL_ARGUMENTS = 1; public: - /* Number of extra fixed slots besides JSSLOT_PRIVATE. */ + /* Number of reserved slots. */ static const uint32 CALL_RESERVED_SLOTS = 2; inline JSObject &getCallObjCallee() const; @@ -828,23 +802,23 @@ struct JSObject : js::gc::Cell { * Date-specific getters and setters. */ - static const uint32 JSSLOT_DATE_UTC_TIME = JSSLOT_PRIVATE; + static const uint32 JSSLOT_DATE_UTC_TIME = 0; /* * Cached slots holding local properties of the date. * These are undefined until the first actual lookup occurs * and are reset to undefined whenever the date's time is modified. */ - static const uint32 JSSLOT_DATE_COMPONENTS_START = JSSLOT_PRIVATE + 1; + static const uint32 JSSLOT_DATE_COMPONENTS_START = 1; - static const uint32 JSSLOT_DATE_LOCAL_TIME = JSSLOT_PRIVATE + 1; - static const uint32 JSSLOT_DATE_LOCAL_YEAR = JSSLOT_PRIVATE + 2; - static const uint32 JSSLOT_DATE_LOCAL_MONTH = JSSLOT_PRIVATE + 3; - static const uint32 JSSLOT_DATE_LOCAL_DATE = JSSLOT_PRIVATE + 4; - static const uint32 JSSLOT_DATE_LOCAL_DAY = JSSLOT_PRIVATE + 5; - static const uint32 JSSLOT_DATE_LOCAL_HOURS = JSSLOT_PRIVATE + 6; - static const uint32 JSSLOT_DATE_LOCAL_MINUTES = JSSLOT_PRIVATE + 7; - static const uint32 JSSLOT_DATE_LOCAL_SECONDS = JSSLOT_PRIVATE + 8; + static const uint32 JSSLOT_DATE_LOCAL_TIME = 1; + static const uint32 JSSLOT_DATE_LOCAL_YEAR = 2; + static const uint32 JSSLOT_DATE_LOCAL_MONTH = 3; + static const uint32 JSSLOT_DATE_LOCAL_DATE = 4; + static const uint32 JSSLOT_DATE_LOCAL_DAY = 5; + static const uint32 JSSLOT_DATE_LOCAL_HOURS = 6; + static const uint32 JSSLOT_DATE_LOCAL_MINUTES = 7; + static const uint32 JSSLOT_DATE_LOCAL_SECONDS = 8; static const uint32 DATE_CLASS_RESERVED_SLOTS = 9; @@ -863,17 +837,18 @@ struct JSObject : js::gc::Cell { * Flat closures with one or more upvars snapshot the upvars' values into a * vector of js::Values referenced from this slot. */ - static const uint32 JSSLOT_FLAT_CLOSURE_UPVARS = JSSLOT_PRIVATE + 1; + static const uint32 JSSLOT_FLAT_CLOSURE_UPVARS = 0; /* * Null closures set or initialized as methods have these slots. See the * "method barrier" comments and methods. */ - static const uint32 JSSLOT_FUN_METHOD_ATOM = JSSLOT_PRIVATE + 1; - static const uint32 JSSLOT_FUN_METHOD_OBJ = JSSLOT_PRIVATE + 2; - static const uint32 JSSLOT_BOUND_FUNCTION_THIS = JSSLOT_PRIVATE + 1; - static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = JSSLOT_PRIVATE + 2; + static const uint32 JSSLOT_FUN_METHOD_ATOM = 0; + static const uint32 JSSLOT_FUN_METHOD_OBJ = 1; + + static const uint32 JSSLOT_BOUND_FUNCTION_THIS = 0; + static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1; public: static const uint32 FUN_CLASS_RESERVED_SLOTS = 2; @@ -899,7 +874,7 @@ struct JSObject : js::gc::Cell { */ private: - static const uint32 JSSLOT_REGEXP_LAST_INDEX = JSSLOT_PRIVATE + 1; + static const uint32 JSSLOT_REGEXP_LAST_INDEX = 0; public: static const uint32 REGEXP_CLASS_RESERVED_SLOTS = 1; @@ -928,12 +903,12 @@ struct JSObject : js::gc::Cell { * - Others (js_XMLClass, js_XMLFilterClass) don't reserve any slots. */ private: - static const uint32 JSSLOT_NAME_PREFIX = JSSLOT_PRIVATE; // shared - static const uint32 JSSLOT_NAME_URI = JSSLOT_PRIVATE + 1; // shared + static const uint32 JSSLOT_NAME_PREFIX = 0; // shared + static const uint32 JSSLOT_NAME_URI = 1; // shared - static const uint32 JSSLOT_NAMESPACE_DECLARED = JSSLOT_PRIVATE + 2; + static const uint32 JSSLOT_NAMESPACE_DECLARED = 2; - static const uint32 JSSLOT_QNAME_LOCAL_NAME = JSSLOT_PRIVATE + 2; + static const uint32 JSSLOT_QNAME_LOCAL_NAME = 2; public: static const uint32 NAMESPACE_CLASS_RESERVED_SLOTS = 3; @@ -973,14 +948,8 @@ struct JSObject : js::gc::Cell { inline bool isCallable(); /* The map field is not initialized here and should be set separately. */ - inline void initCommon(js::Class *aclasp, JSObject *proto, JSObject *parent, - JSContext *cx); - inline void init(js::Class *aclasp, JSObject *proto, JSObject *parent, - JSContext *cx); - inline void init(js::Class *aclasp, JSObject *proto, JSObject *parent, - void *priv, JSContext *cx); - inline void init(js::Class *aclasp, JSObject *proto, JSObject *parent, - const js::Value &privateSlotValue, JSContext *cx); + void init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent, + void *priv, bool useHoles); inline void finish(JSContext *cx); JS_ALWAYS_INLINE void finalize(JSContext *cx, unsigned thindKind); @@ -989,22 +958,21 @@ struct JSObject : js::gc::Cell { * Like init, but also initializes map. The catch: proto must be the result * of a call to js_InitClass(...clasp, ...). */ - inline void initSharingEmptyShape(js::Class *clasp, - JSObject *proto, - JSObject *parent, - const js::Value &privateSlotValue, - JSContext *cx); - inline void initSharingEmptyShape(js::Class *clasp, + inline bool initSharingEmptyShape(JSContext *cx, + js::Class *clasp, JSObject *proto, JSObject *parent, void *priv, - JSContext *cx); + /* gc::FinalizeKind */ unsigned kind); - inline bool hasSlotsArray() const { return !!dslots; } + inline bool hasSlotsArray() const; /* This method can only be called when hasSlotsArray() returns true. */ inline void freeSlotsArray(JSContext *cx); + /* Free the slots array and copy slots that fit into the fixed array. */ + inline void revertToFixedSlots(JSContext *cx); + inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags = 0); bool allocSlot(JSContext *cx, uint32 *slotp); @@ -1125,9 +1093,9 @@ struct JSObject : js::gc::Cell { inline JSObject *getThrowTypeError() const; - const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index); + bool swap(JSContext *cx, JSObject *obj); - void swap(JSObject *obj); + const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index); inline bool canHaveMethodBarrier() const; @@ -1165,22 +1133,29 @@ struct JSObject : js::gc::Cell { inline void initArrayClass(); }; -JS_STATIC_ASSERT(offsetof(JSObject, fslots) % sizeof(js::Value) == 0); +/* Check alignment for any fixed slots allocated after the object. */ +JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(js::Value) == 0); -#define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE) \ - ? JSSLOT_PRIVATE + 1 \ - : JSSLOT_PRIVATE) +inline js::Value* +JSObject::fixedSlots() const { + return (js::Value*) (jsuword(this) + sizeof(JSObject)); +} -#define JSSLOT_FREE(clasp) (JSSLOT_START(clasp) \ - + JSCLASS_RESERVED_SLOTS(clasp)) +inline bool +JSObject::hasSlotsArray() const { return this->slots != fixedSlots(); } -/* - * Maximum capacity of the obj->dslots vector, net of the hidden slot at - * obj->dslots[-1] that is used to store the length of the vector biased by - * JS_INITIAL_NSLOTS (and again net of the slot at index -1). - */ -#define MAX_DSLOTS_LENGTH (~size_t(0) / sizeof(js::Value) - 1) -#define MAX_DSLOTS_LENGTH32 (~uint32(0) / sizeof(js::Value) - 1) +/* static */ inline size_t +JSObject::getFixedSlotOffset(size_t slot) { + return sizeof(JSObject) + (slot * sizeof(js::Value)); +} + +struct JSObject_Slots2 : JSObject { js::Value fslots[2]; }; +struct JSObject_Slots4 : JSObject { js::Value fslots[4]; }; +struct JSObject_Slots8 : JSObject { js::Value fslots[8]; }; +struct JSObject_Slots12 : JSObject { js::Value fslots[12]; }; +struct JSObject_Slots16 : JSObject { js::Value fslots[16]; }; + +#define JSSLOT_FREE(clasp) JSCLASS_RESERVED_SLOTS(clasp) #define OBJ_CHECK_SLOT(obj,slot) JS_ASSERT((obj)->containsSlot(slot)) @@ -1240,7 +1215,7 @@ inline bool JSObject::isBlock() const { return getClass() == &js_BlockClass; } /* * Block scope object macros. The slots reserved by js_BlockClass are: * - * JSSLOT_PRIVATE JSStackFrame * active frame pointer or null + * private JSStackFrame * active frame pointer or null * JSSLOT_BLOCK_DEPTH int depth of block slots in frame * * After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals. @@ -1252,7 +1227,7 @@ inline bool JSObject::isBlock() const { return getClass() == &js_BlockClass; } * whose activation they were created (or null if the with or block object * outlives the frame). */ -static const uint32 JSSLOT_BLOCK_DEPTH = JSSLOT_PRIVATE + 1; +static const uint32 JSSLOT_BLOCK_DEPTH = 0; static const uint32 JSSLOT_BLOCK_FIRST_FREE_SLOT = JSSLOT_BLOCK_DEPTH + 1; inline bool @@ -1267,7 +1242,7 @@ JSObject::isClonedBlock() const return isBlock() && !!getProto(); } -static const uint32 JSSLOT_WITH_THIS = JSSLOT_PRIVATE + 2; +static const uint32 JSSLOT_WITH_THIS = 1; #define OBJ_BLOCK_COUNT(cx,obj) \ (obj)->propertyCount() diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index aa2467098573..f14d9e9ef456 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -121,8 +121,8 @@ JSObject::unbrand(JSContext *cx) inline void JSObject::finalize(JSContext *cx, unsigned thingKind) { - JS_ASSERT(thingKind == js::gc::FINALIZE_OBJECT || - thingKind == js::gc::FINALIZE_FUNCTION); + JS_ASSERT(thingKind >= js::gc::FINALIZE_OBJECT0 && + thingKind <= js::gc::FINALIZE_FUNCTION); /* Cope with stillborn objects that have no map. */ if (!map) @@ -263,8 +263,7 @@ JSObject::setSlotMT(JSContext *cx, uintN slot, const js::Value &value) inline js::Value JSObject::getReservedSlot(uintN index) const { - uint32 slot = JSSLOT_START(getClass()) + index; - return (slot < numSlots()) ? getSlot(slot) : js::UndefinedValue(); + return (index < numSlots()) ? getSlot(index) : js::UndefinedValue(); } inline bool @@ -283,115 +282,100 @@ inline const js::Value & JSObject::getPrimitiveThis() const { JS_ASSERT(isPrimitive()); - return fslots[JSSLOT_PRIMITIVE_THIS]; + return getSlot(JSSLOT_PRIMITIVE_THIS); } inline void JSObject::setPrimitiveThis(const js::Value &pthis) { JS_ASSERT(isPrimitive()); - fslots[JSSLOT_PRIMITIVE_THIS] = pthis; + setSlot(JSSLOT_PRIMITIVE_THIS, pthis); } -inline void -JSObject::staticAssertArrayLengthIsInPrivateSlot() +inline size_t +JSObject::numFixedSlots() const { - JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE); + if (isFunction()) + return JSObject::FUN_CLASS_RESERVED_SLOTS; + if (!hasSlotsArray()) + return capacity; + js::gc::FinalizeKind kind = js::gc::FinalizeKind(arena()->header()->thingKind); + return js::gc::GetGCKindSlots(kind); +} + +inline size_t +JSObject::slotsAndStructSize(uint32 nslots) const +{ + bool isFun = isFunction() && this == (JSObject*) getPrivate(); + + int ndslots = hasSlotsArray() ? nslots : 0; + int nfslots = isFun ? 0 : numFixedSlots(); + + return sizeof(js::Value) * (ndslots + nfslots) + + isFun ? sizeof(JSFunction) : sizeof(JSObject); } inline uint32 JSObject::getArrayLength() const { JS_ASSERT(isArray()); - return fslots[JSSLOT_ARRAY_LENGTH].toPrivateUint32(); + return (uint32)(size_t) getPrivate(); } inline void JSObject::setArrayLength(uint32 length) { JS_ASSERT(isArray()); - fslots[JSSLOT_ARRAY_LENGTH].setPrivateUint32(length); + setPrivate((void*) length); } inline uint32 -JSObject::getDenseArrayCapacity() const +JSObject::getDenseArrayCapacity() { JS_ASSERT(isDenseArray()); - return fslots[JSSLOT_DENSE_ARRAY_CAPACITY].toPrivateUint32(); + return numSlots(); } -inline void -JSObject::setDenseArrayCapacity(uint32 capacity) +inline js::Value* +JSObject::getDenseArrayElements() { JS_ASSERT(isDenseArray()); - JS_ASSERT(!capacity == !dslots); - fslots[JSSLOT_DENSE_ARRAY_CAPACITY].setPrivateUint32(capacity); -} - -inline size_t -JSObject::slotsAndStructSize(uint32 nslots) const -{ - int ndslots; - if (isDenseArray()) - ndslots = getDenseArrayCapacity() + 1; - else { - ndslots = nslots - JS_INITIAL_NSLOTS; - if (ndslots <= 0) - ndslots = 0; - else - ndslots++; /* number of total slots is stored at index -1 */ - } - - return sizeof(js::Value) * ndslots - + (isFunction() && !getPrivate()) ? sizeof(JSFunction) : sizeof(JSObject); + return getSlots(); } inline const js::Value & -JSObject::getDenseArrayElement(uint32 i) const +JSObject::getDenseArrayElement(uintN idx) { JS_ASSERT(isDenseArray()); - JS_ASSERT(i < getDenseArrayCapacity()); - return dslots[i]; + return getSlot(idx); } inline js::Value * -JSObject::addressOfDenseArrayElement(uint32 i) +JSObject::addressOfDenseArrayElement(uintN idx) { JS_ASSERT(isDenseArray()); - JS_ASSERT(i < getDenseArrayCapacity()); - return &dslots[i]; + return &getSlotRef(idx); } inline void -JSObject::setDenseArrayElement(uint32 i, const js::Value &v) +JSObject::setDenseArrayElement(uintN idx, const js::Value &val) { JS_ASSERT(isDenseArray()); - JS_ASSERT(i < getDenseArrayCapacity()); - dslots[i] = v; + setSlot(idx, val); } -inline js::Value * -JSObject::getDenseArrayElements() const +inline bool +JSObject::ensureDenseArrayElements(JSContext *cx, uintN cap) { JS_ASSERT(isDenseArray()); - return dslots; + return ensureSlots(cx, cap); } inline void -JSObject::freeDenseArrayElements(JSContext *cx) +JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap) { JS_ASSERT(isDenseArray()); - if (dslots) { - cx->free(dslots - 1); - dslots = NULL; - } -} - -inline void -JSObject::voidDenseOnlyArraySlots() -{ - JS_ASSERT(isDenseArray()); - fslots[JSSLOT_DENSE_ARRAY_CAPACITY].setUndefined(); + shrinkSlots(cx, cap); } inline void @@ -400,7 +384,7 @@ JSObject::setArgsLength(uint32 argc) JS_ASSERT(isArguments()); JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); JS_ASSERT(UINT32_MAX > (uint64(argc) << ARGS_PACKED_BITS_COUNT)); - fslots[JSSLOT_ARGS_LENGTH].setInt32(argc << ARGS_PACKED_BITS_COUNT); + getSlotRef(JSSLOT_ARGS_LENGTH).setInt32(argc << ARGS_PACKED_BITS_COUNT); JS_ASSERT(!isArgsLengthOverridden()); } @@ -408,7 +392,7 @@ inline uint32 JSObject::getArgsInitialLength() const { JS_ASSERT(isArguments()); - uint32 argc = uint32(fslots[JSSLOT_ARGS_LENGTH].toInt32()) >> ARGS_PACKED_BITS_COUNT; + uint32 argc = uint32(getSlot(JSSLOT_ARGS_LENGTH).toInt32()) >> ARGS_PACKED_BITS_COUNT; JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); return argc; } @@ -417,14 +401,14 @@ inline void JSObject::setArgsLengthOverridden() { JS_ASSERT(isArguments()); - fslots[JSSLOT_ARGS_LENGTH].getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT; + getSlotRef(JSSLOT_ARGS_LENGTH).getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT; } inline bool JSObject::isArgsLengthOverridden() const { JS_ASSERT(isArguments()); - const js::Value &v = fslots[JSSLOT_ARGS_LENGTH]; + const js::Value &v = getSlot(JSSLOT_ARGS_LENGTH); return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT; } @@ -432,14 +416,14 @@ inline js::ArgumentsData * JSObject::getArgsData() const { JS_ASSERT(isArguments()); - return (js::ArgumentsData *) fslots[JSSLOT_ARGS_DATA].toPrivate(); + return (js::ArgumentsData *) getSlot(JSSLOT_ARGS_DATA).toPrivate(); } inline void JSObject::setArgsData(js::ArgumentsData *data) { JS_ASSERT(isArguments()); - fslots[JSSLOT_ARGS_DATA].setPrivate(data); + getSlotRef(JSSLOT_ARGS_DATA).setPrivate(data); } inline const js::Value & @@ -463,7 +447,7 @@ JSObject::getArgsElement(uint32 i) const } inline js::Value * -JSObject::addressOfArgsElement(uint32 i) const +JSObject::addressOfArgsElement(uint32 i) { JS_ASSERT(isArguments()); JS_ASSERT(i < getArgsInitialLength()); @@ -483,49 +467,49 @@ JSObject::setCallObjCallee(JSObject &callee) { JS_ASSERT(isCall()); JS_ASSERT(callee.isFunction()); - return fslots[JSSLOT_CALL_CALLEE].setObject(callee); + return getSlotRef(JSSLOT_CALL_CALLEE).setObject(callee); } inline JSObject & JSObject::getCallObjCallee() const { JS_ASSERT(isCall()); - return fslots[JSSLOT_CALL_CALLEE].toObject(); + return getSlot(JSSLOT_CALL_CALLEE).toObject(); } inline JSFunction * JSObject::getCallObjCalleeFunction() const { JS_ASSERT(isCall()); - return fslots[JSSLOT_CALL_CALLEE].toObject().getFunctionPrivate(); + return getSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate(); } inline const js::Value & JSObject::getCallObjArguments() const { JS_ASSERT(isCall()); - return fslots[JSSLOT_CALL_ARGUMENTS]; + return getSlot(JSSLOT_CALL_ARGUMENTS); } inline void JSObject::setCallObjArguments(const js::Value &v) { JS_ASSERT(isCall()); - fslots[JSSLOT_CALL_ARGUMENTS] = v; + setSlot(JSSLOT_CALL_ARGUMENTS, v); } inline const js::Value & JSObject::getDateUTCTime() const { JS_ASSERT(isDate()); - return fslots[JSSLOT_DATE_UTC_TIME]; + return getSlot(JSSLOT_DATE_UTC_TIME); } inline void JSObject::setDateUTCTime(const js::Value &time) { JS_ASSERT(isDate()); - fslots[JSSLOT_DATE_UTC_TIME] = time; + setSlot(JSSLOT_DATE_UTC_TIME, time); } inline js::Value * @@ -533,7 +517,7 @@ JSObject::getFlatClosureUpvars() const { JS_ASSERT(isFunction()); JS_ASSERT(FUN_FLAT_CLOSURE(getFunctionPrivate())); - return (js::Value *) fslots[JSSLOT_FLAT_CLOSURE_UPVARS].toPrivate(); + return (js::Value *) getSlot(JSSLOT_FLAT_CLOSURE_UPVARS).toPrivate(); } inline js::Value @@ -547,20 +531,21 @@ JSObject::setFlatClosureUpvars(js::Value *upvars) { JS_ASSERT(isFunction()); JS_ASSERT(FUN_FLAT_CLOSURE(getFunctionPrivate())); - fslots[JSSLOT_FLAT_CLOSURE_UPVARS].setPrivate(upvars); + getSlotRef(JSSLOT_FLAT_CLOSURE_UPVARS).setPrivate(upvars); } inline bool JSObject::hasMethodObj(const JSObject& obj) const { - return fslots[JSSLOT_FUN_METHOD_OBJ].isObject() && - &fslots[JSSLOT_FUN_METHOD_OBJ].toObject() == &obj; + return JSSLOT_FUN_METHOD_OBJ < numSlots() && + getSlot(JSSLOT_FUN_METHOD_OBJ).isObject() && + &getSlot(JSSLOT_FUN_METHOD_OBJ).toObject() == &obj; } inline void JSObject::setMethodObj(JSObject& obj) { - fslots[JSSLOT_FUN_METHOD_OBJ].setObject(obj); + getSlotRef(JSSLOT_FUN_METHOD_OBJ).setObject(obj); } inline js::NativeIterator * @@ -579,76 +564,74 @@ inline jsval JSObject::getNamePrefix() const { JS_ASSERT(isNamespace() || isQName()); - return js::Jsvalify(fslots[JSSLOT_NAME_PREFIX]); + return js::Jsvalify(getSlot(JSSLOT_NAME_PREFIX)); } inline void JSObject::setNamePrefix(jsval prefix) { JS_ASSERT(isNamespace() || isQName()); - fslots[JSSLOT_NAME_PREFIX] = js::Valueify(prefix); + setSlot(JSSLOT_NAME_PREFIX, js::Valueify(prefix)); } inline jsval JSObject::getNameURI() const { JS_ASSERT(isNamespace() || isQName()); - return js::Jsvalify(fslots[JSSLOT_NAME_URI]); + return js::Jsvalify(getSlot(JSSLOT_NAME_URI)); } inline void JSObject::setNameURI(jsval uri) { JS_ASSERT(isNamespace() || isQName()); - fslots[JSSLOT_NAME_URI] = js::Valueify(uri); + setSlot(JSSLOT_NAME_URI, js::Valueify(uri)); } inline jsval JSObject::getNamespaceDeclared() const { JS_ASSERT(isNamespace()); - return js::Jsvalify(fslots[JSSLOT_NAMESPACE_DECLARED]); + return js::Jsvalify(getSlot(JSSLOT_NAMESPACE_DECLARED)); } inline void JSObject::setNamespaceDeclared(jsval decl) { JS_ASSERT(isNamespace()); - fslots[JSSLOT_NAMESPACE_DECLARED] = js::Valueify(decl); + setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl)); } inline jsval JSObject::getQNameLocalName() const { JS_ASSERT(isQName()); - return js::Jsvalify(fslots[JSSLOT_QNAME_LOCAL_NAME]); + return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME)); } inline void JSObject::setQNameLocalName(jsval name) { JS_ASSERT(isQName()); - fslots[JSSLOT_QNAME_LOCAL_NAME] = js::Valueify(name); + setSlot(JSSLOT_QNAME_LOCAL_NAME, js::Valueify(name)); } inline JSObject * JSObject::getWithThis() const { - return &fslots[JSSLOT_WITH_THIS].toObject(); + return &getSlot(JSSLOT_WITH_THIS).toObject(); } inline void JSObject::setWithThis(JSObject *thisp) { - fslots[JSSLOT_WITH_THIS].setObject(*thisp); + getSlotRef(JSSLOT_WITH_THIS).setObject(*thisp); } inline void -JSObject::initCommon(js::Class *aclasp, JSObject *proto, JSObject *parent, - JSContext *cx) +JSObject::init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent, + void *priv, bool useHoles) { - JS_STATIC_ASSERT(JSSLOT_PRIVATE + 3 == JS_INITIAL_NSLOTS); - clasp = aclasp; flags = 0; @@ -665,43 +648,22 @@ JSObject::initCommon(js::Class *aclasp, JSObject *proto, JSObject *parent, setProto(proto); setParent(parent); - fslots[JSSLOT_PRIVATE + 1].setUndefined(); - fslots[JSSLOT_PRIVATE + 2].setUndefined(); - dslots = NULL; + privateData = priv; + slots = fixedSlots(); + + /* + * Fill the fixed slots with undefined or array holes. This object must + * already have its capacity filled in, as by js_NewGCObject. + */ + JS_ASSERT(capacity == numFixedSlots()); + ClearValueRange(slots, capacity, useHoles); #ifdef JS_THREADSAFE js_InitTitle(cx, &title); #endif - emptyShape = NULL; -} - -inline void -JSObject::init(js::Class *aclasp, JSObject *proto, JSObject *parent, - const js::Value &privateSlotValue, JSContext *cx) -{ - initCommon(aclasp, proto, parent, cx); - fslots[JSSLOT_PRIVATE] = privateSlotValue; -} - -inline void -JSObject::init(js::Class *aclasp, JSObject *proto, JSObject *parent, - void *priv, JSContext *cx) -{ - initCommon(aclasp, proto, parent, cx); - *(void **)&fslots[JSSLOT_PRIVATE] = priv; -} - -inline void -JSObject::init(js::Class *aclasp, JSObject *proto, JSObject *parent, - JSContext *cx) -{ - initCommon(aclasp, proto, parent, cx); - if (clasp->flags & JSCLASS_HAS_PRIVATE) - *(void **)&fslots[JSSLOT_PRIVATE] = NULL; - else - fslots[JSSLOT_PRIVATE].setUndefined(); + emptyShapes = NULL; } inline void @@ -718,40 +680,43 @@ JSObject::finish(JSContext *cx) #endif } -inline void -JSObject::initSharingEmptyShape(js::Class *aclasp, +inline bool +JSObject::initSharingEmptyShape(JSContext *cx, + js::Class *aclasp, JSObject *proto, JSObject *parent, - const js::Value &privateSlotValue, - JSContext *cx) + void *privateValue, + /* js::gc::FinalizeKind */ unsigned kind) { - init(aclasp, proto, parent, privateSlotValue, cx); + init(cx, aclasp, proto, parent, privateValue, false); + + JS_ASSERT(!isDenseArray()); + + js::EmptyShape *empty = proto->getEmptyShape(cx, aclasp, kind); + if (!empty) + return false; - js::EmptyShape *empty = proto->emptyShape; - JS_ASSERT(empty->getClass() == aclasp); - setMap(empty); -} - -inline void -JSObject::initSharingEmptyShape(js::Class *aclasp, - JSObject *proto, - JSObject *parent, - void *priv, - JSContext *cx) -{ - init(aclasp, proto, parent, priv, cx); - - js::EmptyShape *empty = proto->emptyShape; - JS_ASSERT(empty->getClass() == aclasp); setMap(empty); + return true; } inline void JSObject::freeSlotsArray(JSContext *cx) { JS_ASSERT(hasSlotsArray()); - JS_ASSERT(dslots[-1].toPrivateUint32() > JS_INITIAL_NSLOTS); - cx->free(dslots - 1); + cx->free(slots); +} + +inline void +JSObject::revertToFixedSlots(JSContext *cx) +{ + JS_ASSERT(hasSlotsArray()); + size_t fixed = numFixedSlots(); + JS_ASSERT(capacity >= fixed); + memcpy(fixedSlots(), slots, fixed * sizeof(js::Value)); + freeSlotsArray(cx); + slots = fixedSlots(); + capacity = fixed; } inline bool @@ -830,7 +795,8 @@ class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescri }; static inline bool -InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto) +InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto, + gc::FinalizeKind kind) { JS_ASSERT(clasp->isNative()); JS_ASSERT(proto == obj->getProto()); @@ -841,7 +807,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* pro if (proto) { JS_LOCK_OBJ(cx, proto); if (proto->canProvideEmptyShape(clasp)) { - empty = proto->getEmptyShape(cx, clasp); + empty = proto->getEmptyShape(cx, clasp, kind); JS_UNLOCK_OBJ(cx, proto); if (!empty) goto bad; @@ -851,13 +817,11 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* pro } if (!empty) { - uint32 freeslot = JSSLOT_FREE(clasp); - JS_ASSERT(freeslot >= JSSLOT_PRIVATE); - empty = js::EmptyShape::create(cx, clasp); if (!empty) goto bad; - if (freeslot > JS_INITIAL_NSLOTS && !obj->allocSlots(cx, freeslot)) + uint32 freeslot = JSSLOT_FREE(clasp); + if (freeslot > obj->numSlots() && !obj->allocSlots(cx, freeslot)) goto bad; } @@ -877,7 +841,8 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* pro * and its parent global as parent. */ static inline JSObject * -NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent) +NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, + JSObject *parent, gc::FinalizeKind kind) { JS_ASSERT(proto); JS_ASSERT(proto->isNative()); @@ -887,18 +852,19 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *p * Allocate an object from the GC heap and initialize all its fields before * doing any operation that can potentially trigger GC. */ - JSObject* obj = js_NewGCObject(cx); + JSObject* obj = js_NewGCObject(cx, kind); if (obj) { /* * Default parent to the parent of the prototype, which was set from * the parent of the prototype's constructor. */ - obj->init(clasp, proto, parent, cx); + bool useHoles = (clasp == &js_ArrayClass); + obj->init(cx, clasp, proto, parent, NULL, useHoles); JS_LOCK_OBJ(cx, proto); JS_ASSERT(proto->canProvideEmptyShape(clasp)); - js::EmptyShape *empty = proto->getEmptyShape(cx, clasp); + js::EmptyShape *empty = proto->getEmptyShape(cx, clasp, kind); JS_UNLOCK_OBJ(cx, proto); if (empty) @@ -910,6 +876,13 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *p return obj; } +static inline JSObject * +NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent) +{ + gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + return NewNativeClassInstance(cx, clasp, proto, parent, kind); +} + bool FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop, Class *clasp); @@ -921,7 +894,7 @@ FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject * right default proto and parent for clasp in cx. */ static inline JSObject * -NewBuiltinClassInstance(JSContext *cx, Class *clasp) +NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind) { VOUCH_DOES_NOT_REQUIRE_STACK(); @@ -950,7 +923,14 @@ NewBuiltinClassInstance(JSContext *cx, Class *clasp) return NULL; } - return NewNativeClassInstance(cx, clasp, proto, global); + return NewNativeClassInstance(cx, clasp, proto, global, kind); +} + +static inline JSObject * +NewBuiltinClassInstance(JSContext *cx, Class *clasp) +{ + gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + return NewBuiltinClassInstance(cx, clasp, kind); } static inline JSProtoKey @@ -1001,7 +981,8 @@ namespace detail { template static JS_ALWAYS_INLINE JSObject * -NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) +NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, + gc::FinalizeKind kind) { /* Bootstrap the ur-object, and make it the default prototype object. */ if (withProto == WithProto::Class && !proto) { @@ -1010,8 +991,7 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) return NULL; if (!proto && !js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) return NULL; - } - + } /* * Allocate an object from the GC heap and initialize all its fields before @@ -1020,18 +1000,23 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) * * The should be specialized by the template. */ - JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx); + JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx, kind); if (!obj) goto out; + /* This needs to match up with the size of JSFunction::data_padding. */ + JS_ASSERT_IF(isFunction, kind == gc::FINALIZE_OBJECT2); + /* * Default parent to the parent of the prototype, which was set from * the parent of the prototype's constructor. */ - obj->init(clasp, proto, (!parent && proto) ? proto->getParent() : parent, cx); + obj->init(cx, clasp, proto, + (!parent && proto) ? proto->getParent() : parent, + NULL, clasp == &js_ArrayClass); if (clasp->isNative()) { - if (!InitScopeForObject(cx, obj, clasp, proto)) { + if (!InitScopeForObject(cx, obj, clasp, proto, kind)) { obj = NULL; goto out; } @@ -1043,28 +1028,80 @@ out: Probes::createObject(cx, obj); return obj; } -} +} /* namespace detail */ static JS_ALWAYS_INLINE JSObject * NewFunction(JSContext *cx, JSObject *parent) { - return detail::NewObject(cx, &js_FunctionClass, NULL, parent); + return detail::NewObject(cx, &js_FunctionClass, NULL, parent, + gc::FINALIZE_OBJECT2); +} + +template +static JS_ALWAYS_INLINE JSObject * +NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, + gc::FinalizeKind kind) +{ + return detail::NewObject(cx, clasp, proto, parent, kind); } template static JS_ALWAYS_INLINE JSObject * NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - return detail::NewObject(cx, clasp, proto, parent); + gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + return detail::NewObject(cx, clasp, proto, parent, kind); +} + +template +static JS_ALWAYS_INLINE JSObject * +NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, + gc::FinalizeKind kind) +{ + if (clasp == &js_FunctionClass) + return detail::NewObject(cx, clasp, proto, parent, kind); + return detail::NewObject(cx, clasp, proto, parent, kind); } template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - return (clasp == &js_FunctionClass) - ? detail::NewObject(cx, clasp, proto, parent) - : detail::NewObject(cx, clasp, proto, parent); + gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + return NewObject(cx, clasp, proto, parent, kind); +} + +/* Creates a new array with a zero length and the given finalize kind. */ +static inline JSObject * +NewArrayWithKind(JSContext* cx, gc::FinalizeKind kind) +{ + return NewNonFunction(cx, &js_ArrayClass, NULL, NULL, kind); +} + +/* + * As for js_GetGCObjectKind, where numSlots is a guess at the final size of + * the object, zero if the final size is unknown. + */ +static inline gc::FinalizeKind +GuessObjectGCKind(size_t numSlots, bool isArray) +{ + if (numSlots) + return gc::GetGCObjectKind(numSlots); + return isArray ? gc::FINALIZE_OBJECT8 : gc::FINALIZE_OBJECT4; +} + +/* + * Get the GC kind to use for scripted 'new' on the given class. + * FIXME bug 547327: estimate the size from the allocation site. + */ +static inline gc::FinalizeKind +NewObjectGCKind(JSContext *cx, js::Class *clasp) +{ + if (clasp == &js_ArrayClass || clasp == &js_SlowArrayClass) + return gc::FINALIZE_OBJECT8; + if (clasp == &js_FunctionClass) + return gc::FINALIZE_OBJECT2; + return gc::FINALIZE_OBJECT4; } class AutoPropertyDropper { diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 9de6599212b4..35b7481d844e 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -4492,7 +4492,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_NEWINIT: { - i = GET_INT8(pc); + i = GET_UINT16(pc); LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object); todo = ss->sprinter.offset; diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 0f02588b5823..784db7c1ddd0 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -246,7 +246,7 @@ OPDEF(JSOP_SETLOCAL, 87,"setlocal", NULL, 3, 1, 1, 3, JOF_LOCAL| OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16) /* Object and array literal support. */ -OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 2, 0, 1, 19, JOF_INT8) +OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 5, 0, 1, 19, JOF_UINT16PAIR) OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index cb7a47bec5f0..121ab7259af6 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1090,8 +1090,9 @@ NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObje clasp = &FunctionProxyClass; else clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass; + JSObject *obj = NewNonFunction(cx, clasp, proto, parent); - if (!obj || (construct && !obj->ensureInstanceReservedSlots(cx, 0))) + if (!obj || !obj->ensureInstanceReservedSlots(cx, 0)) return NULL; obj->setSlot(JSSLOT_PROXY_HANDLER, PrivateValue(handler)); obj->setSlot(JSSLOT_PROXY_PRIVATE, priv); @@ -1235,8 +1236,8 @@ static JSFunctionSpec static_methods[] = { extern Class CallableObjectClass; -static const uint32 JSSLOT_CALLABLE_CALL = JSSLOT_PRIVATE; -static const uint32 JSSLOT_CALLABLE_CONSTRUCT = JSSLOT_PRIVATE + 1; +static const uint32 JSSLOT_CALLABLE_CALL = 0; +static const uint32 JSSLOT_CALLABLE_CONSTRUCT = 1; static JSBool callable_Call(JSContext *cx, uintN argc, Value *vp) @@ -1247,7 +1248,7 @@ callable_Call(JSContext *cx, uintN argc, Value *vp) JSObject *callable = &JS_CALLEE(cx, vp).toObject(); JS_ASSERT(callable->getClass() == &CallableObjectClass); - const Value &fval = callable->fslots[JSSLOT_CALLABLE_CALL]; + const Value &fval = callable->getSlot(JSSLOT_CALLABLE_CALL); Value rval; bool ok = ExternalInvoke(cx, thisobj, fval, argc, JS_ARGV(cx, vp), &rval); *vp = rval; @@ -1263,10 +1264,10 @@ callable_Construct(JSContext *cx, uintN argc, Value *vp) JSObject *callable = &vp[0].toObject(); JS_ASSERT(callable->getClass() == &CallableObjectClass); - Value fval = callable->fslots[JSSLOT_CALLABLE_CONSTRUCT]; + Value fval = callable->getSlot(JSSLOT_CALLABLE_CONSTRUCT); if (fval.isUndefined()) { /* We don't have an explicit constructor so allocate a new object and use the call. */ - fval = callable->fslots[JSSLOT_CALLABLE_CALL]; + fval = callable->getSlot(JSSLOT_CALLABLE_CALL); JS_ASSERT(fval.isObject()); /* callable is the constructor, so get callable.prototype is the proto of the new object. */ @@ -1288,7 +1289,7 @@ callable_Construct(JSContext *cx, uintN argc, Value *vp) /* If the call returns an object, return that, otherwise the original newobj. */ Value rval; - if (!ExternalInvoke(cx, newobj, callable->fslots[JSSLOT_CALLABLE_CALL], + if (!ExternalInvoke(cx, newobj, callable->getSlot(JSSLOT_CALLABLE_CALL), argc, vp + 2, &rval)) { return false; } @@ -1346,15 +1347,19 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) JSObject *parent = proxy->getParent(); Class *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &js_ObjectClass; - /* Make a blank object from the recipe fix provided to us. */ - JSObject *newborn = NewNonFunction(cx, clasp, proto, parent); + /* + * Make a blank object from the recipe fix provided to us. This must have + * number of fixed slots as the proxy so that we can swap their contents. + */ + gc::FinalizeKind kind = gc::FinalizeKind(proxy->arena()->header()->thingKind); + JSObject *newborn = NewNonFunction(cx, clasp, proto, parent, kind); if (!newborn) return NULL; AutoObjectRooter tvr2(cx, newborn); if (clasp == &CallableObjectClass) { - newborn->fslots[JSSLOT_CALLABLE_CALL] = GetCall(proxy); - newborn->fslots[JSSLOT_CALLABLE_CONSTRUCT] = GetConstruct(proxy); + newborn->setSlot(JSSLOT_CALLABLE_CALL, GetCall(proxy)); + newborn->setSlot(JSSLOT_CALLABLE_CONSTRUCT, GetConstruct(proxy)); } { @@ -1364,7 +1369,8 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) } /* Trade contents between the newborn object and the proxy. */ - proxy->swap(newborn); + if (!proxy->swap(cx, newborn)) + return false; /* The GC will dispose of the proxy object. */ diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 3f560e955971..950f788f3934 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -128,12 +128,12 @@ class JSProxy { }; /* Shared between object and function proxies. */ -const uint32 JSSLOT_PROXY_HANDLER = JSSLOT_PRIVATE + 0; -const uint32 JSSLOT_PROXY_PRIVATE = JSSLOT_PRIVATE + 1; -const uint32 JSSLOT_PROXY_EXTRA = JSSLOT_PRIVATE + 2; +const uint32 JSSLOT_PROXY_HANDLER = 0; +const uint32 JSSLOT_PROXY_PRIVATE = 1; +const uint32 JSSLOT_PROXY_EXTRA = 2; /* Function proxies only. */ -const uint32 JSSLOT_PROXY_CALL = JSSLOT_PRIVATE + 2; -const uint32 JSSLOT_PROXY_CONSTRUCT = JSSLOT_PRIVATE + 3; +const uint32 JSSLOT_PROXY_CALL = 2; +const uint32 JSSLOT_PROXY_CONSTRUCT = 3; extern JS_FRIEND_API(js::Class) ObjectProxyClass; extern JS_FRIEND_API(js::Class) FunctionProxyClass; diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index 84edd681c571..805977b802eb 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -216,28 +216,28 @@ inline const js::Value & JSObject::getRegExpLastIndex() const { JS_ASSERT(isRegExp()); - return fslots[JSSLOT_REGEXP_LAST_INDEX]; + return getSlot(JSSLOT_REGEXP_LAST_INDEX); } inline void JSObject::setRegExpLastIndex(const js::Value &v) { JS_ASSERT(isRegExp()); - fslots[JSSLOT_REGEXP_LAST_INDEX] = v; + setSlot(JSSLOT_REGEXP_LAST_INDEX, v); } inline void JSObject::setRegExpLastIndex(jsdouble d) { JS_ASSERT(isRegExp()); - fslots[JSSLOT_REGEXP_LAST_INDEX] = js::NumberValue(d); + setSlot(JSSLOT_REGEXP_LAST_INDEX, js::NumberValue(d)); } inline void JSObject::zeroRegExpLastIndex() { JS_ASSERT(isRegExp()); - fslots[JSSLOT_REGEXP_LAST_INDEX].setInt32(0); + getSlotRef(JSSLOT_REGEXP_LAST_INDEX).setInt32(0); } namespace js { class AutoStringRooter; } diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 1a79c88ab8f8..d6080f8c62dd 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -1170,6 +1170,18 @@ JSObject::removeProperty(JSContext *cx, jsid id) */ JS_ASSERT(shape == lastProp); removeLastProperty(); + + /* + * Revert to fixed slots if this was the first dynamically allocated slot, + * preserving invariant that objects with the same shape use the fixed + * slots in the same way. + */ + size_t fixed = numFixedSlots(); + if (shape->slot == fixed) { + JS_ASSERT_IF(!lastProp->isEmptyShape() && lastProp->hasSlot(), + lastProp->slot == fixed - 1); + revertToFixedSlots(cx); + } } updateShape(cx); @@ -1205,6 +1217,14 @@ JSObject::clear(JSContext *cx) if (inDictionaryMode()) shape->listp = &lastProp; + /* + * Revert to fixed slots if we have cleared below the first dynamically + * allocated slot, preserving invariant that objects with the same shape + * use the fixed slots in the same way. + */ + if (hasSlotsArray() && JSSLOT_FREE(getClass()) <= numFixedSlots()) + revertToFixedSlots(cx); + /* * We have rewound to a uniquely-shaped empty scope, so we don't need an * override for this object's shape. diff --git a/js/src/jsscopeinlines.h b/js/src/jsscopeinlines.h index c26f172e31ef..11a9b3cc7215 100644 --- a/js/src/jsscopeinlines.h +++ b/js/src/jsscopeinlines.h @@ -60,19 +60,45 @@ js::Shape::freeTable(JSContext *cx) } inline js::EmptyShape * -JSObject::getEmptyShape(JSContext *cx, js::Class *aclasp) +JSObject::getEmptyShape(JSContext *cx, js::Class *aclasp, + /* gc::FinalizeKind */ unsigned kind) { - if (emptyShape) - JS_ASSERT(aclasp == emptyShape->getClass()); - else - emptyShape = js::EmptyShape::create(cx, aclasp); - return emptyShape; + JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST); + int i = kind - js::gc::FINALIZE_OBJECT0; + + if (!emptyShapes) { + emptyShapes = (js::EmptyShape**) + cx->calloc(sizeof(js::EmptyShape*) * js::gc::JS_FINALIZE_OBJECT_LIMIT); + if (!emptyShapes) + return NULL; + + /* + * Always fill in emptyShapes[0], so canProvideEmptyShape works. + * Other empty shapes are filled in lazily. + */ + emptyShapes[0] = js::EmptyShape::create(cx, aclasp); + if (!emptyShapes[0]) { + cx->free(emptyShapes); + emptyShapes = NULL; + return NULL; + } + } + + JS_ASSERT(aclasp == emptyShapes[0]->getClass()); + + if (!emptyShapes[i]) { + emptyShapes[i] = js::EmptyShape::create(cx, aclasp); + if (!emptyShapes[i]) + return NULL; + } + + return emptyShapes[i]; } inline bool JSObject::canProvideEmptyShape(js::Class *aclasp) { - return !emptyShape || emptyShape->getClass() == aclasp; + return !emptyShapes || emptyShapes[0]->getClass() == aclasp; } inline void diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index c939160a8158..ea0eaebaf241 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2700,9 +2700,7 @@ ptrdiff_t TraceRecorder::nativeGlobalSlot(const Value* p) const { JS_ASSERT(isGlobal(p)); - if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) - return ptrdiff_t(p - globalObj->fslots); - return ptrdiff_t((p - globalObj->dslots) + JS_INITIAL_NSLOTS); + return ptrdiff_t(p - globalObj->slots); } /* Determine the offset in the native global frame for a jsval we track. */ @@ -2716,8 +2714,7 @@ TraceRecorder::nativeGlobalOffset(const Value* p) const bool TraceRecorder::isGlobal(const Value* p) const { - return ((size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) || - (size_t(p - globalObj->dslots) < (globalObj->numSlots() - JS_INITIAL_NSLOTS))); + return (size_t(p - globalObj->slots) < globalObj->numSlots()); } bool @@ -3495,7 +3492,7 @@ struct ArgClosureTraits // Get the offset of our object slots from the object's dslots pointer. static inline uint32 slot_offset(JSObject* obj) { - return JSSLOT_START(&js_CallClass) + JSObject::CALL_RESERVED_SLOTS; + return JSObject::CALL_RESERVED_SLOTS; } // Get the maximum slot index of this type that should be allowed @@ -3526,7 +3523,7 @@ struct VarClosureTraits } static inline uint32 slot_offset(JSObject* obj) { - return JSSLOT_START(&js_CallClass) + JSObject::CALL_RESERVED_SLOTS + + return JSObject::CALL_RESERVED_SLOTS + obj->getCallObjCalleeFunction()->nargs; } @@ -3991,7 +3988,7 @@ TraceRecorder::known(JSObject** p) } /* - * The dslots of the global object are sometimes reallocated by the interpreter. + * The slots of the global object are sometimes reallocated by the interpreter. * This function check for that condition and re-maps the entries of the tracker * accordingly. */ @@ -4000,8 +3997,8 @@ TraceRecorder::checkForGlobalObjectReallocationHelper() { debug_only_print0(LC_TMTracer, "globalObj->dslots relocated, updating tracker\n"); Value* src = global_dslots; - Value* dst = globalObj->dslots; - jsuint length = globalObj->dslots[-1].toPrivateUint32() - JS_INITIAL_NSLOTS; + Value* dst = globalObj->getSlots(); + jsuint length = globalObj->capacity; LIns** map = (LIns**)alloca(sizeof(LIns*) * length); for (jsuint n = 0; n < length; ++n) { map[n] = tracker.get(src); @@ -4009,7 +4006,7 @@ TraceRecorder::checkForGlobalObjectReallocationHelper() } for (jsuint n = 0; n < length; ++n) tracker.set(dst++, map[n]); - global_dslots = globalObj->dslots; + global_dslots = globalObj->getSlots(); } /* Determine whether the current branch is a loop edge (taken or not taken). */ @@ -8837,7 +8834,7 @@ TraceRecorder::incProp(jsint incr, bool pre) CHECK_STATUS_A(inc(v, v_ins, incr, pre)); LIns* dslots_ins = NULL; - stobj_set_slot(obj_ins, slot, dslots_ins, v, v_ins); + stobj_set_slot(obj, obj_ins, slot, dslots_ins, v, v_ins); return ARECORD_CONTINUE; } @@ -9575,7 +9572,7 @@ TraceRecorder::guardPropertyCacheHit(LIns* obj_ins, void TraceRecorder::stobj_set_fslot(LIns *obj_ins, unsigned slot, const Value &v, LIns* v_ins) { - box_value_into(v, v_ins, obj_ins, offsetof(JSObject, fslots) + slot * sizeof(Value), ACCSET_OTHER); + box_value_into(v, v_ins, obj_ins, JSObject::getFixedSlotOffset(slot), ACCSET_OTHER); } void @@ -9583,49 +9580,32 @@ TraceRecorder::stobj_set_dslot(LIns *obj_ins, unsigned slot, LIns*& dslots_ins, const Value &v, LIns* v_ins) { if (!dslots_ins) - dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER); + dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); box_value_into(v, v_ins, dslots_ins, slot * sizeof(Value), ACCSET_OTHER); } void -TraceRecorder::stobj_set_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins, +TraceRecorder::stobj_set_slot(JSObject *obj, LIns* obj_ins, unsigned slot, LIns*& dslots_ins, const Value &v, LIns* v_ins) { - if (slot < JS_INITIAL_NSLOTS) + /* + * A shape guard must have already been generated for obj, which will + * ensure that future objects have the same number of fixed slots. + */ + if (!obj->hasSlotsArray()) { + JS_ASSERT(slot < obj->numSlots()); stobj_set_fslot(obj_ins, slot, v, v_ins); - else - stobj_set_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins, v, v_ins); + } else { + stobj_set_dslot(obj_ins, slot, dslots_ins, v, v_ins); + } } #if JS_BITS_PER_WORD == 32 || JS_BITS_PER_WORD == 64 -void -TraceRecorder::set_array_fslot(LIns *obj_ins, unsigned slot, uint32 val) -{ - /* - * We can assume the destination fslot has been appropriately tagged so we - * can just overwrite the 32-bit payload. - */ - lir->insStore(INS_CONSTU(val), obj_ins, - offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset, - ACCSET_OTHER); -} - LIns* -TraceRecorder::stobj_get_fslot_uint32(LIns* obj_ins, unsigned slot) +TraceRecorder::stobj_get_slot_uint32(LIns* obj_ins, unsigned slot) { - JS_ASSERT(slot < JS_INITIAL_NSLOTS); - return lir->insLoad(LIR_ldi, obj_ins, - offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset, - ACCSET_OTHER); -} - -LIns* -TraceRecorder::stobj_set_fslot_uint32(LIns* value_ins, LIns* obj_ins, unsigned slot) -{ - JS_ASSERT(slot < JS_INITIAL_NSLOTS); - return lir->insStore(LIR_sti, value_ins, obj_ins, - offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset, - ACCSET_OTHER); + LIns *vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); + return lir->insLoad(LIR_ldi, vaddr_ins, slot * sizeof(Value) + sPayloadOffset, ACCSET_OTHER); } #endif @@ -9634,12 +9614,14 @@ TraceRecorder::unbox_slot(JSObject *obj, LIns *obj_ins, uint32 slot, VMSideExit { LIns *vaddr_ins; ptrdiff_t offset; - if (slot < JS_INITIAL_NSLOTS) { + + /* Same guarantee about fixed slots as stobj_set_slot. */ + if (!obj->hasSlotsArray()) { vaddr_ins = obj_ins; - offset = offsetof(JSObject, fslots) + slot * sizeof(Value); + offset = JSObject::getFixedSlotOffset(slot); } else { - vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER); - offset = (slot - JS_INITIAL_NSLOTS) * sizeof(Value); + vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); + offset = slot * sizeof(Value); } const Value &v = obj->getSlot(slot); @@ -9649,12 +9631,11 @@ TraceRecorder::unbox_slot(JSObject *obj, LIns *obj_ins, uint32 slot, VMSideExit #if JS_BITS_PER_WORD == 32 LIns* -TraceRecorder::stobj_get_fslot_private_ptr(LIns *obj_ins, unsigned slot) +TraceRecorder::stobj_get_const_private_ptr(LIns *obj_ins, unsigned slot) { - JS_ASSERT(slot < JS_INITIAL_NSLOTS && slot != JSSLOT_PRIVATE); - return lir->insLoad(LIR_ldi, obj_ins, - offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset, - ACCSET_OTHER, LOAD_CONST); + LIns *vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); + return lir->insLoad(LIR_ldi, vaddr_ins, + slot * sizeof(Value) + sPayloadOffset, ACCSET_OTHER, LOAD_CONST); } void @@ -9818,13 +9799,12 @@ TraceRecorder::box_value_for_native_call(const Value &v, LIns *v_ins) #elif JS_BITS_PER_WORD == 64 LIns* -TraceRecorder::stobj_get_fslot_private_ptr(LIns *obj_ins, unsigned slot) +TraceRecorder::stobj_get_const_private_ptr(LIns *obj_ins, unsigned slot) { /* N.B. On 64-bit, privates are encoded differently from other pointers. */ - JS_ASSERT(slot < JS_INITIAL_NSLOTS && slot != JSSLOT_PRIVATE); - LIns *v_ins = lir->insLoad(LIR_ldq, obj_ins, - offsetof(JSObject, fslots) + slot * sizeof(Value), - ACCSET_OTHER, LOAD_CONST); + LIns *vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER); + LIns *v_ins = lir->insLoad(LIR_ldq, vaddr_ins, + slot * sizeof(Value) + sPayloadOffset, ACCSET_OTHER, LOAD_CONST); return lir->ins2ImmI(LIR_lshq, v_ins, 1); } @@ -10002,8 +9982,17 @@ TraceRecorder::stobj_get_parent(nanojit::LIns* obj_ins) LIns* TraceRecorder::stobj_get_private(nanojit::LIns* obj_ins) { - JS_STATIC_ASSERT(JSSLOT_PRIVATE == 0); - return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, fslots), ACCSET_OTHER); + return lir->insLoad(LIR_ldp, obj_ins, + offsetof(JSObject, privateData), + ACCSET_OTHER); +} + +LIns* +TraceRecorder::stobj_get_private_uint32(nanojit::LIns* obj_ins) +{ + return lir->insLoad(LIR_ldi, obj_ins, + offsetof(JSObject, privateData), + ACCSET_OTHER); } LIns* @@ -10567,7 +10556,7 @@ TraceRecorder::newArguments(LIns* callee_ins, bool strict) guard(false, lir->insEqP_0(argsobj_ins), OOM_EXIT); if (strict) { - LIns* argsData_ins = stobj_get_fslot_private_ptr(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); + LIns* argsData_ins = stobj_get_const_private_ptr(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); ptrdiff_t slotsOffset = offsetof(ArgumentsData, slots); cx->fp()->forEachCanonicalActualArg(BoxArg(this, slotsOffset, argsData_ins)); } @@ -10960,7 +10949,7 @@ TraceRecorder::getClassPrototype(JSObject* ctor, LIns*& proto_ins) // that pval is usable. JS_ASSERT(!pval.isPrimitive()); JSObject *proto = &pval.toObject(); - JS_ASSERT_IF(clasp != &js_ArrayClass, proto->emptyShape->getClass() == clasp); + JS_ASSERT_IF(clasp != &js_ArrayClass, proto->emptyShapes[0]->getClass() == clasp); proto_ins = INS_CONSTOBJ(proto); return RECORD_CONTINUE; @@ -10984,7 +10973,8 @@ TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins) /* Double-check that a native proto has a matching emptyShape. */ if (key != JSProto_Array) { JS_ASSERT(proto->isNative()); - EmptyShape *empty = proto->emptyShape; + JS_ASSERT(proto->emptyShapes); + EmptyShape *empty = proto->emptyShapes[0]; JS_ASSERT(empty); JS_ASSERT(JSCLASS_CACHED_PROTO_KEY(empty->getClass()) == key); } @@ -11919,7 +11909,7 @@ TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, const Shape* shape, set(&obj->getSlotRef(slot), v_ins); } else { LIns* dslots_ins = NULL; - stobj_set_slot(obj_ins, slot, dslots_ins, v, v_ins); + stobj_set_slot(obj, obj_ins, slot, dslots_ins, v, v_ins); } } @@ -12088,7 +12078,7 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, const Shape *sh JS_ASSERT(shape->hasShortID()); LIns* dslots_ins = NULL; - stobj_set_slot(callobj_ins, slot, dslots_ins, v, v_ins); + stobj_set_dslot(callobj_ins, slot, dslots_ins, v, v_ins); return RECORD_CONTINUE; } @@ -12626,8 +12616,8 @@ static bool OkToTraceTypedArrays = false; JS_REQUIRES_STACK void TraceRecorder::guardNotHole(LIns *argsobj_ins, LIns *idx_ins) { - // vp = &argsobj->fslots[JSSLOT_ARGS_DATA].slots[idx] - LIns* argsData_ins = stobj_get_fslot_private_ptr(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); + // vp = &argsobj->slots[JSSLOT_ARGS_DATA].slots[idx] + LIns* argsData_ins = stobj_get_const_private_ptr(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); LIns* slotOffset_ins = lir->ins2(LIR_addp, INS_CONSTWORD(offsetof(ArgumentsData, slots)), lir->insUI2P(lir->ins2ImmI(LIR_muli, idx_ins, sizeof(Value)))); @@ -13070,22 +13060,14 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) // be an integer. CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins)); - if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) { - /* - * Check for negative values bleeding through on 64-bit machines only, - * since we can't allocate large enough arrays for this on 32-bit - * machines. - */ - guard(true, lir->ins2ImmI(LIR_gei, idx_ins, 0), mismatchExit); - } - if (!js_EnsureDenseArrayCapacity(cx, obj, idx.toInt32())) RETURN_STOP_A("couldn't ensure dense array capacity for setelem"); // Grow the array if the index exceeds the capacity. This happens // rarely, eg. less than 1% of the time in SunSpider. LIns* capacity_ins = - addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_CAPACITY), + addName(lir->insLoad(LIR_ldi, obj_ins, + offsetof(JSObject, capacity), ACCSET_OTHER), "capacity"); LIns* br = lir->insBranch(LIR_jt, lir->ins2(LIR_ltui, idx_ins, capacity_ins), NULL); LIns* args[] = { idx_ins, obj_ins, cx_ins }; @@ -13095,7 +13077,7 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) // Get the address of the element. LIns *dslots_ins = - addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER), "dslots"); + addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER), "dslots"); JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this. LIns *addr_ins = lir->ins2(LIR_addp, dslots_ins, lir->ins2ImmI(LIR_lshp, lir->insUI2P(idx_ins), 3)); @@ -13301,7 +13283,7 @@ TraceRecorder::record_JSOP_GETFCSLOT() JSObject& callee = cx->fp()->callee(); LIns* callee_ins = get(&cx->fp()->calleeValue()); - LIns* upvars_ins = stobj_get_fslot_private_ptr(callee_ins, + LIns* upvars_ins = stobj_get_const_private_ptr(callee_ins, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS); unsigned index = GET_UINT16(cx->regs->pc); @@ -13566,7 +13548,7 @@ TraceRecorder::record_JSOP_APPLY() length = aobj->getArrayLength(); guard(true, lir->ins2ImmI(LIR_eqi, - stobj_get_fslot_uint32(aobj_ins, JSObject::JSSLOT_ARRAY_LENGTH), + stobj_get_private_uint32(aobj_ins), length), BRANCH_EXIT); } else if (aobj->isArguments()) { @@ -13947,8 +13929,10 @@ TraceRecorder::denseArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_ * the correct value. */ LIns* capacity_ins = - addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_CAPACITY), + addName(lir->insLoad(LIR_ldi, obj_ins, + offsetof(JSObject, capacity), ACCSET_OTHER), "capacity"); + jsuint capacity = obj->getDenseArrayCapacity(); bool within = (jsuint(idx) < capacity); if (!within) { @@ -13968,9 +13952,9 @@ TraceRecorder::denseArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_ /* Load the value and guard on its type to unbox it. */ LIns* dslots_ins = - addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER), "dslots"); - vp = &obj->dslots[jsuint(idx)]; - JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this. + addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER), "dslots"); + vp = &obj->slots[jsuint(idx)]; + JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this. addr_ins = lir->ins2(LIR_addp, dslots_ins, lir->ins2ImmI(LIR_lshp, lir->insUI2P(idx_ins), 3)); v_ins = unbox_value(*vp, addr_ins, 0, branchExit); @@ -14281,20 +14265,19 @@ TraceRecorder::record_JSOP_UINT16() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_NEWINIT() { - JSProtoKey key = JSProtoKey(GET_INT8(cx->regs->pc)); + JSProtoKey key = JSProtoKey(GET_UINT16(cx->regs->pc)); + uint32 count = GET_UINT16(cx->regs->pc + UINT16_LEN); + LIns* proto_ins; CHECK_STATUS_A(getClassPrototype(key, proto_ins)); LIns *v_ins; if (key == JSProto_Array) { - LIns *args[] = { lir->insImmI(0), proto_ins, cx_ins }; - v_ins = lir->insCall(&js_NewEmptyArray_ci, args); + LIns *args[] = { lir->insImmI(count), cx_ins }; + v_ins = lir->insCall(&js_InitializerArray_ci, args); } else { - LIns *args[] = { proto_ins, cx_ins }; - v_ins = lir->insCall((cx->regs->pc[JSOP_NEWINIT_LENGTH] != JSOP_ENDINIT) - ? &js_NonEmptyObject_ci - : &js_Object_tn_ci, - args); + LIns *args[] = { lir->insImmI(count), cx_ins }; + v_ins = lir->insCall(&js_InitializerObject_ci, args); } guard(false, lir->insEqP_0(v_ins), OOM_EXIT); stack(0, v_ins); @@ -15256,7 +15239,7 @@ TraceRecorder::record_JSOP_LAMBDA_FC() if (fun->u.i.nupvars) { JSUpvarArray *uva = fun->u.i.script->upvars(); - LIns* upvars_ins = stobj_get_fslot_private_ptr(closure_ins, + LIns* upvars_ins = stobj_get_const_private_ptr(closure_ins, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS); for (uint32 i = 0, n = uva->length; i < n; i++) { @@ -15340,7 +15323,7 @@ TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins) { // The following implements JSObject::isArgsLengthOverridden on trace. // ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden. - LIns *len_ins = stobj_get_fslot_uint32(argsobj_ins, JSObject::JSSLOT_ARGS_LENGTH); + LIns *len_ins = stobj_get_slot_uint32(argsobj_ins, JSObject::JSSLOT_ARGS_LENGTH); LIns *ovr_ins = lir->ins2(LIR_andi, len_ins, INS_CONST(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT)); guard(true, lir->insEqI_0(ovr_ins), snapshot(MISMATCH_EXIT)); return len_ins; @@ -16044,7 +16027,7 @@ TraceRecorder::record_JSOP_LENGTH() JS_ASSERT(obj->isSlowArray()); guardClass(obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT), LOAD_NORMAL); } - v_ins = lir->ins1(LIR_i2d, stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_ARRAY_LENGTH)); + v_ins = lir->ins1(LIR_i2d, stobj_get_private_uint32(obj_ins)); } else if (OkToTraceTypedArrays && js_IsTypedArray(obj)) { // Ensure array is a typed array and is the same type as what was written guardClass(obj_ins, obj->getClass(), snapshot(BRANCH_EXIT), LOAD_NORMAL); diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 749209e035e0..3437a387df2f 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1068,7 +1068,7 @@ class TraceRecorder * entries of the tracker accordingly. */ JS_REQUIRES_STACK void checkForGlobalObjectReallocation() { - if (global_dslots != globalObj->dslots) + if (global_dslots != globalObj->getSlots()) checkForGlobalObjectReallocationHelper(); } JS_REQUIRES_STACK void checkForGlobalObjectReallocationHelper(); @@ -1176,21 +1176,20 @@ class TraceRecorder nanojit::LIns* v_ins); void stobj_set_dslot(nanojit::LIns *obj_ins, unsigned slot, nanojit::LIns*& dslots_ins, const Value &v, nanojit::LIns* v_ins); - void stobj_set_slot(nanojit::LIns* obj_ins, unsigned slot, + void stobj_set_slot(JSObject *obj, nanojit::LIns* obj_ins, unsigned slot, nanojit::LIns*& dslots_ins, const Value &v, nanojit::LIns* v_ins); - void set_array_fslot(nanojit::LIns *obj_ins, unsigned slot, uint32 val); - nanojit::LIns* stobj_get_fslot_private_ptr(nanojit::LIns* obj_ins, - unsigned slot); - nanojit::LIns* stobj_get_fslot_uint32(nanojit::LIns* obj_ins, unsigned slot); - nanojit::LIns* stobj_set_fslot_uint32(nanojit::LIns* value_ins, nanojit::LIns* obj_ins, - unsigned slot); + nanojit::LIns* stobj_get_slot_uint32(nanojit::LIns* obj_ins, unsigned slot); nanojit::LIns* unbox_slot(JSObject *obj, nanojit::LIns *obj_ins, uint32 slot, VMSideExit *exit); nanojit::LIns* stobj_get_parent(nanojit::LIns* obj_ins); nanojit::LIns* stobj_get_private(nanojit::LIns* obj_ins); + nanojit::LIns* stobj_get_private_uint32(nanojit::LIns* obj_ins); nanojit::LIns* stobj_get_proto(nanojit::LIns* obj_ins); + /* For slots holding private pointers. */ + nanojit::LIns* stobj_get_const_private_ptr(nanojit::LIns *obj_ins, unsigned slot); + JS_REQUIRES_STACK AbortableRecordingStatus name(Value*& vp, nanojit::LIns*& ins, NameResult& nr); JS_REQUIRES_STACK AbortableRecordingStatus prop(JSObject* obj, nanojit::LIns* obj_ins, uint32 *slotp, nanojit::LIns** v_insp, diff --git a/js/src/jsvalue.h b/js/src/jsvalue.h index f56105589915..d99e187f71e4 100644 --- a/js/src/jsvalue.h +++ b/js/src/jsvalue.h @@ -828,6 +828,18 @@ PrivateValue(void *ptr) return v; } +static JS_ALWAYS_INLINE void +ClearValueRange(Value *vec, uintN len, bool useHoles) +{ + if (useHoles) { + for (uintN i = 0; i < len; i++) + vec[i].setMagic(JS_ARRAY_HOLE); + } else { + for (uintN i = 0; i < len; i++) + vec[i].setUndefined(); + } +} + /******************************************************************************/ /* diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 5339d00eee1b..449c95672c6b 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -179,14 +179,9 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegiste * Finds and returns the address of a known object and slot. */ Address objSlotRef(JSObject *obj, RegisterID reg, uint32 slot) { - if (slot < JS_INITIAL_NSLOTS) { - void *vp = &obj->getSlotRef(slot); - move(ImmPtr(vp), reg); - return Address(reg, 0); - } - move(ImmPtr(&obj->dslots), reg); + move(ImmPtr(&obj->slots), reg); loadPtr(reg, reg); - return Address(reg, (slot - JS_INITIAL_NSLOTS) * sizeof(Value)); + return Address(reg, slot * sizeof(Value)); } #ifdef JS_CPU_X86 diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 0caea46c6123..aa97628b2c2f 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -50,6 +50,7 @@ #include "assembler/jit/ExecutableAllocator.h" #include "assembler/assembler/LinkBuffer.h" #include "FrameState-inl.h" +#include "jsobjinlines.h" #include "jsscriptinlines.h" #include "InlineFrameAssembler.h" #include "jscompartment.h" @@ -1261,10 +1262,13 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_NEWINIT) { - jsint i = GET_INT8(PC); + jsint i = GET_UINT16(PC); + uint32 count = GET_UINT16(PC + UINT16_LEN); + JS_ASSERT(i == JSProto_Array || i == JSProto_Object); prepareStubCall(Uses(0)); + masm.move(Imm32(count), Registers::ArgReg1); if (i == JSProto_Array) stubCall(stubs::NewInitArray); else @@ -1490,8 +1494,8 @@ mjit::Compiler::generateMethod() RegisterID reg = frame.allocReg(); masm.loadPayload(Address(JSFrameReg, JSStackFrame::offsetOfCallee(fun)), reg); // obj->getFlatClosureUpvars() - Address upvarAddress(reg, offsetof(JSObject, fslots) + - JSObject::JSSLOT_FLAT_CLOSURE_UPVARS * sizeof(Value)); + masm.loadPtr(Address(reg, offsetof(JSObject, slots)), reg); + Address upvarAddress(reg, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS * sizeof(Value)); masm.loadPrivate(upvarAddress, reg); // push ((Value *) reg)[index] frame.freeReg(reg); @@ -2514,7 +2518,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck) #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); /* Copy the slot value to the expression stack. */ Address slot(objReg, 1 << 24); @@ -2615,7 +2619,7 @@ mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID obj #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); /* Copy the slot value to the expression stack. */ Address slot(objReg, 1 << 24); @@ -2746,7 +2750,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom) #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); /* Copy the slot value to the expression stack. */ Address slot(objReg, 1 << 24); @@ -2884,7 +2888,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom) #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); /* Copy the slot value to the expression stack. */ Address slot(objReg, 1 << 24); @@ -3052,7 +3056,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom) #elif defined JS_PUNBOX64 Label dslotsLoadLabel = masm.label(); #endif - masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); /* Store RHS into object slot. */ Address slot(objReg, 1 << 24); @@ -3616,8 +3620,7 @@ mjit::Compiler::iter(uintN flags) stubcc.linkExit(nullIterator, Uses(1)); /* Get NativeIterator from iter obj. :FIXME: X64, also most of this function */ - Address privSlot(ioreg, offsetof(JSObject, fslots) + sizeof(Value) * JSSLOT_PRIVATE); - masm.loadPtr(privSlot, nireg); + masm.loadPtr(Address(ioreg, offsetof(JSObject, privateData)), nireg); /* Test for active iterator. */ Address flagsAddr(nireg, offsetof(NativeIterator, flags)); @@ -3801,8 +3804,7 @@ mjit::Compiler::iterEnd() stubcc.linkExit(notIterator, Uses(1)); /* Get private from iter obj. :FIXME: X64 */ - Address privSlot(reg, offsetof(JSObject, fslots) + sizeof(Value) * JSSLOT_PRIVATE); - masm.loadPtr(privSlot, T1); + masm.loadPtr(Address(reg, offsetof(JSObject, privateData)), T1); RegisterID T2 = frame.allocReg(); @@ -3916,7 +3918,7 @@ mjit::Compiler::jsop_getgname(uint32 index) /* Garbage value. */ uint32 slot = 1 << 24; - masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); Address address(objReg, slot); /* @@ -4036,7 +4038,7 @@ mjit::Compiler::jsop_setgname(uint32 index) v = fe->getValue(); } - masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); Address address(objReg, slot); mic.load = masm.label(); diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 7c835e006a7e..250ba2b70334 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1207,26 +1207,21 @@ mjit::Compiler::jsop_setelem() stubcc.linkExit(guardDense, Uses(3)); /* guard within capacity */ - Address capacity(objReg, offsetof(JSObject, fslots) + - JSObject::JSSLOT_DENSE_ARRAY_CAPACITY * sizeof(Value)); + Address capacity(objReg, offsetof(JSObject, capacity)); Jump inRange; MaybeRegisterID maybeIdReg; if (id->isConstant()) { - inRange = masm.branch32(Assembler::LessThanOrEqual, - masm.payloadOf(capacity), + inRange = masm.branch32(Assembler::LessThanOrEqual, capacity, Imm32(id->getValue().toInt32())); } else { maybeIdReg = frame.copyDataIntoReg(id); - inRange = masm.branch32(Assembler::AboveOrEqual, maybeIdReg.reg(), - masm.payloadOf(capacity)); + inRange = masm.branch32(Assembler::AboveOrEqual, maybeIdReg.reg(), capacity); } stubcc.linkExit(inRange, Uses(3)); - /* dslots non-NULL */ - masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); - Jump guardSlots = masm.branchTestPtr(Assembler::Zero, objReg, objReg); - stubcc.linkExit(guardSlots, Uses(3)); + /* load dslots */ + masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); /* guard within capacity */ if (id->isConstant()) { @@ -1297,7 +1292,7 @@ mjit::Compiler::jsop_setelem() extendedObject.linkTo(syncTarget, &stubcc.masm); /* Update the array length if needed. Don't worry about overflow. */ - Address arrayLength(baseReg, offsetof(JSObject, fslots[JSObject::JSSLOT_ARRAY_LENGTH])); + Address arrayLength(baseReg, offsetof(JSObject, privateData)); stubcc.masm.load32(arrayLength, T1); Jump underLength = stubcc.masm.branch32(Assembler::LessThan, idReg, T1); stubcc.masm.move(idReg, T1); @@ -1307,7 +1302,7 @@ mjit::Compiler::jsop_setelem() /* Restore the dslots register if we clobbered it with the object. */ if (baseReg == objReg) - stubcc.masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + stubcc.masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); /* Rejoin OOL path with inline path to do the store itself. */ Jump jmpHoleExit = stubcc.masm.jump(); @@ -1360,22 +1355,17 @@ mjit::Compiler::jsop_getelem_dense(FrameEntry *obj, FrameEntry *id, RegisterID o /* Guard within capacity. */ Jump inRange; - Address capacity(objReg, offsetof(JSObject, fslots) + - JSObject::JSSLOT_DENSE_ARRAY_CAPACITY * sizeof(Value)); + Address capacity(objReg, offsetof(JSObject, capacity)); if (id->isConstant()) { - inRange = masm.branch32(Assembler::LessThanOrEqual, - masm.payloadOf(capacity), + inRange = masm.branch32(Assembler::LessThanOrEqual, capacity, Imm32(id->getValue().toInt32())); } else { - inRange = masm.branch32(Assembler::AboveOrEqual, idReg.reg(), - masm.payloadOf(capacity)); + inRange = masm.branch32(Assembler::AboveOrEqual, idReg.reg(), capacity); } stubcc.linkExit(inRange, Uses(2)); - /* dslots non-NULL */ - masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); - Jump guardSlots = masm.branchTestPtr(Assembler::Zero, objReg, objReg); - stubcc.linkExit(guardSlots, Uses(2)); + /* load dslots */ + masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg); /* guard within capacity */ if (id->isConstant()) { diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index eada7e730948..459e24b0e50f 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -108,8 +108,6 @@ ic::GetGlobalName(VMFrame &f, ic::MICInfo *ic) repatch.repatch(ic->shape, obj->shape()); /* Patch loads. */ - JS_ASSERT(slot >= JS_INITIAL_NSLOTS); - slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); JSC::RepatchBuffer loads(ic->load.executableAddress(), 32, false); #if defined JS_CPU_X86 @@ -189,8 +187,6 @@ ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic) repatch.repatch(ic->shape, obj->shape()); /* Patch loads. */ - JS_ASSERT(slot >= JS_INITIAL_NSLOTS); - slot -= JS_INITIAL_NSLOTS; slot *= sizeof(Value); JSC::RepatchBuffer stores(ic->load.executableAddress(), 32, false); diff --git a/js/src/methodjit/NunboxAssembler.h b/js/src/methodjit/NunboxAssembler.h index 28a606dd0c20..e4fceea43c94 100644 --- a/js/src/methodjit/NunboxAssembler.h +++ b/js/src/methodjit/NunboxAssembler.h @@ -90,13 +90,14 @@ class Assembler : public BaseAssembler return BaseIndex(address.base, address.index, address.scale, address.offset + TAG_OFFSET); } - void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, RegisterID type, RegisterID data) { + void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, bool inlineAccess, + RegisterID type, RegisterID data) { JS_ASSERT(type != data); - Address address(obj, offsetof(JSObject, fslots) + slot * sizeof(Value)); + Address address(obj, JSObject::getFixedSlotOffset(slot)); RegisterID activeAddressReg = obj; - if (slot >= JS_INITIAL_NSLOTS) { - loadPtr(Address(obj, offsetof(JSObject, dslots)), clobber); - address = Address(clobber, (slot - JS_INITIAL_NSLOTS) * sizeof(Value)); + if (!inlineAccess) { + loadPtr(Address(obj, offsetof(JSObject, slots)), clobber); + address = Address(clobber, slot * sizeof(Value)); activeAddressReg = clobber; } if (activeAddressReg == type) { @@ -202,9 +203,8 @@ class Assembler : public BaseAssembler } void loadFunctionPrivate(RegisterID base, RegisterID to) { - Address privSlot(base, offsetof(JSObject, fslots) + - JSSLOT_PRIVATE * sizeof(Value)); - loadPtr(privSlot, to); + Address priv(base, offsetof(JSObject, privateData)); + loadPtr(priv, to); } Jump testNull(Assembler::Condition cond, RegisterID reg) { diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 13c8d4e09cdd..42aa6f6ebdb7 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -250,7 +250,7 @@ class SetPropCompiler : public PICStubCompiler repatcher.relinkCallerToTrampoline(retPtr, target); } - bool patchInline(const Shape *shape) + bool patchInline(const Shape *shape, bool inlineSlot) { JS_ASSERT(!pic.inlinePathPatched); JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress()); @@ -258,7 +258,7 @@ class SetPropCompiler : public PICStubCompiler PICRepatchBuffer repatcher(pic, pic.fastPathStart); int32 offset; - if (shape->slot < JS_INITIAL_NSLOTS) { + if (inlineSlot) { JSC::CodeLocationInstruction istr; istr = pic.storeBack.instructionAtOffset(dslotsLoadOffset()); repatcher.repatchLoadPtrToLEA(istr); @@ -270,12 +270,12 @@ class SetPropCompiler : public PICStubCompiler // Because the offset is wrong, it's necessary to correct it // below. // - int32 diff = int32(offsetof(JSObject, fslots)) - - int32(offsetof(JSObject, dslots)); + int32 diff = int32(JSObject::getFixedSlotOffset(0)) - + int32(offsetof(JSObject, slots)); JS_ASSERT(diff != 0); offset = (int32(shape->slot) * sizeof(Value)) + diff; } else { - offset = (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value); + offset = shape->slot * sizeof(Value); } uint32 shapeOffs = pic.shapeGuard + inlineShapeOffset(); @@ -311,7 +311,7 @@ class SetPropCompiler : public PICStubCompiler repatcher.relink(lastStubSecondShapeGuard, cs); } - bool generateStub(uint32 initialShape, const Shape *shape, bool adding) + bool generateStub(uint32 initialShape, const Shape *shape, bool adding, bool inlineSlot) { /* Exits to the slow path. */ Vector slowExits(cx); @@ -384,28 +384,21 @@ class SetPropCompiler : public PICStubCompiler } } - if (shape->slot < JS_INITIAL_NSLOTS) { + if (inlineSlot) { Address address(pic.objReg, - offsetof(JSObject, fslots) + shape->slot * sizeof(Value)); + JSObject::getFixedSlotOffset(shape->slot)); masm.storeValue(pic.u.vr, address); } else { - /* Check dslots non-zero. */ - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.shapeReg); - Jump emptyDslots = masm.branchPtr(Assembler::Equal, pic.shapeReg, ImmPtr(0)); - if (!slowExits.append(emptyDslots)) - return false; - /* Check capacity. */ - Address capacity(pic.shapeReg, -ptrdiff_t(sizeof(Value))); + Address capacity(pic.objReg, offsetof(JSObject, capacity)); masm.load32(masm.payloadOf(capacity), pic.shapeReg); Jump overCapacity = masm.branch32(Assembler::LessThanOrEqual, pic.shapeReg, Imm32(shape->slot)); if (!slowExits.append(overCapacity)) return false; - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.shapeReg); - Address address(pic.shapeReg, - (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value)); + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.shapeReg); + Address address(pic.shapeReg, shape->slot * sizeof(Value)); masm.storeValue(pic.u.vr, address); } @@ -426,10 +419,10 @@ class SetPropCompiler : public PICStubCompiler masm.store32(pic.shapeReg, flags); } } else if (shape->hasDefaultSetter()) { - Address address(pic.objReg, offsetof(JSObject, fslots) + shape->slot * sizeof(Value)); - if (shape->slot >= JS_INITIAL_NSLOTS) { - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg); - address = Address(pic.objReg, (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value)); + Address address(pic.objReg, JSObject::getFixedSlotOffset(shape->slot)); + if (!inlineSlot) { + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); + address = Address(pic.objReg, shape->slot * sizeof(Value)); } // If the scope is branded, or has a method barrier. It's now necessary @@ -473,9 +466,9 @@ class SetPropCompiler : public PICStubCompiler { if (shape->setterOp() == SetCallVar) slot += fun->nargs; - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg); + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); - Address dslot(pic.objReg, slot * sizeof(Value)); + Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value)); masm.storeValue(pic.u.vr, dslot); } @@ -658,7 +651,7 @@ class SetPropCompiler : public PICStubCompiler if (obj->numSlots() != slots) return disable("insufficient slot capacity"); - return generateStub(initialShape, shape, true); + return generateStub(initialShape, shape, true, !obj->hasSlotsArray()); } AutoPropertyDropper dropper(cx, holder, prop); @@ -686,10 +679,10 @@ class SetPropCompiler : public PICStubCompiler !obj->brandedOrHasMethodBarrier() && shape->hasDefaultSetter() && !obj->isDenseArray()) { - return patchInline(shape); + return patchInline(shape, !obj->hasSlotsArray()); } - return generateStub(obj->shape(), shape, false); + return generateStub(obj->shape(), shape, false, !obj->hasSlotsArray()); } }; @@ -793,9 +786,8 @@ class GetPropCompiler : public PICStubCompiler Address clasp(pic.objReg, offsetof(JSObject, clasp)); Jump notArgs = masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(obj->getClass())); - masm.load32(Address(pic.objReg, - offsetof(JSObject, fslots) - + JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)), + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); + masm.load32(Address(pic.objReg, JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)), pic.objReg); masm.move(pic.objReg, pic.shapeReg); Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg, Imm32(1)); @@ -834,10 +826,7 @@ class GetPropCompiler : public PICStubCompiler ImmPtr(&js_SlowArrayClass)); isDense.linkTo(masm.label(), &masm); - masm.load32(Address(pic.objReg, - offsetof(JSObject, fslots) - + JSObject::JSSLOT_ARRAY_LENGTH * sizeof(Value)), - pic.objReg); + masm.load32(Address(pic.objReg, offsetof(JSObject, privateData)), pic.objReg); Jump oob = masm.branch32(Assembler::Above, pic.objReg, Imm32(JSVAL_INT_MAX)); masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); Jump done = masm.jump(); @@ -920,7 +909,8 @@ class GetPropCompiler : public PICStubCompiler Jump shapeMismatch = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(obj->shape())); if (!shape->isMethod()) { - masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg); + masm.loadSlot(pic.objReg, pic.objReg, shape->slot, !obj->hasSlotsArray(), + pic.shapeReg, pic.objReg); } else { masm.loadValueAsComponents(ObjectValue(shape->methodObject()), pic.shapeReg, pic.objReg); @@ -992,7 +982,7 @@ class GetPropCompiler : public PICStubCompiler PICRepatchBuffer repatcher(pic, pic.fastPathStart); int32 offset; - if (shape->slot < JS_INITIAL_NSLOTS) { + if (!holder->hasSlotsArray()) { JSC::CodeLocationInstruction istr; istr = pic.storeBack.instructionAtOffset(dslotsLoad()); repatcher.repatchLoadPtrToLEA(istr); @@ -1004,12 +994,12 @@ class GetPropCompiler : public PICStubCompiler // Because the offset is wrong, it's necessary to correct it // below. // - int32 diff = int32(offsetof(JSObject, fslots)) - - int32(offsetof(JSObject, dslots)); + int32 diff = int32(JSObject::getFixedSlotOffset(0)) - + int32(offsetof(JSObject, slots)); JS_ASSERT(diff != 0); offset = (int32(shape->slot) * sizeof(Value)) + diff; } else { - offset = (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value); + offset = shape->slot * sizeof(Value); } uint32 shapeOffs = pic.shapeGuard + inlineShapeOffset(); @@ -1116,7 +1106,8 @@ class GetPropCompiler : public PICStubCompiler /* Load the value out of the object. */ if (!shape->isMethod()) { - masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg); + masm.loadSlot(pic.objReg, pic.objReg, shape->slot, !holder->hasSlotsArray(), + pic.shapeReg, pic.objReg); } else { masm.loadValueAsComponents(ObjectValue(shape->methodObject()), pic.shapeReg, pic.objReg); @@ -1323,7 +1314,7 @@ class GetElemCompiler : public PICStubCompiler PICRepatchBuffer repatcher(pic, pic.fastPathStart); int32 offset; - if (shape->slot < JS_INITIAL_NSLOTS) { + if (!holder->hasSlotsArray()) { JSC::CodeLocationInstruction istr = pic.storeBack.instructionAtOffset(dslotsLoad()); repatcher.repatchLoadPtrToLEA(istr); @@ -1334,11 +1325,11 @@ class GetElemCompiler : public PICStubCompiler // Because the offset is wrong, it's necessary to correct it // below. // - int32 diff = int32(offsetof(JSObject, fslots)) - int32(offsetof(JSObject, dslots)); + int32 diff = int32(JSObject::getFixedSlotOffset(0)) - int32(offsetof(JSObject, slots)); JS_ASSERT(diff != 0); offset = (int32(shape->slot) * sizeof(Value)) + diff; } else { - offset = (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value); + offset = shape->slot * sizeof(Value); } uint32 shapeOffset = pic.shapeGuard + inlineShapeOffset(); @@ -1468,7 +1459,8 @@ class GetElemCompiler : public PICStubCompiler /* Load the value out of the object. */ if (!shape->isMethod()) { - masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg); + masm.loadSlot(pic.objReg, pic.objReg, shape->slot, !holder->hasSlotsArray(), + pic.shapeReg, pic.objReg); } else { masm.loadValueAsComponents(ObjectValue(shape->methodObject()), pic.shapeReg, pic.objReg); @@ -1662,7 +1654,7 @@ class ScopeNameCompiler : public PICStubCompiler Jump finalShape = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(holder->shape())); if (!shape->isMethod()) { - masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg); + masm.loadSlot(pic.objReg, pic.objReg, shape->slot, false, pic.shapeReg, pic.objReg); } else { masm.loadValueAsComponents(ObjectValue(shape->methodObject()), pic.shapeReg, pic.objReg); @@ -1764,11 +1756,11 @@ class ScopeNameCompiler : public PICStubCompiler escapedFrame.linkTo(masm.label(), &masm); { - masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg); + masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); if (kind == VAR) slot += fun->nargs; - Address dslot(pic.objReg, slot * sizeof(Value)); + Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value)); /* Safe because type is loaded first. */ masm.loadValueAsComponents(dslot, pic.shapeReg, pic.objReg); diff --git a/js/src/methodjit/PunboxAssembler.h b/js/src/methodjit/PunboxAssembler.h index 0fae0464db09..7f7972ddebb6 100644 --- a/js/src/methodjit/PunboxAssembler.h +++ b/js/src/methodjit/PunboxAssembler.h @@ -93,12 +93,13 @@ class Assembler : public BaseAssembler return address; } - void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, RegisterID type, RegisterID data) { + void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, bool inlineAccess, + RegisterID type, RegisterID data) { JS_ASSERT(type != data); - Address address(obj, offsetof(JSObject, fslots) + slot * sizeof(Value)); - if (slot >= JS_INITIAL_NSLOTS) { - loadPtr(Address(obj, offsetof(JSObject, dslots)), clobber); - address = Address(clobber, (slot - JS_INITIAL_NSLOTS) * sizeof(Value)); + Address address(obj, JSObject::getFixedSlotOffset(slot)); + if (!inlineAccess) { + loadPtr(Address(obj, offsetof(JSObject, slots)), clobber); + address = Address(clobber, slot * sizeof(Value)); } loadValueAsComponents(address, type, data); @@ -222,9 +223,8 @@ class Assembler : public BaseAssembler } void loadFunctionPrivate(RegisterID base, RegisterID to) { - Address privSlot(base, offsetof(JSObject, fslots) + - JSSLOT_PRIVATE * sizeof(Value)); - loadPtr(privSlot, to); + Address priv(base, offsetof(JSObject, privateData)); + loadPtr(priv, to); } Jump testNull(Assembler::Condition cond, RegisterID reg) { diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index c1db93499e55..a1ae52945d9a 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1436,21 +1436,25 @@ stubs::Neg(VMFrame &f) } JSObject * JS_FASTCALL -stubs::NewInitArray(VMFrame &f) +stubs::NewInitArray(VMFrame &f, uint32 count) { - JSObject *obj = js_NewArrayObject(f.cx, 0, NULL); - if (!obj) + JSContext *cx = f.cx; + gc::FinalizeKind kind = GuessObjectGCKind(count, true); + + JSObject *obj = NewArrayWithKind(cx, kind); + if (!obj || !obj->ensureSlots(cx, count)) THROWV(NULL); return obj; } JSObject * JS_FASTCALL -stubs::NewInitObject(VMFrame &f) +stubs::NewInitObject(VMFrame &f, uint32 count) { JSContext *cx = f.cx; + gc::FinalizeKind kind = GuessObjectGCKind(count, false); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); - if (!obj) + JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + if (!obj || !obj->ensureSlots(cx, count)) THROWV(NULL); return obj; diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index e70484669d8a..7ee6b00670a8 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -48,8 +48,8 @@ namespace mjit { namespace stubs { void JS_FASTCALL This(VMFrame &f); -JSObject * JS_FASTCALL NewInitArray(VMFrame &f); -JSObject * JS_FASTCALL NewInitObject(VMFrame &f); +JSObject * JS_FASTCALL NewInitArray(VMFrame &f, uint32 count); +JSObject * JS_FASTCALL NewInitObject(VMFrame &f, uint32 count); JSObject * JS_FASTCALL NewArray(VMFrame &f, uint32 len); void JS_FASTCALL Trap(VMFrame &f, jsbytecode *pc); void JS_FASTCALL Debugger(VMFrame &f, jsbytecode *pc); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index babacf5f9549..832645183b23 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5398,7 +5398,7 @@ main(int argc, char **argv, char **envp) CALIBRATION_DELAY_COUNT = 0; #endif - rt = JS_NewRuntime(128L * 1024L * 1024L); + rt = JS_NewRuntime(160L * 1024L * 1024L); if (!rt) return 1; diff --git a/js/src/trace-test/tests/basic/testBug597736.js b/js/src/trace-test/tests/basic/testBug597736.js index feeadcecdab3..ded33842776f 100644 --- a/js/src/trace-test/tests/basic/testBug597736.js +++ b/js/src/trace-test/tests/basic/testBug597736.js @@ -6,8 +6,8 @@ function leak_test() { // To make sure that we have no references to the function f after this // function returns due via the conservative scan of the native stack we - // loop here twice overwriting the stack and registers with new garabge. - for (var j = 0; j != 2; ++j) { + // loop here multiple times overwriting the stack and registers with new garabge. + for (var j = 0; j != 8; ++j) { var f = Function("a", "var s = 0; for (var i = 0; i != 100; ++i) s += a.b; return s;"); var c = {b: 1, f: f, leakDetection: makeFinalizeObserver()}; f({ __proto__: { __proto__: c}}); @@ -26,7 +26,7 @@ function test() gc(); gc(); var n = finalizeCount(); - assertEq(base < finalizeCount(), true, "Some finalizations must happen"); + assertEq(base + 4 < finalizeCount(), true, "Some finalizations must happen"); } test(); diff --git a/js/src/trace-test/tests/basic/testHoleInDenseArray.js b/js/src/trace-test/tests/basic/testHoleInDenseArray.js index eb041a2055c9..ba7a2c01e16c 100644 --- a/js/src/trace-test/tests/basic/testHoleInDenseArray.js +++ b/js/src/trace-test/tests/basic/testHoleInDenseArray.js @@ -8,7 +8,8 @@ function f(i) { return 1; } -var arr = [ false, false, false, false, false, , , , ]; +/* trailing 'true' ensures array has capacity >= 10 */ +var arr = [ false, false, false, false, false, , , , , , true ]; for (var i = 0; i < 10; ++i) { (s = arr[i]) + f(i); diff --git a/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js b/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js index 30974f3dafd9..11f2ff0a659c 100644 --- a/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js @@ -6,5 +6,5 @@ function caller(obj) { var x = ({ dana : "zuul" }); return x; } -trap(caller, 20, "x = 'success'; nop()"); +trap(caller, 23, "x = 'success'; nop()"); assertEq(caller(this), "success"); diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index b2a19facae8b..86673f214b6b 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -1382,10 +1382,10 @@ DebugCheckWrapperClass(JSObject* obj) // Only use these macros if IS_WRAPPER_CLASS(obj->getClass()) is true. #define IS_WN_WRAPPER_OBJECT(obj) \ (DebugCheckWrapperClass(obj) && \ - obj->getSlot(JSSLOT_START(obj->getClass())).isUndefined()) + obj->getSlot(0).isUndefined()) #define IS_SLIM_WRAPPER_OBJECT(obj) \ (DebugCheckWrapperClass(obj) && \ - !obj->getSlot(JSSLOT_START(obj->getClass())).isUndefined()) + !obj->getSlot(0).isUndefined()) // Use these macros if IS_WRAPPER_CLASS(obj->getClass()) might be false. // Avoid calling them if IS_WRAPPER_CLASS(obj->getClass()) can only be @@ -2289,7 +2289,7 @@ extern JSBool MorphSlimWrapper(JSContext *cx, JSObject *obj); static inline XPCWrappedNativeProto* GetSlimWrapperProto(JSObject *obj) { - const js::Value &v = obj->getSlot(JSSLOT_START(obj->getClass())); + const js::Value &v = obj->getSlot(0); return static_cast(v.toPrivate()); } diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 1e1a76d903ea..43ee48e530e8 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -53,9 +53,9 @@ namespace xpc { using namespace js; -static const uint32 JSSLOT_WN_OBJ = JSSLOT_PRIVATE; -static const uint32 JSSLOT_RESOLVING = JSSLOT_PRIVATE + 1; -static const uint32 JSSLOT_EXPANDO = JSSLOT_PRIVATE + 2; +static const uint32 JSSLOT_WN_OBJ = 0; +static const uint32 JSSLOT_RESOLVING = 1; +static const uint32 JSSLOT_EXPANDO = 2; class ResolvingId { From 51d64df9b8a5f64f500a0d88f04ff6d5413914ac Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Wed, 13 Oct 2010 15:41:53 -0700 Subject: [PATCH 278/284] Fix use of uninitialized variable, bug 584917 followup. r=mrbkap --- js/src/jsobj.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 9cc01a859837..e230eea199a8 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -6556,12 +6556,8 @@ DumpShape(const Shape &shape) JS_FRIEND_API(void) js_DumpObject(JSObject *obj) { - uint32 i, slots; - Class *clasp; - jsuint reservedEnd; - fprintf(stderr, "object %p\n", (void *) obj); - clasp = obj->getClass(); + Class *clasp = obj->getClass(); fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name); fprintf(stderr, "flags:"); @@ -6591,9 +6587,9 @@ js_DumpObject(JSObject *obj) fprintf(stderr, "\n"); if (obj->isDenseArray()) { - slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity()); + uint slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity()); fprintf(stderr, "elements\n"); - for (i = 0; i < slots; i++) { + for (uint i = 0; i < slots; i++) { fprintf(stderr, " %3d: ", i); dumpValue(obj->getDenseArrayElement(i)); fprintf(stderr, "\n"); @@ -6623,9 +6619,9 @@ js_DumpObject(JSObject *obj) fprintf(stderr, "private %p\n", obj->getPrivate()); fprintf(stderr, "slots:\n"); - reservedEnd = i + JSCLASS_RESERVED_SLOTS(clasp); - slots = obj->slotSpan(); - for (; i < slots; i++) { + uint reservedEnd = JSCLASS_RESERVED_SLOTS(clasp); + uint slots = obj->slotSpan(); + for (uint i = 0; i < slots; i++) { fprintf(stderr, " %3d ", i); if (i < reservedEnd) fprintf(stderr, "(reserved) "); From 0276090aa4acc89eb5684971a7194c6bebb44035 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Wed, 13 Oct 2010 16:10:15 -0700 Subject: [PATCH 279/284] Fix memory leak, change uint to unsigned, bug 584917 followup. r=brendan,njn --- js/src/jsgcinlines.h | 12 ++++++------ js/src/jsobj.cpp | 10 +++++----- js/src/jsobjinlines.h | 2 ++ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 162aab29b721..8e76a8248058 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -253,13 +253,13 @@ MarkChildren(JSTracer *trc, JSObject *obj) if (JSObject *parent = obj->getParent()) MarkObject(trc, *parent, "parent"); - if (obj->emptyShapes) { - int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1; - for (int i = 0; i < count; i++) { - if (obj->emptyShapes[i]) - obj->emptyShapes[i]->trace(trc); - } + if (obj->emptyShapes) { + int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1; + for (int i = 0; i < count; i++) { + if (obj->emptyShapes[i]) + obj->emptyShapes[i]->trace(trc); } + } /* Delegate to ops or the native marking op. */ TraceOp op = obj->getOps()->trace; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index e230eea199a8..9a4d104c68fa 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -6587,9 +6587,9 @@ js_DumpObject(JSObject *obj) fprintf(stderr, "\n"); if (obj->isDenseArray()) { - uint slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity()); + unsigned slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity()); fprintf(stderr, "elements\n"); - for (uint i = 0; i < slots; i++) { + for (unsigned i = 0; i < slots; i++) { fprintf(stderr, " %3d: ", i); dumpValue(obj->getDenseArrayElement(i)); fprintf(stderr, "\n"); @@ -6619,9 +6619,9 @@ js_DumpObject(JSObject *obj) fprintf(stderr, "private %p\n", obj->getPrivate()); fprintf(stderr, "slots:\n"); - uint reservedEnd = JSCLASS_RESERVED_SLOTS(clasp); - uint slots = obj->slotSpan(); - for (uint i = 0; i < slots; i++) { + unsigned reservedEnd = JSCLASS_RESERVED_SLOTS(clasp); + unsigned slots = obj->slotSpan(); + for (unsigned i = 0; i < slots; i++) { fprintf(stderr, " %3d ", i); if (i < reservedEnd) fprintf(stderr, "(reserved) "); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index f14d9e9ef456..f103023f5a65 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -675,6 +675,8 @@ JSObject::finish(JSContext *cx) #endif if (hasSlotsArray()) freeSlotsArray(cx); + if (emptyShapes) + cx->free(emptyShapes); #ifdef JS_THREADSAFE js_FinishTitle(cx, &title); #endif From cbe62cd2cbb24e03e69929d63b079331885406e9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 13 Oct 2010 16:39:20 -0700 Subject: [PATCH 280/284] Bug 600779 - TM: allow for branches that are always taken. r=dmandelin. --- js/src/jstracer.cpp | 234 ++++++++++++++++++++++++++++---------------- js/src/jstracer.h | 7 +- 2 files changed, 154 insertions(+), 87 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index ea0eaebaf241..9e971649c27b 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2512,10 +2512,10 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag LIns* counterPtr = INS_CONSTPTR((void *) &JS_THREAD_DATA(cx)->iterationCounter); LIns* counterValue = lir->insLoad(LIR_ldi, counterPtr, 0, ACCSET_OTHER, LOAD_VOLATILE); LIns* test = lir->ins2ImmI(LIR_lti, counterValue, MIN_LOOP_ITERS); - LIns *branch = lir->insBranch(LIR_jf, test, NULL); + LIns *branch = unoptimizableCondBranch(LIR_jf, test); counterValue = lir->ins2(LIR_addi, counterValue, INS_CONST(1)); lir->insStore(counterValue, counterPtr, 0, ACCSET_OTHER); - label(branch); + labelForBranch(branch); } #endif } @@ -3964,8 +3964,7 @@ TraceRecorder::addr(Value* p) { return isGlobal(p) ? lir->ins2(LIR_addp, eos_ins, INS_CONSTWORD(nativeGlobalOffset(p))) - : lir->ins2(LIR_addp, lirbuf->sp, - INS_CONSTWORD(nativespOffset(p))); + : lir->ins2(LIR_addp, lirbuf->sp, INS_CONSTWORD(nativespOffset(p))); } JS_REQUIRES_STACK inline bool @@ -5374,13 +5373,13 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit) #endif LIns* rec = lir->insCall(ci, args); LIns* lr = lir->insLoad(LIR_ldp, rec, offsetof(GuardRecord, exit), ACCSET_OTHER); - LIns* nested = lir->insBranch(LIR_jt, - lir->ins2ImmI(LIR_eqi, - lir->insLoad(LIR_ldi, lr, - offsetof(VMSideExit, exitType), - ACCSET_OTHER), - NESTED_EXIT), - NULL); + LIns* nested = + unoptimizableCondBranch(LIR_jt, + lir->ins2ImmI(LIR_eqi, + lir->insLoad(LIR_ldi, lr, + offsetof(VMSideExit, exitType), + ACCSET_OTHER), + NESTED_EXIT)); /* * If the tree exits on a regular (non-nested) guard, keep updating lastTreeExitGuard @@ -5395,13 +5394,13 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit) * and we unwind the tree call stack. We store the first (innermost) tree call guard in state * and we will try to grow the outer tree the failing call was in starting at that guard. */ - label(nested); - LIns* done2 = lir->insBranch(LIR_jf, - lir->insEqP_0(lir->insLoad(LIR_ldp, - lirbuf->state, - offsetof(TracerState, lastTreeCallGuard), - ACCSET_OTHER)), - NULL); + labelForBranch(nested); + LIns* done2 = + unoptimizableCondBranch(LIR_jf, + lir->insEqP_0(lir->insLoad(LIR_ldp, + lirbuf->state, + offsetof(TracerState, lastTreeCallGuard), + ACCSET_OTHER))); lir->insStore(lr, lirbuf->state, offsetof(TracerState, lastTreeCallGuard), ACCSET_OTHER); lir->insStore(lir->ins2(LIR_addp, lir->insLoad(LIR_ldp, lirbuf->state, offsetof(TracerState, rp), @@ -5413,7 +5412,7 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit) sizeof(void*) == 4 ? 2 : 3))), lirbuf->state, offsetof(TracerState, rpAtLastTreeCall), ACCSET_OTHER); - label(done1, done2); + labelForBranches(done1, done2); /* * Keep updating outermostTreeExit so that TracerState always contains the most recent @@ -8406,12 +8405,14 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) * into -2147483648 / -1, because it can raise an overflow exception. */ if (!d1->isImmI()) { - LIns* gt = lir->insBranch(LIR_jt, lir->ins2ImmI(LIR_gti, d1, 0), NULL); - guard(false, lir->insEqI_0(d1), exit); - guard(false, lir->ins2(LIR_andi, - lir->ins2ImmI(LIR_eqi, d0, 0x80000000), - lir->ins2ImmI(LIR_eqi, d1, -1)), exit); - label(gt); + LIns* br; + if (condBranch(LIR_jt, lir->ins2ImmI(LIR_gti, d1, 0), &br)) { + guard(false, lir->insEqI_0(d1), exit); + guard(false, lir->ins2(LIR_andi, + lir->ins2ImmI(LIR_eqi, d0, 0x80000000), + lir->ins2ImmI(LIR_eqi, d1, -1)), exit); + labelForBranch(br); + } } else { if (d1->immI() == -1) guard(false, lir->ins2ImmI(LIR_eqi, d0, 0x80000000), exit); @@ -8437,14 +8438,15 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) result = lir->ins1(v = LIR_modi, lir->ins2(LIR_divi, d0, d1)); /* If the result is not 0, it is always within the integer domain. */ - LIns* branch = lir->insBranch(LIR_jf, lir->insEqI_0(result), NULL); - - /* - * If the result is zero, we must exit if the lhs is negative since - * the result is -0 in this case, which is not in the integer domain. - */ - guard(false, lir->ins2ImmI(LIR_lti, d0, 0), exit); - label(branch); + LIns* br; + if (condBranch(LIR_jf, lir->insEqI_0(result), &br)) { + /* + * If the result is zero, we must exit if the lhs is negative since + * the result is -0 in this case, which is not in the integer domain. + */ + guard(false, lir->ins2ImmI(LIR_lti, d0, 0), exit); + labelForBranch(br); + } break; } #endif @@ -8482,24 +8484,68 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) return lir->ins1(LIR_i2d, result); } -/* Inserts a label and updates 'branch' to branch to it, if 'branch' is non-NULL. */ -void -TraceRecorder::label(LIns* br) +/* + * If the branch is always taken, return false; the code jumped over by the + * branch need not be generated. If the branch is never taken, return true + * and put NULL in *brOut. Otherwise, return true and put the branch in + * *brOut. + */ +bool +TraceRecorder::condBranch(LOpcode op, LIns* cond, LIns** brOut) { - if (br) - br->setTarget(lir->ins0(LIR_label)); + JS_ASSERT(op == LIR_jt || op == LIR_jf); + *brOut = NULL; + if (cond->isImmI()) { + int32 condval = cond->immI(); + JS_ASSERT(condval == 0 || condval == 1); + if ((op == LIR_jt && condval == 1) || (op == LIR_jf && condval == 0)) + return false; /* branch is always taken */ + } + *brOut = lir->insBranch(op, cond, NULL); + return true; } -/* Similar to the other label(), but for two branches. */ +/* + * Like condBranch(), but for when we know the branch condition cannot be + * optimized to a constant, eg. because one of the operands is the result of a + * volatile load. + */ +LIns* +TraceRecorder::unoptimizableCondBranch(LOpcode op, LIns* cond) +{ + JS_ASSERT(op == LIR_jt || op == LIR_jf); + JS_ASSERT(!cond->isImmI()); + return lir->insBranch(op, cond, NULL); +} + +/* + * Inserts a label and updates 'branch' to branch to it, if 'branch' is non-NULL. + * ('branch' may be NULL if it was a conditional branch and its condition was + * a constant value that resulted in the branch never being taken.) + */ void -TraceRecorder::label(LIns* br1, LIns* br2) +TraceRecorder::labelForBranch(LIns* br) +{ + if (br) { + JS_ASSERT(br->isop(LIR_j) || br->isop(LIR_jt) || br->isop(LIR_jf)); + br->setTarget(lir->ins0(LIR_label)); + } +} + +/* Similar to the other labelForBranch(), but for two branches. */ +void +TraceRecorder::labelForBranches(LIns* br1, LIns* br2) { if (br1 || br2) { LIns* label = lir->ins0(LIR_label); - if (br1) + if (br1) { + JS_ASSERT(br1->isop(LIR_j) || br1->isop(LIR_jt) || br1->isop(LIR_jf)); br1->setTarget(label); - if (br2) + } + if (br2) { + JS_ASSERT(br2->isop(LIR_j) || br2->isop(LIR_jt) || br2->isop(LIR_jf)); br2->setTarget(label); + } } } @@ -10587,17 +10633,22 @@ TraceRecorder::record_JSOP_ARGUMENTS() LIns* mem_ins = lir->insAlloc(sizeof(JSObject *)); - LIns* br1 = lir->insBranch(LIR_jt, lir->insEqP_0(a_ins), NULL); - lir->insStore(a_ins, mem_ins, 0, ACCSET_OTHER); - LIns* br2 = lir->insBranch(LIR_j, NULL, NULL); - - label(br1); - - LIns* call_ins = newArguments(callee_ins, strict); - lir->insStore(call_ins, mem_ins, 0, ACCSET_OTHER); - - label(br2); + LIns* isZero_ins = lir->insEqP_0(a_ins); + if (isZero_ins->isImmI(0)) { + lir->insStore(a_ins, mem_ins, 0, ACCSET_OTHER); + } else if (isZero_ins->isImmI(1)) { + LIns* call_ins = newArguments(callee_ins, strict); + lir->insStore(call_ins, mem_ins, 0, ACCSET_OTHER); + } else { + LIns* br1 = unoptimizableCondBranch(LIR_jt, isZero_ins); + lir->insStore(a_ins, mem_ins, 0, ACCSET_OTHER); + LIns* br2 = lir->insBranch(LIR_j, NULL, NULL); + labelForBranch(br1); + LIns* call_ins = newArguments(callee_ins, strict); + lir->insStore(call_ins, mem_ins, 0, ACCSET_OTHER); + labelForBranch(br2); + } args_ins = lir->insLoad(LIR_ldp, mem_ins, 0, ACCSET_OTHER); } @@ -12530,13 +12581,16 @@ TraceRecorder::getCharCodeAt(JSString *str, LIns* str_ins, LIns* idx_ins, LIns** idx_ins = lir->insUI2P(idx_ins); LIns *length_ins = lir->insLoad(LIR_ldp, str_ins, offsetof(JSString, mLengthAndFlags), ACCSET_OTHER); - LIns *br = lir->insBranch(LIR_jt, - lir->insEqP_0(lir->ins2(LIR_andp, - length_ins, - INS_CONSTWORD(JSString::ROPE_BIT))), - NULL); - lir->insCall(&js_Flatten_ci, &str_ins); - label(br); + LIns *br; + if (condBranch(LIR_jt, + lir->insEqP_0(lir->ins2(LIR_andp, + length_ins, + INS_CONSTWORD(JSString::ROPE_BIT))), + &br)) + { + lir->insCall(&js_Flatten_ci, &str_ins); + labelForBranch(br); + } guard(true, lir->ins2(LIR_ltup, idx_ins, lir->ins2ImmI(LIR_rshup, length_ins, JSString::FLAGS_LENGTH_SHIFT)), @@ -12576,13 +12630,16 @@ TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode, LIns *length_ins = lir->insLoad(LIR_ldp, str_ins, offsetof(JSString, mLengthAndFlags), ACCSET_OTHER); - LIns *br1 = lir->insBranch(LIR_jt, - lir->insEqP_0(lir->ins2(LIR_andp, - length_ins, - INS_CONSTWORD(JSString::ROPE_BIT))), - NULL); - lir->insCall(&js_Flatten_ci, &str_ins); - label(br1); + LIns *br1; + if (condBranch(LIR_jt, + lir->insEqP_0(lir->ins2(LIR_andp, + length_ins, + INS_CONSTWORD(JSString::ROPE_BIT))), + &br1)) + { + lir->insCall(&js_Flatten_ci, &str_ins); + labelForBranch(br1); + } LIns* inRange = lir->ins2(LIR_ltup, idx_ins, @@ -12596,11 +12653,12 @@ TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode, LIns *phi_ins = lir->insAlloc(sizeof(JSString *)); lir->insStore(LIR_stp, INS_CONSTSTR(cx->runtime->emptyString), phi_ins, 0, ACCSET_OTHER); - LIns* br2 = lir->insBranch(LIR_jf, inRange, NULL); - LIns *unitstr_ins = getUnitString(str_ins, idx_ins); - lir->insStore(LIR_stp, unitstr_ins, phi_ins, 0, ACCSET_OTHER); - label(br2); - + LIns* br2; + if (condBranch(LIR_jf, inRange, &br2)) { + LIns *unitstr_ins = getUnitString(str_ins, idx_ins); + lir->insStore(LIR_stp, unitstr_ins, phi_ins, 0, ACCSET_OTHER); + labelForBranch(br2); + } *out = lir->insLoad(LIR_ldp, phi_ins, 0, ACCSET_OTHER); } return RECORD_CONTINUE; @@ -13069,11 +13127,13 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) addName(lir->insLoad(LIR_ldi, obj_ins, offsetof(JSObject, capacity), ACCSET_OTHER), "capacity"); - LIns* br = lir->insBranch(LIR_jt, lir->ins2(LIR_ltui, idx_ins, capacity_ins), NULL); - LIns* args[] = { idx_ins, obj_ins, cx_ins }; - LIns* res_ins = lir->insCall(&js_EnsureDenseArrayCapacity_ci, args); - guard(false, lir->insEqI_0(res_ins), mismatchExit); - label(br); + LIns* br; + if (condBranch(LIR_jt, lir->ins2(LIR_ltui, idx_ins, capacity_ins), &br)) { + LIns* args[] = { idx_ins, obj_ins, cx_ins }; + LIns* res_ins = lir->insCall(&js_EnsureDenseArrayCapacity_ci, args); + guard(false, lir->insEqI_0(res_ins), mismatchExit); + labelForBranch(br); + } // Get the address of the element. LIns *dslots_ins = @@ -13096,12 +13156,14 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) JSVAL_TAG_SHIFT)), #endif INS_CONSTU(JSVAL_TAG_MAGIC)); - LIns* br2 = lir->insBranch(LIR_jf, cond, NULL); - LIns* args2[] = { idx_ins, obj_ins, cx_ins }; - LIns* res_ins2 = addName(lir->insCall(&js_Array_dense_setelem_hole_ci, args2), - "hasNoIndexedProperties"); - guard(false, lir->insEqI_0(res_ins2), mismatchExit); - label(br2); + LIns* br2; + if (condBranch(LIR_jf, cond, &br2)) { + LIns* args[] = { idx_ins, obj_ins, cx_ins }; + LIns* res_ins = addName(lir->insCall(&js_Array_dense_setelem_hole_ci, args), + "hasNoIndexedProperties"); + guard(false, lir->insEqI_0(res_ins), mismatchExit); + labelForBranch(br2); + } // Right, actually set the element. box_value_into(v, v_ins, addr_ins, 0, ACCSET_OTHER); @@ -15347,9 +15409,11 @@ TraceRecorder::record_JSOP_ARGCNT() RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); LIns *a_ins = getFrameObjPtr(fp->addressOfArgs()); if (callDepth == 0) { - LIns *br = lir->insBranch(LIR_jt, lir->insEqP_0(a_ins), NULL); - guardArgsLengthNotAssigned(a_ins); - label(br); + LIns *br; + if (condBranch(LIR_jt, lir->insEqP_0(a_ins), &br)) { + guardArgsLengthNotAssigned(a_ins); + labelForBranch(br); + } } stack(0, lir->insImmD(fp->numActualArgs())); return ARECORD_CONTINUE; diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 3437a387df2f..6370eb8d20cc 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1115,8 +1115,11 @@ class TraceRecorder JS_REQUIRES_STACK nanojit::LIns* alu(nanojit::LOpcode op, jsdouble v0, jsdouble v1, nanojit::LIns* s0, nanojit::LIns* s1); - void label(nanojit::LIns* br); - void label(nanojit::LIns* br1, nanojit::LIns* br2); + bool condBranch(nanojit::LOpcode op, nanojit::LIns* cond, nanojit::LIns** brOut); + nanojit::LIns* unoptimizableCondBranch(nanojit::LOpcode op, nanojit::LIns* cond); + void labelForBranch(nanojit::LIns* br); + void labelForBranches(nanojit::LIns* br1, nanojit::LIns* br2); + nanojit::LIns* i2d(nanojit::LIns* i); nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false); nanojit::LIns* f2u(nanojit::LIns* f); From 6a9ab1077e460fc3a5288237c1580ed61849057a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 13 Oct 2010 16:49:53 -0700 Subject: [PATCH 281/284] Bug 603825 - Fix a bunch of warnings. r=jwalden. --- js/src/jsinterp.cpp | 3 +-- js/src/jsinterp.h | 2 +- js/src/jsinterpinlines.h | 3 ++- js/src/methodjit/Compiler.cpp | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 6105de381916..23280911d768 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -448,7 +448,7 @@ ComputeGlobalThis(JSContext *cx, Value *argv) namespace js { -bool +void ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp) { Value &thisv = vp[1]; @@ -486,7 +486,6 @@ ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp) clasp->name, JS_GetFunctionName(fun), name); } - return false; } bool diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 0edead61202c..8625d96076ec 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -805,7 +805,7 @@ namespace js { * Report an error that the this value passed as |this| in the given arguments * vector is not compatible with the specified class. */ -bool +void ReportIncompatibleMethod(JSContext *cx, Value *vp, Class *clasp); /* diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 013509a3f93f..6040ea635651 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -633,7 +633,8 @@ GetPrimitiveThis(JSContext *cx, Value *vp, T *v) return true; } - return ReportIncompatibleMethod(cx, vp, Behavior::getClass()); + ReportIncompatibleMethod(cx, vp, Behavior::getClass()); + return false; } /* diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index aa97628b2c2f..35fe192c1d17 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2822,7 +2822,9 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom) frame.shift(-1); /* 4) Test if the function can take a primitive. */ +#ifdef DEBUG FrameEntry *funFe = frame.peek(-2); +#endif JS_ASSERT(!funFe->isTypeKnown()); /* From b72cd8856319a4cbe5604b0fe57fce90467ae2a5 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Wed, 13 Oct 2010 17:21:00 -0700 Subject: [PATCH 282/284] Bug 604087 - Deal with GC happening during JS_TransplantWrappers. r=jst/gal --- js/src/jsapi.cpp | 52 +++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e5183b9adb25..08794cf1f320 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1276,35 +1276,41 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) // based on whether the new compartment is same origin with them. Value targetv = ObjectValue(*obj); WrapperVector &vector = cx->runtime->compartments; + AutoValueVector toTransplant(cx); + toTransplant.reserve(vector.length()); + for (JSCompartment **p = vector.begin(), **end = vector.end(); p != end; ++p) { WrapperMap &pmap = (*p)->crossCompartmentWrappers; if (WrapperMap::Ptr wp = pmap.lookup(wrapperv)) { - // We found a wrapper around the outer window! - JSObject *wobj = &wp->value.toObject(); - - // NB: Can't use wp after this point. We remove wp from the map in - // order to ensure that we create a wrapper with the proper guts - // for the brain transplant. - pmap.remove(wp); - - // First, we wrap it in the new compartment. This will return a - // new wrapper. - JSAutoEnterCompartment ec; - JSObject *tobj = obj; - if (!ec.enter(cx, wobj) || !(*p)->wrap(cx, &tobj)) - return NULL; - - // Now, because we need to maintain object identity, we do a brain - // transplant on the old object. At the same time, we update the - // entry in the compartment's wrapper map to point to the old - // wrapper. - JS_ASSERT(tobj != wobj); - if (!wobj->swap(cx, tobj)) - return NULL; - pmap.put(targetv, ObjectValue(*wobj)); + // We found a wrapper. Remember and root it. + toTransplant.append(wp->value); } } + for (Value *begin = toTransplant.begin(), *end = toTransplant.end(); begin != end; ++begin) { + JSObject *wobj = &begin->toObject(); + JSCompartment *wcompartment = wobj->compartment(); + WrapperMap &pmap = wcompartment->crossCompartmentWrappers; + JS_ASSERT(pmap.lookup(wrapperv)); + pmap.remove(wrapperv); + + // First, we wrap it in the new compartment. This will return a + // new wrapper. + JSAutoEnterCompartment ec; + JSObject *tobj = obj; + if (!ec.enter(cx, wobj) || !wcompartment->wrap(cx, &tobj)) + return NULL; + + // Now, because we need to maintain object identity, we do a brain + // transplant on the old object. At the same time, we update the + // entry in the compartment's wrapper map to point to the old + // wrapper. + JS_ASSERT(tobj != wobj); + if (!wobj->swap(cx, tobj)) + return NULL; + pmap.put(targetv, ObjectValue(*wobj)); + } + // Lastly, update the old outer window proxy to point to the new one. { JSAutoEnterCompartment ac; From 32baaed5fefd47c6b384bb04b69e2c3eab95bdb2 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 13 Oct 2010 17:26:40 -0700 Subject: [PATCH 283/284] Bug 604087 - Minor tweaks to JS_TransplantWrapper and JSAutoEnterCompartment. r=mrbkap --- js/src/jsapi.cpp | 8 ++++---- js/src/jsapi.h | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 08794cf1f320..d11289a5b87f 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1296,9 +1296,9 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) // First, we wrap it in the new compartment. This will return a // new wrapper. - JSAutoEnterCompartment ec; + AutoCompartment ac(cx, wobj); JSObject *tobj = obj; - if (!ec.enter(cx, wobj) || !wcompartment->wrap(cx, &tobj)) + if (!ac.enter() || !wcompartment->wrap(cx, &tobj)) return NULL; // Now, because we need to maintain object identity, we do a brain @@ -1313,9 +1313,9 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target) // Lastly, update the old outer window proxy to point to the new one. { - JSAutoEnterCompartment ac; + AutoCompartment ac(cx, wrapper); JSObject *tobj = obj; - if (!ac.enter(cx, wrapper) || !JS_WrapObject(cx, &tobj)) + if (!ac.enter() || !JS_WrapObject(cx, &tobj)) return NULL; if (!wrapper->swap(cx, tobj)) return NULL; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 686a34f00242..7659ba3a0b62 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -980,8 +980,6 @@ class JS_PUBLIC_API(JSAutoEnterCompartment) void swap(JSAutoEnterCompartment &other) { JSCrossCompartmentCall *tmp = call; - if (tmp == reinterpret_cast(1)) - tmp = NULL; call = other.call; other.call = tmp; } From 828b4d7fa9ebc417a36727625ca9ee21012aa9e5 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Wed, 13 Oct 2010 16:54:13 -0700 Subject: [PATCH 284/284] Add a test for bug 604087. r=gal --- js/src/jsapi-tests/Makefile.in | 1 + js/src/jsapi-tests/testBug604087.cpp | 84 ++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 js/src/jsapi-tests/testBug604087.cpp diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index 014162a7fa29..c4a5a43ffb8b 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -69,6 +69,7 @@ CPPSRCS = \ testSameValue.cpp \ testScriptObject.cpp \ testSetPropertyWithNativeGetterStubSetter.cpp \ + testBug604087.cpp \ testTrap.cpp \ testXDR.cpp \ $(NULL) diff --git a/js/src/jsapi-tests/testBug604087.cpp b/js/src/jsapi-tests/testBug604087.cpp new file mode 100644 index 000000000000..bdfab797b3d5 --- /dev/null +++ b/js/src/jsapi-tests/testBug604087.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * Tests JS_TransplantWrappers + */ + +#include "tests.h" +#include "jswrapper.h" + +struct OuterWrapper : JSWrapper +{ + OuterWrapper() : JSWrapper(0) {} + + virtual bool isOuterWindow() { + return true; + } + + static OuterWrapper singleton; +}; + +OuterWrapper +OuterWrapper::singleton; + +static JSObject * +wrap(JSContext *cx, JSObject *toWrap, JSObject *target) +{ + JSAutoEnterCompartment ac; + if (!ac.enter(cx, target)) + return NULL; + + JSObject *wrapper = toWrap; + if (!JS_WrapObject(cx, &wrapper)) + return NULL; + return wrapper; +} + +static JSObject * +PreWrap(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags) +{ + JS_GC(cx); + return obj; +} + +static JSObject * +Wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, uintN flags) +{ + return JSWrapper::New(cx, obj, proto, NULL, &JSCrossCompartmentWrapper::singleton); +} + +BEGIN_TEST(testBug604087) +{ + JSObject *outerObj = JSWrapper::New(cx, global, global->getProto(), global, + &OuterWrapper::singleton); + JSObject *compartment2 = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL); + JSObject *compartment3 = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL); + JSObject *compartment4 = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL); + + JSObject *c2wrapper = wrap(cx, outerObj, compartment2); + CHECK(c2wrapper); + c2wrapper->setProxyExtra(js::Int32Value(2)); + + JSObject *c3wrapper = wrap(cx, outerObj, compartment3); + CHECK(c3wrapper); + c3wrapper->setProxyExtra(js::Int32Value(3)); + + JSObject *c4wrapper = wrap(cx, outerObj, compartment4); + CHECK(c4wrapper); + c4wrapper->setProxyExtra(js::Int32Value(4)); + compartment4 = c4wrapper = NULL; + + JSObject *next; + { + JSAutoEnterCompartment ac; + CHECK(ac.enter(cx, compartment2)); + next = JSWrapper::New(cx, compartment2, compartment2->getProto(), compartment2, + &OuterWrapper::singleton); + CHECK(next); + } + + JS_SetWrapObjectCallbacks(JS_GetRuntime(cx), Wrap, PreWrap); + CHECK(JS_TransplantWrapper(cx, outerObj, next)); + return true; +} +END_TEST(testBug604087)