📄 jsstr.c
字号:
break; num = tmp; } } else { /* ECMA 3, 1-9 or 01-99 */ num = JS7_UNDEC(dc); if (num > res->parenCount) return NULL; cp = dp + 2; dc = *cp; if ((dc != 0) && 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 '`': if (cx->version == JSVERSION_1_2) { /* * JS1.2 imitated the Perl4 bug where left context at each step * in an iterative use of a global regexp started from last match, * not from the start of the target string. But Perl4 does start * $` at the beginning of the target string when it is used in a * substitution, so we emulate that special case here. */ str = rdata->base.str; res->leftContext.chars = JSSTRING_CHARS(str); res->leftContext.length = res->lastMatch.chars - JSSTRING_CHARS(str); } 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;#if JS_HAS_REPLACE_LAMBDA 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 rightContext from the current regexp, since it * gets stuck at the end of the replacement string and may * be clobbered by a RegExp usage in the lambda function. */ JSSubString saveRightContext = cx->regExpStatics.rightContext; /* * 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]);#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); cx->regExpStatics.rightContext = saveRightContext; return ok; }#endif /* JS_HAS_REPLACE_LAMBDA */ 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, 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, 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_HAS_REPLACE_LAMBDA if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) { lambda = JSVAL_TO_OBJECT(argv[1]); repstr = NULL; } else#endif { 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; if (cx->version == JSVERSION_DEFAULT || cx->version > JSVERSION_1_4) rdata.base.flags |= 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;}#endif /* JS_HAS_REGEXPS *//* * 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; jschar *chars; size_t length; /* * 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; if ((size_t)i > JSSTRING_LENGTH(str)) return -1; /* * Perl4 special case for str.split(' '), only if the user has selected * JavaScript1.2 explicitly. Split on whitespace, and skip leading w/s. * Strange but true, apparently modeled after awk. * * NB: we set sep->length to the length of the w/s run, so we must test * sep->chars[1] == 0 to make sure sep is just one space. */ chars = JSSTRING_CHARS(str); length = JSSTRING_LENGTH(str); if (cx->version == JSVERSION_1_2 && !re && *sep->chars == ' ' && sep->chars[1] == 0) { /* Skip leading whitespace if at front of str. */ if (i == 0) { while (JS_ISSPACE(chars[i])) i++; *ip = i; } /* Don't delimit whitespace at end of string. */ if ((size_t)i == length) return -1; /* Skip over the non-whitespace chars. */ while ((size_t)i < length && !JS_ISSPACE(chars[i])) i++; /* Now skip the next run of whitespace. */ j = i; while ((size_t)j < length && JS_ISSPACE(chars[j])) j++; /* Update sep->length to count delimiter chars. */ sep->length = (size_t)(j - i); return i; }#if JS_HAS_REGEXPS /* * 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -