📄 jsexn.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** *//* * JS standard exception implementation. */#include "jsstddef.h"#include <stdlib.h>#include <string.h>#include "jstypes.h"#include "jsbit.h"#include "jsutil.h" /* Added by JSIFY */#include "jsprf.h"#include "jsapi.h"#include "jscntxt.h"#include "jsconfig.h"#include "jsdbgapi.h"#include "jsexn.h"#include "jsfun.h"#include "jsinterp.h"#include "jsnum.h"#include "jsopcode.h"#include "jsscript.h"/* Forward declarations for js_ErrorClass's initializer. */static JSBoolException(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);static voidexn_finalize(JSContext *cx, JSObject *obj);static uint32exn_mark(JSContext *cx, JSObject *obj, void *arg);static voidexn_finalize(JSContext *cx, JSObject *obj);static JSBoolexn_enumerate(JSContext *cx, JSObject *obj);static JSBoolexn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp);JSClass js_ErrorClass = { js_Error_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Error), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, exn_enumerate, (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize, NULL, NULL, NULL, Exception, NULL, NULL, exn_mark, NULL};typedef struct JSStackTraceElem { JSString *funName; size_t argc; const char *filename; uintN ulineno;} JSStackTraceElem;typedef struct JSExnPrivate { /* A copy of the JSErrorReport originally generated. */ JSErrorReport *errorReport; JSString *message; JSString *filename; uintN lineno; size_t stackDepth; JSStackTraceElem stackElems[1];} JSExnPrivate;static JSString *StackTraceToString(JSContext *cx, JSExnPrivate *priv);static JSErrorReport *CopyErrorReport(JSContext *cx, JSErrorReport *report){ /* * We use a single malloc block to make a deep copy of JSErrorReport with * the following layout: * JSErrorReport * array of copies of report->messageArgs * jschar array with characters for all messageArgs * jschar array with characters for ucmessage * jschar array with characters for uclinebuf and uctokenptr * char array with characters for linebuf and tokenptr * char array with characters for filename * Such layout together with the properties enforced by the following * asserts does not need any extra alignment padding. */// JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0);// JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0); size_t filenameSize; size_t linebufSize; size_t uclinebufSize; size_t ucmessageSize; size_t i, argsArraySize, argsCopySize, argSize; size_t mallocSize; JSErrorReport *copy; uint8 *cursor;#define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar)) filenameSize = report->filename ? strlen(report->filename) + 1 : 0; linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0; uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0; ucmessageSize = 0; argsArraySize = 0; argsCopySize = 0; if (report->ucmessage) { ucmessageSize = JS_CHARS_SIZE(report->ucmessage); if (report->messageArgs) { for (i = 0; report->messageArgs[i]; ++i) argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]); /* Non-null messageArgs should have at least one non-null arg. */ JS_ASSERT(i != 0); argsArraySize = (i + 1) * sizeof(const jschar *); } } /* * The mallocSize can not overflow since it represents the sum of the * sizes of already allocated objects. */ mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize + ucmessageSize + uclinebufSize + linebufSize + filenameSize; cursor = (uint8 *)JS_malloc(cx, mallocSize); if (!cursor) return NULL; copy = (JSErrorReport *)cursor; memset(cursor, 0, sizeof(JSErrorReport)); cursor += sizeof(JSErrorReport); if (argsArraySize != 0) { copy->messageArgs = (const jschar **)cursor; cursor += argsArraySize; for (i = 0; report->messageArgs[i]; ++i) { copy->messageArgs[i] = (const jschar *)cursor; argSize = JS_CHARS_SIZE(report->messageArgs[i]); memcpy(cursor, report->messageArgs[i], argSize); cursor += argSize; } copy->messageArgs[i] = NULL; JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize); } if (report->ucmessage) { copy->ucmessage = (const jschar *)cursor; memcpy(cursor, report->ucmessage, ucmessageSize); cursor += ucmessageSize; } if (report->uclinebuf) { copy->uclinebuf = (const jschar *)cursor; memcpy(cursor, report->uclinebuf, uclinebufSize); cursor += uclinebufSize; if (report->uctokenptr) { copy->uctokenptr = copy->uclinebuf + (report->uctokenptr - report->uclinebuf); } } if (report->linebuf) { copy->linebuf = (const char *)cursor; memcpy(cursor, report->linebuf, linebufSize); cursor += linebufSize; if (report->tokenptr) { copy->tokenptr = copy->linebuf + (report->tokenptr - report->linebuf); } } if (report->filename) { copy->filename = (const char *)cursor; memcpy(cursor, report->filename, filenameSize); } JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize); /* Copy non-pointer members. */ copy->lineno = report->lineno; copy->errorNumber = report->errorNumber; /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ copy->flags = report->flags;#undef JS_CHARS_SIZE return copy;}static jsval *GetStackTraceValueBuffer(JSExnPrivate *priv){ /* * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals * that helps to produce more informative stack traces. The following * assert allows us to assume that no gap after stackElems is necessary to * align the buffer properly. */// JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0); return (jsval *)(priv->stackElems + priv->stackDepth);}static JSBoolInitExnPrivate(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) { if (checkAccess) { v = fp->argv[-2]; if (!JSVAL_IS_PRIMITIVE(v) && !checkAccess(cx, JSVAL_TO_OBJECT(v), callerid, JSACC_READ, &v /* ignored */)) { 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_ReportOutOfMemory(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->pc) elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->pc); } ++elem; } JS_ASSERT(priv->stackElems + stackDepth == elem); JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); OBJ_SET_SLOT(cx, 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;}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;}static uint32exn_mark(JSContext *cx, JSObject *obj, void *arg){ JSExnPrivate *priv; JSStackTraceElem *elem; size_t vcount, i; jsval *vp, v; priv = GetExnPrivate(cx, obj); if (priv) { GC_MARK(cx, priv->message, "exception message"); GC_MARK(cx, priv->filename, "exception filename"); elem = priv->stackElems; for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) { if (elem->funName) GC_MARK(cx, elem->funName, "stack trace function name"); if (elem->filename) js_MarkScriptFilename(elem->filename); vcount += elem->argc; } vp = GetStackTraceValueBuffer(priv); for (i = 0; i != vcount; ++i, ++vp) { v = *vp; if (JSVAL_IS_GCTHING(v)) GC_MARK(cx, JSVAL_TO_GCTHING(v), "stack trace argument"); } } return 0;}static voidexn_finalize(JSContext *cx, JSObject *obj){ JSExnPrivate *priv; priv = GetExnPrivate(cx, obj); if (priv) { if (priv->errorReport) JS_free(cx, priv->errorReport); JS_free(cx, priv); }}static JSBoolexn_enumerate(JSContext *cx, JSObject *obj){ JSAtomState *atomState; uintN i; JSAtom *atom; JSObject *pobj; JSProperty *prop; // JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1); static const uint16 offsets[] = { (uint16)offsetof(JSAtomState, messageAtom), (uint16)offsetof(JSAtomState, fileNameAtom), (uint16)offsetof(JSAtomState, lineNumberAtom), (uint16)offsetof(JSAtomState, stackAtom), }; atomState = &cx->runtime->atomState; for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) { atom = *(JSAtom **)((uint8 *)atomState + offsets[i]); if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); } return JS_TRUE;}static JSBoolexn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -