/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
*/
/* 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 <limits>
#include <string.h>
#include "js/Array.h" // JS::IsArrayObject
#include "js/Exception.h"
#include "js/friend/ErrorMessages.h" // JSMSG_*
#include "js/JSON.h"
#include "js/MemoryFunctions.h"
#include "js/Printf.h"
#include "js/PropertyAndElement.h" // JS_GetProperty
#include "jsapi-tests/tests.h"
using namespace js;
class AutoInflatedString {
JSContext*
const cx;
char16_t* chars_;
size_t length_;
public :
explicit AutoInflatedString(JSContext* cx)
: cx(cx), chars_(nullptr), length_(0) {}
~AutoInflatedString() { JS_free(cx, chars_); }
template <size_t N>
void operator =(
const char (&str)[N]) {
length_ = N - 1;
chars_ = InflateString(cx, str, length_);
if (!chars_) {
abort();
}
}
void operator =(
const char * str) {
length_ = strlen(str);
chars_ = InflateString(cx, str, length_);
if (!chars_) {
abort();
}
}
const char16_t* chars()
const {
return chars_; }
size_t length()
const {
return length_; }
};
BEGIN_TEST(testParseJSON_success) {
// Primitives
JS::RootedValue expected(cx);
expected = JS::TrueValue();
CHECK(TryParse(cx,
"true" , expected));
expected = JS::FalseValue();
CHECK(TryParse(cx,
"false" , expected));
expected = JS::NullValue();
CHECK(TryParse(cx,
"null" , expected));
expected.setInt32(0);
CHECK(TryParse(cx,
"0" , expected));
expected.setInt32(1);
CHECK(TryParse(cx,
"1" , expected));
expected.setInt32(-1);
CHECK(TryParse(cx,
"-1" , expected));
expected.setDouble(1);
CHECK(TryParse(cx,
"1" , expected));
expected.setDouble(1.75);
CHECK(TryParse(cx,
"1.75" , expected));
expected.setDouble(9e9);
CHECK(TryParse(cx,
"9e9" , expected));
expected.setDouble(std::numeric_limits<
double >::infinity());
CHECK(TryParse(cx,
"9e99999" , expected));
JS::Rooted<JSLinearString*> str(cx);
const char16_t emptystr[] = {
'\0' };
str = js::NewStringCopyN<CanGC>(cx, emptystr, 0);
CHECK(str);
expected = JS::StringValue(str);
CHECK(TryParse(cx,
"\" \
"" , expected));
const char16_t nullstr[] = {
'\0' };
str = NewString(cx, nullstr);
CHECK(str);
expected = JS::StringValue(str);
CHECK(TryParse(cx,
"\" \\u0000\
"" , expected));
const char16_t backstr[] = {
'\b' };
str = NewString(cx, backstr);
CHECK(str);
expected = JS::StringValue(str);
CHECK(TryParse(cx,
"\" \\b\
"" , expected));
CHECK(TryParse(cx,
"\" \\u0008\
"" , expected));
const char16_t newlinestr[] = {
'\n' ,
};
str = NewString(cx, newlinestr);
CHECK(str);
expected = JS::StringValue(str);
CHECK(TryParse(cx,
"\" \\n\
"" , expected));
CHECK(TryParse(cx,
"\" \\u000A\
"" , expected));
// Arrays
JS::RootedValue v(cx), v2(cx);
JS::RootedObject obj(cx);
bool isArray;
CHECK(Parse(cx,
"[]" , &v));
CHECK(v.isObject());
obj = &v.toObject();
CHECK(JS::IsArrayObject(cx, obj, &isArray));
CHECK(isArray);
CHECK(JS_GetProperty(cx, obj,
"length" , &v2));
CHECK(v2.isInt32(0));
CHECK(Parse(cx,
"[1]" , &v));
CHECK(v.isObject());
obj = &v.toObject();
CHECK(JS::IsArrayObject(cx, obj, &isArray));
CHECK(isArray);
CHECK(JS_GetProperty(cx, obj,
"0" , &v2));
CHECK(v2.isInt32(1));
CHECK(JS_GetProperty(cx, obj,
"length" , &v2));
CHECK(v2.isInt32(1));
// Objects
CHECK(Parse(cx,
"{}" , &v));
CHECK(v.isObject());
obj = &v.toObject();
CHECK(JS::IsArrayObject(cx, obj, &isArray));
CHECK(!isArray);
CHECK(Parse(cx,
"{ \" f\
": 17 }" , &v));
CHECK(v.isObject());
obj = &v.toObject();
CHECK(JS::IsArrayObject(cx, obj, &isArray));
CHECK(!isArray);
CHECK(JS_GetProperty(cx, obj,
"f" , &v2));
CHECK(v2.isInt32(17));
return true ;
}
template <size_t N>
static JSLinearString* NewString(JSContext* cx,
const char16_t (&chars)[N]) {
return js::NewStringCopyN<CanGC>(cx, chars, N);
}
template <size_t N>
inline bool Parse(JSContext* cx,
const char (&input)[N],
JS::MutableHandleValue vp) {
AutoInflatedString str(cx);
str = input;
CHECK(JS_ParseJSON(cx, str.chars(), str.length(), vp));
return true ;
}
template <size_t N>
inline bool TryParse(JSContext* cx,
const char (&input)[N],
JS::HandleValue expected) {
AutoInflatedString str(cx);
RootedValue v(cx);
str = input;
CHECK(JS_ParseJSON(cx, str.chars(), str.length(), &v));
CHECK_SAME(v, expected);
return true ;
}
END_TEST(testParseJSON_success)
BEGIN_TEST(testParseJSON_error) {
CHECK(Error(cx,
"" , 1, 1));
CHECK(Error(cx,
"\n" , 2, 1));
CHECK(Error(cx,
"\r" , 2, 1));
CHECK(Error(cx,
"\r\n" , 2, 1));
CHECK(Error(cx,
"[" , 1, 2));
CHECK(Error(cx,
"[,]" , 1, 2));
CHECK(Error(cx,
"[1,]" , 1, 4));
CHECK(Error(cx,
"{a:2}" , 1, 2));
CHECK(Error(cx,
"{\" a\
":2,}" , 1, 8));
CHECK(Error(cx,
"]" , 1, 1));
CHECK(Error(cx,
"\" ", 1, 2));
CHECK(Error(cx,
"{]" , 1, 2));
CHECK(Error(cx,
"[}" , 1, 2));
CHECK(Error(cx,
"'wrongly-quoted string'" , 1, 1));
CHECK(Error(cx,
"{\" a\
":2 \n b:3}" , 2, 2));
CHECK(Error(cx,
"\n[" , 2, 2));
CHECK(Error(cx,
"\n[,]" , 2, 2));
CHECK(Error(cx,
"\n[1,]" , 2, 4));
CHECK(Error(cx,
"\n{a:2}" , 2, 2));
CHECK(Error(cx,
"\n{\" a\
":2,}" , 2, 8));
CHECK(Error(cx,
"\n]" , 2, 1));
CHECK(Error(cx,
"\" bad string\n\
"" , 1, 12));
CHECK(Error(cx,
"\r'wrongly-quoted string'" , 2, 1));
CHECK(Error(cx,
"\n\" ", 2, 2));
CHECK(Error(cx,
"\n{]" , 2, 2));
CHECK(Error(cx,
"\n[}" , 2, 2));
CHECK(Error(cx,
"{\" a\
":[2,3],\n\" b\
":,5,6}" , 2, 5));
CHECK(Error(cx,
"{\" a\
":2 \r b:3}" , 2, 2));
CHECK(Error(cx,
"\r[" , 2, 2));
CHECK(Error(cx,
"\r[,]" , 2, 2));
CHECK(Error(cx,
"\r[1,]" , 2, 4));
CHECK(Error(cx,
"\r{a:2}" , 2, 2));
CHECK(Error(cx,
"\r{\" a\
":2,}" , 2, 8));
CHECK(Error(cx,
"\r]" , 2, 1));
CHECK(Error(cx,
"\" bad string\r\
"" , 1, 12));
CHECK(Error(cx,
"\r'wrongly-quoted string'" , 2, 1));
CHECK(Error(cx,
"\r\" ", 2, 2));
CHECK(Error(cx,
"\r{]" , 2, 2));
CHECK(Error(cx,
"\r[}" , 2, 2));
CHECK(Error(cx,
"{\" a\
":[2,3],\r\" b\
":,5,6}" , 2, 5));
CHECK(Error(cx,
"{\" a\
":2 \r\n b:3}" , 2, 2));
CHECK(Error(cx,
"\r\n[" , 2, 2));
CHECK(Error(cx,
"\r\n[,]" , 2, 2));
CHECK(Error(cx,
"\r\n[1,]" , 2, 4));
CHECK(Error(cx,
"\r\n{a:2}" , 2, 2));
CHECK(Error(cx,
"\r\n{\" a\
":2,}" , 2, 8));
CHECK(Error(cx,
"\r\n]" , 2, 1));
CHECK(Error(cx,
"\" bad string\r\n\
"" , 1, 12));
CHECK(Error(cx,
"\r\n'wrongly-quoted string'" , 2, 1));
CHECK(Error(cx,
"\r\n\" ", 2, 2));
CHECK(Error(cx,
"\r\n{]" , 2, 2));
CHECK(Error(cx,
"\r\n[}" , 2, 2));
CHECK(Error(cx,
"{\" a\
":[2,3],\r\n\" b\
":,5,6}" , 2, 5));
CHECK(Error(cx,
"\n\" bad string\n\
"" , 2, 12));
CHECK(Error(cx,
"\r\" bad string\r\
"" , 2, 12));
CHECK(Error(cx,
"\r\n\" bad string\r\n\
"" , 2, 12));
CHECK(Error(cx,
"{\n\" a\
":[2,3],\r\" b\
":,5,6}" , 3, 5));
CHECK(Error(cx,
"{\r\" a\
":[2,3],\n\" b\
":,5,6}" , 3, 5));
CHECK(Error(cx,
"[\" \\t\\q
", 1, 6));
CHECK(Error(cx,
"[\" \\t\x00
", 1, 5));
CHECK(Error(cx,
"[\" \\t\x01
", 1, 5));
CHECK(Error(cx,
"[\" \\t\\\x00
", 1, 6));
CHECK(Error(cx,
"[\" \\t\\\x01
", 1, 6));
// Unicode escape errors are messy. The first bad character could be
// non-hexadecimal, or it could be absent entirely. Include tests where
// there's a bad character, followed by zero to as many characters as are
// needed to form a complete Unicode escape sequence, plus one. (The extra
// characters beyond are valuable because our implementation checks for
// too-few subsequent characters first, before checking for subsequent
// non-hexadecimal characters. So \u<END>, \u0<END>, \u00<END>, and
// \u000<END> are all *detected* as invalid by the same code path, but the
// process of computing the first invalid character follows a different
// code path for each. And \uQQQQ, \u0QQQ, \u00QQ, and \u000Q are detected
// as invalid by the same code path [ignoring which precise subexpression
// triggers failure of a single condition], but the computation of the
// first invalid character follows a different code path for each.)
CHECK(Error(cx,
"[\" \\t\\u
", 1, 7));
CHECK(Error(cx,
"[\" \\t\\uZ
", 1, 7));
CHECK(Error(cx,
"[\" \\t\\uZZ
", 1, 7));
CHECK(Error(cx,
"[\" \\t\\uZZZ
", 1, 7));
CHECK(Error(cx,
"[\" \\t\\uZZZZ
", 1, 7));
CHECK(Error(cx,
"[\" \\t\\uZZZZZ
", 1, 7));
CHECK(Error(cx,
"[\" \\t\\u0
", 1, 8));
CHECK(Error(cx,
"[\" \\t\\u0Z
", 1, 8));
CHECK(Error(cx,
"[\" \\t\\u0ZZ
", 1, 8));
CHECK(Error(cx,
"[\" \\t\\u0ZZZ
", 1, 8));
CHECK(Error(cx,
"[\" \\t\\u0ZZZZ
", 1, 8));
CHECK(Error(cx,
"[\" \\t\\u00
", 1, 9));
CHECK(Error(cx,
"[\" \\t\\u00Z
", 1, 9));
CHECK(Error(cx,
"[\" \\t\\u00ZZ
", 1, 9));
CHECK(Error(cx,
"[\" \\t\\u00ZZZ
", 1, 9));
CHECK(Error(cx,
"[\" \\t\\u000
", 1, 10));
CHECK(Error(cx,
"[\" \\t\\u000Z
", 1, 10));
CHECK(Error(cx,
"[\" \\t\\u000ZZ
", 1, 10));
return true ;
}
template <size_t N>
inline bool Error(JSContext* cx,
const char (&input)[N], uint32_t expectedLine,
uint32_t expectedColumn) {
AutoInflatedString str(cx);
RootedValue dummy(cx);
str = input;
bool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy);
CHECK(!ok);
JS::ExceptionStack exnStack(cx);
CHECK(StealPendingExceptionStack(cx, &exnStack));
JS::ErrorReportBuilder report(cx);
CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects));
CHECK(report.report()->errorNumber == JSMSG_JSON_BAD_PARSE);
UniqueChars lineAndColumnASCII =
JS_smprintf(
"line %d column %d" , expectedLine, expectedColumn);
CHECK(strstr(report.toStringResult().c_str(), lineAndColumnASCII.get()) !=
nullptr);
/* We do not execute JS, so there should be no exception thrown. */
CHECK(!JS_IsExceptionPending(cx));
return true ;
}
END_TEST(testParseJSON_error)
static bool Censor(JSContext* cx,
unsigned argc, JS::Value* vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
const unsigned expectedArgCount =
JS::Prefs::experimental_json_parse_with_source() ? 3 : 2;
MOZ_RELEASE_ASSERT(args.length() == expectedArgCount);
MOZ_RELEASE_ASSERT(args[0].isString());
args.rval().setNull();
return true ;
}
BEGIN_TEST(testParseJSON_reviver) {
JSFunction* fun = JS_NewFunction(cx, Censor, 0, 0,
"censor" );
CHECK(fun);
JS::RootedValue filter(cx, JS::ObjectValue(*JS_GetFunctionObject(fun)));
CHECK(TryParse(cx,
"true" , filter));
CHECK(TryParse(cx,
"false" , filter));
CHECK(TryParse(cx,
"null" , filter));
CHECK(TryParse(cx,
"1" , filter));
CHECK(TryParse(cx,
"1.75" , filter));
CHECK(TryParse(cx,
"[]" , filter));
CHECK(TryParse(cx,
"[1]" , filter));
CHECK(TryParse(cx,
"{}" , filter));
return true ;
}
template <size_t N>
inline bool TryParse(JSContext* cx,
const char (&input)[N],
JS::HandleValue filter) {
AutoInflatedString str(cx);
JS::RootedValue v(cx);
str = input;
CHECK(JS_ParseJSONWithReviver(cx, str.chars(), str.length(), filter, &v));
CHECK(v.isNull());
return true ;
}
END_TEST(testParseJSON_reviver)
Messung V0.5 C=95 H=90 G=92
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland