/js/src/jsstr.cpp
http://github.com/zpao/v8monkey · C++ · 4356 lines · 3507 code · 476 blank · 373 comment · 684 complexity · fbf53656a39e95829f89c223c92027e4 MD5 · raw file
Large files are truncated click here to view the full file
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=99:
- *
- * ***** 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 "mozilla/Attributes.h"
- #include <stdlib.h>
- #include <string.h>
- #include "jstypes.h"
- #include "jsutil.h"
- #include "jshash.h"
- #include "jsprf.h"
- #include "jsapi.h"
- #include "jsarray.h"
- #include "jsatom.h"
- #include "jsbool.h"
- #include "jscntxt.h"
- #include "jsgc.h"
- #include "jsinterp.h"
- #include "jslock.h"
- #include "jsnum.h"
- #include "jsobj.h"
- #include "jsopcode.h"
- #include "jsprobes.h"
- #include "jsscope.h"
- #include "jsstr.h"
- #include "jsversion.h"
- #include "builtin/RegExp.h"
- #include "vm/GlobalObject.h"
- #include "vm/RegExpObject.h"
- #include "jsinferinlines.h"
- #include "jsobjinlines.h"
- #include "jsautooplen.h" // generated headers last
- #include "vm/RegExpObject-inl.h"
- #include "vm/RegExpStatics-inl.h"
- #include "vm/StringObject-inl.h"
- #include "vm/String-inl.h"
- using namespace js;
- using namespace js::gc;
- using namespace js::types;
- using namespace js::unicode;
- static JSLinearString *
- ArgToRootedString(JSContext *cx, CallArgs &args, uintN argno)
- {
- if (argno >= args.length())
- return cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
- Value &arg = args[argno];
- JSString *str = ToString(cx, arg);
- if (!str)
- return NULL;
- arg = StringValue(str);
- return str->ensureLinear(cx);
- }
- /*
- * Forward declarations for URI encode/decode and helper routines
- */
- static JSBool
- str_decodeURI(JSContext *cx, uintN argc, Value *vp);
- static JSBool
- str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp);
- static JSBool
- str_encodeURI(JSContext *cx, uintN argc, Value *vp);
- static JSBool
- str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp);
- static const uint32_t INVALID_UTF8 = UINT32_MAX;
- static uint32_t
- Utf8ToOneUcs4Char(const uint8_t *utf8Buffer, int utf8Length);
- /*
- * Global string methods
- */
- /* ES5 B.2.1 */
- static JSBool
- str_escape(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
- JSLinearString *str = ArgToRootedString(cx, args, 0);
- if (!str)
- return false;
- size_t length = str->length();
- const jschar *chars = str->chars();
- static const uint8_t shouldPassThrough[256] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1, /* !"#$%&'()*+,-./ */
- 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* 0123456789:;<=>? */
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* @ABCDEFGHIJKLMNO */
- 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* PQRSTUVWXYZ[\]^_ */
- 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* `abcdefghijklmno */
- 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* pqrstuvwxyz{\}~ DEL */
- };
- /* In step 7, exactly 69 characters should pass through unencoded. */
- #ifdef DEBUG
- size_t count = 0;
- for (size_t i = 0; i < sizeof(shouldPassThrough); i++) {
- if (shouldPassThrough[i]) {
- count++;
- }
- }
- JS_ASSERT(count == 69);
- #endif
- /* Take a first pass and see how big the result string will need to be. */
- size_t newlength = length;
- for (size_t i = 0; i < length; i++) {
- jschar ch = chars[i];
- if (ch < 128 && shouldPassThrough[ch])
- continue;
- /* The character will be encoded as %XX or %uXXXX. */
- newlength += (ch < 256) ? 2 : 5;
- /*
- * This overflow test works because newlength is incremented by at
- * most 5 on each iteration.
- */
- if (newlength < length) {
- js_ReportAllocationOverflow(cx);
- return false;
- }
- }
- if (newlength >= ~(size_t)0 / sizeof(jschar)) {
- js_ReportAllocationOverflow(cx);
- return false;
- }
- jschar *newchars = (jschar *) cx->malloc_((newlength + 1) * sizeof(jschar));
- if (!newchars)
- return false;
- size_t i, ni;
- for (i = 0, ni = 0; i < length; i++) {
- jschar ch = chars[i];
- if (ch < 128 && shouldPassThrough[ch]) {
- newchars[ni++] = ch;
- } else if (ch < 256) {
- 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;
- JSString *retstr = js_NewString(cx, newchars, newlength);
- if (!retstr) {
- cx->free_(newchars);
- return false;
- }
- args.rval() = StringValue(retstr);
- return true;
- }
- static inline bool
- Unhex4(const jschar *chars, jschar *result)
- {
- jschar a = chars[0],
- b = chars[1],
- c = chars[2],
- d = chars[3];
- if (!(JS7_ISHEX(a) && JS7_ISHEX(b) && JS7_ISHEX(c) && JS7_ISHEX(d)))
- return false;
- *result = (((((JS7_UNHEX(a) << 4) + JS7_UNHEX(b)) << 4) + JS7_UNHEX(c)) << 4) + JS7_UNHEX(d);
- return true;
- }
- static inline bool
- Unhex2(const jschar *chars, jschar *result)
- {
- jschar a = chars[0],
- b = chars[1];
- if (!(JS7_ISHEX(a) && JS7_ISHEX(b)))
- return false;
- *result = (JS7_UNHEX(a) << 4) + JS7_UNHEX(b);
- return true;
- }
- /* ES5 B.2.2 */
- static JSBool
- str_unescape(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- /* Step 1. */
- JSLinearString *str = ArgToRootedString(cx, args, 0);
- if (!str)
- return false;
- /* Step 2. */
- size_t length = str->length();
- const jschar *chars = str->chars();
- /* Step 3. */
- StringBuffer sb(cx);
- /*
- * Note that the spec algorithm has been optimized to avoid building
- * a string in the case where no escapes are present.
- */
- /* Step 4. */
- size_t k = 0;
- bool building = false;
- while (true) {
- /* Step 5. */
- if (k == length) {
- JSLinearString *result;
- if (building) {
- result = sb.finishString();
- if (!result)
- return false;
- } else {
- result = str;
- }
- args.rval() = StringValue(result);
- return true;
- }
- /* Step 6. */
- jschar c = chars[k];
- /* Step 7. */
- if (c != '%')
- goto step_18;
- /* Step 8. */
- if (k > length - 6)
- goto step_14;
- /* Step 9. */
- if (chars[k + 1] != 'u')
- goto step_14;
- #define ENSURE_BUILDING \
- JS_BEGIN_MACRO \
- if (!building) { \
- building = true; \
- if (!sb.reserve(length)) \
- return false; \
- sb.infallibleAppend(chars, chars + k); \
- } \
- JS_END_MACRO
- /* Step 10-13. */
- if (Unhex4(&chars[k + 2], &c)) {
- ENSURE_BUILDING;
- k += 5;
- goto step_18;
- }
- step_14:
- /* Step 14. */
- if (k > length - 3)
- goto step_18;
- /* Step 15-17. */
- if (Unhex2(&chars[k + 1], &c)) {
- ENSURE_BUILDING;
- k += 2;
- }
- step_18:
- if (building)
- sb.infallibleAppend(c);
- /* Step 19. */
- k += 1;
- }
- #undef ENSURE_BUILDING
- }
- #if JS_HAS_UNEVAL
- static JSBool
- str_uneval(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str = js_ValueToSource(cx, args.length() != 0 ? args[0] : UndefinedValue());
- if (!str)
- return false;
- args.rval() = StringValue(str);
- return true;
- }
- #endif
- const char js_escape_str[] = "escape";
- const char js_unescape_str[] = "unescape";
- #if JS_HAS_UNEVAL
- const char js_uneval_str[] = "uneval";
- #endif
- const char js_decodeURI_str[] = "decodeURI";
- const char js_encodeURI_str[] = "encodeURI";
- const char js_decodeURIComponent_str[] = "decodeURIComponent";
- const char js_encodeURIComponent_str[] = "encodeURIComponent";
- static JSFunctionSpec string_functions[] = {
- JS_FN(js_escape_str, str_escape, 1,0),
- JS_FN(js_unescape_str, str_unescape, 1,0),
- #if JS_HAS_UNEVAL
- JS_FN(js_uneval_str, str_uneval, 1,0),
- #endif
- JS_FN(js_decodeURI_str, str_decodeURI, 1,0),
- JS_FN(js_encodeURI_str, str_encodeURI, 1,0),
- JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1,0),
- JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1,0),
- JS_FS_END
- };
- jschar js_empty_ucstr[] = {0};
- JSSubString js_EmptySubString = {0, js_empty_ucstr};
- static const uintN STRING_ELEMENT_ATTRS = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
- static JSBool
- str_enumerate(JSContext *cx, JSObject *obj)
- {
- JSString *str = obj->getPrimitiveThis().toString();
- for (size_t i = 0, length = str->length(); i < length; i++) {
- JSString *str1 = js_NewDependentString(cx, str, i, 1);
- if (!str1)
- return false;
- if (!obj->defineElement(cx, i, StringValue(str1),
- JS_PropertyStub, JS_StrictPropertyStub,
- STRING_ELEMENT_ATTRS)) {
- return false;
- }
- }
- return true;
- }
- static JSBool
- str_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
- JSObject **objp)
- {
- if (!JSID_IS_INT(id))
- return JS_TRUE;
- JSString *str = obj->getPrimitiveThis().toString();
- jsint slot = JSID_TO_INT(id);
- if ((size_t)slot < str->length()) {
- JSString *str1 = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(slot));
- if (!str1)
- return JS_FALSE;
- if (!obj->defineElement(cx, uint32_t(slot), StringValue(str1), NULL, NULL,
- STRING_ELEMENT_ATTRS)) {
- return JS_FALSE;
- }
- *objp = obj;
- }
- return JS_TRUE;
- }
- Class js::StringClass = {
- js_String_str,
- JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
- JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_String),
- JS_PropertyStub, /* addProperty */
- JS_PropertyStub, /* delProperty */
- JS_PropertyStub, /* getProperty */
- JS_StrictPropertyStub, /* setProperty */
- str_enumerate,
- (JSResolveOp)str_resolve,
- JS_ConvertStub
- };
- /*
- * Returns a JSString * for the |this| value associated with 'call', or throws
- * a TypeError if |this| is null or undefined. This algorithm is the same as
- * calling CheckObjectCoercible(this), then returning ToString(this), as all
- * String.prototype.* methods do (other than toString and valueOf).
- */
- static JS_ALWAYS_INLINE JSString *
- ThisToStringForStringProto(JSContext *cx, CallReceiver call)
- {
- JS_CHECK_RECURSION(cx, return NULL);
- if (call.thisv().isString())
- return call.thisv().toString();
- if (call.thisv().isObject()) {
- JSObject *obj = &call.thisv().toObject();
- if (obj->isString() &&
- ClassMethodIsNative(cx, obj,
- &StringClass,
- ATOM_TO_JSID(cx->runtime->atomState.toStringAtom),
- js_str_toString))
- {
- call.thisv() = obj->getPrimitiveThis();
- return call.thisv().toString();
- }
- } else if (call.thisv().isNullOrUndefined()) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
- call.thisv().isNull() ? "null" : "undefined", "object");
- return NULL;
- }
- JSString *str = ToStringSlow(cx, call.thisv());
- if (!str)
- return NULL;
- call.thisv().setString(str);
- return str;
- }
- #if JS_HAS_TOSOURCE
- /*
- * String.prototype.quote is generic (as are most string methods), unlike
- * toSource, toString, and valueOf.
- */
- static JSBool
- str_quote(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- str = js_QuoteString(cx, str, '"');
- if (!str)
- return false;
- args.rval() = StringValue(str);
- return true;
- }
- static JSBool
- str_toSource(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str;
- bool ok;
- if (!BoxedPrimitiveMethodGuard(cx, args, str_toSource, &str, &ok))
- return ok;
- str = js_QuoteString(cx, str, '"');
- if (!str)
- return false;
- StringBuffer sb(cx);
- if (!sb.append("(new String(") || !sb.append(str) || !sb.append("))"))
- return false;
- str = sb.finishString();
- if (!str)
- return false;
- args.rval() = StringValue(str);
- return true;
- }
- #endif /* JS_HAS_TOSOURCE */
- JSBool
- js_str_toString(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str;
- bool ok;
- if (!BoxedPrimitiveMethodGuard(cx, args, js_str_toString, &str, &ok))
- return ok;
- args.rval() = StringValue(str);
- return true;
- }
- /*
- * Java-like string native methods.
- */
- JS_ALWAYS_INLINE bool
- ValueToIntegerRange(JSContext *cx, const Value &v, int32_t *out)
- {
- if (v.isInt32()) {
- *out = v.toInt32();
- } else {
- double d;
- if (!ToInteger(cx, v, &d))
- return false;
- if (d > INT32_MAX)
- *out = INT32_MAX;
- else if (d < INT32_MIN)
- *out = INT32_MIN;
- else
- *out = int32_t(d);
- }
- return true;
- }
- static JSBool
- str_substring(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- int32_t length, begin, end;
- if (args.length() > 0) {
- end = length = int32_t(str->length());
- if (!ValueToIntegerRange(cx, args[0], &begin))
- return false;
- if (begin < 0)
- begin = 0;
- else if (begin > length)
- begin = length;
- if (args.length() > 1 && !args[1].isUndefined()) {
- if (!ValueToIntegerRange(cx, args[1], &end))
- return false;
- if (end > length) {
- end = length;
- } else {
- if (end < 0)
- end = 0;
- if (end < begin) {
- int32_t tmp = begin;
- begin = end;
- end = tmp;
- }
- }
- }
- str = js_NewDependentString(cx, str, size_t(begin), size_t(end - begin));
- if (!str)
- return false;
- }
- args.rval() = StringValue(str);
- return true;
- }
- JSString* JS_FASTCALL
- js_toLowerCase(JSContext *cx, JSString *str)
- {
- size_t n = str->length();
- const jschar *s = str->getChars(cx);
- if (!s)
- return NULL;
- jschar *news = (jschar *) cx->malloc_((n + 1) * sizeof(jschar));
- if (!news)
- return NULL;
- for (size_t i = 0; i < n; i++)
- news[i] = unicode::ToLowerCase(s[i]);
- news[n] = 0;
- str = js_NewString(cx, news, n);
- if (!str) {
- cx->free_(news);
- return NULL;
- }
- return str;
- }
- static inline bool
- ToLowerCaseHelper(JSContext *cx, CallReceiver call)
- {
- JSString *str = ThisToStringForStringProto(cx, call);
- if (!str)
- return false;
- str = js_toLowerCase(cx, str);
- if (!str)
- return false;
- call.rval() = StringValue(str);
- return true;
- }
- static JSBool
- str_toLowerCase(JSContext *cx, uintN argc, Value *vp)
- {
- return ToLowerCaseHelper(cx, CallArgsFromVp(argc, vp));
- }
- static JSBool
- str_toLocaleLowerCase(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- /*
- * Forcefully ignore the first (or any) argument and return toLowerCase(),
- * ECMA has reserved that argument, presumably for defining the locale.
- */
- if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {
- JSString *str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- Value result;
- if (!cx->localeCallbacks->localeToLowerCase(cx, str, &result))
- return false;
- args.rval() = result;
- return true;
- }
- return ToLowerCaseHelper(cx, args);
- }
- JSString* JS_FASTCALL
- js_toUpperCase(JSContext *cx, JSString *str)
- {
- size_t n = str->length();
- const jschar *s = str->getChars(cx);
- if (!s)
- return NULL;
- jschar *news = (jschar *) cx->malloc_((n + 1) * sizeof(jschar));
- if (!news)
- return NULL;
- for (size_t i = 0; i < n; i++)
- news[i] = unicode::ToUpperCase(s[i]);
- news[n] = 0;
- str = js_NewString(cx, news, n);
- if (!str) {
- cx->free_(news);
- return NULL;
- }
- return str;
- }
- static JSBool
- ToUpperCaseHelper(JSContext *cx, CallReceiver call)
- {
- JSString *str = ThisToStringForStringProto(cx, call);
- if (!str)
- return false;
- str = js_toUpperCase(cx, str);
- if (!str)
- return false;
- call.rval() = StringValue(str);
- return true;
- }
- static JSBool
- str_toUpperCase(JSContext *cx, uintN argc, Value *vp)
- {
- return ToUpperCaseHelper(cx, CallArgsFromVp(argc, vp));
- }
- static JSBool
- str_toLocaleUpperCase(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- /*
- * Forcefully ignore the first (or any) argument and return toUpperCase(),
- * ECMA has reserved that argument, presumably for defining the locale.
- */
- if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {
- JSString *str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- Value result;
- if (!cx->localeCallbacks->localeToUpperCase(cx, str, &result))
- return false;
- args.rval() = result;
- return true;
- }
- return ToUpperCaseHelper(cx, args);
- }
- static JSBool
- str_localeCompare(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- if (args.length() == 0) {
- args.rval() = Int32Value(0);
- } else {
- JSString *thatStr = ToString(cx, args[0]);
- if (!thatStr)
- return false;
- if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
- args[0].setString(thatStr);
- Value result;
- if (!cx->localeCallbacks->localeCompare(cx, str, thatStr, &result))
- return true;
- args.rval() = result;
- return true;
- }
- int32_t result;
- if (!CompareStrings(cx, str, thatStr, &result))
- return false;
- args.rval() = Int32Value(result);
- }
- return true;
- }
- JSBool
- js_str_charAt(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str;
- size_t i;
- if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) {
- str = args.thisv().toString();
- i = size_t(args[0].toInt32());
- if (i >= str->length())
- goto out_of_range;
- } else {
- str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- double d = 0.0;
- if (args.length() > 0 && !ToInteger(cx, args[0], &d))
- return false;
- if (d < 0 || str->length() <= d)
- goto out_of_range;
- i = size_t(d);
- }
- str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, i);
- if (!str)
- return false;
- args.rval() = StringValue(str);
- return true;
- out_of_range:
- args.rval() = StringValue(cx->runtime->emptyString);
- return true;
- }
- JSBool
- js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str;
- size_t i;
- if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) {
- str = args.thisv().toString();
- i = size_t(args[0].toInt32());
- if (i >= str->length())
- goto out_of_range;
- } else {
- str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- double d = 0.0;
- if (args.length() > 0 && !ToInteger(cx, args[0], &d))
- return false;
- if (d < 0 || str->length() <= d)
- goto out_of_range;
- i = size_t(d);
- }
- const jschar *chars;
- chars = str->getChars(cx);
- if (!chars)
- return false;
- args.rval() = Int32Value(chars[i]);
- return true;
- out_of_range:
- args.rval() = DoubleValue(js_NaN);
- return true;
- }
- /*
- * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen.
- * The patlen argument must be positive and no greater than sBMHPatLenMax.
- *
- * Return the index of pat in text, or -1 if not found.
- */
- static const jsuint sBMHCharSetSize = 256; /* ISO-Latin-1 */
- static const jsuint sBMHPatLenMax = 255; /* skip table element is uint8_t */
- static const jsint sBMHBadPattern = -2; /* return value if pat is not ISO-Latin-1 */
- jsint
- js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
- const jschar *pat, jsuint patlen)
- {
- uint8_t skip[sBMHCharSetSize];
- JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
- for (jsuint i = 0; i < sBMHCharSetSize; i++)
- skip[i] = (uint8_t)patlen;
- jsuint m = patlen - 1;
- for (jsuint i = 0; i < m; i++) {
- jschar c = pat[i];
- if (c >= sBMHCharSetSize)
- return sBMHBadPattern;
- skip[c] = (uint8_t)(m - i);
- }
- jschar c;
- for (jsuint k = m;
- k < textlen;
- k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
- for (jsuint i = k, j = m; ; i--, j--) {
- if (text[i] != pat[j])
- break;
- if (j == 0)
- return static_cast<jsint>(i); /* safe: max string size */
- }
- }
- return -1;
- }
- struct MemCmp {
- typedef jsuint Extent;
- static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, jsuint patlen) {
- return (patlen - 1) * sizeof(jschar);
- }
- static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
- return memcmp(p, t, extent) == 0;
- }
- };
- struct ManualCmp {
- typedef const jschar *Extent;
- static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, jsuint patlen) {
- return pat + patlen;
- }
- static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) {
- for (; p != extent; ++p, ++t) {
- if (*p != *t)
- return false;
- }
- return true;
- }
- };
- template <class InnerMatch>
- static jsint
- UnrolledMatch(const jschar *text, jsuint textlen, const jschar *pat, jsuint patlen)
- {
- JS_ASSERT(patlen > 0 && textlen > 0);
- const jschar *textend = text + textlen - (patlen - 1);
- const jschar p0 = *pat;
- const jschar *const patNext = pat + 1;
- const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
- uint8_t fixup;
- const jschar *t = text;
- switch ((textend - t) & 7) {
- case 0: if (*t++ == p0) { fixup = 8; goto match; }
- case 7: if (*t++ == p0) { fixup = 7; goto match; }
- case 6: if (*t++ == p0) { fixup = 6; goto match; }
- case 5: if (*t++ == p0) { fixup = 5; goto match; }
- case 4: if (*t++ == p0) { fixup = 4; goto match; }
- case 3: if (*t++ == p0) { fixup = 3; goto match; }
- case 2: if (*t++ == p0) { fixup = 2; goto match; }
- case 1: if (*t++ == p0) { fixup = 1; goto match; }
- }
- while (t != textend) {
- if (t[0] == p0) { t += 1; fixup = 8; goto match; }
- if (t[1] == p0) { t += 2; fixup = 7; goto match; }
- if (t[2] == p0) { t += 3; fixup = 6; goto match; }
- if (t[3] == p0) { t += 4; fixup = 5; goto match; }
- if (t[4] == p0) { t += 5; fixup = 4; goto match; }
- if (t[5] == p0) { t += 6; fixup = 3; goto match; }
- if (t[6] == p0) { t += 7; fixup = 2; goto match; }
- if (t[7] == p0) { t += 8; fixup = 1; goto match; }
- t += 8;
- continue;
- do {
- if (*t++ == p0) {
- match:
- if (!InnerMatch::match(patNext, t, extent))
- goto failed_match;
- return t - text - 1;
- }
- failed_match:;
- } while (--fixup > 0);
- }
- return -1;
- }
- static JS_ALWAYS_INLINE jsint
- StringMatch(const jschar *text, jsuint textlen,
- const jschar *pat, jsuint patlen)
- {
- if (patlen == 0)
- return 0;
- if (textlen < patlen)
- return -1;
- #if defined(__i386__) || defined(_M_IX86) || defined(__i386)
- /*
- * Given enough registers, the unrolled loop below is faster than the
- * following loop. 32-bit x86 does not have enough registers.
- */
- if (patlen == 1) {
- const jschar p0 = *pat;
- for (const jschar *c = text, *end = text + textlen; c != end; ++c) {
- if (*c == p0)
- return c - text;
- }
- return -1;
- }
- #endif
- /*
- * If the text or pattern string is short, BMH will be more expensive than
- * the basic linear scan due to initialization cost and a more complex loop
- * body. While the correct threshold is input-dependent, we can make a few
- * conservative observations:
- * - When |textlen| is "big enough", the initialization time will be
- * proportionally small, so the worst-case slowdown is minimized.
- * - When |patlen| is "too small", even the best case for BMH will be
- * slower than a simple scan for large |textlen| due to the more complex
- * loop body of BMH.
- * From this, the values for "big enough" and "too small" are determined
- * empirically. See bug 526348.
- */
- if (textlen >= 512 && patlen >= 11 && patlen <= sBMHPatLenMax) {
- jsint index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
- if (index != sBMHBadPattern)
- return index;
- }
- /*
- * For big patterns with large potential overlap we want the SIMD-optimized
- * speed of memcmp. For small patterns, a simple loop is faster.
- *
- * FIXME: Linux memcmp performance is sad and the manual loop is faster.
- */
- return
- #if !defined(__linux__)
- patlen > 128 ? UnrolledMatch<MemCmp>(text, textlen, pat, patlen)
- :
- #endif
- UnrolledMatch<ManualCmp>(text, textlen, pat, patlen);
- }
- static const size_t sRopeMatchThresholdRatioLog2 = 5;
- /*
- * RopeMatch takes the text to search, the patern to search for in the text.
- * RopeMatch returns false on OOM and otherwise returns the match index through
- * the 'match' outparam (-1 for not found).
- */
- static bool
- RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, jsint *match)
- {
- JS_ASSERT(textstr->isRope());
- if (patlen == 0) {
- *match = 0;
- return true;
- }
- if (textstr->length() < patlen) {
- *match = -1;
- return true;
- }
- /*
- * List of leaf nodes in the rope. If we run out of memory when trying to
- * append to this list, we can still fall back to StringMatch, so use the
- * system allocator so we don't report OOM in that case.
- */
- Vector<JSLinearString *, 16, SystemAllocPolicy> strs;
- /*
- * We don't want to do rope matching if there is a poor node-to-char ratio,
- * since this means spending a lot of time in the match loop below. We also
- * need to build the list of leaf nodes. Do both here: iterate over the
- * nodes so long as there are not too many.
- */
- {
- size_t textstrlen = textstr->length();
- size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
- StringSegmentRange r(cx);
- if (!r.init(textstr))
- return false;
- while (!r.empty()) {
- if (threshold-- == 0 || !strs.append(r.front())) {
- const jschar *chars = textstr->getChars(cx);
- if (!chars)
- return false;
- *match = StringMatch(chars, textstrlen, pat, patlen);
- return true;
- }
- if (!r.popFront())
- return false;
- }
- }
- /* Absolute offset from the beginning of the logical string textstr. */
- jsint pos = 0;
- for (JSLinearString **outerp = strs.begin(); outerp != strs.end(); ++outerp) {
- /* Try to find a match within 'outer'. */
- JSLinearString *outer = *outerp;
- const jschar *chars = outer->chars();
- size_t len = outer->length();
- jsint matchResult = StringMatch(chars, len, pat, patlen);
- if (matchResult != -1) {
- /* Matched! */
- *match = pos + matchResult;
- return true;
- }
- /* Try to find a match starting in 'outer' and running into other nodes. */
- const jschar *const text = chars + (patlen > len ? 0 : len - patlen + 1);
- const jschar *const textend = chars + len;
- const jschar p0 = *pat;
- const jschar *const p1 = pat + 1;
- const jschar *const patend = pat + patlen;
- for (const jschar *t = text; t != textend; ) {
- if (*t++ != p0)
- continue;
- JSLinearString **innerp = outerp;
- const jschar *ttend = textend;
- for (const jschar *pp = p1, *tt = t; pp != patend; ++pp, ++tt) {
- while (tt == ttend) {
- if (++innerp == strs.end()) {
- *match = -1;
- return true;
- }
- JSLinearString *inner = *innerp;
- tt = inner->chars();
- ttend = tt + inner->length();
- }
- if (*pp != *tt)
- goto break_continue;
- }
- /* Matched! */
- *match = pos + (t - chars) - 1; /* -1 because of *t++ above */
- return true;
- break_continue:;
- }
- pos += len;
- }
- *match = -1;
- return true;
- }
- static JSBool
- str_indexOf(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- JSLinearString *patstr = ArgToRootedString(cx, args, 0);
- if (!patstr)
- return false;
- jsuint textlen = str->length();
- const jschar *text = str->getChars(cx);
- if (!text)
- return false;
- jsuint patlen = patstr->length();
- const jschar *pat = patstr->chars();
- jsuint start;
- if (args.length() > 1) {
- if (args[1].isInt32()) {
- jsint i = args[1].toInt32();
- if (i <= 0) {
- start = 0;
- } else if (jsuint(i) > textlen) {
- start = textlen;
- textlen = 0;
- } else {
- start = i;
- text += start;
- textlen -= start;
- }
- } else {
- jsdouble d;
- if (!ToInteger(cx, args[1], &d))
- return false;
- if (d <= 0) {
- start = 0;
- } else if (d > textlen) {
- start = textlen;
- textlen = 0;
- } else {
- start = (jsint)d;
- text += start;
- textlen -= start;
- }
- }
- } else {
- start = 0;
- }
- jsint match = StringMatch(text, textlen, pat, patlen);
- args.rval() = Int32Value((match == -1) ? -1 : start + match);
- return true;
- }
- static JSBool
- str_lastIndexOf(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *textstr = ThisToStringForStringProto(cx, args);
- if (!textstr)
- return false;
- size_t textlen = textstr->length();
- const jschar *text = textstr->getChars(cx);
- if (!text)
- return false;
- JSLinearString *patstr = ArgToRootedString(cx, args, 0);
- if (!patstr)
- return false;
- size_t patlen = patstr->length();
- const jschar *pat = patstr->chars();
- jsint i = textlen - patlen; // Start searching here
- if (i < 0) {
- args.rval() = Int32Value(-1);
- return true;
- }
- if (args.length() > 1) {
- if (args[1].isInt32()) {
- jsint j = args[1].toInt32();
- if (j <= 0)
- i = 0;
- else if (j < i)
- i = j;
- } else {
- double d;
- if (!ToNumber(cx, args[1], &d))
- return false;
- if (!JSDOUBLE_IS_NaN(d)) {
- d = js_DoubleToInteger(d);
- if (d <= 0)
- i = 0;
- else if (d < i)
- i = (jsint)d;
- }
- }
- }
- if (patlen == 0) {
- args.rval() = Int32Value(i);
- return true;
- }
- const jschar *t = text + i;
- const jschar *textend = text - 1;
- const jschar p0 = *pat;
- const jschar *patNext = pat + 1;
- const jschar *patEnd = pat + patlen;
- for (; t != textend; --t) {
- if (*t == p0) {
- const jschar *t1 = t + 1;
- for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) {
- if (*t1 != *p1)
- goto break_continue;
- }
- args.rval() = Int32Value(t - text);
- return true;
- }
- break_continue:;
- }
- args.rval() = Int32Value(-1);
- return true;
- }
- static JSBool
- js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight)
- {
- CallReceiver call = CallReceiverFromVp(vp);
- JSString *str = ThisToStringForStringProto(cx, call);
- if (!str)
- return false;
- size_t length = str->length();
- const jschar *chars = str->getChars(cx);
- if (!chars)
- return false;
- size_t begin = 0;
- size_t end = length;
- if (trimLeft) {
- while (begin < length && unicode::IsSpace(chars[begin]))
- ++begin;
- }
- if (trimRight) {
- while (end > begin && unicode::IsSpace(chars[end - 1]))
- --end;
- }
- str = js_NewDependentString(cx, str, begin, end - begin);
- if (!str)
- return false;
- call.rval() = StringValue(str);
- return true;
- }
- static JSBool
- str_trim(JSContext *cx, uintN argc, Value *vp)
- {
- return js_TrimString(cx, vp, JS_TRUE, JS_TRUE);
- }
- static JSBool
- str_trimLeft(JSContext *cx, uintN argc, Value *vp)
- {
- return js_TrimString(cx, vp, JS_TRUE, JS_FALSE);
- }
- static JSBool
- str_trimRight(JSContext *cx, uintN argc, Value *vp)
- {
- return js_TrimString(cx, vp, JS_FALSE, JS_TRUE);
- }
- /*
- * Perl-inspired string functions.
- */
- /* Result of a successfully performed flat match. */
- class FlatMatch
- {
- JSAtom *patstr;
- const jschar *pat;
- size_t patlen;
- int32_t match_;
- friend class RegExpGuard;
- public:
- FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
- JSLinearString *pattern() const { return patstr; }
- size_t patternLength() const { return patlen; }
- /*
- * Note: The match is -1 when the match is performed successfully,
- * but no match is found.
- */
- int32_t match() const { return match_; }
- };
- static inline bool
- IsRegExpMetaChar(jschar c)
- {
- switch (c) {
- /* Taken from the PatternCharacter production in 15.10.1. */
- case '^': case '$': case '\\': case '.': case '*': case '+':
- case '?': case '(': case ')': case '[': case ']': case '{':
- case '}': case '|':
- return true;
- default:
- return false;
- }
- }
- static inline bool
- HasRegExpMetaChars(const jschar *chars, size_t length)
- {
- for (size_t i = 0; i < length; ++i) {
- if (IsRegExpMetaChar(chars[i]))
- return true;
- }
- return false;
- }
- /*
- * RegExpGuard factors logic out of String regexp operations.
- *
- * |optarg| indicates in which argument position RegExp flags will be found, if
- * present. This is a Mozilla extension and not part of any ECMA spec.
- */
- class RegExpGuard
- {
- RegExpGuard(const RegExpGuard &) MOZ_DELETE;
- void operator=(const RegExpGuard &) MOZ_DELETE;
- RegExpShared::Guard re_;
- FlatMatch fm;
- /*
- * Upper bound on the number of characters we are willing to potentially
- * waste on searching for RegExp meta-characters.
- */
- static const size_t MAX_FLAT_PAT_LEN = 256;
- static JSAtom *
- flattenPattern(JSContext *cx, JSAtom *patstr)
- {
- StringBuffer sb(cx);
- if (!sb.reserve(patstr->length()))
- return NULL;
- static const jschar ESCAPE_CHAR = '\\';
- const jschar *chars = patstr->chars();
- size_t len = patstr->length();
- for (const jschar *it = chars; it != chars + len; ++it) {
- if (IsRegExpMetaChar(*it)) {
- if (!sb.append(ESCAPE_CHAR) || !sb.append(*it))
- return NULL;
- } else {
- if (!sb.append(*it))
- return NULL;
- }
- }
- return sb.finishAtom();
- }
- public:
- RegExpGuard() {}
- /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
- bool init(JSContext *cx, CallArgs args, bool convertVoid = false)
- {
- if (args.length() != 0 && IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
- RegExpShared *shared = RegExpToShared(cx, args[0].toObject());
- if (!shared)
- return false;
- re_.init(*shared);
- } else {
- if (convertVoid && (args.length() == 0 || args[0].isUndefined())) {
- fm.patstr = cx->runtime->emptyString;
- return true;
- }
- JSString *arg = ArgToRootedString(cx, args, 0);
- if (!arg)
- return false;
- fm.patstr = js_AtomizeString(cx, arg);
- if (!fm.patstr)
- return false;
- }
- return true;
- }
- /*
- * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
- * pattern string, or a lengthy pattern string can thwart this process.
- *
- * |checkMetaChars| looks for regexp metachars in the pattern string.
- *
- * Return whether flat matching could be used.
- *
- * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
- */
- const FlatMatch *
- tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
- bool checkMetaChars = true)
- {
- if (re_.initialized())
- return NULL;
- fm.pat = fm.patstr->chars();
- fm.patlen = fm.patstr->length();
- if (optarg < argc)
- return NULL;
- if (checkMetaChars &&
- (fm.patlen > MAX_FLAT_PAT_LEN || HasRegExpMetaChars(fm.pat, fm.patlen))) {
- return NULL;
- }
- /*
- * textstr could be a rope, so we want to avoid flattening it for as
- * long as possible.
- */
- if (textstr->isRope()) {
- if (!RopeMatch(cx, textstr, fm.pat, fm.patlen, &fm.match_))
- return NULL;
- } else {
- const jschar *text = textstr->asLinear().chars();
- size_t textlen = textstr->length();
- fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen);
- }
- return &fm;
- }
- /* If the pattern is not already a regular expression, make it so. */
- bool normalizeRegExp(JSContext *cx, bool flat, uintN optarg, CallArgs args)
- {
- if (re_.initialized())
- return true;
- /* Build RegExp from pattern string. */
- JSString *opt;
- if (optarg < args.length()) {
- opt = ToString(cx, args[optarg]);
- if (!opt)
- return false;
- } else {
- opt = NULL;
- }
- JSAtom *patstr;
- if (flat) {
- patstr = flattenPattern(cx, fm.patstr);
- if (!patstr)
- return false;
- } else {
- patstr = fm.patstr;
- }
- JS_ASSERT(patstr);
- RegExpShared *re = cx->compartment->regExps.get(cx, patstr, opt);
- if (!re)
- return false;
- re_.init(*re);
- return true;
- }
- RegExpShared ®Exp() { return *re_; }
- };
- /* ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
- static JS_ALWAYS_INLINE bool
- Matched(RegExpExecType type, const Value &v)
- {
- return (type == RegExpTest) ? v.isTrue() : !v.isNull();
- }
- typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
- /*
- * BitOR-ing these flags allows the DoMatch caller to control when how the
- * RegExp engine is called and when callbacks are fired.
- */
- enum MatchControlFlags {
- TEST_GLOBAL_BIT = 0x1, /* use RegExp.test for global regexps */
- TEST_SINGLE_BIT = 0x2, /* use RegExp.test for non-global regexps */
- CALLBACK_ON_SINGLE_BIT = 0x4, /* fire callback on non-global match */
- MATCH_ARGS = TEST_GLOBAL_BIT,
- MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
- REPLACE_ARGS = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
- };
- /* Factor out looping and matching logic. */
- static bool
- DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, RegExpShared &re,
- DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
- {
- JSLinearString *linearStr = str->ensureLinear(cx);
- if (!linearStr)
- return false;
- const jschar *chars = linearStr->chars();
- size_t length = linearStr->length();
- if (re.global()) {
- RegExpExecType type = (flags & TEST_GLOBAL_BIT) ? RegExpTest : RegExpExec;
- for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
- if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval))
- return false;
- if (!Matched(type, *rval))
- break;
- if (!callback(cx, res, count, data))
- return false;
- if (!res->matched())
- ++i;
- }
- } else {
- RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec;
- bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
- size_t i = 0;
- if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval))
- return false;
- if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data))
- return false;
- }
- return true;
- }
- static bool
- BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, CallArgs *args)
- {
- if (fm.match() < 0) {
- args->rval() = NullValue();
- return true;
- }
- /* For this non-global match, produce a RegExp.exec-style array. */
- JSObject *obj = NewSlowEmptyArray(cx);
- if (!obj)
- return false;
- if (!obj->defineElement(cx, 0, StringValue(fm.pattern())) ||
- !obj->defineProperty(cx, cx->runtime->atomState.indexAtom, Int32Value(fm.match())) ||
- !obj->defineProperty(cx, cx->runtime->atomState.inputAtom, StringValue(textstr)))
- {
- return false;
- }
- args->rval() = ObjectValue(*obj);
- return true;
- }
- typedef JSObject **MatchArgType;
- /*
- * DoMatch will only callback on global matches, hence this function builds
- * only the "array of matches" returned by match on global regexps.
- */
- static bool
- MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
- {
- JS_ASSERT(count <= JSID_INT_MAX); /* by max string length */
- JSObject *&arrayobj = *static_cast<MatchArgType>(p);
- if (!arrayobj) {
- arrayobj = NewDenseEmptyArray(cx);
- if (!arrayobj)
- return false;
- }
- Value v;
- return res->createLastMatch(cx, &v) && arrayobj->defineElement(cx, count, v);
- }
- JSBool
- js::str_match(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- RegExpGuard g;
- if (!g.init(cx, args, true))
- return false;
- if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length()))
- return BuildFlatMatchArray(cx, str, *fm, &args);
- /* Return if there was an error in tryFlatMatch. */
- if (cx->isExceptionPending())
- return false;
- if (!g.normalizeRegExp(cx, false, 1, args))
- return false;
- JSObject *array = NULL;
- MatchArgType arg = &array;
- RegExpStatics *res = cx->regExpStatics();
- Value rval;
- if (!DoMatch(cx, res, str, g.regExp(), MatchCallback, arg, MATCH_ARGS, &rval))
- return false;
- if (g.regExp().global())
- args.rval() = ObjectOrNullValue(array);
- else
- args.rval() = rval;
- return true;
- }
- JSBool
- js::str_search(JSContext *cx, uintN argc, Value *vp)
- {
- CallArgs args = CallArgsFromVp(argc, vp);
- JSString *str = ThisToStringForStringProto(cx, args);
- if (!str)
- return false;
- RegExpGuard g;
- if (!g.init(cx, args, true))
- return false;
- if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length())) {
- args.rval() = Int32Value(fm->match());
- return true;
- }
- if (cx->isExceptionPending()) /* from tryFlatMatch */
- return false;
- if (!g.normalizeRegExp(cx, false, 1, args))
- return false;
- JSLinearString *linearStr = str->ensureLinear(cx);
- if (!linearStr)
- return false;
- const jschar *chars = linearStr->chars();
- size_t length = linearStr->length();
- RegExpStatics *res = cx->regExpStatics();
- /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
- size_t i = 0;
- Value result;
- if (!ExecuteRegExp(cx, res, g.regExp(), linearStr, chars, length, &i, RegExpTest, &result))
- return false;
- if (result.isTrue())
- args.rval() = Int32Value(res->matchStart());
- else
- args.rval() = Int32Value(-1);
- return true;
- }
- struct ReplaceData
- {
- ReplaceData(JSContext *cx)
- : sb(cx)
- {}
- JSString *str; /* 'this' parameter object as a string */
- RegExpGuard g; /* regexp parameter object and private data */
- JSObject *lambda; /* replacement function object or null */
- JSObject *elembase; /* object for function(a){return b[a]} replace */
- JSLinearString *repstr; /* replacement string */
- const jschar *dollar; /* null or pointer to first $ in repstr */
- const jschar *dollarEnd; /* limit pointer for js_strchr_limit */
- jsint leftIndex; /* left context index in str->chars */
- JSSubString dollarStr; /* for "$$" InterpretDollar result */
- bool calledBack; /* record whether callback has been called */
- InvokeArgsGuard args; /* arguments for lambda call */
- StringBuffer sb; /* buffer built during DoMatch */
- };
- static bool
- InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jschar *ep,
- ReplaceData &rdata, JSSubString *out, size_t *skip)
- {…