📄 jsstr.c
字号:
}typedef struct ReplaceData { GlobData base; /* base struct state */ JSObject *lambda; /* replacement function object or null */ JSString *repstr; /* replacement string */ jschar *dollar; /* null or pointer to first $ in repstr */ jschar *dollarEnd; /* limit pointer for js_strchr_limit */ jschar *chars; /* result chars, null initially */ size_t length; /* result length, 0 initially */ jsint index; /* index in result of next replacement */ jsint leftIndex; /* left context index in base.str->chars */ JSSubString dollarStr; /* for "$$" interpret_dollar result */} ReplaceData;static JSSubString *interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, size_t *skip){ JSRegExpStatics *res; jschar dc, *cp; uintN num, tmp; JS_ASSERT(*dp == '$'); /* If there is only a dollar, bail now */ if (dp + 1 >= ep) return NULL; /* Interpret all Perl match-induced dollar variables. */ res = &cx->regExpStatics; dc = dp[1]; if (JS7_ISDEC(dc)) { /* ECMA-262 Edition 3: 1-9 or 01-99 */ num = JS7_UNDEC(dc); if (num > res->parenCount) return NULL; cp = dp + 2; if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { tmp = 10 * num + JS7_UNDEC(dc); if (tmp <= res->parenCount) { cp++; num = tmp; } } if (num == 0) return NULL; /* Adjust num from 1 $n-origin to 0 array-index-origin. */ num--; *skip = cp - dp; return REGEXP_PAREN_SUBSTRING(res, num); } *skip = 2; switch (dc) { case '$': rdata->dollarStr.chars = dp; rdata->dollarStr.length = 1; return &rdata->dollarStr; case '&': return &res->lastMatch; case '+': return &res->lastParen; case '`': return &res->leftContext; case '\'': return &res->rightContext; } return NULL;}static JSBoolfind_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep){ JSString *repstr; size_t replen, skip; jschar *dp, *ep; JSSubString *sub; JSObject *lambda; lambda = rdata->lambda; if (lambda) { uintN argc, i, j, m, n, p; jsval *sp, *oldsp, rval; void *mark; JSStackFrame *fp; JSBool ok; /* * Save the regExpStatics from the current regexp, since they may be * clobbered by a RegExp usage in the lambda function. Note that all * members of JSRegExpStatics are JSSubStrings, so not GC roots, save * input, which is rooted otherwise via argv[-1] in str_replace. */ JSRegExpStatics save = cx->regExpStatics; JSBool freeMoreParens = JS_FALSE; /* * In the lambda case, not only do we find the replacement string's * length, we compute repstr and return it via rdata for use within * do_replace. The lambda is called with arguments ($&, $1, $2, ..., * index, input), i.e., all the properties of a regexp match array. * For $&, etc., we must create string jsvals from cx->regExpStatics. * We grab up stack space to keep the newborn strings GC-rooted. */ p = rdata->base.regexp->parenCount; argc = 1 + p + 2; sp = js_AllocStack(cx, 2 + argc, &mark); if (!sp) return JS_FALSE; /* Push lambda and its 'this' parameter. */ *sp++ = OBJECT_TO_JSVAL(lambda); *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda));#define PUSH_REGEXP_STATIC(sub) \ JS_BEGIN_MACRO \ JSString *str = js_NewStringCopyN(cx, \ cx->regExpStatics.sub.chars, \ cx->regExpStatics.sub.length, \ 0); \ if (!str) { \ ok = JS_FALSE; \ goto lambda_out; \ } \ *sp++ = STRING_TO_JSVAL(str); \ JS_END_MACRO /* Push $&, $1, $2, ... */ PUSH_REGEXP_STATIC(lastMatch); i = 0; m = cx->regExpStatics.parenCount; n = JS_MIN(m, 9); for (j = 0; i < n; i++, j++) PUSH_REGEXP_STATIC(parens[j]); for (j = 0; i < m; i++, j++) PUSH_REGEXP_STATIC(moreParens[j]); /* * We need to clear moreParens in the top-of-stack cx->regExpStatics * to it won't be possibly realloc'ed, leaving the bottom-of-stack * moreParens pointing to freed memory. */ cx->regExpStatics.moreParens = NULL; freeMoreParens = JS_TRUE;#undef PUSH_REGEXP_STATIC /* Make sure to push undefined for any unmatched parens. */ for (; i < p; i++) *sp++ = JSVAL_VOID; /* Push match index and input string. */ *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length); *sp++ = STRING_TO_JSVAL(rdata->base.str); /* Lift current frame to include the args and do the call. */ fp = cx->fp; oldsp = fp->sp; fp->sp = sp; ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL); rval = fp->sp[-1]; fp->sp = oldsp; if (ok) { /* * NB: we count on the newborn string root to hold any string * created by this js_ValueToString that would otherwise be GC- * able, until we use rdata->repstr in do_replace. */ repstr = js_ValueToString(cx, rval); if (!repstr) { ok = JS_FALSE; } else { rdata->repstr = repstr; *sizep = JSSTRING_LENGTH(repstr); } } lambda_out: js_FreeStack(cx, mark); if (freeMoreParens) JS_free(cx, cx->regExpStatics.moreParens); cx->regExpStatics = save; return ok; } repstr = rdata->repstr; replen = JSSTRING_LENGTH(repstr); for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { sub = interpret_dollar(cx, dp, ep, rdata, &skip); if (sub) { replen += sub->length - skip; dp += skip; } else dp++; } *sizep = replen; return JS_TRUE;}static voiddo_replace(JSContext *cx, ReplaceData *rdata, jschar *chars){ JSString *repstr; jschar *bp, *cp, *dp, *ep; size_t len, skip; JSSubString *sub; repstr = rdata->repstr; bp = cp = JSSTRING_CHARS(repstr); for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { len = dp - cp; js_strncpy(chars, cp, len); chars += len; cp = dp; sub = interpret_dollar(cx, dp, ep, rdata, &skip); if (sub) { len = sub->length; js_strncpy(chars, sub->chars, len); chars += len; cp += skip; dp += skip; } else { dp++; } } js_strncpy(chars, cp, JSSTRING_LENGTH(repstr) - (cp - bp));}static JSBoolreplace_glob(JSContext *cx, jsint count, GlobData *data){ ReplaceData *rdata; JSString *str; size_t leftoff, leftlen, replen, growth; const jschar *left; jschar *chars; rdata = (ReplaceData *)data; str = data->str; leftoff = rdata->leftIndex; left = JSSTRING_CHARS(str) + leftoff; leftlen = cx->regExpStatics.lastMatch.chars - left; rdata->leftIndex = cx->regExpStatics.lastMatch.chars - JSSTRING_CHARS(str); rdata->leftIndex += cx->regExpStatics.lastMatch.length; if (!find_replen(cx, rdata, &replen)) return JS_FALSE; growth = leftlen + replen; chars = (jschar *) (rdata->chars ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1) * sizeof(jschar)) : JS_malloc(cx, (growth + 1) * sizeof(jschar))); if (!chars) { JS_free(cx, rdata->chars); rdata->chars = NULL; return JS_FALSE; } rdata->chars = chars; rdata->length += growth; chars += rdata->index; rdata->index += growth; js_strncpy(chars, left, leftlen); chars += leftlen; do_replace(cx, rdata, chars); return JS_TRUE;}static JSBoolstr_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ JSObject *lambda; JSString *repstr, *str; ReplaceData rdata; JSBool ok; jschar *chars; size_t leftlen, rightlen, length; if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) { lambda = JSVAL_TO_OBJECT(argv[1]); repstr = NULL; } else { if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1])) return JS_FALSE; repstr = JSVAL_TO_STRING(argv[1]); lambda = NULL; } /* * For ECMA Edition 3, the first argument is to be converted to a string * to match in a "flat" sense (without regular expression metachars having * special meanings) UNLESS the first arg is a RegExp object. */ rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT; rdata.base.optarg = 2; rdata.lambda = lambda; rdata.repstr = repstr; if (repstr) { rdata.dollarEnd = JSSTRING_CHARS(repstr) + JSSTRING_LENGTH(repstr); rdata.dollar = js_strchr_limit(JSSTRING_CHARS(repstr), '$', rdata.dollarEnd); } else { rdata.dollar = rdata.dollarEnd = NULL; } rdata.chars = NULL; rdata.length = 0; rdata.index = 0; rdata.leftIndex = 0; ok = match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval); if (!ok) return JS_FALSE; if (!rdata.chars) { if ((rdata.base.flags & GLOBAL_REGEXP) || *rval != JSVAL_TRUE) { /* Didn't match even once. */ *rval = STRING_TO_JSVAL(rdata.base.str); goto out; } leftlen = cx->regExpStatics.leftContext.length; ok = find_replen(cx, &rdata, &length); if (!ok) goto out; length += leftlen; chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) { ok = JS_FALSE; goto out; } js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen); do_replace(cx, &rdata, chars + leftlen); rdata.chars = chars; rdata.length = length; } rightlen = cx->regExpStatics.rightContext.length; length = rdata.length + rightlen; chars = (jschar *) JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar)); if (!chars) { JS_free(cx, rdata.chars); ok = JS_FALSE; goto out; } js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars, rightlen); chars[length] = 0; str = js_NewString(cx, chars, length, 0); if (!str) { JS_free(cx, chars); ok = JS_FALSE; goto out; } *rval = STRING_TO_JSVAL(str);out: /* If KEEP_REGEXP is still set, it's our job to destroy regexp now. */ if (rdata.base.flags & KEEP_REGEXP) js_DestroyRegExp(cx, rdata.base.regexp); return ok;}/* * Subroutine used by str_split to find the next split point in str, starting * at offset *ip and looking either for the separator substring given by sep, * or for the next re match. In the re case, return the matched separator in * *sep, and the possibly updated offset in *ip. * * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next * separator occurrence if found, or str->length if no separator is found. */static jsintfind_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, JSSubString *sep){ jsint i, j, k; size_t length; jschar *chars; /* * Stop if past end of string. If at end of string, we will compare the * null char stored there (by js_NewString*) to sep->chars[j] in the while * loop at the end of this function, so that * * "ab,".split(',') => ["ab", ""] * * and the resulting array converts back to the string "ab," for symmetry. * However, we ape Perl and do this only if there is a sufficiently large * limit argument (see str_split). */ i = *ip; length = JSSTRING_LENGTH(str); if ((size_t)i > length) return -1; chars = JSSTRING_CHARS(str); /* * Match a regular expression against the separator at or above index i. * Call js_ExecuteRegExp with true for the test argument. On successful * match, get the separator from cx->regExpStatics.lastMatch. */ if (re) { size_t index; jsval rval; again: /* JS1.2 deviated from Perl by never matching at end of string. */ index = (size_t)i; if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) return -2; if (rval != JSVAL_TRUE) { /* Mismatch: ensure our caller advances i past end of string. */ sep->length = 1; return length; } i = (jsint)index; *sep = cx->regExpStatics.lastMatch; if (sep->length == 0) { /* * Empty string match: never split on an empty match at the start * of a find_split cycle. Same rule as for an empty global match * in match_or_replace. */ if (i == *ip) { /* * "Bump-along" to avoid sticking at an empty match, but don't * bump past end of string -- our caller must do that by adding * sep->length to our return value. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -