!5313 JSON Parse Refactor

Merge pull request !5313 from 许杰/jsonparse_refactor
This commit is contained in:
openharmony_ci 2023-11-25 03:10:35 +00:00 committed by Gitee
commit 9cc8a59978
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
10 changed files with 261 additions and 144 deletions

View File

@ -53,10 +53,23 @@ enum class Tokens : uint8_t {
TOKEN_ILLEGAL,
};
struct JsonContinuation {
enum class ContinuationType : uint8_t {
RETURN = 0,
ARRAY,
OBJECT,
};
JsonContinuation(ContinuationType type, size_t index) : type_(type), index_(index) {}
ContinuationType type_ {ContinuationType::RETURN};
size_t index_ {0};
};
template<typename T>
class JsonParser {
protected:
using Text = const T *;
using ContType = JsonContinuation::ContinuationType;
// Instantiation of the class is prohibited
JsonParser() = default;
explicit JsonParser(JSThread *thread) : thread_(thread) {}
@ -78,6 +91,11 @@ protected:
auto vm = thread_->GetEcmaVM();
factory_ = vm->GetFactory();
env_ = *vm->GetGlobalEnv();
JSHandle<JSFunction> arrayFunc(env_->GetArrayFunction());
initialJSArrayClass_ = JSHandle<JSHClass>(thread_, JSFunction::GetOrCreateInitialJSHClass(thread_, arrayFunc));
JSHandle<JSFunction> objectFunc(env_->GetObjectFunction());
initialJSObjectClass_ =
JSHandle<JSHClass>(thread_, JSFunction::GetOrCreateInitialJSHClass(thread_, objectFunc));
SkipEndWhiteSpace();
range_ = end_;
@ -85,30 +103,197 @@ protected:
return JSHandle<JSTaggedValue>(thread_, result);
}
JSTaggedValue ParseJSONText(bool inObjorArr = false)
inline bool IsInObjOrArray(ContType type)
{
SkipStartWhiteSpace();
Tokens token = ParseToken();
switch (token) {
case Tokens::OBJECT:
return ParseObject(inObjorArr);
case Tokens::ARRAY:
return ParseArray(inObjorArr);
case Tokens::LITERAL_TRUE:
return ParseLiteralTrue();
case Tokens::LITERAL_FALSE:
return ParseLiteralFalse();
case Tokens::LITERAL_NULL:
return ParseLiteralNull();
case Tokens::NUMBER:
return ParseNumber(inObjorArr);
case Tokens::STRING:
return ParseString(inObjorArr);
default:
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception());
return type == ContType::ARRAY || type == ContType::OBJECT;
}
inline bool EmptyArrayCheck()
{
GetNextNonSpaceChar();
return *current_ == ']';
}
inline bool EmptyObjectCheck()
{
GetNextNonSpaceChar();
return *current_ == '}';
}
JSTaggedValue ParseJSONText()
{
JSHandle<JSTaggedValue> parseValue;
std::vector<JsonContinuation> continuationList;
std::vector<JSHandle<JSTaggedValue>> elementsList;
std::vector<JSHandle<JSTaggedValue>> propertyList;
continuationList.reserve(16); // 16: initial capacity
elementsList.reserve(16); // 16: initial capacity
propertyList.reserve(16); // 16: initial capacity
JsonContinuation continuation(ContType::RETURN, 0);
while (true) {
while (true) {
SkipStartWhiteSpace();
Tokens token = ParseToken();
switch (token) {
case Tokens::OBJECT:
if (EmptyObjectCheck()) {
parseValue = JSHandle<JSTaggedValue>(factory_->NewJSObject(initialJSObjectClass_));
GetNextNonSpaceChar();
break;
}
continuationList.emplace_back(std::move(continuation));
continuation = JsonContinuation(ContType::OBJECT, propertyList.size());
SkipStartWhiteSpace();
if (*current_ == '"') {
propertyList.emplace_back(JSHandle<JSTaggedValue>(thread_, ParseString(true)));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
} else {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object Prop in JSON",
JSTaggedValue::Exception());
}
SkipStartWhiteSpace();
if (*current_ == ':') {
Advance();
} else {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON",
JSTaggedValue::Exception());
}
continue;
case Tokens::ARRAY:
if (EmptyArrayCheck()) {
parseValue = JSHandle<JSTaggedValue>(factory_->NewJSArray(0, initialJSArrayClass_));
GetNextNonSpaceChar();
break;
}
continuationList.emplace_back(std::move(continuation));
continuation = JsonContinuation(ContType::ARRAY, elementsList.size());
continue;
case Tokens::LITERAL_TRUE:
parseValue = JSHandle<JSTaggedValue>(thread_, ParseLiteralTrue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
case Tokens::LITERAL_FALSE:
parseValue = JSHandle<JSTaggedValue>(thread_, ParseLiteralFalse());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
case Tokens::LITERAL_NULL:
parseValue = JSHandle<JSTaggedValue>(thread_, ParseLiteralNull());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
case Tokens::NUMBER:
parseValue = JSHandle<JSTaggedValue>(thread_, ParseNumber(IsInObjOrArray(continuation.type_)));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
case Tokens::STRING:
parseValue = JSHandle<JSTaggedValue>(thread_, ParseString(IsInObjOrArray(continuation.type_)));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
break;
default:
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception());
}
break;
}
while (true) {
switch (continuation.type_) {
case ContType::RETURN:
ASSERT(continuationList.empty());
ASSERT(elementsList.empty());
ASSERT(propertyList.empty());
if (current_ <= range_) {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON",
JSTaggedValue::Exception());
}
return parseValue.GetTaggedValue();
case ContType::ARRAY: {
elementsList.emplace_back(parseValue);
SkipStartWhiteSpace();
if (*current_ == ',') {
Advance();
break;
}
parseValue = CreateJsonArray(continuation, elementsList);
if (*current_ != ']') {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON",
JSTaggedValue::Exception());
}
GetNextNonSpaceChar();
elementsList.resize(continuation.index_);
continuation = std::move(continuationList.back());
continuationList.pop_back();
continue;
}
case ContType::OBJECT: {
propertyList.emplace_back(parseValue);
SkipStartWhiteSpace();
if (*current_ == ',') {
GetNextNonSpaceChar();
if (*current_ == '"') {
propertyList.emplace_back(JSHandle<JSTaggedValue>(thread_, ParseString(true)));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
} else {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object Prop in JSON",
JSTaggedValue::Exception());
}
SkipStartWhiteSpace();
if (*current_ == ':') {
Advance();
} else {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON",
JSTaggedValue::Exception());
}
break;
}
parseValue = CreateJsonObject(continuation, propertyList);
if (*current_ == '}') {
GetNextNonSpaceChar();
} else {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON",
JSTaggedValue::Exception());
}
propertyList.resize(continuation.index_);
continuation = std::move(continuationList.back());
continuationList.pop_back();
continue;
}
}
break;
}
}
}
JSHandle<JSTaggedValue> CreateJsonArray(JsonContinuation continuation,
std::vector<JSHandle<JSTaggedValue>> &elementsList)
{
size_t start = continuation.index_;
size_t size = elementsList.size() - start;
JSHandle<JSArray> array = factory_->NewJSArray(size, initialJSArrayClass_);
JSHandle<TaggedArray> elements = factory_->NewJsonFixedArray(start, size, elementsList);
JSHandle<JSObject> obj(array);
obj->SetElements(thread_, elements);
return JSHandle<JSTaggedValue>(array);
}
JSHandle<JSTaggedValue> CreateJsonObject(JsonContinuation continuation,
std::vector<JSHandle<JSTaggedValue>> &propertyList)
{
size_t start = continuation.index_;
size_t size = propertyList.size() - start;
JSHandle<JSObject> obj = factory_->NewJSObject(initialJSObjectClass_);
for (size_t i = 0; i < size; i += 2) { // 2: prop name and value
JSTaggedValue res = ObjectFastOperator::SetPropertyByValue<true>(thread_, obj.GetTaggedValue(),
propertyList[start + i].GetTaggedValue(), propertyList[start + i + 1].GetTaggedValue());
if (res.IsHole()) {
// slow path
JSTaggedValue::SetProperty(thread_, JSHandle<JSTaggedValue>(obj), propertyList[start + i],
propertyList[start + i + 1], true);
}
}
return JSHandle<JSTaggedValue>(obj);
}
JSTaggedValue ParseNumber(bool inObjorArr = false)
{
if (inObjorArr) {
@ -116,7 +301,8 @@ protected:
int32_t fastInteger = 0;
bool isNumber = ReadNumberRange(isFast, fastInteger);
if (!isNumber) {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception());
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON Array Or Object",
JSTaggedValue::Exception());
}
if (isFast) {
return JSTaggedValue(fastInteger);
@ -143,7 +329,7 @@ protected:
}
std::string strNum(current, end_ + 1);
current_ = end_;
current_ = end_ + 1;
errno = 0; // reset errno to 0 to avoid errno has been changed
double v = std::strtod(strNum.c_str(), nullptr);
if (errno == ERANGE) {
@ -221,6 +407,7 @@ protected:
}
}
ASSERT(res.size() <= static_cast<size_t>(UINT32_MAX));
Advance();
return factory_->NewFromUtf8Literal(reinterpret_cast<const uint8_t *>(res.c_str()), res.size())
.GetTaggedValue();
}
@ -229,111 +416,6 @@ protected:
virtual JSTaggedValue ParseString(bool inObjorArr = false) = 0;
JSTaggedValue ParseArray(bool inObjorArr = false)
{
if (UNLIKELY(*range_ != ']' && !inObjorArr)) {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception());
}
Advance();
SkipStartWhiteSpace();
if (*current_ == ']') {
JSHandle<JSArray> arr = factory_->NewJSArray();
return arr.GetTaggedValue();
}
JSTaggedValue value;
std::vector<JSHandle<JSTaggedValue>> vec;
while (current_ <= range_) {
value = ParseJSONText(true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
vec.emplace_back(JSHandle<JSTaggedValue>(thread_, value));
GetNextNonSpaceChar();
if (*current_ == ',') {
Advance();
} else if (*current_ == ']') {
if (inObjorArr || current_ == range_) {
return CreateJsonArray(vec);
} else {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception());
}
}
}
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception());
}
inline JSTaggedValue CreateJsonArray(std::vector<JSHandle<JSTaggedValue>> &vec)
{
JSHandle<JSArray> array = factory_->NewJSArray();
size_t arrayLength = vec.size();
array->SetArrayLength(thread_, arrayLength);
JSHandle<TaggedArray> newElements = factory_->NewTaggedArray(arrayLength);
for (size_t i = 0; i < arrayLength; i++) {
newElements->Set(thread_, i, vec[i]);
}
JSHandle<JSObject> obj(array);
obj->SetElements(thread_, newElements);
return array.GetTaggedValue();
}
JSTaggedValue ParseObject(bool inObjorArr = false)
{
if (UNLIKELY(*range_ != '}' && !inObjorArr)) {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception());
}
JSHandle<JSFunction> proto(env_->GetObjectFunction());
JSHandle<JSObject> result = factory_->NewJSObjectByConstructor(proto);
Advance();
if (*current_ == '}') {
return result.GetTaggedValue();
}
JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
JSTaggedValue value;
while (current_ <= range_) {
SkipStartWhiteSpace();
if (*current_ == '"') {
keyHandle.Update(ParseString(true));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
} else {
if (*current_ == '}' && (inObjorArr || current_ == range_)) {
return result.GetTaggedValue();
}
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception());
}
GetNextNonSpaceChar();
if (*current_ == ':') {
Advance();
} else {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception());
}
value = ParseJSONText(true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
// fast path
JSTaggedValue res = ObjectFastOperator::SetPropertyByValue<true>(thread_, result.GetTaggedValue(),
keyHandle.GetTaggedValue(), value);
if (res.IsHole()) {
// slow path
JSTaggedValue::SetProperty(thread_, JSHandle<JSTaggedValue>(result), keyHandle,
JSHandle<JSTaggedValue>(thread_, value), true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
}
GetNextNonSpaceChar();
if (*current_ == ',') {
Advance();
} else if (*current_ == '}') {
if (inObjorArr || current_ == range_) {
return result.GetTaggedValue();
} else {
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception());
}
}
}
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception());
}
void SkipEndWhiteSpace()
{
while (current_ != end_) {
@ -401,7 +483,7 @@ protected:
if (UNLIKELY(remainingLength < 3)) { // 3: literalTrue length - 1
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception());
}
bool isMatch = MatchText(literalTrue, 3); // 3: literalTrue length - 1
bool isMatch = MatchText(literalTrue, 4); // 4: literalTrue length
if (LIKELY(isMatch)) {
return JSTaggedValue::True();
}
@ -415,7 +497,7 @@ protected:
if (UNLIKELY(remainingLength < 4)) { // 4: literalFalse length - 1
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception());
}
bool isMatch = MatchText(literalFalse, 4); // 4: literalFalse length - 1
bool isMatch = MatchText(literalFalse, 5); // 5: literalFalse length
if (LIKELY(isMatch)) {
return JSTaggedValue::False();
}
@ -429,7 +511,7 @@ protected:
if (UNLIKELY(remainingLength < 3)) { // 3: literalNull length - 1
THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception());
}
bool isMatch = MatchText(literalNull, 3); // 3: literalNull length - 1
bool isMatch = MatchText(literalNull, 4); // 4: literalNull length
if (LIKELY(isMatch)) {
return JSTaggedValue::Null();
}
@ -439,7 +521,7 @@ protected:
bool MatchText(const char *str, uint32_t matchLen)
{
// first char is already matched
for (uint32_t pos = 1; pos <= matchLen; ++pos) {
for (uint32_t pos = 1; pos < matchLen; ++pos) {
if (current_[pos] != str[pos]) {
return false;
}
@ -472,7 +554,7 @@ protected:
i = (i * 10) + ((*current) - '0');
}
fastInteger = i * sign;
current_ = advance - 1;
current_ = advance;
return true;
}
isFast = false;
@ -679,6 +761,8 @@ protected:
JSThread *thread_ {nullptr};
ObjectFactory *factory_ {nullptr};
GlobalEnv *env_ {nullptr};
JSHandle<JSHClass> initialJSArrayClass_;
JSHandle<JSHClass> initialJSObjectClass_;
};
class Utf8JsonParser : public JsonParser<uint8_t> {
@ -692,10 +776,11 @@ public:
JSHandle<JSTaggedValue> Parse(EcmaString *str)
{
ASSERT(str != nullptr);
uint32_t len = EcmaStringAccessor(str).GetLength();
auto stringAccessor = EcmaStringAccessor(str);
uint32_t len = stringAccessor.GetLength();
ASSERT(len != UINT32_MAX);
CVector<uint8_t> buf(len + 1);
EcmaStringAccessor(str).WriteToFlatUtf8(buf.data(), len);
stringAccessor.WriteToFlatUtf8(buf.data(), len);
Text begin = buf.data();
return Launch(begin, begin + len);
}
@ -720,7 +805,7 @@ private:
ASSERT(strLength <= static_cast<size_t>(UINT32_MAX));
JSTaggedValue res = factory_->NewCompressedUtf8(
reinterpret_cast<const uint8_t *>(current_), strLength).GetTaggedValue();
current_ = end_;
current_ = end_ + 1;
return res;
}
} else {
@ -733,6 +818,7 @@ private:
}
if (LIKELY(isFastString)) {
std::string_view value(reinterpret_cast<const char *>(current_), end_ - current_);
current_ = end_ + 1;
ASSERT(value.size() <= static_cast<size_t>(UINT32_MAX));
return factory_->NewFromUtf8LiteralCompress(
reinterpret_cast<const uint8_t *>(value.data()), value.size()).GetTaggedValue();
@ -812,13 +898,13 @@ private:
if (isFastString) {
if (isAscii) {
std::string value(current_, end_); // from uint16_t* to std::string, can't use std::string_view
current_ = end_;
current_ = end_ + 1;
ASSERT(value.size() <= static_cast<size_t>(UINT32_MAX));
return factory_->NewFromUtf8LiteralCompress(
reinterpret_cast<const uint8_t *>(value.c_str()), value.size()).GetTaggedValue();
}
std::u16string_view value(reinterpret_cast<const char16_t *>(current_), end_ - current_);
current_ = end_;
current_ = end_ + 1;
ASSERT(value.size() <= static_cast<size_t>(UINT32_MAX));
return factory_->NewFromUtf16LiteralNotCompress(
reinterpret_cast<const uint16_t *>(value.data()), value.size()).GetTaggedValue();
@ -835,11 +921,13 @@ private:
if (isAscii) {
std::string value(current_, end_); // from uint16_t* to std::string, can't use std::string_view
ASSERT(value.size() <= static_cast<size_t>(UINT32_MAX));
current_ = end_ + 1;
return factory_->NewFromUtf8LiteralCompress(
reinterpret_cast<const uint8_t *>(value.c_str()), value.size()).GetTaggedValue();
}
std::u16string_view value(reinterpret_cast<const char16_t *>(current_), end_ - current_);
ASSERT(value.size() <= static_cast<size_t>(UINT32_MAX));
current_ = end_ + 1;
return factory_->NewFromUtf16LiteralNotCompress(
reinterpret_cast<const uint16_t *>(value.data()), value.size()).GetTaggedValue();
}

View File

@ -125,7 +125,7 @@ HWTEST_F_L0(JsonParserTest, Parser_003)
JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII(
"\t\r \n{\t\r \n \"json\"\t\r\n:\t\r \n{\t\r \n}\t\r \n,\t\r \n \"prop2\"\t\r \n:\t\r \n [\t\r \nfalse\t\r"
"\n,\t\r \nnull\t\r \ntrue\t\r,123.456\t\r \n]\t\r \n}\t\r \n"));
"\n,\t\r \nnull\t\r, \ntrue\t\r,123.456\t\r \n]\t\r \n}\t\r \n"));
JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg)); // JSON Object
JSHandle<JSTaggedValue> result = parser.Parse(*handleStr);
EXPECT_TRUE(result->IsECMAObject());

View File

@ -671,6 +671,32 @@ JSHandle<JSArray> ObjectFactory::NewJSArray()
return JSHandle<JSArray>(NewJSObjectByConstructor(function));
}
JSHandle<JSArray> ObjectFactory::NewJSArray(size_t length, JSHandle<JSHClass> &hclass)
{
JSHandle<JSObject> obj = NewJSObject(hclass);
JSArray::Cast(*obj)->SetLength(length);
JSArray::Cast(*obj)->SetTrackInfo(thread_, JSTaggedValue::Undefined());
auto accessor = thread_->GlobalConstants()->GetArrayLengthAccessor();
JSArray::Cast(*obj)->SetPropertyInlinedProps(thread_, JSArray::LENGTH_INLINE_PROPERTY_INDEX, accessor);
return JSHandle<JSArray>(obj);
}
JSHandle<TaggedArray> ObjectFactory::NewJsonFixedArray(size_t start, size_t length,
const std::vector<JSHandle<JSTaggedValue>> &vec)
{
if (length == 0) {
return EmptyArray();
}
MemSpaceType spaceType = length < LENGTH_THRESHOLD ? MemSpaceType::SEMI_SPACE : MemSpaceType::OLD_SPACE;
JSHandle<TaggedArray> array = NewTaggedArrayWithoutInit(length, spaceType);
array->SetExtraLength(0);
for (size_t i = 0; i < length; i++) {
array->Set(thread_, i, vec[start + i]);
}
return array;
}
JSHandle<JSForInIterator> ObjectFactory::NewJSForinIterator(const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> keys,
const JSHandle<JSTaggedValue> cachedHclass)

View File

@ -317,6 +317,9 @@ public:
JSHandle<job::PendingJob> NewPendingJob(const JSHandle<JSFunction> &func, const JSHandle<TaggedArray> &argv);
JSHandle<JSArray> NewJSArray();
JSHandle<JSArray> NewJSArray(size_t length, JSHandle<JSHClass> &hclass);
JSHandle<TaggedArray> NewJsonFixedArray(size_t start, size_t length,
const std::vector<JSHandle<JSTaggedValue>> &vec);
JSHandle<JSProxy> NewJSProxy(const JSHandle<JSTaggedValue> &target, const JSHandle<JSTaggedValue> &handler);
JSHandle<JSRealm> NewJSRealm();

View File

@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
SyntaxError: Unexpected Array in JSON
SyntaxError: Unexpected Number in JSON Array Or Object
exception_case1.js:18:18
exception_case1.js:20:1

View File

@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
SyntaxError: Unexpected Array in JSON
SyntaxError: Unexpected Number in JSON Array Or Object
exception_case11.ts:22:22
exception_case11.ts:25:1

View File

@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
SyntaxError: Unexpected Array in JSON
SyntaxError: Unexpected Number in JSON Array Or Object
exception_case12.ts:21:21
exception_case12.ts:24:1

View File

@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
SyntaxError: Unexpected Array in JSON
SyntaxError: Unexpected Number in JSON Array Or Object
exception_case8.js:18:18
exception_case8.js:22:22
exception_case8.js:24:1

View File

@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
SyntaxError: Unexpected Array in JSON
SyntaxError: Unexpected Number in JSON Array Or Object
at foo1 (maybe inlined). depth: 2
at foo3 (maybe inlined). depth: 1
at foo4 (maybe inlined). depth: 0

View File

@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
SyntaxError: Unexpected Array in JSON
SyntaxError: Unexpected Number in JSON Array Or Object
at Student (hidden:26:26)
at foo1 (maybe inlined). depth: 1
at foo2 (maybe inlined). depth: 0