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 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; }
extern JSObject * js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope) { assertSameCompartment(cx, scope); JSExnPrivate *priv = GetExnPrivate(errobj); uint32 stackDepth = priv->stackDepth; size_t valueCount = 0; for (uint32 i = 0; i < stackDepth; i++) valueCount += priv->stackElems[i].argc; size_t size = offsetof(JSExnPrivate, stackElems) + stackDepth * sizeof(JSStackTraceElem) + valueCount * sizeof(jsval); JSExnPrivate *copy = (JSExnPrivate *)cx->malloc_(size); if (!copy) return NULL; struct AutoFree { JSContext *cx; JSExnPrivate *p; ~AutoFree() { if (p) { cx->free_(p->errorReport); cx->free_(p); } } } autoFree = {cx, copy}; // Copy each field. Don't bother copying the stack elements. if (priv->errorReport) { copy->errorReport = CopyErrorReport(cx, priv->errorReport); if (!copy->errorReport) return NULL; } else { copy->errorReport = NULL; } copy->message.init(priv->message); if (!cx->compartment->wrap(cx, ©->message)) return NULL; JS::Anchor<JSString *> messageAnchor(copy->message); copy->filename.init(priv->filename); if (!cx->compartment->wrap(cx, ©->filename)) return NULL; JS::Anchor<JSString *> filenameAnchor(copy->filename); copy->lineno = priv->lineno; copy->stackDepth = 0; copy->exnType = priv->exnType; // Create the Error object. JSObject *proto; if (!js_GetClassPrototype(cx, scope->getGlobal(), GetExceptionProtoKey(copy->exnType), &proto)) return NULL; JSObject *copyobj = NewNativeClassInstance(cx, &ErrorClass, proto, proto->getParent()); copyobj->setPrivate(copy); autoFree.p = NULL; return copyobj; }
ClonedBlockObject * ClonedBlockObject::create(JSContext *cx, StaticBlockObject &block, StackFrame *fp) { RootedVarTypeObject type(cx); type = block.getNewType(cx); if (!type) return NULL; HeapSlot *slots; if (!PreallocateObjectDynamicSlots(cx, block.lastProperty(), &slots)) return NULL; RootedVarShape shape(cx); shape = block.lastProperty(); JSObject *obj = JSObject::create(cx, FINALIZE_KIND, shape, type, slots); if (!obj) return NULL; /* Set the parent if necessary, as for call objects. */ JSObject &global = fp->scopeChain().global(); if (&global != obj->getParent()) { JS_ASSERT(obj->getParent() == NULL); if (!obj->setParent(cx, &global)) return NULL; } JS_ASSERT(!obj->inDictionaryMode()); JS_ASSERT(obj->slotSpan() >= block.slotCount() + RESERVED_SLOTS); obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(block.stackDepth())); obj->setPrivate(js_FloatingFrameIfGenerator(cx, fp)); if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx)) return NULL; return &obj->asClonedBlock(); }
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); }
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; }
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; }
static JSBool Exception(JSContext *cx, uintN argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); /* * 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. */ Value protov; if (!args.callee().getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov)) return false; if (!protov.isObject()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error"); return false; } JSObject *errProto = &protov.toObject(); JSObject *obj = NewNativeClassInstance(cx, &ErrorClass, errProto, errProto->getParent()); if (!obj) return false; /* Set the 'message' property. */ JSString *message; if (args.length() != 0 && !args[0].isUndefined()) { message = js_ValueToString(cx, args[0]); if (!message) return false; args[0].setString(message); } else { message = NULL; } /* Find the scripted caller. */ FrameRegsIter iter(cx); while (!iter.done() && !iter.fp()->isScriptFrame()) ++iter; /* Set the 'fileName' property. */ JSString *filename; if (args.length() > 1) { filename = js_ValueToString(cx, args[1]); if (!filename) return false; args[1].setString(filename); } else { if (!iter.done()) { filename = FilenameToString(cx, iter.fp()->script()->filename); if (!filename) return false; } else { filename = cx->runtime->emptyString; } } /* Set the 'lineNumber' property. */ uint32_t lineno; if (args.length() > 2) { if (!ValueToECMAUint32(cx, args[2], &lineno)) return false; } else { lineno = iter.done() ? 0 : js_FramePCToLineNumber(cx, iter.fp(), iter.pc()); } intN exnType = args.callee().getReservedSlot(JSSLOT_ERROR_EXNTYPE).toInt32(); if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType)) return false; args.rval().setObject(*obj); return true; }
/* * Construct a call object for the given bindings. If this is a call object * for a function invocation, callee should be the function being called. * Otherwise it must be a call object for eval of strict mode code, and callee * must be null. */ CallObject * CallObject::create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee) { RootedVarShape shape(cx); shape = script->bindings.callObjectShape(cx); if (shape == NULL) return NULL; gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots() + 1); RootedVarTypeObject type(cx); type = cx->compartment->getEmptyType(cx); if (!type) return NULL; HeapSlot *slots; if (!PreallocateObjectDynamicSlots(cx, shape, &slots)) return NULL; JSObject *obj = JSObject::create(cx, kind, shape, type, slots); if (!obj) return NULL; /* * Update the parent for bindings associated with non-compileAndGo scripts, * whose call objects do not have a consistent global variable and need * to be updated dynamically. */ JSObject &global = enclosing.global(); if (&global != obj->getParent()) { JS_ASSERT(obj->getParent() == NULL); if (!obj->setParent(cx, &global)) return NULL; } #ifdef DEBUG JS_ASSERT(!obj->inDictionaryMode()); for (Shape::Range r = obj->lastProperty(); !r.empty(); r.popFront()) { const Shape &s = r.front(); if (s.hasSlot()) { JS_ASSERT(s.slot() + 1 == obj->slotSpan()); break; } } #endif if (!obj->asScope().setEnclosingScope(cx, enclosing)) return NULL; JS_ASSERT_IF(callee, callee->isFunction()); obj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee)); obj->initFixedSlot(ARGUMENTS_SLOT, MagicValue(JS_UNASSIGNED_ARGUMENTS)); /* * If |bindings| is for a function that has extensible parents, that means * its Call should have its own shape; see BaseShape::extensibleParents. */ if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx)) return NULL; return &obj->asCall(); }