uintN js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj) { if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass) return 0; return JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS)); }
static JSBool stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { *bp = !JSVAL_IS_PRIMITIVE(v) && OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_StopIterationClass; return JS_TRUE; }
static JSString * ValueToShortSource(JSContext *cx, jsval v) { JSString *str; /* Avoid toSource bloat and fallibility for object types. */ if (JSVAL_IS_PRIMITIVE(v)) { str = js_ValueToSource(cx, v); } else if (VALUE_IS_FUNCTION(cx, v)) { /* * XXX Avoid function decompilation bloat for now. */ str = JS_GetFunctionId(JS_ValueToFunction(cx, v)); 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_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name); str = JS_NewStringCopyZ(cx, buf); } return str; }
/* * Python-esque sequence operations. */ static JSBool array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *nobj, *aobj; jsuint length, alength, slot; uintN i; jsval v; jsid id, id2; /* Treat obj as the first argument; see ECMA 15.4.4.4. */ --argv; JS_ASSERT(obj == JSVAL_TO_OBJECT(argv[0])); /* Create a new Array object and store it in the rval local root. */ nobj = js_NewArrayObject(cx, 0, NULL); if (!nobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(nobj); /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ length = 0; for (i = 0; i <= argc; i++) { v = argv[i]; if (JSVAL_IS_OBJECT(v)) { aobj = JSVAL_TO_OBJECT(v); if (aobj && OBJ_GET_CLASS(cx, aobj) == &js_ArrayClass) { if (!OBJ_GET_PROPERTY(cx, aobj, (jsid)cx->runtime->atomState.lengthAtom, &v)) { return JS_FALSE; } if (!ValueIsLength(cx, v, &alength)) return JS_FALSE; for (slot = 0; slot < alength; slot++) { if (!IndexToId(cx, slot, &id)) return JS_FALSE; if (!IndexToId(cx, length + slot, &id2)) return JS_FALSE; if (!OBJ_GET_PROPERTY(cx, aobj, id, &v)) return JS_FALSE; if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v)) return JS_FALSE; } length += alength; continue; } } if (!IndexToId(cx, length, &id)) return JS_FALSE; if (!OBJ_SET_PROPERTY(cx, nobj, id, &v)) return JS_FALSE; length++; } return JS_TRUE; }
JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda) { JSClass *clasp; JSScope *scope; uint32 i, n; JSPropertyDesc *pd; JSScopeProperty *sprop; clasp = OBJ_GET_CLASS(cx, obj); if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DESCRIBE_PROPS, clasp->name); return JS_FALSE; } if (!clasp->enumerate(cx, obj)) return JS_FALSE; /* have no props, or object's scope has not mutated from that of proto */ scope = OBJ_SCOPE(obj); if (scope->object != obj || scope->entryCount == 0) { pda->length = 0; pda->array = NULL; return JS_TRUE; } n = scope->entryCount; if (n > scope->map.nslots) n = scope->map.nslots; pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc)); if (!pd) return JS_FALSE; i = 0; for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) continue; if (!js_AddRoot(cx, &pd[i].id, NULL)) goto bad; if (!js_AddRoot(cx, &pd[i].value, NULL)) goto bad; if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i])) goto bad; if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL)) goto bad; if (++i == n) break; } pda->length = i; pda->array = pd; return JS_TRUE; bad: pda->length = i + 1; pda->array = pd; JS_PutPropertyDescArray(cx, pda); return JS_FALSE; }
static JSBool IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval) { JSObject *iterable; jsval state; uintN flags; JSBool foreach, ok; jsid id; JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass); iterable = OBJ_GET_PARENT(cx, obj); JS_ASSERT(iterable); state = STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE); if (JSVAL_IS_NULL(state)) goto stop; flags = JSVAL_TO_INT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_FLAGS)); JS_ASSERT(!(flags & JSITER_ENUMERATE)); foreach = (flags & JSITER_FOREACH) != 0; ok = #if JS_HAS_XML_SUPPORT (foreach && OBJECT_IS_XML(cx, iterable)) ? ((JSXMLObjectOps *) iterable->map->ops)-> enumerateValues(cx, iterable, JSENUMERATE_NEXT, &state, &id, rval) : #endif OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id); if (!ok) return JS_FALSE; STOBJ_SET_SLOT(obj, JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) goto stop; if (foreach) { #if JS_HAS_XML_SUPPORT if (!OBJECT_IS_XML(cx, iterable) && !OBJ_GET_PROPERTY(cx, iterable, id, rval)) { return JS_FALSE; } #endif if (!NewKeyValuePair(cx, id, *rval, rval)) return JS_FALSE; } else { *rval = ID_TO_VALUE(id); } return JS_TRUE; stop: JS_ASSERT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE) == JSVAL_NULL); *rval = JSVAL_HOLE; return JS_TRUE; }
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval) { uintN flags; /* Fast path for native iterators */ if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) { flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS)); if (flags & JSITER_ENUMERATE) return CallEnumeratorNext(cx, iterobj, flags, rval); /* * Call next directly as all the methods of the native iterator are * read-only and permanent. */ if (!IteratorNextImpl(cx, iterobj, rval)) return JS_FALSE; } else { jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval)) return JS_FALSE; if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) { /* Check for StopIteration. */ if (!cx->throwing || JSVAL_IS_PRIMITIVE(cx->exception) || OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(cx->exception)) != &js_StopIterationClass) { return JS_FALSE; } /* Inline JS_ClearPendingException(cx). */ cx->throwing = JS_FALSE; cx->exception = JSVAL_VOID; *rval = JSVAL_HOLE; return JS_TRUE; } } return JS_TRUE; }
static JSExnPrivate * GetExnPrivate(JSContext *cx, JSObject *obj) { jsval privateValue; JSExnPrivate *priv; JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ErrorClass); privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (JSVAL_IS_VOID(privateValue)) return NULL; priv = (JSExnPrivate *)JSVAL_TO_PRIVATE(privateValue); JS_ASSERT(priv); return priv; }
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_GET_CLASS(cx, obj) != &js_ErrorClass) return NULL; priv = GetExnPrivate(cx, obj); if (!priv) return NULL; return priv->errorReport; }
JS_FRIEND_API(bool) JS_FASTCALL js_CloseIterator(JSContext *cx, jsval v) { JSObject *obj; JSClass *clasp; JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); obj = JSVAL_TO_OBJECT(v); clasp = OBJ_GET_CLASS(cx, obj); if (clasp == &js_IteratorClass) { js_CloseNativeIterator(cx, obj); } #if JS_HAS_GENERATORS else if (clasp == &js_GeneratorClass) { if (!CloseGenerator(cx, obj)) return JS_FALSE; } #endif return JS_TRUE; }
JSErrorReport * js_ErrorFromException(JSContext *cx, jsval exn) { JSObject *obj; JSExnPrivate *privateData; jsval privateValue; if (JSVAL_IS_PRIMITIVE(exn)) return NULL; obj = JSVAL_TO_OBJECT(exn); if (OBJ_GET_CLASS(cx, obj) != &ExceptionClass) return NULL; privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (JSVAL_IS_VOID(privateValue)) return NULL; privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue); if (!privateData) return NULL; JS_ASSERT(privateData->errorReport); return privateData->errorReport; }
#ifdef DUMP_CALL_TABLE if (cx->options & JSOPTION_LOGCALL_TOSOURCE) { const char *classname = OBJ_GET_CLASS(cx, obj)->name; size_t classnchars = strlen(classname); static const char classpropid[] = "C"; const char *cp; size_t onchars = nchars; /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */ classnchars += sizeof classpropid - 1 + 2 + 2; if (ida->length) classnchars += 2; /* 2 for the braces, 1 for the terminator */ chars = (jschar *) realloc((ochars = chars), (nchars + classnchars + 2 + 1) * sizeof(jschar)); if (!chars) { free(ochars); goto error; } chars[nchars++] = '{'; /* 1 from the 2 braces */ for (cp = classpropid; *cp; cp++) chars[nchars++] = (jschar) *cp; chars[nchars++] = ':'; chars[nchars++] = ' '; /* 2 for ': ' */ chars[nchars++] = '"'; for (cp = classname; *cp; cp++) chars[nchars++] = (jschar) *cp; chars[nchars++] = '"'; /* 2 quotes */
static JSBool Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uint32 lineno; JSString *message, *filename; JSStackFrame *fp; if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* * 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 * js_NewObject to find the class prototype, we must get the class * prototype ourselves. */ if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), ATOM_TO_JSID(cx->runtime->atomState .classPrototypeAtom), rval)) return JS_FALSE; obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL, 0); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); } /* * 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_GET_CLASS(cx, obj) == &js_ErrorClass) STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, JSVAL_VOID); /* Set the 'message' property. */ if (argc != 0) { message = js_ValueToString(cx, argv[0]); if (!message) return JS_FALSE; argv[0] = STRING_TO_JSVAL(message); } else { message = cx->runtime->emptyString; } /* Set the 'fileName' property. */ if (argc > 1) { filename = js_ValueToString(cx, argv[1]); if (!filename) return JS_FALSE; argv[1] = STRING_TO_JSVAL(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. */ if (argc > 2) { lineno = js_ValueToECMAUint32(cx, &argv[2]); if (JSVAL_IS_NULL(argv[2])) return JS_FALSE; } else { if (!fp) fp = JS_GetScriptedCaller(cx, NULL); lineno = (fp && fp->regs) ? js_PCToLineNumber(cx, fp->script, fp->regs->pc) : 0; } return (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) || InitExnPrivate(cx, obj, message, filename, lineno, NULL); }
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) { JSObject *obj; JSTempValueRooter tvr; JSAtom *atom; JSClass *clasp; JSExtendedClass *xclasp; JSBool ok; JSObject *iterobj; jsval arg; JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE))); /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); /* XXX work around old valueOf call hidden beneath js_ValueToObject */ if (!JSVAL_IS_PRIMITIVE(*vp)) { obj = JSVAL_TO_OBJECT(*vp); } else { /* * Enumerating over null and undefined gives an empty enumerator. * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of * the first production in 12.6.4 and step 4 of the second production, * but it's "web JS" compatible. */ if ((flags & JSITER_ENUMERATE)) { if (!js_ValueToObject(cx, *vp, &obj)) return JS_FALSE; if (!obj) goto default_iter; } else { obj = js_ValueToNonNullObject(cx, *vp); if (!obj) return JS_FALSE; } } JS_ASSERT(obj); JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); clasp = OBJ_GET_CLASS(cx, obj); if ((clasp->flags & JSCLASS_IS_EXTENDED) && (xclasp = (JSExtendedClass *) clasp)->iteratorObject) { iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH)); if (!iterobj) goto bad; *vp = OBJECT_TO_JSVAL(iterobj); } else { atom = cx->runtime->atomState.iteratorAtom; #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp)) goto bad; } else #endif { if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp)) goto bad; } if (JSVAL_IS_VOID(*vp)) { default_iter: /* * Fail over to the default enumerating native iterator. * * Create iterobj with a NULL parent to ensure that we use the * correct scope chain to lookup the iterator's constructor. Since * we use the parent slot to keep track of the iterable, we must * fix it up after. */ iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL, 0); if (!iterobj) goto bad; /* Store in *vp to protect it from GC (callers must root vp). */ *vp = OBJECT_TO_JSVAL(iterobj); if (!InitNativeIterator(cx, iterobj, obj, flags)) goto bad; } else { arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) { goto bad; } if (JSVAL_IS_PRIMITIVE(*vp)) { const char *printable = js_AtomToPrintableString(cx, atom); if (printable) { js_ReportValueError2(cx, JSMSG_BAD_ITERATOR_RETURN, JSDVG_SEARCH_STACK, *vp, NULL, printable); } goto bad; } } } ok = JS_TRUE; out: if (obj) JS_POP_TEMP_ROOT(cx, &tvr); return ok; bad: ok = JS_FALSE; goto out; }
static JSBool InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, JSString *filename, uintN lineno, JSErrorReport *report) { JSCheckAccessOp checkAccess; JSErrorReporter older; JSExceptionState *state; jsval callerid, v; JSStackFrame *fp, *fpstop; size_t stackDepth, valueCount, size; JSBool overflow; JSExnPrivate *priv; JSStackTraceElem *elem; jsval *values; JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass); /* * Prepare stack trace data. * * Set aside any error reporter for cx and save its exception state * so we can suppress any checkAccess failures. Such failures should stop * the backtrace procedure, not result in a failure of this constructor. */ checkAccess = cx->runtime->checkObjectAccess; older = JS_SetErrorReporter(cx, NULL); state = JS_SaveExceptionState(cx); callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); stackDepth = 0; valueCount = 0; for (fp = cx->fp; fp; fp = fp->down) { if (fp->fun && fp->argv) { v = JSVAL_NULL; if (checkAccess && !checkAccess(cx, fp->callee, callerid, JSACC_READ, &v)) { break; } valueCount += fp->argc; } ++stackDepth; } JS_RestoreExceptionState(cx, state); JS_SetErrorReporter(cx, older); fpstop = fp; size = offsetof(JSExnPrivate, stackElems); overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); size += stackDepth * sizeof(JSStackTraceElem); overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); size += valueCount * sizeof(jsval); if (overflow) { js_ReportAllocationOverflow(cx); return JS_FALSE; } priv = (JSExnPrivate *)JS_malloc(cx, size); if (!priv) return JS_FALSE; /* * We initialize errorReport with a copy of report after setting the * private slot, to prevent GC accessing a junk value we clear the field * here. */ priv->errorReport = NULL; priv->message = message; priv->filename = filename; priv->lineno = lineno; priv->stackDepth = stackDepth; values = GetStackTraceValueBuffer(priv); elem = priv->stackElems; for (fp = cx->fp; fp != fpstop; fp = fp->down) { if (!fp->fun) { elem->funName = NULL; elem->argc = 0; } else { elem->funName = fp->fun->atom ? ATOM_TO_STRING(fp->fun->atom) : cx->runtime->emptyString; elem->argc = fp->argc; memcpy(values, fp->argv, fp->argc * sizeof(jsval)); values += fp->argc; } elem->ulineno = 0; elem->filename = NULL; if (fp->script) { elem->filename = fp->script->filename; if (fp->regs) elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc); } ++elem; } JS_ASSERT(priv->stackElems + stackDepth == elem); JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); STOBJ_SET_SLOT(exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv)); if (report) { /* * Construct a new copy of the error report struct. We can't use the * error report struct that was passed in, because it's allocated on * the stack, and also because it may point to transient data in the * JSTokenStream. */ priv->errorReport = CopyErrorReport(cx, report); if (!priv->errorReport) { /* The finalizer realeases priv since it is in the private slot. */ return JS_FALSE; } } return JS_TRUE; }
JSBool js_ReportUncaughtException(JSContext *cx) { jsval exn; JSObject *exnObject; jsval roots[5]; JSTempValueRooter tvr; JSErrorReport *reportp, report; JSString *str; const char *bytes; JSBool ok; if (!JS_IsExceptionPending(cx)) return JS_TRUE; if (!JS_GetPendingException(cx, &exn)) return JS_FALSE; memset(roots, 0, sizeof roots); JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr); /* * 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, exn); if (!str) { bytes = "unknown (can't convert to string)"; } else { roots[1] = STRING_TO_JSVAL(str); bytes = js_GetStringBytes(cx, str); if (!bytes) { ok = JS_FALSE; goto out; } } ok = JS_TRUE; if (!reportp && exnObject && OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) { const char *filename; uint32 lineno; ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]); if (!ok) goto out; if (JSVAL_IS_STRING(roots[2])) { bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2])); if (!bytes) { ok = JS_FALSE; goto out; } } ok = JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]); if (!ok) goto out; str = js_ValueToString(cx, roots[3]); if (!str) { ok = JS_FALSE; goto out; } filename = StringToFilename(cx, str); if (!filename) { ok = JS_FALSE; goto out; } ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]); if (!ok) goto out; lineno = js_ValueToECMAUint32 (cx, &roots[4]); ok = !JSVAL_IS_NULL(roots[4]); if (!ok) goto out; reportp = &report; memset(&report, 0, sizeof report); report.filename = filename; report.lineno = (uintN) lineno; } 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); } out: JS_POP_TEMP_ROOT(cx, &tvr); return ok; }
/* * This get function is specific to Array.prototype.length and other array * instance length properties. It calls back through the class get function * in case some magic happens there (see call_getProperty in jsfun.c). */ static JSBool array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp); }
static JSBool CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) { JSObject *obj, *origobj; jsval state; JSBool foreach; jsid id; JSObject *obj2; JSBool cond; JSClass *clasp; JSExtendedClass *xclasp; JSProperty *prop; JSString *str; JS_ASSERT(flags & JSITER_ENUMERATE); JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass); obj = STOBJ_GET_PARENT(iterobj); origobj = STOBJ_GET_PROTO(iterobj); state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE); if (JSVAL_IS_NULL(state)) goto stop; foreach = (flags & JSITER_FOREACH) != 0; #if JS_HAS_XML_SUPPORT /* * Treat an XML object specially only when it starts the prototype chain. * Otherwise we need to do the usual deleted and shadowed property checks. */ if (obj == origobj && OBJECT_IS_XML(cx, obj)) { if (foreach) { JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, &id, rval)) { return JS_FALSE; } } else { if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; } STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) goto stop; } else #endif { restart: if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) { #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { /* * We just finished enumerating an XML obj that is present on * the prototype chain of a non-XML origobj. Stop further * prototype chain searches because XML objects don't * enumerate prototypes. */ JS_ASSERT(origobj != obj); JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); } else #endif { obj = OBJ_GET_PROTO(cx, obj); if (obj) { STOBJ_SET_PARENT(iterobj, obj); if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) return JS_FALSE; STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (!JSVAL_IS_NULL(state)) goto restart; } } goto stop; } /* Skip properties not in obj when looking from origobj. */ if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) return JS_FALSE; if (!prop) goto restart; OBJ_DROP_PROPERTY(cx, obj2, prop); /* * If the id was found in a prototype object or an unrelated object * (specifically, not in an inner object for obj), skip it. This step * means that all OBJ_LOOKUP_PROPERTY implementations must return an * object further along on the prototype chain, or else possibly an * object returned by the JSExtendedClass.outerObject optional hook. */ if (obj != obj2) { cond = JS_FALSE; clasp = OBJ_GET_CLASS(cx, obj2); if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *) clasp; cond = xclasp->outerObject && xclasp->outerObject(cx, obj2) == obj; } if (!cond) goto restart; } if (foreach) { /* Get property querying the original object. */ if (!OBJ_GET_PROPERTY(cx, origobj, id, rval)) return JS_FALSE; } } if (foreach) { if (flags & JSITER_KEYVALUE) { if (!NewKeyValuePair(cx, id, *rval, rval)) return JS_FALSE; } } else { /* Make rval a string for uniformity and compatibility. */ str = js_ValueToString(cx, ID_TO_VALUE(id)); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); } return JS_TRUE; stop: JS_ASSERT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL); *rval = JSVAL_HOLE; return JS_TRUE; }
JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, JSPropertyDesc *pd) { JSPropertyOp getter; JSScope *scope; JSScopeProperty *aprop; jsval lastException; JSBool wasThrowing; pd->id = ID_TO_VALUE(sprop->id); wasThrowing = cx->throwing; if (wasThrowing) { lastException = cx->exception; if (JSVAL_IS_GCTHING(lastException) && !js_AddRoot(cx, &lastException, "lastException")) { return JS_FALSE; } cx->throwing = JS_FALSE; } if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) { if (!cx->throwing) { pd->flags = JSPD_ERROR; pd->value = JSVAL_VOID; } else { pd->flags = JSPD_EXCEPTION; pd->value = cx->exception; } } else { pd->flags = 0; } cx->throwing = wasThrowing; if (wasThrowing) { cx->exception = lastException; if (JSVAL_IS_GCTHING(lastException)) js_RemoveRoot(cx->runtime, &lastException); } getter = sprop->getter; pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) #if JS_HAS_CALL_OBJECT | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0) #endif /* JS_HAS_CALL_OBJECT */ | ((getter == js_GetArgument) ? JSPD_ARGUMENT : 0) | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); #if JS_HAS_CALL_OBJECT /* for Call Object 'real' getter isn't passed in to us */ if (OBJ_GET_CLASS(cx, obj) == &js_CallClass && getter == js_CallClass.getProperty) { /* * Property of a heavyweight function's variable object having the * class-default getter. It's either an argument if permanent, or a * nested function if impermanent. Local variables have a special * getter (js_GetCallVariable, tested above) and setter, and not the * class default. */ pd->flags |= (sprop->attrs & JSPROP_PERMANENT) ? JSPD_ARGUMENT : JSPD_VARIABLE; } #endif /* JS_HAS_CALL_OBJECT */ pd->spare = 0; pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)) ? sprop->shortid : 0; pd->alias = JSVAL_VOID; scope = OBJ_SCOPE(obj); if (SPROP_HAS_VALID_SLOT(sprop, scope)) { for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) { if (aprop != sprop && aprop->slot == sprop->slot) { pd->alias = ID_TO_VALUE(aprop->id); break; } } } return JS_TRUE; }
JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler handler, void *closure) { JSAtom *atom; jsid propid; JSObject *pobj; JSScopeProperty *sprop; JSRuntime *rt; JSWatchPoint *wp; JSPropertyOp watcher; if (!OBJ_IS_NATIVE(obj)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH, OBJ_GET_CLASS(cx, obj)->name); return JS_FALSE; } if (JSVAL_IS_INT(id)) { propid = (jsid)id; atom = NULL; } else { atom = js_ValueToStringAtom(cx, id); if (!atom) return JS_FALSE; propid = (jsid)atom; } if (!js_LookupProperty(cx, obj, propid, &pobj, (JSProperty **)&sprop)) return JS_FALSE; rt = cx->runtime; if (!sprop) { /* Check for a deleted symbol watchpoint, which holds its property. */ sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid); if (!sprop) { /* Make a new property in obj so we can watch for the first set. */ if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE, (JSProperty **)&sprop)) { sprop = NULL; } } } else if (pobj != obj) { /* Clone the prototype property so we can watch the right object. */ jsval value; JSPropertyOp getter, setter; uintN attrs; if (OBJ_IS_NATIVE(pobj)) { value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)) ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) : JSVAL_VOID; getter = sprop->getter; setter = sprop->setter; attrs = sprop->attrs; } else { if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) { OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); return JS_FALSE; } getter = setter = JS_PropertyStub; attrs = JSPROP_ENUMERATE; } OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); if (!js_DefineProperty(cx, obj, propid, value, getter, setter, attrs, (JSProperty **)&sprop)) { sprop = NULL; } } if (!sprop) return JS_FALSE; wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid); if (!wp) { watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter); if (!watcher) return JS_FALSE; wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp); if (!wp) return JS_FALSE; wp->handler = NULL; wp->closure = NULL; if (!js_AddRoot(cx, &wp->closure, "wp->closure")) { JS_free(cx, wp); return JS_FALSE; } JS_APPEND_LINK(&wp->links, &rt->watchPointList); wp->object = obj; wp->sprop = sprop; JS_ASSERT(sprop->setter != js_watch_set); wp->setter = sprop->setter; wp->nrefs = 1; sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, sprop->getter, watcher); if (!sprop) return DropWatchPoint(cx, wp); } wp->handler = handler; wp->closure = closure; OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop); return JS_TRUE; }
JSBool js_ReportUncaughtException(JSContext *cx) { jsval exn, *vp; JSObject *exnObject; void *mark; JSErrorReport *reportp, report; JSString *str; const char *bytes; JSBool ok; if (!JS_IsExceptionPending(cx)) return JS_TRUE; if (!JS_GetPendingException(cx, &exn)) return JS_FALSE; /* * 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; vp = NULL; #ifdef __GNUC__ /* suppress bogus gcc warnings */ mark = NULL; #endif } else { exnObject = JSVAL_TO_OBJECT(exn); vp = js_AllocStack(cx, 5, &mark); if (!vp) { ok = JS_FALSE; goto out; } vp[0] = exn; } #if JS_HAS_ERROR_EXCEPTIONS reportp = js_ErrorFromException(cx, exn); #else reportp = NULL; #endif /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ str = js_ValueToString(cx, exn); if (!str) { bytes = "unknown (can't convert to string)"; } else { if (vp) vp[1] = STRING_TO_JSVAL(str); bytes = js_GetStringBytes(str); } ok = JS_TRUE; if (!reportp && exnObject && OBJ_GET_CLASS(cx, exnObject) == &ExceptionClass) { const char *filename; uint32 lineno; ok = JS_GetProperty(cx, exnObject, js_message_str, &vp[2]); if (!ok) goto out; if (JSVAL_IS_STRING(vp[2])) bytes = JS_GetStringBytes(JSVAL_TO_STRING(vp[2])); ok = JS_GetProperty(cx, exnObject, js_filename_str, &vp[3]); if (!ok) goto out; str = js_ValueToString(cx, vp[3]); if (!str) { ok = JS_FALSE; goto out; } filename = StringToFilename(cx, str); ok = JS_GetProperty(cx, exnObject, js_lineno_str, &vp[4]); if (!ok) goto out; ok = js_ValueToECMAUint32 (cx, vp[4], &lineno); if (!ok) goto out; reportp = &report; memset(&report, 0, sizeof report); report.filename = filename; report.lineno = (uintN) lineno; } if (!reportp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNCAUGHT_EXCEPTION, bytes); } else { /* Flag the error as an exception. */ reportp->flags |= JSREPORT_EXCEPTION; js_ReportErrorAgain(cx, bytes, reportp); } JS_ClearPendingException(cx); out: if (exnObject) js_FreeStack(cx, mark); return ok; }
static JSBool Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool ok; jsval pval; int32 lineno; JSString *message, *filename; if (cx->creatingException) return JS_FALSE; cx->creatingException = JS_TRUE; if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* * 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 * js_NewObject to find the class prototype, we must get the class * prototype ourselves. */ ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), (jsid)cx->runtime->atomState.classPrototypeAtom, &pval); if (!ok) goto out; obj = js_NewObject(cx, &ExceptionClass, JSVAL_TO_OBJECT(pval), NULL); if (!obj) { ok = JS_FALSE; goto out; } *rval = OBJECT_TO_JSVAL(obj); } /* * 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_GET_CLASS(cx, obj) == &ExceptionClass) OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID); /* Set the 'message' property. */ if (argc != 0) { message = js_ValueToString(cx, argv[0]); if (!message) { ok = JS_FALSE; goto out; } } else { message = cx->runtime->emptyString; } /* Set the 'fileName' property. */ if (argc > 1) { filename = js_ValueToString(cx, argv[1]); if (!filename) { ok = JS_FALSE; goto out; } } else { filename = cx->runtime->emptyString; } /* Set the 'lineNumber' property. */ if (argc > 2) { ok = js_ValueToInt32(cx, argv[2], &lineno); if (!ok) goto out; } else { lineno = 0; } ok = InitExceptionObject(cx, obj, message, filename, lineno); out: cx->creatingException = JS_FALSE; return ok; }
static JSBool InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message, JSString *filename, uintN lineno) { JSCheckAccessOp checkAccess; JSErrorReporter older; JSExceptionState *state; jschar *stackbuf; size_t stacklen, stackmax; JSStackFrame *fp; jsval callerid, v; JSBool ok; JSString *argsrc, *stack; uintN i, ulineno; const char *cp; char ulnbuf[11]; if (!JS_DefineProperty(cx, obj, js_message_str, STRING_TO_JSVAL(message), NULL, NULL, JSPROP_ENUMERATE)) { return JS_FALSE; } if (!JS_DefineProperty(cx, obj, js_filename_str, STRING_TO_JSVAL(filename), NULL, NULL, JSPROP_ENUMERATE)) { return JS_FALSE; } if (!JS_DefineProperty(cx, obj, js_lineno_str, INT_TO_JSVAL(lineno), NULL, NULL, JSPROP_ENUMERATE)) { return JS_FALSE; } /* * Set the 'stack' property. * * First, set aside any error reporter for cx and save its exception state * so we can suppress any checkAccess failures. Such failures should stop * the backtrace procedure, not result in a failure of this constructor. */ checkAccess = cx->runtime->checkObjectAccess; if (checkAccess) { older = JS_SetErrorReporter(cx, NULL); state = JS_SaveExceptionState(cx); } #ifdef __GNUC__ /* suppress bogus gcc warnings */ else { older = NULL; state = NULL; } #endif callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); /* * Prepare to allocate a jschar buffer at stackbuf, where stacklen indexes * the next free jschar slot, and with room for at most stackmax non-null * jschars. If stackbuf is non-null, it always contains an extra slot for * the null terminator we'll store at the end, as a backstop. * * All early returns must goto done after this point, till the after-loop * cleanup code has run! */ stackbuf = NULL; stacklen = stackmax = 0; ok = JS_TRUE; #define APPEND_CHAR_TO_STACK(c) \ JS_BEGIN_MACRO \ if (stacklen == stackmax) { \ void *ptr_; \ stackmax = stackmax ? 2 * stackmax : 64; \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) { \ ok = JS_FALSE; \ goto done; \ } \ stackbuf = ptr_; \ } \ stackbuf[stacklen++] = (c); \ JS_END_MACRO #define APPEND_STRING_TO_STACK(str) \ JS_BEGIN_MACRO \ JSString *str_ = str; \ size_t length_ = JSSTRING_LENGTH(str_); \ if (stacklen + length_ > stackmax) { \ void *ptr_; \ stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) { \ ok = JS_FALSE; \ goto done; \ } \ stackbuf = ptr_; \ } \ js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_); \ stacklen += length_; \ JS_END_MACRO for (fp = cx->fp; fp; fp = fp->down) { if (checkAccess) { v = (fp->fun && fp->argv) ? fp->argv[-2] : JSVAL_NULL; if (!JSVAL_IS_PRIMITIVE(v)) { ok = checkAccess(cx, fp->fun->object, callerid, JSACC_READ, &v); if (!ok) { ok = JS_TRUE; break; } } } if (fp->fun) { if (fp->fun->atom) APPEND_STRING_TO_STACK(ATOM_TO_STRING(fp->fun->atom)); APPEND_CHAR_TO_STACK('('); for (i = 0; i < fp->argc; i++) { /* Avoid toSource bloat and fallibility for object types. */ v = fp->argv[i]; if (JSVAL_IS_PRIMITIVE(v)) { argsrc = js_ValueToSource(cx, v); } else if (JSVAL_IS_FUNCTION(cx, v)) { /* XXX Avoid function decompilation bloat for now. */ argsrc = JS_GetFunctionId(JS_ValueToFunction(cx, v)); if (!argsrc) argsrc = js_ValueToSource(cx, v); } 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_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name); argsrc = JS_NewStringCopyZ(cx, buf); } if (!argsrc) { ok = JS_FALSE; goto done; } if (i > 0) APPEND_CHAR_TO_STACK(','); APPEND_STRING_TO_STACK(argsrc); } APPEND_CHAR_TO_STACK(')'); } APPEND_CHAR_TO_STACK('@'); if (fp->script && fp->script->filename) { for (cp = fp->script->filename; *cp; cp++) APPEND_CHAR_TO_STACK(*cp); } APPEND_CHAR_TO_STACK(':'); if (fp->script && fp->pc) { ulineno = js_PCToLineNumber(fp->script, fp->pc); JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", ulineno); for (cp = ulnbuf; *cp; cp++) APPEND_CHAR_TO_STACK(*cp); } else { APPEND_CHAR_TO_STACK('0'); } APPEND_CHAR_TO_STACK('\n'); } #undef APPEND_CHAR_TO_STACK #undef APPEND_STRING_TO_STACK done: if (checkAccess) { if (ok) JS_RestoreExceptionState(cx, state); else JS_DropExceptionState(cx, state); JS_SetErrorReporter(cx, older); } if (!ok) { JS_free(cx, stackbuf); return JS_FALSE; } if (!stackbuf) { stack = cx->runtime->emptyString; } else { /* NB: if stackbuf was allocated, it has room for the terminator. */ JS_ASSERT(stacklen <= stackmax); if (stacklen < stackmax) { /* * Realloc can fail when shrinking on some FreeBSD versions, so * don't use JS_realloc here; simply let the oversized allocation * be owned by the string in that rare case. */ void *shrunk = realloc(stackbuf, (stacklen+1) * sizeof(jschar)); if (shrunk) stackbuf = shrunk; } stackbuf[stacklen] = 0; stack = js_NewString(cx, stackbuf, stacklen, 0); if (!stack) { JS_free(cx, stackbuf); return JS_FALSE; } } return JS_DefineProperty(cx, obj, js_stack_str, STRING_TO_JSVAL(stack), NULL, NULL, JSPROP_ENUMERATE); }