/* * Fixup wrappers with moved keys or values. */ void JSCompartment::fixupCrossCompartmentWrappers(JSTracer *trc) { for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { Value val = e.front().value(); if (IsForwarded(val)) { val = Forwarded(val); e.front().value().set(val); } // CrossCompartmentKey's hash does not depend on the debugger object, // so update it but do not rekey if it changes CrossCompartmentKey key = e.front().key(); if (key.debugger) key.debugger = MaybeForwarded(key.debugger); if (key.wrapped && IsForwarded(key.wrapped)) { key.wrapped = Forwarded(key.wrapped); e.rekeyFront(key, key); } if (!zone()->isCollecting() && val.isObject()) { // Call the trace hook to update any pointers to relocated things. JSObject *obj = &val.toObject(); const Class *clasp = obj->getClass(); if (clasp->trace) clasp->trace(trc, obj); } } }
bool ObjectValueMap::findZoneEdges() { /* * For unmarked weakmap keys with delegates in a different zone, add a zone * edge to ensure that the delegate zone does finish marking after the key * zone. */ JS::AutoAssertNoGC nogc; Zone *mapZone = compartment->zone(); for (Range r = all(); !r.empty(); r.popFront()) { JSObject *key = r.front().key(); if (key->isMarked(BLACK) && !key->isMarked(GRAY)) continue; JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp; if (!op) continue; JSObject *delegate = op(key); if (!delegate) continue; Zone *delegateZone = delegate->zone(); if (delegateZone == mapZone) continue; if (!delegateZone->gcZoneGroupEdges.put(key->zone())) return false; } return true; }
bool ObjectValueMap::findZoneEdges() { /* * For unmarked weakmap keys with delegates in a different zone, add a zone * edge to ensure that the delegate zone finishes marking before the key * zone. */ JS::AutoSuppressGCAnalysis nogc; for (Range r = all(); !r.empty(); r.popFront()) { JSObject* key = r.front().key(); if (key->asTenured().isMarked(BLACK) && !key->asTenured().isMarked(GRAY)) continue; JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp(); if (!op) continue; JSObject* delegate = op(key); if (!delegate) continue; Zone* delegateZone = delegate->zone(); if (delegateZone == zone) continue; if (!delegateZone->gcZoneGroupEdges.put(key->zone())) return false; } return true; }
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_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); }
// We should be able to assert this for *any* fp->scopeChain(). static void AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj) { #ifdef DEBUG for (JSObject *o = &scopeobj; o; o = o->enclosingScope()) { if (JSObjectOp op = o->getClass()->ext.innerObject) { Rooted<JSObject*> obj(cx, o); JS_ASSERT(op(cx, obj) == o); } } #endif }
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; }
PerfMeasurement* ExtractPerfMeasurement(jsval wrapper) { if (JSVAL_IS_PRIMITIVE(wrapper)) return 0; // This is what JS_GetInstancePrivate does internally. We can't // call JS_anything from here, because we don't have a JSContext. JSObject *obj = JSVAL_TO_OBJECT(wrapper); if (obj->getClass() != js::Valueify(&pm_class)) return 0; return (PerfMeasurement*) obj->getPrivate(); }
PerfMeasurement* ExtractPerfMeasurement(Value wrapper) { if (wrapper.isPrimitive()) return 0; // This is what JS_GetInstancePrivate does internally. We can't // call JS_anything from here, because we don't have a JSContext. JSObject* obj = wrapper.toObjectOrNull(); if (obj->getClass() != js::Valueify(&pm_class)) return 0; return (PerfMeasurement*) obj->as<js::NativeObject>().getPrivate(); }
NS_IMETHODIMP nsHTMLAudioElement::MozWriteAudio(const jsval &aData, JSContext *aCx, PRUint32 *aRetVal) { if (!mAudioStream) { return NS_ERROR_DOM_INVALID_STATE_ERR; } if (JSVAL_IS_PRIMITIVE(aData)) { return NS_ERROR_DOM_TYPE_MISMATCH_ERR; } JSObject *darray = JSVAL_TO_OBJECT(aData); js::AutoValueRooter tsrc_tvr(aCx); js::TypedArray *tsrc = NULL; // Allow either Float32Array or plain JS Array if (darray->getClass() == &js::TypedArray::fastClasses[js::TypedArray::TYPE_FLOAT32]) { tsrc = js::TypedArray::fromJSObject(darray); } else if (JS_IsArrayObject(aCx, darray)) { JSObject *nobj = js_CreateTypedArrayWithArray(aCx, js::TypedArray::TYPE_FLOAT32, darray); if (!nobj) { return NS_ERROR_DOM_TYPE_MISMATCH_ERR; } *tsrc_tvr.jsval_addr() = OBJECT_TO_JSVAL(nobj); tsrc = js::TypedArray::fromJSObject(nobj); } else { return NS_ERROR_DOM_TYPE_MISMATCH_ERR; } PRUint32 dataLength = tsrc->length; // Make sure that we are going to write the correct amount of data based // on number of channels. if (dataLength % mChannels != 0) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } // Don't write more than can be written without blocking. PRUint32 writeLen = NS_MIN(mAudioStream->Available(), dataLength); nsresult rv = mAudioStream->Write(tsrc->data, writeLen, PR_TRUE); if (NS_FAILED(rv)) { return rv; } // Return the actual amount written. *aRetVal = writeLen; return rv; }
static JSBool WeakMap_set(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); bool ok; JSObject *obj = NonGenericMethodGuard(cx, args, WeakMap_set, &WeakMapClass, &ok); if (!obj) return ok; if (args.length() < 1) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, "WeakMap.set", "0", "s"); return false; } JSObject *key = GetKeyArg(cx, args); if (!key) return false; Value value = (args.length() > 1) ? args[1] : UndefinedValue(); ObjectValueMap *map = GetObjectMap(obj); if (!map) { map = cx->new_<ObjectValueMap>(cx, obj); if (!map->init()) { cx->delete_(map); goto out_of_memory; } obj->setPrivate(map); } if (!map->put(key, value)) goto out_of_memory; // Preserve wrapped native keys to prevent wrapper optimization. if (key->getClass()->ext.isWrappedNative) { if (!cx->runtime->preserveWrapperCallback || !cx->runtime->preserveWrapperCallback(cx, key)) { JS_ReportWarning(cx, "Failed to preserve wrapper of wrapped native weak map key."); } } args.rval().setUndefined(); return true; out_of_memory: JS_ReportOutOfMemory(cx); return false; }
/* * During incremental GC, we return from drainMarkStack without having processed * the entire stack. At that point, JS code can run and reallocate slot arrays * that are stored on the stack. To prevent this from happening, we replace all * ValueArrayTag stack items with SavedValueArrayTag. In the latter, slots * pointers are replaced with slot indexes. * * We also replace the slot array end pointer (which can be derived from the obj * pointer) with the object's class. During JS executation, array slowification * can cause the layout of slots to change. We can observe that slowification * happened if the class changed; in that case, we completely rescan the array. */ void GCMarker::saveValueRanges() { for (uintptr_t *p = stack.tos; p > stack.stack; ) { uintptr_t tag = *--p & StackTagMask; if (tag == ValueArrayTag) { p -= 2; SlotArrayLayout *arr = reinterpret_cast<SlotArrayLayout *>(p); JSObject *obj = arr->obj; if (obj->getClass() == &ArrayClass) { HeapSlot *vp = obj->getDenseArrayElements(); JS_ASSERT(arr->start >= vp && arr->end == vp + obj->getDenseArrayInitializedLength()); arr->index = arr->start - vp; } else { HeapSlot *vp = obj->fixedSlots(); unsigned nfixed = obj->numFixedSlots(); if (arr->start == arr->end) { arr->index = obj->slotSpan(); } else if (arr->start >= vp && arr->start < vp + nfixed) { JS_ASSERT(arr->end == vp + Min(nfixed, obj->slotSpan())); arr->index = arr->start - vp; } else { JS_ASSERT(arr->start >= obj->slots && arr->end == obj->slots + obj->slotSpan() - nfixed); arr->index = (arr->start - obj->slots) + nfixed; } } arr->clasp = obj->getClass(); p[2] |= SavedValueArrayTag; } else if (tag == SavedValueArrayTag) { p -= 2; } } }
JSObject * JSObject::unwrap(uintN *flagsp) { JSObject *wrapped = this; uintN flags = 0; while (wrapped->isWrapper()) { flags |= static_cast<JSWrapper *>(wrapped->getProxyHandler())->flags(); wrapped = wrapped->getProxyPrivate().toObjectOrNull(); if (wrapped->getClass()->ext.innerObject) break; } if (flagsp) *flagsp = flags; return wrapped; }
JSErrorReport * js_ErrorFromException(JSContext *cx, jsval exn) { JSObject *obj; JSExnPrivate *priv; if (JSVAL_IS_PRIMITIVE(exn)) return NULL; obj = JSVAL_TO_OBJECT(exn); if (obj->getClass() != &js_ErrorClass) return NULL; priv = GetExnPrivate(cx, obj); if (!priv) return NULL; return priv->errorReport; }
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_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { 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; } *rval = 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, argv, rval, JS_FALSE); }
static JSBool HasProperty(JSContext* cx, JSObject* obj, jsid id) { // Check that we know how the lookup op will behave. for (JSObject* pobj = obj; pobj; pobj = pobj->getProto()) { if (pobj->getOps()->lookupProperty) return JS_NEITHER; Class* clasp = pobj->getClass(); if (clasp->resolve != JS_ResolveStub && clasp != &StringClass) return JS_NEITHER; } JSObject* obj2; JSProperty* prop; if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop)) return JS_NEITHER; return prop != NULL; }
static bool WeakMap_set_impl(JSContext *cx, CallArgs args) { JS_ASSERT(IsWeakMap(args.thisv())); if (args.length() < 1) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, "WeakMap.set", "0", "s"); return false; } JSObject *key = GetKeyArg(cx, args); if (!key) return false; Value value = (args.length() > 1) ? args[1] : UndefinedValue(); Rooted<JSObject*> thisObj(cx, &args.thisv().toObject()); ObjectValueMap *map = GetObjectMap(thisObj); if (!map) { map = cx->new_<ObjectValueMap>(cx, thisObj.get()); if (!map->init()) { cx->delete_(map); JS_ReportOutOfMemory(cx); return false; } thisObj->setPrivate(map); } if (!map->put(key, value)) { JS_ReportOutOfMemory(cx); return false; } // Preserve wrapped native keys to prevent wrapper optimization. if (key->getClass()->ext.isWrappedNative) { if (!cx->runtime->preserveWrapperCallback || !cx->runtime->preserveWrapperCallback(cx, key)) { JS_ReportWarning(cx, "Failed to preserve wrapper of wrapped native weak map key."); } } args.rval().setUndefined(); return true; }
static JSString * ValueToShortSource(JSContext *cx, const Value &v) { JSString *str; /* Avoid toSource bloat and fallibility for object types. */ if (!v.isObject()) return js_ValueToSource(cx, v); JSObject *obj = &v.toObject(); AutoCompartment ac(cx, obj); if (!ac.enter()) return NULL; if (obj->isFunction()) { /* * XXX Avoid function decompilation bloat for now. */ str = JS_GetFunctionId(obj->toFunction()); if (!str && !(str = js_ValueToSource(cx, v))) { /* * Continue to soldier on if the function couldn't be * converted into a string. */ JS_ClearPendingException(cx); str = JS_NewStringCopyZ(cx, "[unknown function]"); } } else { /* * XXX Avoid toString on objects, it takes too long and uses too much * memory, for too many classes (see Mozilla bug 166743). */ char buf[100]; JS_snprintf(buf, sizeof buf, "[object %s]", obj->getClass()->name); str = JS_NewStringCopyZ(cx, buf); } ac.leave(); if (!str || !cx->compartment->wrap(cx, &str)) return NULL; return str; }
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); }
bool ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg) { StackFrame *genfp = gen->floatingFrame(); HeapValue *genvp = gen->floatingStack; unsigned vplen = (HeapValue *)genfp - genvp; unsigned nvars = vplen + VALUES_PER_STACK_FRAME + genfp->numSlots(); Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &gfg->pushedSeg_); if (!firstUnused) return false; StackFrame *stackfp = reinterpret_cast<StackFrame *>(firstUnused + vplen); Value *stackvp = (Value *)stackfp - vplen; /* Save this for popGeneratorFrame. */ gfg->gen_ = gen; gfg->stackvp_ = stackvp; /* * Trigger incremental barrier on the floating frame's generator object. * This is normally traced through only by associated arguments/call * objects, but only when the generator is not actually on the stack. * We don't need to worry about generational barriers as the generator * object has a trace hook and cannot be nursery allocated. */ JSObject *genobj = js_FloatingFrameToGenerator(genfp)->obj; JS_ASSERT(genobj->getClass()->trace); JSObject::writeBarrierPre(genobj); /* Copy from the generator's floating frame to the stack. */ stackfp->stealFrameAndSlots<Value, HeapValue, StackFrame::NoPostBarrier>( stackfp, stackvp, genfp, genvp, gen->regs.sp); stackfp->resetGeneratorPrev(cx); stackfp->unsetFloatingGenerator(); gfg->regs_.rebaseFromTo(gen->regs, *stackfp); gfg->prevRegs_ = seg_->pushRegs(gfg->regs_); JS_ASSERT(space().firstUnused() == gfg->regs_.sp); gfg->setPushed(*this); return true; }
js::NukeCrossCompartmentWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter, js::NukeReferencesToWindow nukeReferencesToWindow) { CHECK_REQUEST(cx); JSRuntime *rt = cx->runtime; // Iterate through scopes looking for system cross compartment wrappers // that point to an object that shares a global with obj. for (CompartmentsIter c(rt); !c.done(); c.next()) { if (!sourceFilter.match(c)) continue; // Iterate the wrappers looking for anything interesting. WrapperMap &pmap = c->crossCompartmentWrappers; for (WrapperMap::Enum e(pmap); !e.empty(); e.popFront()) { // Some cross-compartment wrappers are for strings. We're not // interested in those. const CrossCompartmentKey &k = e.front().key; if (k.kind != CrossCompartmentKey::ObjectWrapper) continue; JSObject *wobj = &e.front().value.get().toObject(); JSObject *wrapped = UnwrapObject(wobj); if (nukeReferencesToWindow == DontNukeWindowReferences && wrapped->getClass()->ext.innerObject) continue; if (targetFilter.match(wrapped->compartment())) { // We found a wrapper to nuke. e.removeFront(); NukeCrossCompartmentWrapper(wobj); } } } return JS_TRUE; }
js::NukeCrossCompartmentWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter, js::NukeReferencesToWindow nukeReferencesToWindow) { CHECK_REQUEST(cx); JSRuntime* rt = cx->runtime(); // Iterate through scopes looking for system cross compartment wrappers // that point to an object that shares a global with obj. for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (!sourceFilter.match(c)) continue; // Iterate the wrappers looking for anything interesting. for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { // Some cross-compartment wrappers are for strings. We're not // interested in those. const CrossCompartmentKey& k = e.front().key(); if (k.kind != CrossCompartmentKey::ObjectWrapper) continue; AutoWrapperRooter wobj(cx, WrapperValue(e)); JSObject* wrapped = UncheckedUnwrap(wobj); if (nukeReferencesToWindow == DontNukeWindowReferences && wrapped->getClass()->ext.innerObject) continue; if (targetFilter.match(wrapped->compartment())) { // We found a wrapper to nuke. e.removeFront(); NukeCrossCompartmentWrapper(cx, wobj); } } } return 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); }
bool JSCompartment::wrap(JSContext *cx, Value *vp) { JS_ASSERT(cx->compartment == this); unsigned flags = 0; JS_CHECK_RECURSION(cx, return false); /* Only GC things have to be wrapped or copied. */ if (!vp->isMarkable()) return true; if (vp->isString()) { JSString *str = vp->toString(); /* If the string is already in this compartment, we are done. */ if (str->compartment() == this) return true; /* If the string is an atom, we don't have to copy. */ if (str->isAtom()) { JS_ASSERT(str->compartment() == cx->runtime->atomsCompartment); return true; } } /* * 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().global(); } else { global = JS_ObjectToInnerObject(cx, cx->globalObject); if (!global) return false; } /* Unwrap incoming objects. */ if (vp->isObject()) { JSObject *obj = &vp->toObject(); /* If the object is already in this compartment, we are done. */ if (obj->compartment() == this) return true; /* Translate StopIteration singleton. */ if (obj->isStopIteration()) return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp); /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { obj = UnwrapObject(&vp->toObject(), true, &flags); vp->setObject(*obj); if (obj->compartment() == this) return true; if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } vp->setObject(*obj); if (obj->compartment() == this) return true; } else { if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); 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. */ if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) { *vp = p->value; if (vp->isObject()) { JSObject *obj = &vp->toObject(); JS_ASSERT(obj->isCrossCompartmentWrapper()); if (global->getClass() != &dummy_class && obj->getParent() != global) { do { if (!obj->setParent(cx, global)) return false; obj = obj->getProto(); } while (obj && obj->isCrossCompartmentWrapper()); } } return true; } if (vp->isString()) { Value orig = *vp; JSString *str = vp->toString(); const jschar *chars = str->getChars(cx); if (!chars) return false; JSString *wrapped = js_NewStringCopyN(cx, chars, str->length()); if (!wrapped) return false; vp->setString(wrapped); return crossCompartmentWrappers.put(orig, *vp); } JSObject *obj = &vp->toObject(); /* * Recurse to wrap the prototype. Long prototype chains will run out of * stack, causing an error in CHECK_RECURSE. * * Wrapping the proto before creating the new wrapper and adding it to the * cache helps avoid leaving a bad entry in the cache on OOM. But note that * if we wrapped both proto and parent, we would get infinite recursion * here (since Object.prototype->parent->proto leads to Object.prototype * itself). */ JSObject *proto = obj->getProto(); 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, global, flags); if (!wrapper) return false; vp->setObject(*wrapper); if (wrapper->getProto() != proto && !SetProto(cx, wrapper, proto, false)) return false; if (!crossCompartmentWrappers.put(GetProxyPrivate(wrapper), *vp)) return false; if (!wrapper->setParent(cx, global)) return false; return true; }
JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing, JS::TraceKind kind, bool details) { const char* name = nullptr; /* silence uninitialized warning */ size_t n; if (bufsize == 0) return; switch (kind) { case JS::TraceKind::Object: { name = static_cast<JSObject*>(thing)->getClass()->name; break; } case JS::TraceKind::Script: name = "script"; break; case JS::TraceKind::String: name = ((JSString*)thing)->isDependent() ? "substring" : "string"; break; case JS::TraceKind::Symbol: name = "symbol"; break; case JS::TraceKind::BaseShape: name = "base_shape"; break; case JS::TraceKind::JitCode: name = "jitcode"; break; case JS::TraceKind::LazyScript: name = "lazyscript"; break; case JS::TraceKind::Shape: name = "shape"; break; case JS::TraceKind::ObjectGroup: name = "object_group"; break; default: name = "INVALID"; break; } n = strlen(name); if (n > bufsize - 1) n = bufsize - 1; js_memcpy(buf, name, n + 1); buf += n; bufsize -= n; *buf = '\0'; if (details && bufsize > 2) { switch (kind) { case JS::TraceKind::Object: { JSObject* obj = (JSObject*)thing; if (obj->is<JSFunction>()) { JSFunction* fun = &obj->as<JSFunction>(); if (fun->displayAtom()) { *buf++ = ' '; bufsize--; PutEscapedString(buf, bufsize, fun->displayAtom(), 0); } } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) { JS_snprintf(buf, bufsize, " %p", obj->as<NativeObject>().getPrivate()); } else { JS_snprintf(buf, bufsize, " <no private>"); } break; } case JS::TraceKind::Script: { JSScript* script = static_cast<JSScript*>(thing); JS_snprintf(buf, bufsize, " %s:%" PRIuSIZE, script->filename(), script->lineno()); break; } case JS::TraceKind::String: { *buf++ = ' '; bufsize--; JSString* str = (JSString*)thing; if (str->isLinear()) { bool willFit = str->length() + strlen("<length > ") + CountDecimalDigits(str->length()) < bufsize; n = JS_snprintf(buf, bufsize, "<length %d%s> ", (int)str->length(), willFit ? "" : " (truncated)"); buf += n; bufsize -= n; PutEscapedString(buf, bufsize, &str->asLinear(), 0); } else { JS_snprintf(buf, bufsize, "<rope: length %d>", (int)str->length()); } break; } case JS::TraceKind::Symbol: { JS::Symbol* sym = static_cast<JS::Symbol*>(thing); if (JSString* desc = sym->description()) { if (desc->isLinear()) { *buf++ = ' '; bufsize--; PutEscapedString(buf, bufsize, &desc->asLinear(), 0); } else { JS_snprintf(buf, bufsize, "<nonlinear desc>"); } } else { JS_snprintf(buf, bufsize, "<null>"); } break; } default: break; } } buf[bufsize - 1] = '\0'; }
static JSBool Exception(JSContext *cx, uintN argc, Value *vp) { JSString *message, *filename; JSStackFrame *fp; /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when * called as functions, without operator new. But as we do not give * each constructor a distinct JSClass, whose .name member is used by * NewNativeClassInstance to find the class prototype, we must get the * class prototype ourselves. */ JSObject &callee = vp[0].toObject(); Value protov; if (!callee.getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov)) return JS_FALSE; if (!protov.isObject()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error"); return JS_FALSE; } JSObject *errProto = &protov.toObject(); JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent()); if (!obj) return JS_FALSE; /* * If it's a new object of class Exception, then null out the private * data so that the finalizer doesn't attempt to free it. */ if (obj->getClass() == &js_ErrorClass) obj->setPrivate(NULL); /* Set the 'message' property. */ Value *argv = vp + 2; if (argc != 0 && !argv[0].isUndefined()) { message = js_ValueToString(cx, argv[0]); if (!message) return JS_FALSE; argv[0].setString(message); } else { message = NULL; } /* Set the 'fileName' property. */ if (argc > 1) { filename = js_ValueToString(cx, argv[1]); if (!filename) return JS_FALSE; argv[1].setString(filename); fp = NULL; } else { fp = js_GetScriptedCaller(cx, NULL); if (fp) { filename = FilenameToString(cx, fp->script()->filename); if (!filename) return JS_FALSE; } else { filename = cx->runtime->emptyString; } } /* Set the 'lineNumber' property. */ uint32_t lineno; if (argc > 2) { if (!ValueToECMAUint32(cx, argv[2], &lineno)) return JS_FALSE; } else { if (!fp) fp = js_GetScriptedCaller(cx, NULL); lineno = (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0; } if (obj->getClass() == &js_ErrorClass && !InitExnPrivate(cx, obj, message, filename, lineno, NULL)) { return JS_FALSE; } vp->setObject(*obj); return JS_TRUE; }
JSBool js_ReportUncaughtException(JSContext *cx) { jsval exn; JSObject *exnObject; jsval roots[5]; JSErrorReport *reportp, report; JSString *str; const char *bytes; if (!JS_IsExceptionPending(cx)) return true; if (!JS_GetPendingException(cx, &exn)) return false; PodArrayZero(roots); AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), Valueify(roots)); /* * Because js_ValueToString below could error and an exception object * could become unrooted, we must root exnObject. Later, if exnObject is * non-null, we need to root other intermediates, so allocate an operand * stack segment to protect all of these values. */ if (JSVAL_IS_PRIMITIVE(exn)) { exnObject = NULL; } else { exnObject = JSVAL_TO_OBJECT(exn); roots[0] = exn; } JS_ClearPendingException(cx); reportp = js_ErrorFromException(cx, exn); /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ str = js_ValueToString(cx, Valueify(exn)); JSAutoByteString bytesStorage; if (!str) { bytes = "unknown (can't convert to string)"; } else { roots[1] = STRING_TO_JSVAL(str); if (!bytesStorage.encode(cx, str)) return false; bytes = bytesStorage.ptr(); } JSAutoByteString filename; if (!reportp && exnObject && exnObject->getClass() == &js_ErrorClass) { if (!JS_GetProperty(cx, exnObject, js_message_str, &roots[2])) return false; if (JSVAL_IS_STRING(roots[2])) { bytesStorage.clear(); if (!bytesStorage.encode(cx, str)) return false; bytes = bytesStorage.ptr(); } if (!JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3])) return false; str = js_ValueToString(cx, Valueify(roots[3])); if (!str || !filename.encode(cx, str)) return false; if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4])) return false; uint32_t lineno; if (!ValueToECMAUint32(cx, Valueify(roots[4]), &lineno)) return false; reportp = &report; PodZero(&report); report.filename = filename.ptr(); report.lineno = (uintN) lineno; if (JSVAL_IS_STRING(roots[2])) { JSFixedString *fixed = JSVAL_TO_STRING(roots[2])->ensureFixed(cx); if (!fixed) return false; report.ucmessage = fixed->chars(); } } if (!reportp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNCAUGHT_EXCEPTION, bytes); } else { /* Flag the error as an exception. */ reportp->flags |= JSREPORT_EXCEPTION; /* Pass the exception object. */ JS_SetPendingException(cx, exn); js_ReportErrorAgain(cx, bytes, reportp); JS_ClearPendingException(cx); } return true; }
static bool Snapshot(JSContext *cx, JSObject *obj, uintN flags, AutoIdVector *props) { IdSet ht(cx); if (!ht.init(32)) return NULL; JSObject *pobj = obj; do { Class *clasp = pobj->getClass(); if (pobj->isNative() && !pobj->getOps()->enumerate && !(clasp->flags & JSCLASS_NEW_ENUMERATE)) { if (!clasp->enumerate(cx, pobj)) return false; if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props)) return false; } else if (pobj->isDenseArray()) { if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, ht, props)) return false; } else { if (pobj->isProxy()) { AutoIdVector proxyProps(cx); if (flags & JSITER_OWNONLY) { if (flags & JSITER_HIDDEN) { if (!JSProxy::getOwnPropertyNames(cx, pobj, proxyProps)) return false; } else { if (!JSProxy::keys(cx, pobj, proxyProps)) return false; } } else { if (!JSProxy::enumerate(cx, pobj, proxyProps)) return false; } for (size_t n = 0, len = proxyProps.length(); n < len; n++) { if (!Enumerate(cx, obj, pobj, proxyProps[n], true, flags, ht, props)) return false; } /* Proxy objects enumerate the prototype on their own, so we are done here. */ break; } Value state; JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT; if (!pobj->enumerate(cx, op, &state, NULL)) return false; if (state.isMagic(JS_NATIVE_ENUMERATE)) { if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props)) return false; } else { while (true) { jsid id; if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id)) return false; if (state.isNull()) break; if (!Enumerate(cx, obj, pobj, id, true, flags, ht, props)) return false; } } } if ((flags & JSITER_OWNONLY) || pobj->isXML()) break; } while ((pobj = pobj->getProto()) != NULL); return true; }
JSScript * frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame, const CompileOptions &options, const jschar *chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */) { RootedString source(cx, source_); class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(options.filename, options.lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, options.compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); if (!CheckLength(cx, length)) return NULL; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return NULL; AutoAttachToRuntime attacher(cx->runtime, ss); SourceCompressionToken sct(cx); if (!cx->hasRunOption(JSOPTION_ONLY_CNG_SOURCE) || options.compileAndGo) { if (!ss->setSourceCopy(cx, chars, length, false, &sct)) return NULL; } Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return NULL; parser.sct = &sct; SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL, StrictModeFromContext(cx)); TreeContext tc(&parser, &sc, staticLevel, /* bodyid = */ 0); if (!tc.init()) return NULL; bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame->isFunctionFrame(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, ss, 0, length)); if (!script) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &sc, script, callerFrame, !!globalScope, options.lineno); if (!bce.init()) return NULL; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode) sc.strictModeState = StrictMode::STRICT; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = js_AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (callerFrame && callerFrame->isFunctionFrame()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ ObjectBox *funbox = parser.newObjectBox(callerFrame->fun()); if (!funbox) return NULL; funbox->emitLink = bce.objectList.lastbox; bce.objectList.lastbox = funbox; bce.objectList.length++; } } ParseNode *pn; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif TokenStream &tokenStream = parser.tokenStream; { ParseNode *stringsAtStart = ListNode::create(PNK_STATEMENTLIST, &parser); if (!stringsAtStart) return NULL; stringsAtStart->makeEmpty(); bool ok = parser.processDirectives(stringsAtStart) && EmitTree(cx, &bce, stringsAtStart); parser.freeTree(stringsAtStart); if (!ok) return NULL; } JS_ASSERT(sc.strictModeState != StrictMode::UNKNOWN); for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } pn = parser.statement(); if (!pn) return NULL; if (!FoldConstants(cx, pn, &parser)) return NULL; if (!AnalyzeFunctions(&parser, callerFrame)) return NULL; tc.functionList = NULL; if (!EmitTree(cx, &bce, pn)) return NULL; #if JS_HAS_XML_SUPPORT if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) onlyXML = false; #endif parser.freeTree(pn); } #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via <script src="http://victim.com/foo.xml">. * For background, see: * * https://bugzilla.mozilla.org/show_bug.cgi?id=336551 */ if (pn && onlyXML && !callerFrame) { parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM); return NULL; } #endif // It's an error to use |arguments| in a function that has a rest parameter. if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) { PropertyName *arguments = cx->runtime->atomState.argumentsAtom; for (AtomDefnRange r = tc.lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST); return NULL; } } // We're not in a function context, so we don't expect any bindings. JS_ASSERT(!sc.bindings.hasBinding(cx, arguments)); } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return NULL; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return NULL; bce.tellDebuggerAboutCompiledScript(cx); return script; }