📄 jsarray.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** 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 array class. */#include "jsstddef.h"#include <stdlib.h>#include <string.h>#include "jstypes.h"#include "jsutil.h" /* Added by JSIFY */#include "jsapi.h"#include "jsarray.h"#include "jsatom.h"#include "jscntxt.h"#include "jsconfig.h"#include "jsfun.h"#include "jsgc.h"#include "jsinterp.h"#include "jslock.h"#include "jsnum.h"#include "jsobj.h"#include "jsstr.h"/* 2^32 - 1 as a number and a string */#define MAXINDEX 4294967295u#define MAXSTR "4294967295"/* * Determine if the id represents an array index. * * An id is an array index according to ECMA by (15.4): * * "Array objects give special treatment to a certain class of property names. * A property name P (in the form of a string value) is an array index if and * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal * to 2^32-1." * * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id) * except that by using signed 32-bit integers we miss the top half of the * valid range. This function checks the string representation itself; note * that calling a standard conversion routine might allow strings such as * "08" or "4.0" as array indices, which they are not. */static JSBoolIdIsIndex(jsid id, jsuint *indexp){ JSString *str; jschar *cp; if (JSVAL_IS_INT(id)) { jsint i; i = JSVAL_TO_INT(id); if (i < 0) return JS_FALSE; *indexp = (jsuint)i; return JS_TRUE; } /* It must be a string. */ str = JSVAL_TO_STRING(id); cp = JSSTRING_CHARS(str); if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) { jsuint index = JS7_UNDEC(*cp++); jsuint oldIndex = 0; jsuint c = 0; if (index != 0) { while (JS7_ISDEC(*cp)) { oldIndex = index; c = JS7_UNDEC(*cp); index = 10*index + c; cp++; } } /* Make sure all characters were consumed and that it couldn't * have overflowed. */ if (*cp == 0 && (oldIndex < (MAXINDEX / 10) || (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) { *indexp = index; return JS_TRUE; } } return JS_FALSE;}static JSBoolValueIsLength(JSContext *cx, jsval v, jsuint *lengthp){ jsint i; jsdouble d; if (JSVAL_IS_INT(v)) { i = JSVAL_TO_INT(v); if (i < 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return JS_FALSE; } *lengthp = (jsuint) i; return JS_TRUE; } if (!js_ValueToNumber(cx, v, &d)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return JS_FALSE; } if (!js_DoubleToECMAUint32(cx, d, (uint32 *)lengthp)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return JS_FALSE; } if (JSDOUBLE_IS_NaN(d) || (uint32) d != *lengthp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return JS_FALSE; } return JS_TRUE;}JSBooljs_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp){ jsid id; jsint i; jsval v; id = (jsid) cx->runtime->atomState.lengthAtom; if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) return JS_FALSE; /* Short-circuit, because js_ValueToECMAUint32 fails when * called during init time. */ if (JSVAL_IS_INT(v)) { i = JSVAL_TO_INT(v); /* jsuint cast does ToUint32. */ *lengthp = (jsuint)i; return JS_TRUE; } return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp);}static JSBoolIndexToValue(JSContext *cx, jsuint length, jsval *vp){ if (length <= JSVAL_INT_MAX) { *vp = INT_TO_JSVAL(length); return JS_TRUE; } return js_NewDoubleValue(cx, (jsdouble)length, vp);}static JSBoolIndexToId(JSContext *cx, jsuint length, jsid *idp){ JSString *str; JSAtom *atom; if (length <= JSVAL_INT_MAX) { *idp = (jsid) INT_TO_JSVAL(length); } else { str = js_NumberToString(cx, (jsdouble)length); if (!str) return JS_FALSE; atom = js_AtomizeString(cx, str, 0); if (!atom) return JS_FALSE; *idp = (jsid)atom; } return JS_TRUE;}JSBooljs_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length){ jsval v; jsid id; if (!IndexToValue(cx, length, &v)) return JS_FALSE; id = (jsid) cx->runtime->atomState.lengthAtom; return OBJ_SET_PROPERTY(cx, obj, id, &v);}JSBooljs_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp){ JSErrorReporter older; jsid id; JSBool ok; jsval v; older = JS_SetErrorReporter(cx, NULL); id = (jsid) cx->runtime->atomState.lengthAtom; ok = OBJ_GET_PROPERTY(cx, obj, id, &v); JS_SetErrorReporter(cx, older); if (!ok) return JS_FALSE; return ValueIsLength(cx, v, lengthp);}/* * 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 JSBoolarray_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp);}static JSBoolarray_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ jsuint newlen, oldlen, slot; jsid id2; jsval junk; if (!ValueIsLength(cx, *vp, &newlen)) return JS_FALSE; if (!js_GetLengthProperty(cx, obj, &oldlen)) return JS_FALSE; slot = oldlen; while (slot > newlen) { --slot; if (!IndexToId(cx, slot, &id2)) return JS_FALSE; if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) return JS_FALSE; } return IndexToValue(cx, newlen, vp);}static JSBoolarray_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ jsuint index, length; if (!(IdIsIndex(id, &index))) return JS_TRUE; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; if (index >= length) { length = index + 1; return js_SetLengthProperty(cx, obj, length); } return JS_TRUE;}static JSBoolarray_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp){ jsuint length; if (cx->version == JSVERSION_1_2) { if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; switch (type) { case JSTYPE_NUMBER: return IndexToValue(cx, length, vp); case JSTYPE_BOOLEAN: *vp = BOOLEAN_TO_JSVAL(length > 0); return JS_TRUE; default: return JS_TRUE; } } return js_TryValueOf(cx, obj, type, vp);}JSClass js_ArrayClass = { "Array", 0, array_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, array_convert, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS};static JSBoolarray_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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -