jsstr.c
来自「一个基于alice开发的机器人」· C语言 代码 · 共 2,095 行 · 第 1/5 页
C
2,095 行
/* -*- 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 string type implementation.
*
* In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these
* native methods store strings (possibly newborn) converted from their 'this'
* parameter and arguments on the stack: 'this' conversions at argv[-1], arg
* conversions at their index (argv[0], argv[1]). This is a legitimate method
* of rooting things that might lose their newborn root due to subsequent GC
* allocations in the same native method.
*/
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jshash.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jsbool.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsregexp.h"
#include "jsstr.h"
#if JS_HAS_REPLACE_LAMBDA
#include "jsinterp.h"
#endif
#define JSSTRDEP_RECURSION_LIMIT 100
size_t
js_MinimizeDependentStrings(JSString *str, int level, JSString **basep)
{
JSString *base;
size_t start, length;
JS_ASSERT(JSSTRING_IS_DEPENDENT(str));
base = JSSTRDEP_BASE(str);
start = JSSTRDEP_START(str);
if (JSSTRING_IS_DEPENDENT(base)) {
if (level < JSSTRDEP_RECURSION_LIMIT) {
start += js_MinimizeDependentStrings(base, level + 1, &base);
} else {
do {
start += JSSTRDEP_START(base);
base = JSSTRDEP_BASE(base);
} while (JSSTRING_IS_DEPENDENT(base));
}
if (start == 0) {
JS_ASSERT(JSSTRING_IS_PREFIX(str));
JSPREFIX_SET_BASE(str, base);
} else if (start <= JSSTRDEP_START_MASK) {
length = JSSTRDEP_LENGTH(str);
JSSTRDEP_SET_START_AND_LENGTH(str, start, length);
JSSTRDEP_SET_BASE(str, base);
}
}
*basep = base;
return start;
}
jschar *
js_GetDependentStringChars(JSString *str)
{
size_t start;
JSString *base;
start = js_MinimizeDependentStrings(str, 0, &base);
JS_ASSERT(!JSSTRING_IS_DEPENDENT(base));
JS_ASSERT(start < base->length);
return base->chars + start;
}
jschar *
js_GetStringChars(JSString *str)
{
if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(NULL, str))
return NULL;
*js_GetGCThingFlags(str) &= ~GCF_MUTABLE;
return str->chars;
}
JSString *
js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
{
size_t rn, ln, lrdist, n;
jschar *rs, *ls, *s;
JSDependentString *ldep; /* non-null if left should become dependent */
JSString *str;
if (JSSTRING_IS_DEPENDENT(right)) {
rn = JSSTRDEP_LENGTH(right);
rs = JSSTRDEP_CHARS(right);
} else {
rn = right->length;
rs = right->chars;
}
if (rn == 0)
return left;
if (JSSTRING_IS_DEPENDENT(left) ||
!(*js_GetGCThingFlags(left) & GCF_MUTABLE)) {
/* We must copy if left does not own a buffer to realloc. */
ln = JSSTRING_LENGTH(left);
if (ln == 0)
return right;
ls = JSSTRING_CHARS(left);
s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar));
if (!s)
return NULL;
js_strncpy(s, ls, ln);
ldep = NULL;
} else {
/* We can realloc left's space and make it depend on our result. */
ln = left->length;
if (ln == 0)
return right;
ls = left->chars;
s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar));
if (!s)
return NULL;
/* Take care: right could depend on left! */
lrdist = (size_t)(rs - ls);
if (lrdist < ln)
rs = s + lrdist;
left->chars = ls = s;
ldep = JSSTRDEP(left);
}
js_strncpy(s + ln, rs, rn);
n = ln + rn;
s[n] = 0;
str = js_NewString(cx, s, n, GCF_MUTABLE);
if (!str) {
/* Out of memory: clean up any space we (re-)allocated. */
if (!ldep) {
JS_free(cx, s);
} else {
s = JS_realloc(cx, ls, (ln + 1) * sizeof(jschar));
if (s)
left->chars = s;
}
} else {
/* Morph left into a dependent prefix if we realloc'd its buffer. */
if (ldep) {
JSPREFIX_SET_LENGTH(ldep, ln);
JSPREFIX_SET_BASE(ldep, str);
#ifdef DEBUG
{
JSRuntime *rt = cx->runtime;
JS_RUNTIME_METER(rt, liveDependentStrings);
JS_RUNTIME_METER(rt, totalDependentStrings);
JS_LOCK_RUNTIME_VOID(rt,
(rt->strdepLengthSum += (double)ln,
rt->strdepLengthSquaredSum += (double)ln * (double)ln));
}
#endif
}
}
return str;
}
/*
* May be called with null cx by js_GetStringChars, above; and by the jslock.c
* MAKE_STRING_IMMUTABLE file-local macro.
*/
const jschar *
js_UndependString(JSContext *cx, JSString *str)
{
size_t n, size;
jschar *s;
if (JSSTRING_IS_DEPENDENT(str)) {
n = JSSTRDEP_LENGTH(str);
size = (n + 1) * sizeof(jschar);
s = (jschar *) (cx ? JS_malloc(cx, size) : malloc(size));
if (!s)
return NULL;
js_strncpy(s, JSSTRDEP_CHARS(str), n);
s[n] = 0;
str->length = n;
str->chars = s;
#ifdef DEBUG
if (cx) {
JSRuntime *rt = cx->runtime;
JS_RUNTIME_UNMETER(rt, liveDependentStrings);
JS_RUNTIME_UNMETER(rt, totalDependentStrings);
JS_LOCK_RUNTIME_VOID(rt,
(rt->strdepLengthSum -= (double)n,
rt->strdepLengthSquaredSum -= (double)n * (double)n));
}
#endif
}
return str->chars;
}
/*
* Forward declarations for URI encode/decode and helper routines
*/
static JSBool
str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval);
static JSBool
str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval);
static JSBool
str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval);
static JSBool
str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval);
static int
OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char);
static uint32
Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);
/*
* Contributions from the String class to the set of methods defined for the
* global object. escape and unescape used to be defined in the Mocha library,
* but as ECMA decided to spec them, they've been moved to the core engine
* and made ECMA-compliant. (Incomplete escapes are interpreted as literal
* characters by unescape.)
*/
/*
* Stuff to emulate the old libmocha escape, which took a second argument
* giving the type of escape to perform. Retained for compatibility, and
* copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
*/
#define URL_XALPHAS ((uint8) 1)
#define URL_XPALPHAS ((uint8) 2)
#define URL_PATH ((uint8) 4)
static const uint8 urlCharType[256] =
/* Bit 0 xalpha -- the alphas
* Bit 1 xpalpha -- as xalpha but
* converts spaces to plus and plus to %20
* Bit 2 ... path -- as xalphas but doesn't escape '/'
*/
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
0, };
/* This matches the ECMA escape set when mask is 7 (default.) */
#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))
/* See ECMA-262 15.1.2.4. */
JSBool
js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSString *str;
size_t i, ni, length, newlength;
const jschar *chars;
jschar *newchars;
jschar ch;
jsint mask;
jsdouble d;
const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
if (argc > 1) {
if (!js_ValueToNumber(cx, argv[1], &d))
return JS_FALSE;
if (!JSDOUBLE_IS_FINITE(d) ||
(mask = (jsint)d) != d ||
mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
{
char numBuf[12];
JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_STRING_MASK, numBuf);
return JS_FALSE;
}
}
str = js_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
argv[0] = STRING_TO_JSVAL(str);
chars = JSSTRING_CHARS(str);
length = newlength = JSSTRING_LENGTH(str);
/* Take a first pass and see how big the result string will need to be. */
for (i = 0; i < length; i++) {
if ((ch = chars[i]) < 128 && IS_OK(ch, mask))
continue;
if (ch < 256) {
if (mask == URL_XPALPHAS && ch == ' ')
continue; /* The character will be encoded as '+' */
newlength += 2; /* The character will be encoded as %XX */
} else {
newlength += 5; /* The character will be encoded as %uXXXX */
}
}
newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar));
if (!newchars)
return JS_FALSE;
for (i = 0, ni = 0; i < length; i++) {
if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
newchars[ni++] = ch;
} else if (ch < 256) {
if (mask == URL_XPALPHAS && ch == ' ') {
newchars[ni++] = '+'; /* convert spaces to pluses */
} else {
newchars[ni++] = '%';
newchars[ni++] = digits[ch >> 4];
newchars[ni++] = digits[ch & 0xF];
}
} else {
newchars[ni++] = '%';
newchars[ni++] = 'u';
newchars[ni++] = digits[ch >> 12];
newchars[ni++] = digits[(ch & 0xF00) >> 8];
newchars[ni++] = digits[(ch & 0xF0) >> 4];
newchars[ni++] = digits[ch & 0xF];
}
}
JS_ASSERT(ni == newlength);
newchars[newlength] = 0;
str = js_NewString(cx, newchars, newlength, 0);
if (!str) {
JS_free(cx, newchars);
return JS_FALSE;
}
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
#undef IS_OK
/* See ECMA-262 15.1.2.5 */
static JSBool
str_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSString *str;
size_t i, ni, length;
const jschar *chars;
jschar *newchars;
jschar ch;
str = js_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
argv[0] = STRING_TO_JSVAL(str);
chars = JSSTRING_CHARS(str);
length = JSSTRING_LENGTH(str);
/* Don't bother allocating less space for the new string. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?