bool SavedStacks::getLocation(JSContext *cx, const FrameIter &iter, MutableHandleLocationValue locationp) { // We should only ever be caching location values for scripts in this // compartment. Otherwise, we would get dead cross-compartment scripts in // the cache because our compartment's sweep method isn't called when their // compartment gets collected. assertSameCompartment(cx, this, iter.compartment()); // When we have a |JSScript| for this frame, use a potentially memoized // location from our PCLocationMap and copy it into |locationp|. When we do // not have a |JSScript| for this frame (asm.js frames), we take a slow path // that doesn't employ memoization, and update |locationp|'s slots directly. if (!iter.hasScript()) { if (const char16_t *displayURL = iter.scriptDisplayURL()) { locationp->source = AtomizeChars(cx, displayURL, js_strlen(displayURL)); } else { const char *filename = iter.scriptFilename() ? iter.scriptFilename() : ""; locationp->source = Atomize(cx, filename, strlen(filename)); } if (!locationp->source) return false; locationp->line = iter.computeLine(&locationp->column); return true; } RootedScript script(cx, iter.script()); jsbytecode *pc = iter.pc(); PCKey key(script, pc); PCLocationMap::AddPtr p = pcLocationMap.lookupForAdd(key); if (!p) { RootedAtom source(cx); if (const char16_t *displayURL = iter.scriptDisplayURL()) { source = AtomizeChars(cx, displayURL, js_strlen(displayURL)); } else { const char *filename = script->filename() ? script->filename() : ""; source = Atomize(cx, filename, strlen(filename)); } if (!source) return false; uint32_t column; uint32_t line = PCToLineNumber(script, pc, &column); LocationValue value(source, line, column); if (!pcLocationMap.add(p, key, value)) return false; } locationp.set(p->value()); return true; }
bool report(Census &census, MutableHandleValue report) { JSContext *cx = census.cx; RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); if (!obj) return false; for (typename Table::Range r = table.all(); !r.empty(); r.popFront()) { EachType &entry = r.front().value(); RootedValue entryReport(cx); if (!entry.report(census, &entryReport)) return false; const jschar *name = r.front().key(); MOZ_ASSERT(name); JSAtom *atom = AtomizeChars(cx, name, js_strlen(name)); if (!atom) return false; RootedId entryId(cx, AtomToId(atom)); if (!JSObject::defineGeneric(cx, obj, entryId, entryReport)) return false; } report.setObject(*obj); return true; }
/* * The arguments from ap need to be packaged up into an array and stored * into the report struct. * * The format string addressed by the error number may contain operands * identified by the format {N}, where N is a decimal digit. Each of these * is to be replaced by the Nth argument from the va_list. The complete * message is placed into reportp->ucmessage converted to a JSString. * * Returns true if the expansion succeeds (can fail if out of memory). */ JSBool js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, void *userRef, const uintN errorNumber, char **messagep, JSErrorReport *reportp, JSBool *warningp, JSBool charArgs, va_list ap) { const JSErrorFormatString *efs; int i; int argCount; *warningp = JSREPORT_IS_WARNING(reportp->flags); if (*warningp && JS_HAS_WERROR_OPTION(cx)) { reportp->flags &= ~JSREPORT_WARNING; *warningp = JS_FALSE; } *messagep = NULL; /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */ if (!callback || callback == js_GetErrorMessage) efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber); else efs = callback(userRef, NULL, errorNumber); if (efs) { size_t totalArgsLength = 0; size_t argLengths[10]; /* only {0} thru {9} supported */ argCount = efs->argCount; JS_ASSERT(argCount <= 10); if (argCount > 0) { /* * Gather the arguments into an array, and accumulate * their sizes. We allocate 1 more than necessary and * null it out to act as the caboose when we free the * pointers later. */ reportp->messageArgs = (const jschar **) JS_malloc(cx, sizeof(jschar *) * (argCount + 1)); if (!reportp->messageArgs) return JS_FALSE; reportp->messageArgs[argCount] = NULL; for (i = 0; i < argCount; i++) { if (charArgs) { char *charArg = va_arg(ap, char *); size_t charArgLength = strlen(charArg); reportp->messageArgs[i] = js_InflateString(cx, charArg, &charArgLength); if (!reportp->messageArgs[i]) goto error; } else { reportp->messageArgs[i] = va_arg(ap, jschar *); } argLengths[i] = js_strlen(reportp->messageArgs[i]); totalArgsLength += argLengths[i]; } /* NULL-terminate for easy copying. */ reportp->messageArgs[i] = NULL; }
static void getString(CacheAndIndex* cacheAndIndex) { for (int i = 0; i < NUM_ITERATIONS; i++) { auto str = STRINGS[cacheAndIndex->index % NUM_STRINGS]; auto dupe = js::DuplicateString(str); MOZ_RELEASE_ASSERT(dupe); auto deduped = cacheAndIndex->cache->getOrCreate(mozilla::Move(dupe), js_strlen(str)); MOZ_RELEASE_ASSERT(deduped.isSome()); MOZ_RELEASE_ASSERT(js_strcmp(str, deduped->chars()) == 0); { auto cloned = deduped->clone(); // We should be de-duplicating and giving back the same string. MOZ_RELEASE_ASSERT(deduped->chars() == cloned.chars()); } } js_delete(cacheAndIndex); }
/* * The arguments from ap need to be packaged up into an array and stored * into the report struct. * * The format string addressed by the error number may contain operands * identified by the format {N}, where N is a decimal digit. Each of these * is to be replaced by the Nth argument from the va_list. The complete * message is placed into reportp->ucmessage converted to a JSString. * * Returns true if the expansion succeeds (can fail if out of memory). */ JSBool js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, void *userRef, const uintN errorNumber, char **messagep, JSErrorReport *reportp, JSBool *warningp, JSBool charArgs, va_list ap) { const JSErrorFormatString *efs; int i; int argCount; *warningp = JSREPORT_IS_WARNING(reportp->flags); if (*warningp && JS_HAS_WERROR_OPTION(cx)) { reportp->flags &= ~JSREPORT_WARNING; *warningp = JS_FALSE; } *messagep = NULL; if (callback) { efs = callback(userRef, NULL, errorNumber); if (efs) { size_t totalArgsLength = 0; size_t argLengths[10]; /* only {0} thru {9} supported */ argCount = efs->argCount; JS_ASSERT(argCount <= 10); if (argCount > 0) { /* * Gather the arguments into an array, and accumulate * their sizes. We allocate 1 more than necessary and * null it out to act as the caboose when we free the * pointers later. */ reportp->messageArgs = (const jschar **) JS_malloc(cx, sizeof(jschar *) * (argCount + 1)); if (!reportp->messageArgs) return JS_FALSE; reportp->messageArgs[argCount] = NULL; for (i = 0; i < argCount; i++) { if (charArgs) { char *charArg = va_arg(ap, char *); reportp->messageArgs[i] = js_InflateString(cx, charArg, strlen(charArg)); if (!reportp->messageArgs[i]) goto error; } else reportp->messageArgs[i] = va_arg(ap, jschar *); argLengths[i] = js_strlen(reportp->messageArgs[i]); totalArgsLength += argLengths[i]; } /* NULL-terminate for easy copying. */ reportp->messageArgs[i] = NULL; } /* * Parse the error format, substituting the argument X * for {X} in the format. */ if (argCount > 0) { if (efs->format) { const char *fmt; const jschar *arg; jschar *out; int expandedArgs = 0; size_t expandedLength = strlen(efs->format) - (3 * argCount) /* exclude the {n} */ + totalArgsLength; /* * Note - the above calculation assumes that each argument * is used once and only once in the expansion !!! */ reportp->ucmessage = out = (jschar *) JS_malloc(cx, (expandedLength + 1) * sizeof(jschar)); if (!out) goto error; fmt = efs->format; while (*fmt) { if (*fmt == '{') { if (isdigit(fmt[1])) { int d = JS7_UNDEC(fmt[1]); JS_ASSERT(expandedArgs < argCount); arg = reportp->messageArgs[d]; js_strncpy(out, arg, argLengths[d]); out += argLengths[d]; fmt += 3; expandedArgs++; continue; } } /* * is this kosher? */ *out++ = (unsigned char)(*fmt++); } JS_ASSERT(expandedArgs == argCount); *out = 0; *messagep = js_DeflateString(cx, reportp->ucmessage, (size_t)(out - reportp->ucmessage)); if (!*messagep) goto error; } } else { /* * Zero arguments: the format string (if it exists) is the * entire message. */ if (efs->format) { *messagep = JS_strdup(cx, efs->format); if (!*messagep) goto error; reportp->ucmessage = js_InflateString(cx, *messagep, strlen(*messagep)); if (!reportp->ucmessage) goto error; } } }
/* * Copy everything interesting about an error into allocated memory. */ static JSExnPrivate * exn_newPrivate(JSContext *cx, JSErrorReport *report) { intN i; JSExnPrivate *newPrivate; JSErrorReport *newReport; size_t capacity; newPrivate = (JSExnPrivate *)JS_malloc(cx, sizeof (JSExnPrivate)); if (!newPrivate) return NULL; memset(newPrivate, 0, sizeof (JSExnPrivate)); /* Copy the error report */ newReport = (JSErrorReport *)JS_malloc(cx, sizeof (JSErrorReport)); if (!newReport) goto error; memset(newReport, 0, sizeof (JSErrorReport)); newPrivate->errorReport = newReport; if (report->filename != NULL) { newReport->filename = JS_strdup(cx, report->filename); if (!newReport->filename) goto error; } else { newReport->filename = NULL; } newReport->lineno = report->lineno; /* * We don't need to copy linebuf and tokenptr, because they * point into the deflated string cache. (currently?) */ newReport->linebuf = report->linebuf; newReport->tokenptr = report->tokenptr; /* * But we do need to copy uclinebuf, uctokenptr, because they're * pointers into internal tokenstream structs, and may go away. */ if (report->uclinebuf != NULL) { capacity = js_strlen(report->uclinebuf) + 1; newReport->uclinebuf = (const jschar *)JS_malloc(cx, capacity * sizeof(jschar)); if (!newReport->uclinebuf) goto error; js_strncpy((jschar *)newReport->uclinebuf, report->uclinebuf, capacity); newReport->uctokenptr = newReport->uclinebuf + (report->uctokenptr - report->uclinebuf); } else { newReport->uclinebuf = newReport->uctokenptr = NULL; } if (report->ucmessage != NULL) { capacity = js_strlen(report->ucmessage) + 1; newReport->ucmessage = (const jschar *)JS_malloc(cx, capacity * sizeof(jschar)); if (!newReport->ucmessage) goto error; js_strncpy((jschar *)newReport->ucmessage, report->ucmessage, capacity); if (report->messageArgs) { for (i = 0; report->messageArgs[i] != NULL; i++) continue; JS_ASSERT(i); newReport->messageArgs = (const jschar **)JS_malloc(cx, (i + 1) * sizeof(jschar *)); if (!newReport->messageArgs) goto error; for (i = 0; report->messageArgs[i] != NULL; i++) { capacity = js_strlen(report->messageArgs[i]) + 1; newReport->messageArgs[i] = (const jschar *)JS_malloc(cx, capacity * sizeof(jschar)); if (!newReport->messageArgs[i]) goto error; js_strncpy((jschar *)(newReport->messageArgs[i]), report->messageArgs[i], capacity); } newReport->messageArgs[i] = NULL; } else { newReport->messageArgs = NULL; } } else { newReport->ucmessage = NULL; newReport->messageArgs = NULL; } newReport->errorNumber = report->errorNumber; /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ newReport->flags = report->flags; return newPrivate; error: exn_destroyPrivate(cx, newPrivate); return NULL; }
static JSBool array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, jsval *rval, JSBool localeString) { JSBool ok; jsval v; jsuint length, index; jschar *chars, *ochars; size_t nchars, growth, seplen, tmplen; const jschar *sepstr; JSString *str; JSHashEntry *he; JSObject *obj2; ok = js_GetLengthProperty(cx, obj, &length); if (!ok) return JS_FALSE; he = js_EnterSharpObject(cx, obj, NULL, &chars); if (!he) return JS_FALSE; if (literalize) { if (IS_SHARP(he)) { #if JS_HAS_SHARP_VARS nchars = js_strlen(chars); #else chars[0] = '['; chars[1] = ']'; chars[2] = 0; nchars = 2; #endif goto make_string; } /* * Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the * terminating 0. */ growth = (1 + 3 + 1) * sizeof(jschar); if (!chars) { nchars = 0; chars = (jschar *) malloc(growth); if (!chars) goto done; } else { MAKE_SHARP(he); nchars = js_strlen(chars); chars = (jschar *) realloc((ochars = chars), nchars * sizeof(jschar) + growth); if (!chars) { free(ochars); goto done; } } chars[nchars++] = '['; } else { /* * Free any sharp variable definition in chars. Normally, we would * MAKE_SHARP(he) so that only the first sharp variable annotation is * a definition, and all the rest are references, but in the current * case of (!literalize), we don't need chars at all. */ if (chars) JS_free(cx, chars); chars = NULL; nchars = 0; /* Return the empty string on a cycle as well as on empty join. */ if (IS_BUSY(he) || length == 0) { js_LeaveSharpObject(cx, NULL); *rval = JS_GetEmptyStringValue(cx); return ok; } /* Flag he as BUSY so we can distinguish a cycle from a join-point. */ MAKE_BUSY(he); } sepstr = NULL; seplen = JSSTRING_LENGTH(sep); v = JSVAL_NULL; for (index = 0; index < length; index++) { ok = JS_GetElement(cx, obj, index, &v); if (!ok) goto done; if (!literalize && (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))) { str = cx->runtime->emptyString; } else { if (localeString) { if (!js_ValueToObject(cx, v, &obj2) || !js_TryMethod(cx, obj2, cx->runtime->atomState.toLocaleStringAtom, 0, NULL, &v)) { str = NULL; } else { str = js_ValueToString(cx, v); } } else { str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v); } if (!str) { ok = JS_FALSE; goto done; } } /* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */ growth = (nchars + (sepstr ? seplen : 0) + JSSTRING_LENGTH(str) + 3 + 1) * sizeof(jschar); if (!chars) { chars = (jschar *) malloc(growth); if (!chars) goto done; } else { chars = (jschar *) realloc((ochars = chars), growth); if (!chars) { free(ochars); goto done; } } if (sepstr) { js_strncpy(&chars[nchars], sepstr, seplen); nchars += seplen; } sepstr = JSSTRING_CHARS(sep); tmplen = JSSTRING_LENGTH(str); js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); nchars += tmplen; } done: if (literalize) { if (chars) { if (JSVAL_IS_VOID(v)) { chars[nchars++] = ','; chars[nchars++] = ' '; } chars[nchars++] = ']'; } } else { CLEAR_BUSY(he); } js_LeaveSharpObject(cx, NULL); if (!ok) { if (chars) free(chars); return ok; } make_string: if (!chars) { JS_ReportOutOfMemory(cx); return JS_FALSE; } chars[nchars] = 0; str = js_NewString(cx, chars, nchars, 0); if (!str) { free(chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; }
JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jschar *chars; size_t nchars; char *clazz, *prefix; JSString *str; #if JS_HAS_OBJECT_LITERAL if (cx->version >= JSVERSION_1_2) { JSProperty *list, *prop; JSBool ok; char *comma, *idquote, *valquote; jsval id, val; JSString *idstr, *valstr; #if JS_HAS_SHARP_VARS PRHashEntry *he; #endif /* Early returns after this must unlock, or goto done with ok set. */ JS_LOCK(cx); list = obj->map->props; #if JS_HAS_SHARP_VARS he = js_EnterSharpObject(cx, obj, &chars); if (!he) { JS_UNLOCK(cx); return JS_FALSE; } if (IS_SHARP(he)) { /* we didn't enter -- obj is already sharp */ JS_UNLOCK(cx); nchars = js_strlen(chars); goto make_string; } MAKE_SHARP(he); #else /* Shut off recursion by temporarily clearing obj's property list. */ obj->map->props = NULL; chars = NULL; #endif ok = JS_TRUE; /* Allocate 2 + 1 for "{}" and the terminator. */ if (!chars) { chars = malloc((2 + 1) * sizeof(jschar)); nchars = 0; } else { nchars = js_strlen(chars); chars = realloc(chars, (nchars + 2 + 1) * sizeof(jschar)); } if (!chars) goto done; chars[nchars++] = '{'; comma = NULL; for (prop = list; prop; prop = prop->next) { if (!prop->symbols || !(prop->flags & JSPROP_ENUMERATE)) continue; /* Get strings for id and val and GC-root them via argv. */ id = js_IdToValue(sym_id(prop->symbols)); idstr = js_ValueToString(cx, id); if (idstr) argv[0] = STRING_TO_JSVAL(idstr); val = prop->object->slots[prop->slot]; valstr = js_ValueToString(cx, val); if (!idstr || !valstr) { ok = JS_FALSE; goto done; } argv[1] = STRING_TO_JSVAL(valstr); /* If id is a non-identifier string, it needs to be quoted. */ if (JSVAL_IS_STRING(id) && !js_IsIdentifier(idstr)) { idquote = "'"; idstr = js_EscapeString(cx, idstr, *idquote); if (!idstr) { ok = JS_FALSE; goto done; } argv[0] = STRING_TO_JSVAL(idstr); } else { idquote = NULL; } /* Same for val, except it can't be an identifier. */ if (JSVAL_IS_STRING(val)) { valquote = "\""; valstr = js_EscapeString(cx, valstr, *valquote); if (!valstr) { ok = JS_FALSE; goto done; } argv[1] = STRING_TO_JSVAL(valstr); } else { valquote = NULL; } /* Allocate 1 + 1 at end for closing brace and terminating 0. */ chars = realloc(chars, (nchars + (comma ? 2 : 0) + (idquote ? 2 : 0) + idstr->length + 1 + (valquote ? 2 : 0) + valstr->length + 1 + 1) * sizeof(jschar)); if (!chars) goto done; if (comma) { chars[nchars++] = comma[0]; chars[nchars++] = comma[1]; } comma = ", "; if (idquote) chars[nchars++] = *idquote; js_strncpy(&chars[nchars], idstr->chars, idstr->length); nchars += idstr->length; if (idquote) chars[nchars++] = *idquote; chars[nchars++] = ':'; if (valquote) chars[nchars++] = *valquote; js_strncpy(&chars[nchars], valstr->chars, valstr->length); nchars += valstr->length; if (valquote) chars[nchars++] = *valquote; } done: if (chars) { chars[nchars++] = '}'; chars[nchars] = 0; } #if JS_HAS_SHARP_VARS js_LeaveSharpObject(cx); #else /* Restore obj's property list before bailing on error. */ obj->map->props = list; #endif JS_UNLOCK(cx); if (!ok) { if (chars) free(chars); return ok; } } else #endif /* JS_HAS_OBJECT_LITERAL */ { clazz = obj->map->clasp->name; nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ chars = malloc((nchars + 1) * sizeof(jschar)); if (chars) { prefix = "[object "; nchars = 0; while ((chars[nchars] = (jschar)*prefix) != 0) nchars++, prefix++; while ((chars[nchars] = (jschar)*clazz) != 0) nchars++, clazz++; chars[nchars++] = ']'; chars[nchars] = 0; } } #if JS_HAS_SHARP_VARS make_string: #endif if (!chars) { JS_ReportOutOfMemory(cx); return JS_FALSE; } str = js_NewString(cx, chars, nchars, 0); if (!str) { free(chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; }