Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -298,10 +298,12 @@ 4BB25E8B139C388A00F574EA /* OFString+Serialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB25E85139C388A00F574EA /* OFString+Serialization.m */; }; 4BB25E8C139C388A00F574EA /* OFXMLElement+Serialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BB25E86139C388A00F574EA /* OFXMLElement+Serialization.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BB25E8D139C388A00F574EA /* OFXMLElement+Serialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB25E87139C388A00F574EA /* OFXMLElement+Serialization.m */; }; 4BB524C1143D1E4E0085FBCC /* OFProcess.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BB524BF143D1E4E0085FBCC /* OFProcess.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BB524C2143D1E4E0085FBCC /* OFProcess.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB524C0143D1E4E0085FBCC /* OFProcess.m */; settings = {ATTRIBUTES = (Public, ); }; }; + 4BC090441584F6760040640F /* OFInvalidJSONException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BC090421584F6760040640F /* OFInvalidJSONException.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4BC090451584F6760040640F /* OFInvalidJSONException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BC090431584F6760040640F /* OFInvalidJSONException.m */; }; 4BD653C5143B8489006182F0 /* OFTCPSocket+SOCKS5.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD653C3143B8489006182F0 /* OFTCPSocket+SOCKS5.h */; }; 4BD653C6143B8489006182F0 /* OFTCPSocket+SOCKS5.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD653C4143B8489006182F0 /* OFTCPSocket+SOCKS5.m */; }; 4BD98C03133814220048DD5B /* objfw-defs.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD98C011338140B0048DD5B /* objfw-defs.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BDF37B51338055600F9A81A /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BDF37B41338055600F9A81A /* config.h */; }; 4BF33AFB133807590059CEF7 /* ObjFW.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B3D23761337FBC800DD29B8 /* ObjFW.framework */; }; @@ -661,10 +663,12 @@ 4BB50DD012F863C700C9393F /* of_asprintf.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = of_asprintf.m; path = src/of_asprintf.m; sourceTree = SOURCE_ROOT; }; 4BB524BF143D1E4E0085FBCC /* OFProcess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFProcess.h; path = src/OFProcess.h; sourceTree = ""; }; 4BB524C0143D1E4E0085FBCC /* OFProcess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFProcess.m; path = src/OFProcess.m; sourceTree = ""; }; 4BBA36C411406AB700CBA3AC /* atomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = atomic.h; path = src/atomic.h; sourceTree = ""; }; 4BBA36C511406AB700CBA3AC /* macros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macros.h; path = src/macros.h; sourceTree = ""; }; + 4BC090421584F6760040640F /* OFInvalidJSONException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFInvalidJSONException.h; path = src/exceptions/OFInvalidJSONException.h; sourceTree = ""; }; + 4BC090431584F6760040640F /* OFInvalidJSONException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFInvalidJSONException.m; path = src/exceptions/OFInvalidJSONException.m; sourceTree = ""; }; 4BD653C3143B8489006182F0 /* OFTCPSocket+SOCKS5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFTCPSocket+SOCKS5.h"; path = "src/OFTCPSocket+SOCKS5.h"; sourceTree = ""; }; 4BD653C4143B8489006182F0 /* OFTCPSocket+SOCKS5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OFTCPSocket+SOCKS5.m"; path = "src/OFTCPSocket+SOCKS5.m"; sourceTree = ""; }; 4BD86D801237A6C600ED9912 /* OFBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFBlock.h; path = src/OFBlock.h; sourceTree = SOURCE_ROOT; }; 4BD86D811237A6C600ED9912 /* OFBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFBlock.m; path = src/OFBlock.m; sourceTree = SOURCE_ROOT; }; 4BD98C011338140B0048DD5B /* objfw-defs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objfw-defs.h"; path = "src/objfw-defs.h"; sourceTree = SOURCE_ROOT; }; @@ -792,10 +796,12 @@ 4B17FF92133A317B003E6DCD /* OFInvalidArgumentException.m */, 4B17FF97133A3242003E6DCD /* OFInvalidEncodingException.h */, 4B17FF98133A3243003E6DCD /* OFInvalidEncodingException.m */, 4B17FF9B133A32B7003E6DCD /* OFInvalidFormatException.h */, 4B17FF9C133A32B8003E6DCD /* OFInvalidFormatException.m */, + 4BC090421584F6760040640F /* OFInvalidJSONException.h */, + 4BC090431584F6760040640F /* OFInvalidJSONException.m */, 4B17FFB7133A380F003E6DCD /* OFInvalidServerReplyException.h */, 4B17FFB8133A3813003E6DCD /* OFInvalidServerReplyException.m */, 4B29BC5D133AC9C40004B236 /* OFLinkFailedException.h */, 4B29BC5E133AC9C40004B236 /* OFLinkFailedException.m */, 4B90B79C133AD87D00BD33CB /* OFListenFailedException.h */, @@ -1229,10 +1235,11 @@ 4B17FFAD133A3591003E6DCD /* OFHTTPRequestFailedException.h in Headers */, 4B17FFA5133A3411003E6DCD /* OFInitializationFailedException.h in Headers */, 4B17FF93133A317C003E6DCD /* OFInvalidArgumentException.h in Headers */, 4B17FF99133A3245003E6DCD /* OFInvalidEncodingException.h in Headers */, 4B17FF9D133A32BA003E6DCD /* OFInvalidFormatException.h in Headers */, + 4BC090441584F6760040640F /* OFInvalidJSONException.h in Headers */, 4B17FFB9133A381D003E6DCD /* OFInvalidServerReplyException.h in Headers */, 4B29BC61133AC9CB0004B236 /* OFLinkFailedException.h in Headers */, 4B90B7A6133AD87D00BD33CB /* OFListenFailedException.h in Headers */, 4B17FFA1133A3335003E6DCD /* OFMalformedXMLException.h in Headers */, 4B17FF8F133A3036003E6DCD /* OFMemoryNotPartOfObjectException.h in Headers */, @@ -1522,10 +1529,11 @@ 4B17FFAE133A3591003E6DCD /* OFHTTPRequestFailedException.m in Sources */, 4B17FFA6133A3411003E6DCD /* OFInitializationFailedException.m in Sources */, 4B17FF94133A317C003E6DCD /* OFInvalidArgumentException.m in Sources */, 4B17FF9A133A3245003E6DCD /* OFInvalidEncodingException.m in Sources */, 4B17FF9E133A32BA003E6DCD /* OFInvalidFormatException.m in Sources */, + 4BC090451584F6760040640F /* OFInvalidJSONException.m in Sources */, 4B17FFBA133A381D003E6DCD /* OFInvalidServerReplyException.m in Sources */, 4B29BC62133AC9CB0004B236 /* OFLinkFailedException.m in Sources */, 4B90B7A7133AD87D00BD33CB /* OFListenFailedException.m in Sources */, 4B17FFA2133A3335003E6DCD /* OFMalformedXMLException.m in Sources */, 4B17FF90133A3036003E6DCD /* OFMemoryNotPartOfObjectException.m in Sources */, Index: src/OFString+JSONValue.m ================================================================== --- src/OFString+JSONValue.m +++ src/OFString+JSONValue.m @@ -25,28 +25,35 @@ #import "OFArray.h" #import "OFDictionary.h" #import "OFNumber.h" #import "OFNull.h" -#import "OFInvalidEncodingException.h" +#import "OFInvalidJSONException.h" #import "macros.h" int _OFString_JSONValue_reference; -static id nextObject(const char *restrict *, const char*); +static id nextObject(const char *restrict *, const char*, + size_t *restrict line); static void -skipWhitespaces(const char *restrict *pointer, const char *stop) +skipWhitespaces(const char *restrict *pointer, const char *stop, + size_t *restrict line) { while (*pointer < stop && (**pointer == ' ' || **pointer == '\t' || - **pointer == '\r' || **pointer == '\n')) + **pointer == '\r' || **pointer == '\n')) { + if (**pointer == '\n') + (*line)++; + (*pointer)++; + } } static void -skipComment(const char *restrict *pointer, const char *stop) +skipComment(const char *restrict *pointer, const char *stop, + size_t *restrict line) { if (**pointer != '/') return; if (*pointer + 1 >= stop) @@ -64,37 +71,43 @@ (*pointer)++; return; } lastIsAsterisk = (**pointer == '*'); + + if (**pointer == '\n') + (*line)++; (*pointer)++; } - } else { + } else if (**pointer == '/') { (*pointer)++; while (*pointer < stop) { if (**pointer == '\r' || **pointer == '\n') { (*pointer)++; + (*line)++; return; } (*pointer)++; } - } + } else + (*pointer)--; } static void -skipWhitespacesAndComments(const char *restrict *pointer, const char *stop) +skipWhitespacesAndComments(const char *restrict *pointer, const char *stop, + size_t *restrict line) { const char *old = NULL; while (old != *pointer) { old = *pointer; - skipWhitespaces(pointer, stop); - skipComment(pointer, stop); + skipWhitespaces(pointer, stop, line); + skipComment(pointer, stop, line); } } static inline uint16_t parseUnicodeEscape(const char *pointer, const char *stop) @@ -124,11 +137,12 @@ return ret; } static inline OFString* -parseString(const char *restrict *pointer, const char *stop) +parseString(const char *restrict *pointer, const char *stop, + size_t *restrict line) { char *buffer; size_t i = 0; char delimiter = **pointer; @@ -231,16 +245,19 @@ break; case '\r': (*pointer)++; - if (*pointer < stop && **pointer == '\n') + if (*pointer < stop && **pointer == '\n') { (*pointer)++; + (*line)++; + } break; case '\n': (*pointer)++; + (*line)++; break; default: free(buffer); return nil; } @@ -258,10 +275,11 @@ (*pointer)++; return ret; /* Newlines in strings are disallowed */ } else if (**pointer == '\n' || **pointer == '\r') { + (*line)++; free(buffer); return nil; } else { buffer[i++] = **pointer; (*pointer)++; @@ -371,49 +389,50 @@ */ return nil; } static inline OFMutableArray* -parseArray(const char *restrict *pointer, const char *stop) +parseArray(const char *restrict *pointer, const char *stop, + size_t *restrict line) { OFMutableArray *array = [OFMutableArray array]; if (++(*pointer) >= stop) return nil; while (**pointer != ']') { id object; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; if (**pointer == ']') break; if (**pointer == ',') { (*pointer)++; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop || **pointer != ']') return nil; break; } - if ((object = nextObject(pointer, stop)) == nil) + if ((object = nextObject(pointer, stop, line)) == nil) return nil; [array addObject: object]; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; if (**pointer == ',') { (*pointer)++; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; } else if (**pointer != ']') return nil; @@ -423,70 +442,71 @@ return array; } static inline OFMutableDictionary* -parseDictionary(const char *restrict *pointer, const char *stop) +parseDictionary(const char *restrict *pointer, const char *stop, + size_t *restrict line) { OFMutableDictionary *dictionary = [OFMutableDictionary dictionary]; if (++(*pointer) >= stop) return nil; while (**pointer != '}') { id key, object; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; if (**pointer == '}') break; if (**pointer == ',') { (*pointer)++; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop || **pointer != '}') return nil; break; } - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer + 1 >= stop) return nil; if ((**pointer >= 'a' && **pointer <= 'z') || (**pointer >= 'A' && **pointer <= 'Z') || **pointer == '_' || **pointer == '$' || **pointer == '\\') key = parseIdentifier(pointer, stop); else - key = nextObject(pointer, stop); + key = nextObject(pointer, stop, line); if (key == nil) return nil; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer + 1 >= stop || **pointer != ':') return nil; (*pointer)++; - if ((object = nextObject(pointer, stop)) == nil) + if ((object = nextObject(pointer, stop, line)) == nil) return nil; [dictionary setObject: object forKey: key]; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; if (**pointer == ',') { (*pointer)++; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; } else if (**pointer != '}') return nil; @@ -496,11 +516,12 @@ return dictionary; } static inline OFNumber* -parseNumber(const char *restrict *pointer, const char *stop) +parseNumber(const char *restrict *pointer, const char *stop, + size_t *restrict line) { BOOL isHex = (*pointer + 1 < stop && (*pointer)[1] == 'x'); BOOL hasDecimal = NO; size_t i; OFString *string; @@ -511,12 +532,16 @@ hasDecimal = YES; if ((*pointer)[i] == ' ' || (*pointer)[i] == '\t' || (*pointer)[i] == '\r' || (*pointer)[i] == '\n' || (*pointer)[i] == ',' || (*pointer)[i] == ']' || - (*pointer)[i] == '}') + (*pointer)[i] == '}') { + if ((*pointer)[i] == '\n') + (*line)++; + break; + } } string = [[OFString alloc] initWithUTF8String: *pointer length: i]; *pointer += i; @@ -537,25 +562,26 @@ return number; } static id -nextObject(const char *restrict *pointer, const char *stop) +nextObject(const char *restrict *pointer, const char *stop, + size_t *restrict line) { - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; switch (**pointer) { case '"': case '\'': - return parseString(pointer, stop); + return parseString(pointer, stop, line); case '[': - return parseArray(pointer, stop); + return parseArray(pointer, stop, line); case '{': - return parseDictionary(pointer, stop); + return parseDictionary(pointer, stop, line); case 't': if (*pointer + 3 >= stop) return nil; if (memcmp(*pointer, "true", 4)) @@ -594,11 +620,11 @@ case '7': case '8': case '9': case '-': case '.': - return parseNumber(pointer, stop); + return parseNumber(pointer, stop, line); default: return nil; } } @@ -606,15 +632,17 @@ - (id)JSONValue { const char *pointer = [self UTF8String]; const char *stop = pointer + [self UTF8StringLength]; id object; + size_t line = 1; - object = nextObject(&pointer, stop); - skipWhitespacesAndComments(&pointer, stop); + object = nextObject(&pointer, stop, &line); + skipWhitespacesAndComments(&pointer, stop, &line); if (pointer < stop || object == nil) - @throw [OFInvalidEncodingException exceptionWithClass: isa]; + @throw [OFInvalidJSONException exceptionWithClass: isa + line: line]; return object; } @end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -93,10 +93,11 @@ #import "OFHTTPRequestFailedException.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidEncodingException.h" #import "OFInvalidFormatException.h" +#import "OFInvalidJSONException.h" #import "OFInvalidServerReplyException.h" #import "OFLinkFailedException.h" #import "OFListenFailedException.h" #import "OFMalformedXMLException.h" #import "OFMemoryNotPartOfObjectException.h" Index: src/exceptions/Makefile ================================================================== --- src/exceptions/Makefile +++ src/exceptions/Makefile @@ -26,10 +26,11 @@ OFHashAlreadyCalculatedException.m \ OFInitializationFailedException.m \ OFInvalidArgumentException.m \ OFInvalidEncodingException.m \ OFInvalidFormatException.m \ + OFInvalidJSONException.m \ OFInvalidServerReplyException.m \ OFLinkFailedException.m \ OFListenFailedException.m \ OFMalformedXMLException.m \ OFMemoryNotPartOfObjectException.m \ ADDED src/exceptions/OFInvalidJSONException.h Index: src/exceptions/OFInvalidJSONException.h ================================================================== --- src/exceptions/OFInvalidJSONException.h +++ src/exceptions/OFInvalidJSONException.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFException.h" + +/** + * \brief An exception indicating a JSON representation is invalid. + */ +@interface OFInvalidJSONException: OFException +{ + size_t line; +} + +#ifdef OF_HAVE_PROPERTIES +@property (readonly) size_t line; +#endif + +/** + * \param class_ The class of the object which caused the exception + * \param line The line in which the parsing error encountered + * \return A new invalid JSON exception + */ ++ exceptionWithClass: (Class)class_ + line: (size_t)line; + +/** + * Initializes an already allocated invalid JSON exception. + * + * \param class_ The class of the object which caused the exception + * \param line The line in which the parsing error encountered + * \return An initialized invalid JSON exception + */ +- initWithClass: (Class)class_ + line: (size_t)line; + +/** + * \return The line in which parsing the JSON representation failed + */ +- (size_t)line; +@end ADDED src/exceptions/OFInvalidJSONException.m Index: src/exceptions/OFInvalidJSONException.m ================================================================== --- src/exceptions/OFInvalidJSONException.m +++ src/exceptions/OFInvalidJSONException.m @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFInvalidJSONException.h" +#import "OFString.h" + +#import "OFNotImplementedException.h" + +@implementation OFInvalidJSONException ++ exceptionWithClass: (Class)class_ + line: (size_t)line +{ + return [[[self alloc] initWithClass: class_ + line: line] autorelease]; +} + +- initWithClass: (Class)class_ + line: (size_t)line_ +{ + self = [super initWithClass: class_]; + + line = line_; + + return self; +} + +- init +{ + Class c = isa; + [self release]; + @throw [OFNotImplementedException exceptionWithClass: c + selector: _cmd]; +} + +- (OFString*)description +{ + if (description != nil) + return description; + + description = [[OFString alloc] initWithFormat: + @"The JSON representation class %@ tried to parse is invalid in " + @"line %zd!", inClass, line]; + + return description; +} + +- (size_t)line +{ + return line; +} +@end Index: tests/OFJSONTests.m ================================================================== --- tests/OFJSONTests.m +++ tests/OFJSONTests.m @@ -21,11 +21,11 @@ #import "OFDictionary.h" #import "OFNumber.h" #import "OFNull.h" #import "OFAutoreleasePool.h" -#import "OFInvalidEncodingException.h" +#import "OFInvalidJSONException.h" #import "TestsAppDelegate.h" static OFString *module = @"OFJSON"; @@ -49,17 +49,17 @@ TEST(@"-[JSONValue #1]", [[s JSONValue] isEqual: d]) TEST(@"-[JSONRepresentation]", [[d JSONRepresentation] isEqual: @"{\"foo\":\"ba\\r\",\"x\":[0.5,15,null,\"foo\",false]}"]) - EXPECT_EXCEPTION(@"-[JSONValue #2]", OFInvalidEncodingException, + EXPECT_EXCEPTION(@"-[JSONValue #2]", OFInvalidJSONException, [@"{" JSONValue]) - EXPECT_EXCEPTION(@"-[JSONValue #3]", OFInvalidEncodingException, + EXPECT_EXCEPTION(@"-[JSONValue #3]", OFInvalidJSONException, [@"]" JSONValue]) - EXPECT_EXCEPTION(@"-[JSONValue #4]", OFInvalidEncodingException, + EXPECT_EXCEPTION(@"-[JSONValue #4]", OFInvalidJSONException, [@"bar" JSONValue]) - EXPECT_EXCEPTION(@"-[JSONValue #5]", OFInvalidEncodingException, + EXPECT_EXCEPTION(@"-[JSONValue #5]", OFInvalidJSONException, [@"[\"a\" \"b\"]" JSONValue]) [pool drain]; } @end