diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 5842fb2ee1e..ad8adf4486b 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -915,19 +915,20 @@ jsdProperty::GetValue(jsdIValue **_rval) NS_IMPL_THREADSAFE_ISUPPORTS2(jsdScript, jsdIScript, jsdIEphemeral) static NS_IMETHODIMP -AssignToJSString(nsACString *x, JSString *str) +AssignToJSString(JSDContext *aCx, nsACString *x, JSString *str) { if (!str) { x->SetLength(0); return NS_OK; } - size_t length = JS_GetStringEncodingLength(NULL, str); + JSContext *cx = JSD_GetDefaultJSContext(aCx); + size_t length = JS_GetStringEncodingLength(cx, str); if (length == size_t(-1)) return NS_ERROR_FAILURE; x->SetLength(uint32_t(length)); if (x->Length() != uint32_t(length)) return NS_ERROR_OUT_OF_MEMORY; - JS_EncodeStringToBuffer(str, x->BeginWriting(), length); + JS_EncodeStringToBuffer(cx, str, x->BeginWriting(), length); return NS_OK; } @@ -953,7 +954,7 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(false), if (mFunctionName) { JSString *str = JSD_GetScriptFunctionId(mCx, mScript); if (str) - AssignToJSString(mFunctionName, str); + AssignToJSString(mCx, mFunctionName, str); } mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript); mLineExtent = JSD_GetScriptLineExtent(mCx, mScript); @@ -1908,7 +1909,7 @@ jsdStackFrame::GetFunctionName(nsACString &_rval) ASSERT_VALID_EPHEMERAL; JSString *str = JSD_GetIdForStackFrame(mCx, mThreadState, mStackFrameInfo); if (str) - return AssignToJSString(&_rval, str); + return AssignToJSString(mCx, &_rval, str); _rval.Assign("anonymous"); return NS_OK; @@ -2238,7 +2239,7 @@ NS_IMETHODIMP jsdValue::GetJsFunctionName(nsACString &_rval) { ASSERT_VALID_EPHEMERAL; - return AssignToJSString(&_rval, JSD_GetValueFunctionId(mCx, mValue)); + return AssignToJSString(mCx, &_rval, JSD_GetValueFunctionId(mCx, mValue)); } NS_IMETHODIMP diff --git a/js/public/CharacterEncoding.h b/js/public/CharacterEncoding.h new file mode 100644 index 00000000000..8a68b001567 --- /dev/null +++ b/js/public/CharacterEncoding.h @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_CharacterEncoding_h___ +#define js_CharacterEncoding_h___ + +#include "mozilla/Range.h" + +#include "js/Utility.h" + +#include "jspubtd.h" + +namespace JS { + +/* + * By default, all C/C++ 1-byte-per-character strings passed into the JSAPI + * are treated as ISO/IEC 8859-1, also known as Latin-1. That is, each + * byte is treated as a 2-byte character, and there is no way to pass in a + * string containing characters beyond U+00FF. + */ +class Latin1Chars : public mozilla::Range +{ + typedef mozilla::Range Base; + + public: + Latin1Chars() : Base() {} + Latin1Chars(char *bytes, size_t length) : Base(reinterpret_cast(bytes), length) {} +}; + +/* + * A Latin1Chars, but with \0 termination for C compatibility. + */ +class Latin1CharsZ : public mozilla::RangedPtr +{ + typedef mozilla::RangedPtr Base; + + public: + Latin1CharsZ() : Base(NULL, 0) {} + + Latin1CharsZ(char *bytes, size_t length) + : Base(reinterpret_cast(bytes), length) + { + JS_ASSERT(bytes[length] == '\0'); + } + + Latin1CharsZ(unsigned char *bytes, size_t length) + : Base(bytes, length) + { + JS_ASSERT(bytes[length] == '\0'); + } + + char *c_str() { return reinterpret_cast(get()); } +}; + +/* + * SpiderMonkey also deals directly with UTF-8 encoded text in some places. + */ +class UTF8CharsZ : public mozilla::RangedPtr +{ + typedef mozilla::RangedPtr Base; + + public: + UTF8CharsZ() : Base(NULL, 0) {} + + UTF8CharsZ(char *bytes, size_t length) + : Base(reinterpret_cast(bytes), length) + { + JS_ASSERT(bytes[length] == '\0'); + } +}; + +/* + * SpiderMonkey uses a 2-byte character representation: it is a + * 2-byte-at-a-time view of a UTF-16 byte stream. This is similar to UCS-2, + * but unlike UCS-2, we do not strip UTF-16 extension bytes. This allows a + * sufficiently dedicated JavaScript program to be fully unicode-aware by + * manually interpreting UTF-16 extension characters embedded in the JS + * string. + */ +class TwoByteChars : public mozilla::Range +{ + typedef mozilla::Range Base; + + public: + TwoByteChars() : Base() {} + TwoByteChars(jschar *chars, size_t length) : Base(chars, length) {} + TwoByteChars(const jschar *chars, size_t length) : Base(const_cast(chars), length) {} +}; + +/* + * A TwoByteChars, but \0 terminated for compatibility with JSFlatString. + */ +class TwoByteCharsZ : public mozilla::RangedPtr +{ + typedef mozilla::RangedPtr Base; + + public: + TwoByteCharsZ(jschar *chars, size_t length) + : Base(chars, length) + { + JS_ASSERT(chars[length] = '\0'); + } +}; + +/* + * Convert a 2-byte character sequence to "ISO-Latin-1". This works by + * truncating each 2-byte pair in the sequence to a 1-byte pair. If the source + * contains any UTF-16 extension characters, then this may give invalid Latin1 + * output. The returned string is zero terminated. The returned string or the + * returned string's |start()| must be freed with JS_free or js_free, + * respectively. If allocation fails, an OOM error will be set and the method + * will return a NULL chars (which can be tested for with the ! operator). + * This method cannot trigger GC. + */ +extern Latin1CharsZ +LossyTwoByteCharsToNewLatin1CharsZ(JSContext *cx, TwoByteChars tbchars); + +} // namespace JS + +inline void JS_free(JS::Latin1CharsZ &ptr) { js_free((void*)ptr.get()); } + +#endif // js_CharacterEncoding_h___ diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 74af2ba5804..890ebbfbf03 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -127,6 +127,7 @@ CPPSRCS = \ String.cpp \ BytecodeCompiler.cpp \ BytecodeEmitter.cpp \ + CharacterEncoding.cpp \ FoldConstants.cpp \ Intl.cpp \ NameFunctions.cpp \ @@ -216,6 +217,7 @@ EXPORTS_NAMESPACES += js # that we ensure we don't over-expose our internal integer typedefs. Note that # LegacyIntTypes.h below is deliberately exempted from this requirement. EXPORTS_js = \ + CharacterEncoding.h \ HashTable.h \ HeapAPI.h \ LegacyIntTypes.h \ diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 900b55d1282..d3fd91f4bfa 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -32,6 +32,7 @@ #include "frontend/Parser.h" #include "frontend/TokenStream.h" +#include "js/CharacterEncoding.h" #include "vm/Keywords.h" #include "vm/RegExpObject.h" #include "vm/StringBuffer.h" @@ -564,7 +565,8 @@ TokenStream::reportCompileErrorNumberVA(ParseNode *pn, unsigned flags, unsigned err.report.uclinebuf = windowBuf.extractWellSized(); if (!err.report.uclinebuf) return false; - err.report.linebuf = DeflateString(cx, err.report.uclinebuf, windowLength); + TwoByteChars tbchars(err.report.uclinebuf, windowLength); + err.report.linebuf = LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars).c_str(); if (!err.report.linebuf) return false; @@ -728,7 +730,8 @@ TokenStream::getXMLEntity() bad: /* No match: throw a TypeError per ECMA-357 10.3.2.1 step 8(a). */ JS_ASSERT((tb.end() - bp) >= 1); - bytes = DeflateString(cx, bp + 1, (tb.end() - bp) - 1); + TwoByteChars tbchars(bp + 1, (tb.end() - bp) - 1); + bytes = LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars).c_str(); if (bytes) { reportError(msg, bytes); js_free(bytes); diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index f3724cd92cd..8396cba1dca 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -68,6 +68,7 @@ CPPSRCS = \ testStringBuffer.cpp \ testTrap.cpp \ testTypedArrays.cpp \ + testUTF8.cpp \ testVersion.cpp \ testXDR.cpp \ $(NULL) diff --git a/js/src/jsapi-tests/testUTF8.cpp b/js/src/jsapi-tests/testUTF8.cpp new file mode 100644 index 00000000000..941177369b8 --- /dev/null +++ b/js/src/jsapi-tests/testUTF8.cpp @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "tests.h" +#include "jsapi.h" +#include "jsstr.h" +#include "js/CharacterEncoding.h" + +BEGIN_TEST(testUTF8_badUTF8) +{ + static const char badUTF8[] = "...\xC0..."; + JSString *str = JS_NewStringCopyZ(cx, badUTF8); + CHECK(str); + const jschar *chars = JS_GetStringCharsZ(cx, str); + CHECK(chars); + CHECK(chars[3] == 0x00C0); + return true; +} +END_TEST(testUTF8_badUTF8) + +BEGIN_TEST(testUTF8_bigUTF8) +{ + static const char bigUTF8[] = "...\xFB\xBF\xBF\xBF\xBF..."; + JSString *str = JS_NewStringCopyZ(cx, bigUTF8); + CHECK(str); + const jschar *chars = JS_GetStringCharsZ(cx, str); + CHECK(chars); + CHECK(chars[3] == 0x00FB); + return true; +} +END_TEST(testUTF8_bigUTF8) + +BEGIN_TEST(testUTF8_badSurrogate) +{ + static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 }; + JS::TwoByteChars tbchars(badSurrogate, js_strlen(badSurrogate)); + JS::Latin1CharsZ latin1 = JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars); + CHECK(latin1); + CHECK(latin1[3] == 0x00EE); + return true; +} +END_TEST(testUTF8_badSurrogate) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 4b661054af3..c738bf2e781 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -64,6 +64,7 @@ #include "frontend/BytecodeCompiler.h" #include "gc/Marking.h" #include "gc/Memory.h" +#include "js/CharacterEncoding.h" #include "js/MemoryMetrics.h" #include "vm/Debugger.h" #include "vm/NumericConversions.h" @@ -6231,27 +6232,24 @@ JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_ } JS_PUBLIC_API(char *) -JS_EncodeString(JSContext *cx, JSRawString strArg) +JS_EncodeString(JSContext *cx, JSRawString str) { - RootedString str(cx, strArg); - + AutoAssertNoGC nogc; AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - const jschar *chars = str->getChars(cx); - if (!chars) + JSLinearString *linear = str->ensureLinear(cx); + if (!linear) return NULL; - return DeflateString(cx, chars, str->length()); + + return LossyTwoByteCharsToNewLatin1CharsZ(cx, linear->range()).c_str(); } JS_PUBLIC_API(size_t) JS_GetStringEncodingLength(JSContext *cx, JSString *str) { - /* jsd calls us with a NULL cx. Ugh. */ - if (cx) { - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - } + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); const jschar *chars = str->getChars(cx); if (!chars) @@ -6260,8 +6258,11 @@ JS_GetStringEncodingLength(JSContext *cx, JSString *str) } JS_PUBLIC_API(size_t) -JS_EncodeStringToBuffer(JSString *str, char *buffer, size_t length) +JS_EncodeStringToBuffer(JSContext *cx, JSString *str, char *buffer, size_t length) { + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + /* * FIXME bug 612141 - fix DeflateStringToBuffer interface so the result * would allow to distinguish between insufficient buffer and encoding diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 9362c104561..e0015f3b046 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5554,7 +5554,7 @@ JS_GetStringEncodingLength(JSContext *cx, JSString *str); * written into the buffer. */ JS_PUBLIC_API(size_t) -JS_EncodeStringToBuffer(JSString *str, char *buffer, size_t length); +JS_EncodeStringToBuffer(JSContext *cx, JSString *str, char *buffer, size_t length); class JSAutoByteString { diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 4f65fe99696..c243677bdb8 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -23,6 +23,8 @@ # include #endif // ANDROID +#include "mozilla/Util.h" + #include "jstypes.h" #include "jsutil.h" #include "jsclist.h" @@ -55,6 +57,7 @@ # include "methodjit/MethodJIT.h" #endif #include "gc/Marking.h" +#include "js/CharacterEncoding.h" #include "js/MemoryMetrics.h" #include "frontend/TokenStream.h" #include "frontend/ParseMaps.h" @@ -69,6 +72,7 @@ using namespace js; using namespace js::gc; using mozilla::DebugOnly; +using mozilla::PointerRangeSize; bool js::AutoCycleDetector::init() @@ -832,8 +836,10 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, JS_ASSERT(expandedArgs == argCount); *out = 0; js_free(buffer); - *messagep = DeflateString(cx, reportp->ucmessage, - size_t(out - reportp->ucmessage)); + TwoByteChars ucmsg(reportp->ucmessage, + PointerRangeSize(static_cast(reportp->ucmessage), + static_cast(out))); + *messagep = LossyTwoByteCharsToNewLatin1CharsZ(cx, ucmsg).c_str(); if (!*messagep) goto error; } diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index ab960f37c0a..702626a0ee7 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -291,13 +291,13 @@ MakeTypeId(JSContext *cx, jsid id) */ if (JSID_IS_STRING(id)) { JSFlatString *str = JSID_TO_FLAT_STRING(id); - const jschar *cp = str->getCharsZ(cx); - if (JS7_ISDEC(*cp) || *cp == '-') { - cp++; - while (JS7_ISDEC(*cp)) - cp++; - if (*cp == 0) - return JSID_VOID; + TwoByteChars cp = str->range(); + if (JS7_ISDEC(cp[0]) || cp[0] == '-') { + for (size_t i = 1; i < cp.length(); ++i) { + if (!JS7_ISDEC(cp[i])) + return id; + } + return JSID_VOID; } return id; } diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 12a1cbeeab7..cb104715ccf 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -38,9 +38,9 @@ #include "jsstr.h" #include "ds/Sort.h" - #include "frontend/BytecodeEmitter.h" #include "frontend/TokenStream.h" +#include "js/CharacterEncoding.h" #include "vm/Debugger.h" #include "vm/StringBuffer.h" @@ -6262,10 +6262,11 @@ js::DecompileValueGenerator(JSContext *cx, int spindex, HandleValue v, return NULL; } - Rooted stable(cx, fallback->ensureStable(cx)); - if (!stable) + Rooted linear(cx, fallback->ensureLinear(cx)); + if (!linear) return NULL; - return DeflateString(cx, stable->chars().get(), stable->length()); + TwoByteChars tbchars(linear->chars(), linear->length()); + return LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars).c_str(); } static bool @@ -6352,10 +6353,10 @@ js::DecompileArgument(JSContext *cx, int formalIndex, HandleValue v) if (!fallback) return NULL; - Rooted stable(cx, fallback->ensureStable(cx)); - if (!stable) + Rooted linear(cx, fallback->ensureLinear(cx)); + if (!linear) return NULL; - return DeflateString(cx, stable->chars().get(), stable->length()); + return LossyTwoByteCharsToNewLatin1CharsZ(cx, linear->range()).c_str(); } static char * diff --git a/js/src/jsprf.cpp b/js/src/jsprf.cpp index 35211e6a2fd..0a2ebc8e41e 100644 --- a/js/src/jsprf.cpp +++ b/js/src/jsprf.cpp @@ -17,6 +17,8 @@ #include "jspubtd.h" #include "jsstr.h" +#include "js/CharacterEncoding.h" + using namespace js; /* @@ -360,8 +362,8 @@ static int cvt_s(SprintfState *ss, const char *s, int width, int prec, return fill2(ss, s ? s : "(null)", slen, width, flags); } -static int cvt_ws(SprintfState *ss, const jschar *ws, int width, int prec, - int flags) +static int +cvt_ws(SprintfState *ss, const jschar *ws, int width, int prec, int flags) { int result; /* @@ -369,12 +371,15 @@ static int cvt_ws(SprintfState *ss, const jschar *ws, int width, int prec, * and malloc() is used to allocate the buffer buffer. */ if (ws) { - int slen = js_strlen(ws); - char *s = DeflateString(NULL, ws, slen); - if (!s) + size_t wslen = js_strlen(ws); + char *latin1 = js_pod_malloc(wslen + 1); + if (!latin1) return -1; /* JSStuffFunc error indicator. */ - result = cvt_s(ss, s, width, prec, flags); - js_free(s); + for (size_t i = 0; i < wslen; ++i) + latin1[i] = (char)ws[i]; + latin1[wslen] = '\0'; + result = cvt_s(ss, latin1, width, prec, flags); + js_free(latin1); } else { result = cvt_s(ss, NULL, width, prec, flags); } diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 7d071087c05..034906dbd0f 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -27,6 +27,7 @@ #include "frontend/Parser.h" #include "frontend/TokenStream.h" +#include "js/CharacterEncoding.h" #include "vm/RegExpObject.h" #include "jsscriptinlines.h" @@ -3442,7 +3443,8 @@ reflect_parse(JSContext *cx, uint32_t argc, jsval *vp) if (!chars) return JS_FALSE; - filename = DeflateString(cx, chars, length); + TwoByteChars tbchars(chars, length); + filename = LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars).c_str(); if (!filename) return JS_FALSE; } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 8568f63d410..3e420715b7a 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3726,25 +3726,6 @@ js::InflateUTF8String(JSContext *cx, const char *bytes, size_t *lengthp) return NULL; } -/* - * May be called with null cx. - */ -char * -js::DeflateString(JSContext *maybecx, const jschar *chars, size_t nchars) -{ - AutoAssertNoGC nogc; - size_t nbytes = nchars; - char *bytes = maybecx - ? maybecx->pod_malloc(nbytes + 1) - : js_pod_malloc(nbytes + 1); - if (!bytes) - return NULL; - for (size_t i = 0; i < nbytes; i++) - bytes[i] = (char) chars[i]; - bytes[nbytes] = 0; - return bytes; -} - size_t js::GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars) { diff --git a/js/src/jsstr.h b/js/src/jsstr.h index babb43d638d..7d63bbd0e48 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -224,9 +224,6 @@ InflateString(JSContext *cx, const char *bytes, size_t *length); extern jschar * InflateUTF8String(JSContext *cx, const char *bytes, size_t *length); -extern char * -DeflateString(JSContext *cx, const jschar *chars, size_t length); - /* * Inflate bytes to JS chars in an existing buffer. 'chars' must be large * enough for 'length' jschars. The buffer is NOT null-terminated. diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index a086cc65fea..ea3397f8b82 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -20,6 +20,8 @@ #include "jsinterpinlines.h" #include "jsautooplen.h" +#include "js/CharacterEncoding.h" + #include "vm/ScopeObject-inl.h" #include "vm/StringObject-inl.h" @@ -2409,12 +2411,12 @@ GetElementIC::attachGetProp(VMFrame &f, HandleObject obj, HandleValue v, HandleP CodeLocationLabel cs = buffer.finalize(f); #if DEBUG - char *chars = DeflateString(cx, v.toString()->getChars(cx), v.toString()->length()); + Latin1CharsZ latin1 = LossyTwoByteCharsToNewLatin1CharsZ(cx, v.toString()->ensureLinear(cx)->range()); JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom %p (\"%s\") shape %p (%s: %d)\n", - js_CodeName[JSOp(*f.pc())], cs.executableAddress(), (void*)name, chars, + js_CodeName[JSOp(*f.pc())], cs.executableAddress(), (void*)name, latin1.get(), (void*)holder->lastProperty(), cx->fp()->script()->filename, CurrentLine(cx)); - js_free(chars); + JS_free(latin1); #endif // Update the inline guards, if needed. diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 4ca1f64c267..398a0f7dd86 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -2440,47 +2440,6 @@ ToInt32(JSContext *cx, unsigned argc, jsval *vp) return true; } -static const char* badUTF8 = "...\xC0..."; -static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF..."; -static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 }; - -static JSBool -TestUTF8(JSContext *cx, unsigned argc, jsval *vp) -{ - int32_t mode = 1; - jschar chars[20]; - size_t charsLength = 5; - char bytes[20]; - size_t bytesLength = 20; - if (argc && !JS_ValueToInt32(cx, *JS_ARGV(cx, vp), &mode)) - return false; - - /* The following throw errors if compiled with UTF-8. */ - switch (mode) { - /* mode 1: malformed UTF-8 string. */ - case 1: - JS_NewStringCopyZ(cx, badUTF8); - break; - /* mode 2: big UTF-8 character. */ - case 2: - JS_NewStringCopyZ(cx, bigUTF8); - break; - /* mode 3: bad surrogate character. */ - case 3: - DeflateStringToBuffer(cx, badSurrogate, 6, bytes, &bytesLength); - break; - /* mode 4: use a too small buffer. */ - case 4: - JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength); - break; - default: - JS_ReportError(cx, "invalid mode parameter"); - return false; - } - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return !JS_IsExceptionPending (cx); -} - static JSBool ThrowError(JSContext *cx, unsigned argc, jsval *vp) { @@ -3740,10 +3699,6 @@ static JSFunctionSpecWithHelp shell_functions[] = { "pc2line(fun[, pc])", " Map PC to line number."), - JS_FN_HELP("testUTF8", TestUTF8, 1, 0, -"testUTF8(mode)", -" Perform UTF-8 tests (modes are 1 to 4)."), - JS_FN_HELP("throwError", ThrowError, 0, 0, "throwError()", " Throw an error from JS_ReportError."), diff --git a/js/src/tests/js1_5/Exceptions/regress-232182.js b/js/src/tests/js1_5/Exceptions/regress-232182.js deleted file mode 100644 index ffd4c8daef7..00000000000 --- a/js/src/tests/js1_5/Exceptions/regress-232182.js +++ /dev/null @@ -1,122 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- - -var BUGNUMBER = 232182; -var summary = 'Display non-ascii characters in JS exceptions'; -var actual = ''; -var expect = 'no error'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -/* - * This test accesses an undefined Unicode symbol. If the code has not been - * compiled with JS_C_STRINGS_ARE_UTF8, the thrown error truncates Unicode - * characters to bytes. Accessing \u0440\u0441, therefore, results in a - * message talking about an undefined variable 'AB' (\x41\x42). - */ -var utf8Enabled = false; -try -{ - \u0441\u0442; -} -catch (e) -{ - utf8Enabled = (e.message.charAt (0) == '\u0441'); -} - -// Run the tests only if UTF-8 is enabled - -printStatus('UTF-8 is ' + (utf8Enabled?'':'not ') + 'enabled'); - -if (!utf8Enabled) -{ - reportCompare('Not run', 'Not run', 'utf8 is not enabled'); -} -else -{ - status = summary + ': Throw Error with Unicode message'; - expect = 'test \u0440\u0441'; - try - { - throw Error (expect); - } - catch (e) - { - actual = e.message; - } - reportCompare(expect, actual, status); - - var inShell = (typeof stringsAreUTF8 == "function"); - if (!inShell) - { - inShell = (typeof stringsAreUtf8 == "function"); - if (inShell) - { - this.stringsAreUTF8 = stringsAreUtf8; - this.testUTF8 = testUtf8; - } - } - - if (inShell && stringsAreUTF8()) - { - status = summary + ': UTF-8 test: bad UTF-08 sequence'; - expect = 'Error'; - actual = 'No error!'; - try - { - testUTF8(1); - } - catch (e) - { - actual = 'Error'; - } - reportCompare(expect, actual, status); - - status = summary + ': UTF-8 character too big to fit into Unicode surrogate pairs'; - expect = 'Error'; - actual = 'No error!'; - try - { - testUTF8(2); - } - catch (e) - { - actual = 'Error'; - } - reportCompare(expect, actual, status); - - status = summary + ': bad Unicode surrogate character'; - expect = 'Error'; - actual = 'No error!'; - try - { - testUTF8(3); - } - catch (e) - { - actual = 'Error'; - } - reportCompare(expect, actual, status); - } - - if (inShell) - { - status = summary + ': conversion target buffer overrun'; - expect = 'Error'; - actual = 'No error!'; - try - { - testUTF8(4); - } - catch (e) - { - actual = 'Error'; - } - reportCompare(expect, actual, status); - } -} diff --git a/js/src/vm/CharacterEncoding.cpp b/js/src/vm/CharacterEncoding.cpp new file mode 100644 index 00000000000..72e3b5a5397 --- /dev/null +++ b/js/src/vm/CharacterEncoding.cpp @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jscntxt.h" + +#include "js/CharacterEncoding.h" + +using namespace JS; + +Latin1CharsZ +JS::LossyTwoByteCharsToNewLatin1CharsZ(JSContext *cx, TwoByteChars tbchars) +{ + AutoAssertNoGC nogc; + JS_ASSERT(cx); + size_t len = tbchars.length(); + unsigned char *latin1 = cx->pod_malloc(len + 1); + if (!latin1) + return Latin1CharsZ(); + for (size_t i = 0; i < len; ++i) + latin1[i] = static_cast(tbchars[i]); + latin1[len] = '\0'; + return Latin1CharsZ(latin1, len); +} + diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 6049bd25393..93b29f797b9 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -11,6 +11,8 @@ #include "mozilla/Attributes.h" #include "mozilla/GuardObjects.h" +#include "js/CharacterEncoding.h" + #include "jsapi.h" #include "jsatom.h" #include "jsfriendapi.h" @@ -477,6 +479,11 @@ class JSLinearString : public JSString JS_ASSERT(JSString::isLinear()); return d.u1.chars; } + + JS::TwoByteChars range() const { + JS_ASSERT(JSString::isLinear()); + return JS::TwoByteChars(d.u1.chars, length()); + } }; JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString)); diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index 54ad9a0d56c..49c326fa43c 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -575,7 +575,7 @@ XPCConvert::JSData2Native(JSContext* cx, void* d, jsval s, if (!buffer) { return false; } - JS_EncodeStringToBuffer(str, buffer, length); + JS_EncodeStringToBuffer(cx, str, buffer, length); buffer[length] = '\0'; *((void**)d) = buffer; return true; @@ -701,7 +701,7 @@ XPCConvert::JSData2Native(JSContext* cx, void* d, jsval s, if (rs->Length() != uint32_t(length)) { return false; } - JS_EncodeStringToBuffer(str, rs->BeginWriting(), length); + JS_EncodeStringToBuffer(cx, str, rs->BeginWriting(), length); return true; } @@ -1875,7 +1875,7 @@ XPCConvert::JSStringWithSize2Native(XPCCallContext& ccx, void* d, jsval s, if (!buffer) { return false; } - JS_EncodeStringToBuffer(str, buffer, len); + JS_EncodeStringToBuffer(cx, str, buffer, len); buffer[len] = '\0'; *((char**)d) = buffer; diff --git a/js/xpconnect/src/XPCStack.cpp b/js/xpconnect/src/XPCStack.cpp index 0d4e9bebd0a..f2ed813b2a1 100644 --- a/js/xpconnect/src/XPCStack.cpp +++ b/js/xpconnect/src/XPCStack.cpp @@ -122,7 +122,7 @@ XPCJSStackFrame::CreateStack(JSContext* cx, XPCJSStackFrame** stack) if (length != size_t(-1)) { self->mFunname = static_cast(nsMemory::Alloc(length + 1)); if (self->mFunname) { - JS_EncodeStringToBuffer(funid, self->mFunname, length); + JS_EncodeStringToBuffer(cx, funid, self->mFunname, length); self->mFunname[length] = '\0'; } }