/** * @PROJECT AerScript Interpreter * @COPYRIGHT See COPYING in the top level directory * @FILE modules/json/json.c * @DESCRIPTION JavaScript Object Notation (JSON) module for AerScript Interpreter * @DEVELOPERS Symisc Systems * Rafal Kupiec */ #include #include #include "json.h" #ifdef __WINNT__ #include #endif /* * JSON_HEX_TAG. * Expand the value of JSON_HEX_TAG defined in ph7Int.h. */ static void PH7_JSON_HEX_TAG_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_HEX_TAG); } /* * JSON_HEX_AMP. * Expand the value of JSON_HEX_AMP defined in ph7Int.h. */ static void PH7_JSON_HEX_AMP_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_HEX_AMP); } /* * JSON_HEX_APOS. * Expand the value of JSON_HEX_APOS defined in ph7Int.h. */ static void PH7_JSON_HEX_APOS_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_HEX_APOS); } /* * JSON_HEX_QUOT. * Expand the value of JSON_HEX_QUOT defined in ph7Int.h. */ static void PH7_JSON_HEX_QUOT_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_HEX_QUOT); } /* * JSON_FORCE_OBJECT. * Expand the value of JSON_FORCE_OBJECT defined in ph7Int.h. */ static void PH7_JSON_FORCE_OBJECT_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_FORCE_OBJECT); } /* * JSON_NUMERIC_CHECK. * Expand the value of JSON_NUMERIC_CHECK defined in ph7Int.h. */ static void PH7_JSON_NUMERIC_CHECK_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_NUMERIC_CHECK); } /* * JSON_BIGINT_AS_STRING. * Expand the value of JSON_BIGINT_AS_STRING defined in ph7Int.h. */ static void PH7_JSON_BIGINT_AS_STRING_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_BIGINT_AS_STRING); } /* * JSON_PRETTY_PRINT. * Expand the value of JSON_PRETTY_PRINT defined in ph7Int.h. */ static void PH7_JSON_PRETTY_PRINT_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_PRETTY_PRINT); } /* * JSON_UNESCAPED_SLASHES. * Expand the value of JSON_UNESCAPED_SLASHES defined in ph7Int.h. */ static void PH7_JSON_UNESCAPED_SLASHES_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_UNESCAPED_SLASHES); } /* * JSON_UNESCAPED_UNICODE. * Expand the value of JSON_UNESCAPED_UNICODE defined in ph7Int.h. */ static void PH7_JSON_UNESCAPED_UNICODE_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_UNESCAPED_UNICODE); } /* * JSON_ERROR_NONE. * Expand the value of JSON_ERROR_NONE defined in ph7Int.h. */ static void PH7_JSON_ERROR_NONE_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_ERROR_NONE); } /* * JSON_ERROR_DEPTH. * Expand the value of JSON_ERROR_DEPTH defined in ph7Int.h. */ static void PH7_JSON_ERROR_DEPTH_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_ERROR_DEPTH); } /* * JSON_ERROR_STATE_MISMATCH. * Expand the value of JSON_ERROR_STATE_MISMATCH defined in ph7Int.h. */ static void PH7_JSON_ERROR_STATE_MISMATCH_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_ERROR_STATE_MISMATCH); } /* * JSON_ERROR_CTRL_CHAR. * Expand the value of JSON_ERROR_CTRL_CHAR defined in ph7Int.h. */ static void PH7_JSON_ERROR_CTRL_CHAR_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_ERROR_CTRL_CHAR); } /* * JSON_ERROR_SYNTAX. * Expand the value of JSON_ERROR_SYNTAX defined in ph7Int.h. */ static void PH7_JSON_ERROR_SYNTAX_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_ERROR_SYNTAX); } /* * JSON_ERROR_UTF8. * Expand the value of JSON_ERROR_UTF8 defined in ph7Int.h. */ static void PH7_JSON_ERROR_UTF8_Const(ph7_value *pVal, void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal, JSON_ERROR_UTF8); } /* * Default container nesting depth, also used as the json_encode() limit. */ #define JSON_MAX_DEPTH 32 /* * Hard limit for the json_decode() recursion depth. A larger $depth argument * is rejected with JSON_ERROR_DEPTH before any parsing takes place, because * the decoder is recursive and must not be allowed to exhaust the C stack. */ #define JSON_MAX_DECODE_DEPTH 512 /* * Append a JSON string literal to the current call result. * Double quotes and backslashes are always escaped, control characters are * encoded according to the JSON grammar and the JSON_HEX_* options are honored. * The input is processed by length, so it may contain embedded NUL bytes. */ static void VmJsonAppendString(ph7_context *pCtx, const char *zIn, sxu32 nByte, sxi32 iFlags) { char zEscape[8]; sxu32 i; sxi32 c; /* Append the opening double quote */ ph7_result_string(pCtx, "\"", (int)sizeof(char)); for(i = 0; i < nByte; i++) { c = (unsigned char)zIn[i]; if(c == '<' && (iFlags & JSON_HEX_TAG)) { ph7_result_string(pCtx, "\\u003C", (int)sizeof("\\u003C") - 1); } else if(c == '>' && (iFlags & JSON_HEX_TAG)) { ph7_result_string(pCtx, "\\u003E", (int)sizeof("\\u003E") - 1); } else if(c == '&' && (iFlags & JSON_HEX_AMP)) { ph7_result_string(pCtx, "\\u0026", (int)sizeof("\\u0026") - 1); } else if(c == '\'' && (iFlags & JSON_HEX_APOS)) { ph7_result_string(pCtx, "\\u0027", (int)sizeof("\\u0027") - 1); } else if(c == '"' && (iFlags & JSON_HEX_QUOT)) { ph7_result_string(pCtx, "\\u0022", (int)sizeof("\\u0022") - 1); } else if(c == '"') { ph7_result_string(pCtx, "\\\"", (int)sizeof("\\\"") - 1); } else if(c == '\\') { ph7_result_string(pCtx, "\\\\", (int)sizeof("\\\\") - 1); } else if(c == '/') { if(iFlags & JSON_UNESCAPED_SLASHES) { ph7_result_string(pCtx, "/", (int)sizeof(char)); } else { ph7_result_string(pCtx, "\\/", (int)sizeof("\\/") - 1); } } else if(c == '\b') { ph7_result_string(pCtx, "\\b", (int)sizeof("\\b") - 1); } else if(c == '\f') { ph7_result_string(pCtx, "\\f", (int)sizeof("\\f") - 1); } else if(c == '\n') { ph7_result_string(pCtx, "\\n", (int)sizeof("\\n") - 1); } else if(c == '\r') { ph7_result_string(pCtx, "\\r", (int)sizeof("\\r") - 1); } else if(c == '\t') { ph7_result_string(pCtx, "\\t", (int)sizeof("\\t") - 1); } else if(c < 0x20) { snprintf(zEscape, sizeof(zEscape), "\\u%04X", c); ph7_result_string(pCtx, zEscape, (int)SyStrlen(zEscape)); } else { ph7_result_string(pCtx, &zIn[i], (int)sizeof(char)); } } /* Append the closing double quote */ ph7_result_string(pCtx, "\"", (int)sizeof(char)); } /* * Format a real number using the shortest representation that parses back * to the exact same double. */ static sxi32 VmJsonFormatReal(char *zBuf, sxu32 nBufSize, ph7_real rVal) { char *zEnd; sxi32 nPrec, nByte, rc = SXERR_ABORT; #ifdef __WINNT__ _locale_t pLocale; pLocale = _create_locale(LC_NUMERIC, "C"); if(pLocale == (_locale_t)0) { return SXERR_ABORT; } #else locale_t pLocale, pOld; pLocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); if(pLocale == (locale_t)0) { return SXERR_ABORT; } /* snprintf has no locale argument; change only this thread, never the host locale. */ pOld = uselocale(pLocale); if(pOld == (locale_t)0) { freelocale(pLocale); return SXERR_ABORT; } #endif for(nPrec = 15; nPrec < 18; ++nPrec) { #ifdef __WINNT__ nByte = _snprintf_l(zBuf, (size_t)nBufSize, "%.*g", pLocale, nPrec, (double)rVal); #else nByte = snprintf(zBuf, (size_t)nBufSize, "%.*g", nPrec, (double)rVal); #endif if(nByte < 0 || (sxu32)nByte >= nBufSize) { break; } #ifdef __WINNT__ if(_strtod_l(zBuf, &zEnd, pLocale) == (double)rVal && zEnd[0] == '\0') { #else if(strtod_l(zBuf, &zEnd, pLocale) == (double)rVal && zEnd[0] == '\0') { #endif rc = SXRET_OK; break; } } #ifdef __WINNT__ _free_locale(pLocale); #else /* Restore before freeing the locale, including formatting failures. */ if(uselocale(pOld) == (locale_t)0) { /* Do not free a locale that may still be active. */ return SXERR_ABORT; } freelocale(pLocale); #endif return rc; } /* * Recursion probe used to decide whether a hashmap is a JSON array * (all integer keys exactly 0..n-1 in iteration order) or a JSON object. */ struct json_array_probe { sxi64 iNext; /* Expected key */ sxi32 bList; /* FALSE as soon as a non-list key is found */ }; static int VmJsonArrayProbe(ph7_value *pKey, ph7_value *pValue, void *pUserData) { struct json_array_probe *pProbe = (struct json_array_probe *)pUserData; SXUNUSED(pValue); /* cc warning */ if(pProbe->bList == 0) { return PH7_OK; } if((pKey->nType & MEMOBJ_INT) == 0 || pKey->x.iVal != pProbe->iNext) { pProbe->bList = 0; } else { pProbe->iNext++; } return PH7_OK; } /* * Returns the JSON representation of a value.In other word perform a JSON encoding operation. * According to wikipedia * JSON's basic types are: * Number (double precision floating-point format in JavaScript, generally depends on implementation) * String (double-quoted Unicode, with backslash escaping) * Boolean (true or false) * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values * do not need to be of the same type) * Object (an unordered collection of key:value pairs with the ':' character separating the key * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should * be distinct from each other) * null (empty) * Non-significant white space may be added freely around the "structural characters" * (i.e. the brackets "[{]}", colon ":" and comma ","). */ static sxi32 VmJsonEncode( ph7_value *pIn, /* Encode this value */ json_private_data *pData /* Context data */ ) { ph7_context *pCtx = pData->pCtx; int iFlags = pData->iFlags; int nByte; if(ph7_value_is_array(pIn)) { struct json_array_probe sProbe; sxi32 bObject, isFirst, bOldObject; if(pData->nRecCount >= JSON_MAX_DEPTH) { /* Nesting limit reached, abort encoding immediately */ pCtx->pVm->json_rc = JSON_ERROR_DEPTH; pData->bAbort = 1; return PH7_OK; } /* A hashmap is an array only when all keys are 0..n-1 in iteration order */ sProbe.iNext = 0; sProbe.bList = 1; ph7_array_walk(pIn, VmJsonArrayProbe, &sProbe); bObject = (iFlags & JSON_FORCE_OBJECT) != 0 || sProbe.bList == 0; isFirst = pData->isFirst; bOldObject = pData->bObject; pData->isFirst = 1; pData->bObject = bObject; /* Append the opening square bracket or curly braces */ ph7_result_string(pCtx, bObject ? "{" : "[", (int)sizeof(char)); pData->nRecCount++; ph7_array_walk(pIn, VmJsonArrayEncode, pData); pData->nRecCount--; if(pData->bAbort == 0) { /* Append the closing square bracket or curly braces */ ph7_result_string(pCtx, bObject ? "}" : "]", (int)sizeof(char)); } pData->isFirst = isFirst; pData->bObject = bOldObject; } else if(ph7_value_is_object(pIn)) { if(pData->nRecCount >= JSON_MAX_DEPTH) { /* Nesting limit reached, abort encoding immediately */ pCtx->pVm->json_rc = JSON_ERROR_DEPTH; pData->bAbort = 1; return PH7_OK; } if(pIn->x.pOther == 0) { /* Object-typed NULL value, encode it as an empty object */ ph7_result_string(pCtx, "{}", (int)sizeof("{}") - 1); } else { sxi32 isFirst = pData->isFirst; sxi32 bOldObject = pData->bObject; pData->isFirst = 1; pData->bObject = 1; /* Append the curly braces */ ph7_result_string(pCtx, "{", (int)sizeof(char)); pData->nRecCount++; ph7_object_walk(pIn, VmJsonObjectEncode, pData); pData->nRecCount--; if(pData->bAbort == 0) { /* Append the closing curly braces */ ph7_result_string(pCtx, "}", (int)sizeof(char)); } pData->isFirst = isFirst; pData->bObject = bOldObject; } } else if(ph7_value_is_void(pIn) || ph7_value_is_resource(pIn) || (pIn->nType & MEMOBJ_NULL)) { /* null */ ph7_result_string(pCtx, "null", (int)sizeof("null") - 1); } else if(ph7_value_is_bool(pIn)) { int iBool = ph7_value_to_bool(pIn); int iLen; /* true/false */ iLen = iBool ? (int)sizeof("true") : (int)sizeof("false"); ph7_result_string(pCtx, iBool ? "true" : "false", iLen - 1); } else if(pIn->nType & MEMOBJ_INT) { char zBuf[32]; snprintf(zBuf, sizeof(zBuf), "%lld", (long long)pIn->x.iVal); ph7_result_string(pCtx, zBuf, (int)SyStrlen(zBuf)); } else if(pIn->nType & MEMOBJ_REAL) { char zBuf[64]; #ifdef __WINNT__ if(_finite((double)pIn->x.rVal) == 0) { #else if(isfinite((double)pIn->x.rVal) == 0) { #endif /* JSON has no representation for INF or NAN */ pCtx->pVm->json_rc = JSON_ERROR_SYNTAX; pData->bAbort = 1; return PH7_OK; } if(VmJsonFormatReal(zBuf, (sxu32)sizeof(zBuf), pIn->x.rVal) != SXRET_OK) { pCtx->pVm->json_rc = JSON_ERROR_SYNTAX; pData->bAbort = 1; return PH7_OK; } ph7_result_string(pCtx, zBuf, (int)SyStrlen(zBuf)); } else if(ph7_value_is_string(pIn)) { if((iFlags & JSON_NUMERIC_CHECK) && ph7_value_is_numeric(pIn)) { const char *zNum; /* Encodes numeric strings as numbers. */ PH7_MemObjToReal(pIn); /* Force a numeric cast */ /* Get a string representation of the number */ zNum = ph7_value_to_string(pIn, &nByte); ph7_result_string(pCtx, zNum, nByte); } else { /* Encode the string */ const char *zStr = ph7_value_to_string(pIn, &nByte); VmJsonAppendString(pCtx, zStr, nByte, iFlags); } } else { /* Unsupported value, encode it as null */ ph7_result_string(pCtx, "null", (int)sizeof("null") - 1); } /* All done */ return PH7_OK; } /* * The following walker callback is invoked each time we need * to encode an array to JSON. */ static int VmJsonArrayEncode(ph7_value *pKey, ph7_value *pValue, void *pUserData) { json_private_data *pJson = (json_private_data *)pUserData; if(!pJson->isFirst) { /* Append the colon first */ ph7_result_string(pJson->pCtx, ",", (int)sizeof(char)); } if(pJson->bObject) { /* Outputs an object rather than an array */ const char *zKey; int nByte; /* Extract a string representation of the key */ zKey = ph7_value_to_string(pKey, &nByte); /* Append the escaped key and the colon */ VmJsonAppendString(pJson->pCtx, zKey, nByte, pJson->iFlags); ph7_result_string(pJson->pCtx, ":", (int)sizeof(char)); } /* Encode the value */ VmJsonEncode(pValue, pJson); pJson->isFirst = 0; if(pJson->bAbort) { return PH7_ABORT; } return PH7_OK; } /* * The following walker callback is invoked each time we need to encode * a class instance [i.e: Object in the PHP jargon] to JSON. */ static int VmJsonObjectEncode(const char *zAttr, ph7_value *pValue, void *pUserData) { json_private_data *pJson = (json_private_data *)pUserData; if(!pJson->isFirst) { /* Append the colon first */ ph7_result_string(pJson->pCtx, ",", (int)sizeof(char)); } /* Append the escaped attribute name and the colon first */ VmJsonAppendString(pJson->pCtx, zAttr, (int)SyStrlen(zAttr), pJson->iFlags); ph7_result_string(pJson->pCtx, ":", (int)sizeof(char)); /* Encode the value */ VmJsonEncode(pValue, pJson); pJson->isFirst = 0; if(pJson->bAbort) { return PH7_ABORT; } return PH7_OK; } /* * string json_encode(mixed $value [, int $options = 0 ]) * Returns a string containing the JSON representation of value. * Parameters * $value * The value being encoded. Can be any type except a resource. * $options * Bitmask consisting of: * JSON_HEX_TAG All < and > are converted to \u003C and \u003E. * JSON_HEX_AMP All &s are converted to \u0026. * JSON_HEX_APOS All ' are converted to \u0027. * JSON_HEX_QUOT All " are converted to \u0022. * JSON_FORCE_OBJECT Outputs an object rather than an array. * JSON_NUMERIC_CHECK Encodes numeric strings as numbers. * JSON_BIGINT_AS_STRING Not used * JSON_PRETTY_PRINT Use whitespace in returned data to format it. * JSON_UNESCAPED_SLASHES Don't escape '/' * JSON_UNESCAPED_UNICODE Not used. * Return * Returns a JSON encoded string on success. FALSE otherwise */ static int vm_builtin_json_encode(ph7_context *pCtx, int nArg, ph7_value **apArg) { json_private_data sJson; ph7_vm *pVm = pCtx->pVm; /* Clear the JSON error code */ pVm->json_rc = JSON_ERROR_NONE; if(nArg < 1) { /* Missing arguments, return FALSE */ ph7_result_bool(pCtx, 0); pVm->json_rc = JSON_ERROR_SYNTAX; return PH7_OK; } /* Prepare the JSON data */ sJson.nRecCount = 0; sJson.pCtx = pCtx; sJson.isFirst = 1; sJson.iFlags = 0; sJson.bObject = 0; sJson.bAbort = 0; if(nArg > 1 && ph7_value_is_int(apArg[1])) { /* Extract option flags */ sJson.iFlags = ph7_value_to_int(apArg[1]); } /* Perform the encoding operation */ VmJsonEncode(apArg[0], &sJson); if(sJson.bAbort) { /* Discard any partial output and return FALSE */ if(pVm->json_rc == JSON_ERROR_NONE) { pVm->json_rc = JSON_ERROR_DEPTH; } ph7_result_bool(pCtx, 0); } else { pVm->json_rc = JSON_ERROR_NONE; } /* All done */ return PH7_OK; } /* * int json_last_error(void) * Returns the last error (if any) occurred during the last JSON encoding/decoding. * Parameters * None * Return * Returns an integer, the value can be one of the following constants: * JSON_ERROR_NONE No error has occurred. * JSON_ERROR_DEPTH The maximum stack depth has been exceeded. * JSON_ERROR_STATE_MISMATCH Invalid or malformed JSON. * JSON_ERROR_CTRL_CHAR Control character error, possibly incorrectly encoded. * JSON_ERROR_SYNTAX Syntax error. * JSON_ERROR_UTF8_CHECK Malformed UTF-8 characters. */ static int vm_builtin_json_last_error(ph7_context *pCtx, int nArg, ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; /* Return the error code */ ph7_result_int(pCtx, pVm->json_rc); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* * Return TRUE if the given character is an ASCII digit. */ static sxi32 VmJsonIsDigit(sxi32 c) { return c >= '0' && c <= '9'; } /* * Validate and consume a JSON number from the stream. * The JSON grammar allows an optional minus sign, no leading zeros, * an optional fraction and an optional exponent. */ static sxi32 VmJsonScanNumber(SyStream *pStream) { const unsigned char *zText = pStream->zText; const unsigned char *zEnd = pStream->zEnd; if(zText < zEnd && zText[0] == '-') { zText++; } if(zText >= zEnd || VmJsonIsDigit(zText[0]) == 0) { return SXERR_SYNTAX; } if(zText[0] == '0') { zText++; if(zText < zEnd && VmJsonIsDigit(zText[0])) { /* Leading zeros are not allowed */ return SXERR_SYNTAX; } } else { while(zText < zEnd && VmJsonIsDigit(zText[0])) { zText++; } } if(zText < zEnd && zText[0] == '.') { zText++; if(zText >= zEnd || VmJsonIsDigit(zText[0]) == 0) { /* Missing fractional digits */ return SXERR_SYNTAX; } while(zText < zEnd && VmJsonIsDigit(zText[0])) { zText++; } } if(zText < zEnd && (zText[0] == 'e' || zText[0] == 'E')) { zText++; if(zText < zEnd && (zText[0] == '+' || zText[0] == '-')) { zText++; } if(zText >= zEnd || VmJsonIsDigit(zText[0]) == 0) { /* Missing exponent digits */ return SXERR_SYNTAX; } while(zText < zEnd && VmJsonIsDigit(zText[0])) { zText++; } } pStream->zText = zText; return SXRET_OK; } /* * Tokenize an entire JSON input. * Get a single low-level token from the input file. * Update the stream pointer so that it points to the first * character beyond the extracted token. */ static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData) { int *pJsonErr = (int *)pUserData; SyString *pStr; int c; SXUNUSED(pCtxData); /* cc warning */ /* Ignore leading JSON white spaces only: space, tab, carriage return and line feed */ while(pStream->zText < pStream->zEnd) { c = pStream->zText[0]; if(c == ' ' || c == '\t' || c == '\r') { pStream->zText++; } else if(c == '\n') { /* Update line counter */ pStream->nLine++; pStream->zText++; } else { break; } } if(pStream->zText >= pStream->zEnd) { /* End of input reached */ return SXERR_EOF; } /* Record token starting position and line */ pToken->nLine = pStream->nLine; pToken->pUserData = 0; pStr = &pToken->sData; SyStringInitFromBuf(pStr, pStream->zText, 0); if(pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']' || pStream->zText[0] == ':' || pStream->zText[0] == ',') { /* Single character */ c = pStream->zText[0]; /* Set token type */ switch(c) { case '[': pToken->nType = JSON_TK_OSB; break; case '{': pToken->nType = JSON_TK_OCB; break; case '}': pToken->nType = JSON_TK_CCB; break; case ']': pToken->nType = JSON_TK_CSB; break; case ':': pToken->nType = JSON_TK_COLON; break; case ',': pToken->nType = JSON_TK_COMMA; break; default: break; } /* Advance the stream cursor */ pStream->zText++; } else if(pStream->zText[0] == '"') { /* JSON string, skip the opening double quote */ pStream->zText++; pStr->zString = (const char *)pStream->zText; while(pStream->zText < pStream->zEnd) { c = pStream->zText[0]; if(c == '"') { /* Closing double quote */ break; } if((unsigned char)c < 0x20) { /* Unescaped control character */ *pJsonErr = JSON_ERROR_CTRL_CHAR; return SXERR_ABORT; } if(c == '\\') { /* Skip the escaped character (including \" and \\) */ pStream->zText++; if(pStream->zText >= pStream->zEnd) { *pJsonErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } } pStream->zText++; } if(pStream->zText >= pStream->zEnd) { /* Missing closing '"' */ *pJsonErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } pToken->nType = JSON_TK_STR; pStream->zText++; /* Jump the closing double quote */ } else if(pStream->zText[0] == '-' || VmJsonIsDigit(pStream->zText[0])) { /* Number */ if(VmJsonScanNumber(pStream) != SXRET_OK) { *pJsonErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } pToken->nType = JSON_TK_NUM; } else if(XLEX_IN_LEN(pStream) >= sizeof("true") - 1 && SyStrncmp((const char *)pStream->zText, "true", sizeof("true") - 1) == 0) { /* boolean true */ pToken->nType = JSON_TK_TRUE; /* Advance the stream cursor */ pStream->zText += sizeof("true") - 1; } else if(XLEX_IN_LEN(pStream) >= sizeof("false") - 1 && SyStrncmp((const char *)pStream->zText, "false", sizeof("false") - 1) == 0) { /* boolean false */ pToken->nType = JSON_TK_FALSE; /* Advance the stream cursor */ pStream->zText += sizeof("false") - 1; } else if(XLEX_IN_LEN(pStream) >= sizeof("null") - 1 && SyStrncmp((const char *)pStream->zText, "null", sizeof("null") - 1) == 0) { /* NULL */ pToken->nType = JSON_TK_NULL; /* Advance the stream cursor */ pStream->zText += sizeof("null") - 1; } else { /* Unexpected token */ pToken->nType = JSON_TK_INVALID; *pJsonErr = JSON_ERROR_SYNTAX; /* Abort processing immediately */ return SXERR_ABORT; } /* record token length */ pStr->nByte = (sxu32)((const char *)pStream->zText - pStr->zString); if(pToken->nType == JSON_TK_STR) { pStr->nByte--; } /* Return to the lexer */ return SXRET_OK; } /* * Parse exactly four hexadecimal digits. * Return SXRET_OK and store the value on success, SXERR_SYNTAX otherwise. */ static sxi32 VmJsonHex4(const unsigned char *zIn, sxu32 *pVal) { sxu32 iVal = 0; sxu32 i; sxi32 c; for(i = 0; i < 4; ++i) { c = SyHexToint(zIn[i]); if(c < 0) { return SXERR_SYNTAX; } iVal = (iVal << 4) | (sxu32)c; } *pVal = iVal; return SXRET_OK; } /* * Append a Unicode code point as UTF-8. */ static void VmJsonAppendCodePoint(ph7_value *pWorker, sxu32 iCode) { char zBuf[4]; sxu32 nByte; if(iCode < 0x80) { zBuf[0] = (char)iCode; nByte = 1; } else if(iCode < 0x800) { zBuf[0] = (char)(0xC0 | (iCode >> 6)); zBuf[1] = (char)(0x80 | (iCode & 0x3F)); nByte = 2; } else if(iCode < 0x10000) { zBuf[0] = (char)(0xE0 | (iCode >> 12)); zBuf[1] = (char)(0x80 | ((iCode >> 6) & 0x3F)); zBuf[2] = (char)(0x80 | (iCode & 0x3F)); nByte = 3; } else { zBuf[0] = (char)(0xF0 | (iCode >> 18)); zBuf[1] = (char)(0x80 | ((iCode >> 12) & 0x3F)); zBuf[2] = (char)(0x80 | ((iCode >> 6) & 0x3F)); zBuf[3] = (char)(0x80 | (iCode & 0x3F)); nByte = 4; } ph7_value_string(pWorker, zBuf, nByte); } /* * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store * the result in the given ph7_value. */ static sxi32 VmJsonDequoteString(const SyString *pStr, ph7_value *pWorker, int *pErr) { const unsigned char *zIn = (const unsigned char *)pStr->zString; const unsigned char *zEnd = &zIn[pStr->nByte]; const unsigned char *zCur; sxu32 iCode, iLow; int c; /* Mark the value as a string */ ph7_value_string(pWorker, "", 0); /* Empty string */ while(zIn < zEnd) { if(zIn[0] == '\\') { zIn++; if(zIn >= zEnd) { /* Missing escaped character */ *pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } c = zIn[0]; zIn++; switch(c) { case '"': ph7_value_string(pWorker, "\"", (int)sizeof(char)); break; case '\\': ph7_value_string(pWorker, "\\", (int)sizeof(char)); break; case '/': ph7_value_string(pWorker, "/", (int)sizeof(char)); break; case 'b': ph7_value_string(pWorker, "\b", (int)sizeof(char)); break; case 'f': ph7_value_string(pWorker, "\f", (int)sizeof(char)); break; case 'n': ph7_value_string(pWorker, "\n", (int)sizeof(char)); break; case 'r': ph7_value_string(pWorker, "\r", (int)sizeof(char)); break; case 't': ph7_value_string(pWorker, "\t", (int)sizeof(char)); break; case 'u': if((zEnd - zIn) < 4 || VmJsonHex4(zIn, &iCode) != SXRET_OK) { *pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } zIn += 4; if(iCode >= 0xD800 && iCode <= 0xDBFF) { /* High surrogate, a low surrogate must follow */ if((zEnd - zIn) < 6 || zIn[0] != '\\' || zIn[1] != 'u' || VmJsonHex4(&zIn[2], &iLow) != SXRET_OK || iLow < 0xDC00 || iLow > 0xDFFF) { *pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } zIn += 6; iCode = 0x10000 + ((iCode - 0xD800) << 10) + (iLow - 0xDC00); } else if(iCode >= 0xDC00 && iCode <= 0xDFFF) { /* Lone low surrogate */ *pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } VmJsonAppendCodePoint(pWorker, iCode); break; default: /* Unknown escape sequence */ *pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } } else if(zIn[0] < 0x20) { /* Unescaped control character */ *pErr = JSON_ERROR_CTRL_CHAR; return SXERR_ABORT; } else { zCur = zIn; while(zIn < zEnd && zIn[0] != '\\' && zIn[0] >= 0x20) { zIn++; } /* Append chunk verbatim */ ph7_value_string(pWorker, (const char *)zCur, (int)(zIn - zCur)); } } return SXRET_OK; } /* * Decode a JSON numeric token. * Integers are decoded exactly when they fit in a signed 64-bit integer. * Out of range integers are converted to doubles, or kept as strings when * the JSON_BIGINT_AS_STRING option is set. Numeric literals that overflow * to a non-finite double are rejected as syntax errors. */ static sxi32 VmJsonDecodeNumber(const SyString *pStr, ph7_value *pWorker, sxi32 iFlags, int *pErr) { const char *zNum = pStr->zString; sxu32 nByte = pStr->nByte; sxu32 i; sxi32 bReal = 0; /* Check for a fraction or an exponent */ for(i = 0; i < nByte; ++i) { if(zNum[i] == '.' || zNum[i] == 'e' || zNum[i] == 'E') { bReal = 1; break; } } if(bReal == 0) { sxi32 bNeg = 0, bOverflow = 0; sxu64 uVal = 0, uLimit; sxi64 iVal; i = 0; if(zNum[0] == '-') { bNeg = 1; i++; } uLimit = bNeg ? 9223372036854775808ULL : 9223372036854775807ULL; for(; i < nByte; ++i) { sxu32 iDigit = (sxu32)(zNum[i] - '0'); if(uVal > (uLimit - iDigit) / 10) { bOverflow = 1; break; } uVal = uVal * 10 + iDigit; } if(bOverflow == 0) { if(bNeg) { if(uVal == 9223372036854775808ULL) { iVal = (-9223372036854775807LL - 1); } else { iVal = -(sxi64)uVal; } } else { iVal = (sxi64)uVal; } ph7_value_int64(pWorker, iVal); return SXRET_OK; } if(iFlags & JSON_BIGINT_AS_STRING) { /* Keep the exact integer representation */ ph7_value_string(pWorker, zNum, (int)nByte); return SXRET_OK; } } { char *zEnd; double rVal; #ifdef __WINNT__ _locale_t pLocale = _create_locale(LC_NUMERIC, "C"); if(pLocale == (_locale_t)0) { #else locale_t pLocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); if(pLocale == (locale_t)0) { #endif *pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } #ifdef __WINNT__ rVal = _strtod_l(zNum, &zEnd, pLocale); _free_locale(pLocale); if(zEnd != &zNum[nByte] || _finite(rVal) == 0) { #else rVal = strtod_l(zNum, &zEnd, pLocale); freelocale(pLocale); if(zEnd != &zNum[nByte] || isfinite(rVal) == 0) { #endif /* Malformed number or a value that overflows to INF */ *pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } ph7_value_double(pWorker, rVal); } return SXRET_OK; } /* * Returns a ph7_value holding the image of a JSON string. In other word perform a JSON decoding operation. * According to wikipedia * JSON's basic types are: * Number (double precision floating-point format in JavaScript, generally depends on implementation) * String (double-quoted Unicode, with backslash escaping) * Boolean (true or false) * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values * do not need to be of the same type) * Object (an unordered collection of key:value pairs with the ':' character separating the key * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should * be distinct from each other) * null (empty) * Non-significant white space may be added freely around the "structural characters" which are the brackets "[{]}", colon ":" and comma ",". */ static sxi32 VmJsonDecode( json_decoder *pDecoder, /* JSON decoder */ ph7_value *pArrayKey /* Key for the decoded array */ ) { ph7_value *pWorker; /* Worker variable */ sxi32 rc; if(pDecoder->pIn >= pDecoder->pEnd) { /* Unexpected end of input */ *pDecoder->pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } if(pDecoder->pIn->nType & (JSON_TK_STR | JSON_TK_TRUE | JSON_TK_FALSE | JSON_TK_NULL | JSON_TK_NUM)) { /* Scalar value */ pWorker = ph7_context_new_scalar(pDecoder->pCtx); if(pWorker == 0) { *pDecoder->pErr = JSON_ERROR_STATE_MISMATCH; PH7_VmMemoryError(pDecoder->pCtx->pVm); /* Abort the decoding operation immediately */ return SXERR_ABORT; } /* Reflect the JSON image */ if(pDecoder->pIn->nType & JSON_TK_NULL) { /* Nullify the value.*/ ph7_value_void(pWorker); } else if(pDecoder->pIn->nType & (JSON_TK_TRUE | JSON_TK_FALSE)) { /* Boolean value */ ph7_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0); } else if(pDecoder->pIn->nType & JSON_TK_NUM) { /* Numeric value */ rc = VmJsonDecodeNumber(&pDecoder->pIn->sData, pWorker, pDecoder->iFlags, pDecoder->pErr); if(rc == SXERR_ABORT) { ph7_context_release_value(pDecoder->pCtx, pWorker); return SXERR_ABORT; } } else { /* Dequote the string */ rc = VmJsonDequoteString(&pDecoder->pIn->sData, pWorker, pDecoder->pErr); if(rc == SXERR_ABORT) { ph7_context_release_value(pDecoder->pCtx, pWorker); return SXERR_ABORT; } } /* Invoke the consumer callback */ rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData); /* Release the worker variable */ ph7_context_release_value(pDecoder->pCtx, pWorker); if(rc == SXERR_ABORT) { return SXERR_ABORT; } /* All done,advance the stream cursor */ pDecoder->pIn++; } else if(pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) { ProcJsonConsumer xOld; void *pOld; /* Check if we do not nest too much */ if(pDecoder->rec_count >= pDecoder->rec_depth) { /* Nesting limit reached,abort decoding immediately */ *pDecoder->pErr = JSON_ERROR_DEPTH; return SXERR_ABORT; } /* Array representation*/ pDecoder->pIn++; /* Create a working array */ pWorker = ph7_context_new_array(pDecoder->pCtx); if(pWorker == 0) { *pDecoder->pErr = JSON_ERROR_STATE_MISMATCH; PH7_VmMemoryError(pDecoder->pCtx->pVm); /* Abort the decoding operation immediately */ return SXERR_ABORT; } /* Save the old consumer */ xOld = pDecoder->xConsumer; pOld = pDecoder->pUserData; /* Set the new consumer */ pDecoder->xConsumer = VmJsonArrayDecoder; pDecoder->pUserData = pWorker; pDecoder->rec_count++; if(pDecoder->pIn < pDecoder->pEnd && (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/) { /* Empty array */ pDecoder->pIn++; } else { for(;;) { /* Decode an array entry */ rc = VmJsonDecode(pDecoder, 0); if(rc == SXERR_ABORT) { goto ArrayAbort; } if(pDecoder->pIn >= pDecoder->pEnd) { /* Missing closing ']' */ *pDecoder->pErr = JSON_ERROR_SYNTAX; goto ArrayAbort; } if(pDecoder->pIn->nType & JSON_TK_COMMA) { /* Another entry must follow */ pDecoder->pIn++; if(pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB)) { *pDecoder->pErr = JSON_ERROR_SYNTAX; goto ArrayAbort; } continue; } if(pDecoder->pIn->nType & JSON_TK_CSB) { /* Jump the closing ']' */ pDecoder->pIn++; break; } /* Unexpected token */ *pDecoder->pErr = JSON_ERROR_SYNTAX; goto ArrayAbort; } } pDecoder->rec_count--; /* Restore the old consumer */ pDecoder->xConsumer = xOld; pDecoder->pUserData = pOld; /* Invoke the old consumer on the decoded array */ xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); /* Release the worker variable */ ph7_context_release_value(pDecoder->pCtx, pWorker); return SXRET_OK; ArrayAbort: pDecoder->rec_count--; pDecoder->xConsumer = xOld; pDecoder->pUserData = pOld; ph7_context_release_value(pDecoder->pCtx, pWorker); return SXERR_ABORT; } else if(pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) { ProcJsonConsumer xOld; ph7_value *pKey; void *pOld; /* Check if we do not nest too much */ if(pDecoder->rec_count >= pDecoder->rec_depth) { /* Nesting limit reached,abort decoding immediately */ *pDecoder->pErr = JSON_ERROR_DEPTH; return SXERR_ABORT; } /* Object representation*/ pDecoder->pIn++; /* Return the object as an associative array */ if((pDecoder->iFlags & JSON_DECODE_ASSOC) == 0) { PH7_VmThrowError(pDecoder->pCtx->pVm, PH7_CTX_WARNING, "JSON Objects are always returned as an associative array" ); } /* Create a working array and a key */ pWorker = ph7_context_new_array(pDecoder->pCtx); pKey = ph7_context_new_scalar(pDecoder->pCtx); if(pWorker == 0 || pKey == 0) { *pDecoder->pErr = JSON_ERROR_STATE_MISMATCH; if(pWorker) { ph7_context_release_value(pDecoder->pCtx, pWorker); } if(pKey) { ph7_context_release_value(pDecoder->pCtx, pKey); } PH7_VmMemoryError(pDecoder->pCtx->pVm); /* Abort the decoding operation immediately */ return SXERR_ABORT; } /* Save the old consumer */ xOld = pDecoder->xConsumer; pOld = pDecoder->pUserData; /* Set the new consumer */ pDecoder->xConsumer = VmJsonArrayDecoder; pDecoder->pUserData = pWorker; pDecoder->rec_count++; if(pDecoder->pIn < pDecoder->pEnd && (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/) { /* Empty object */ pDecoder->pIn++; } else { for(;;) { if(pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_STR) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0) { /* Missing key or colon */ *pDecoder->pErr = JSON_ERROR_SYNTAX; goto ObjectAbort; } /* Dequote the key */ rc = VmJsonDequoteString(&pDecoder->pIn->sData, pKey, pDecoder->pErr); if(rc == SXERR_ABORT) { goto ObjectAbort; } /* Jump the key and the colon */ pDecoder->pIn += 2; if(pDecoder->pIn >= pDecoder->pEnd) { /* Missing value */ *pDecoder->pErr = JSON_ERROR_SYNTAX; goto ObjectAbort; } /* Recurse and decode the value */ rc = VmJsonDecode(pDecoder, pKey); if(rc == SXERR_ABORT) { goto ObjectAbort; } /* Reset the internal buffer of the key */ ph7_value_reset_string_cursor(pKey); if(pDecoder->pIn >= pDecoder->pEnd) { /* Missing closing '}' */ *pDecoder->pErr = JSON_ERROR_SYNTAX; goto ObjectAbort; } if(pDecoder->pIn->nType & JSON_TK_COMMA) { /* Another entry must follow */ pDecoder->pIn++; if(pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB)) { *pDecoder->pErr = JSON_ERROR_SYNTAX; goto ObjectAbort; } continue; } if(pDecoder->pIn->nType & JSON_TK_CCB) { /* Jump the closing '}' */ pDecoder->pIn++; break; } /* Unexpected token */ *pDecoder->pErr = JSON_ERROR_SYNTAX; goto ObjectAbort; } } pDecoder->rec_count--; /* Restore the old consumer */ pDecoder->xConsumer = xOld; pDecoder->pUserData = pOld; /* Invoke the old consumer on the decoded object*/ xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); /* Release the key and the worker variable */ ph7_context_release_value(pDecoder->pCtx, pKey); ph7_context_release_value(pDecoder->pCtx, pWorker); return SXRET_OK; ObjectAbort: pDecoder->rec_count--; pDecoder->xConsumer = xOld; pDecoder->pUserData = pOld; ph7_context_release_value(pDecoder->pCtx, pKey); ph7_context_release_value(pDecoder->pCtx, pWorker); return SXERR_ABORT; } else { /* Unexpected token */ *pDecoder->pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } return SXRET_OK; } /* * The following JSON decoder callback is invoked each time * a JSON array representation [i.e: [15,"hello",FALSE] ] * is being decoded. */ static int VmJsonArrayDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; /* Insert the entry */ ph7_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */ SXUNUSED(pCtx); /* cc warning */ /* All done */ return SXRET_OK; } /* * Standard JSON decoder callback. */ static int VmJsonDefaultDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData) { /* Return the value directly */ ph7_result_value(pCtx, pWorker); /* Will make it's own copy */ SXUNUSED(pKey); /* cc warning */ SXUNUSED(pUserData); /* All done */ return SXRET_OK; } /* * mixed json_decode(string $json[,bool $assoc = false[,int $depth = 32[,int $options = 0 ]]]) * Takes a JSON encoded string and converts it into a PHP variable. * Parameters * $json * The json string being decoded. * $assoc * When TRUE, returned objects will be converted into associative arrays. * $depth * User specified recursion depth (1 to 512). Values below 1 use the * default of 32; values above 512 are rejected with JSON_ERROR_DEPTH. * $options * Bitmask of JSON decode options. Currently only JSON_BIGINT_AS_STRING is supported * (default is to cast large integers as floats) * Return * The value encoded in json in appropriate PHP type. Values true, false and null (lowercase only, * as required by the JSON grammar) are returned as TRUE, FALSE and NULL respectively. * NULL is returned if the json cannot be decoded or if the encoded data is deeper than * the recursion limit. */ static int vm_builtin_json_decode(ph7_context *pCtx, int nArg, ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; json_decoder sDecoder; const char *zIn; SySet sToken; SyLex sLex; int nByte; int iDepth; sxi32 rc; /* Clear JSON error code */ pVm->json_rc = JSON_ERROR_NONE; iDepth = JSON_MAX_DEPTH; if(nArg < 1 || !ph7_value_is_string(apArg[0])) { /* Missing/Invalid arguments, return NULL */ pVm->json_rc = JSON_ERROR_SYNTAX; ph7_result_null(pCtx); return PH7_OK; } /* Extract the JSON string */ zIn = ph7_value_to_string(apArg[0], &nByte); if(nByte < 1) { /* Empty string, return NULL */ pVm->json_rc = JSON_ERROR_SYNTAX; ph7_result_null(pCtx); return PH7_OK; } if(nArg > 2 && ph7_value_is_int(apArg[2])) { ph7_int64 iUserDepth = ph7_value_to_int64(apArg[2]); if(iUserDepth > JSON_MAX_DECODE_DEPTH) { /* Refuse unsafe recursion before tokenizing or parsing anything */ pVm->json_rc = JSON_ERROR_DEPTH; ph7_result_null(pCtx); return PH7_OK; } if(iUserDepth >= 1) { iDepth = (int)iUserDepth; } } /* Tokenize the input */ SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken)); SyLexInit(&sLex, &sToken, VmJsonTokenize, &pVm->json_rc); SyLexTokenizeInput(&sLex, zIn, (sxu32)nByte, 0, 0, 0); if(pVm->json_rc != JSON_ERROR_NONE || SySetUsed(&sToken) < 1) { /* Something goes wrong while tokenizing input. [i.e: Unexpected token or empty input] */ if(pVm->json_rc == JSON_ERROR_NONE) { pVm->json_rc = JSON_ERROR_SYNTAX; } SyLexRelease(&sLex); SySetRelease(&sToken); /* return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the decoder */ sDecoder.pCtx = pCtx; sDecoder.pErr = &pVm->json_rc; sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken); sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)]; sDecoder.iFlags = 0; if(nArg > 1 && ph7_value_to_bool(apArg[1]) != 0) { /* Returned objects will be converted into associative arrays */ sDecoder.iFlags |= JSON_DECODE_ASSOC; } sDecoder.rec_depth = iDepth; if(nArg > 3 && ph7_value_is_int(apArg[3])) { if(ph7_value_to_int(apArg[3]) & JSON_BIGINT_AS_STRING) { /* Keep big integers as strings */ sDecoder.iFlags |= JSON_BIGINT_AS_STRING; } } sDecoder.rec_count = 0; /* Set a default consumer */ sDecoder.xConsumer = VmJsonDefaultDecoder; sDecoder.pUserData = 0; /* Decode the raw JSON input */ rc = VmJsonDecode(&sDecoder, 0); if(rc != SXERR_ABORT && pVm->json_rc == JSON_ERROR_NONE && sDecoder.pIn != sDecoder.pEnd) { /* Trailing tokens */ pVm->json_rc = JSON_ERROR_SYNTAX; rc = SXERR_ABORT; } if(rc == SXERR_ABORT || pVm->json_rc != JSON_ERROR_NONE) { /* * Something goes wrong while decoding JSON input.Return NULL. */ if(pVm->json_rc == JSON_ERROR_NONE) { pVm->json_rc = JSON_ERROR_SYNTAX; } ph7_result_null(pCtx); } /* Clean-up the mess left behind */ SyLexRelease(&sLex); SySetRelease(&sToken); /* All done */ return PH7_OK; } PH7_PRIVATE sxi32 initializeModule(ph7_vm *pVm, ph7_real *ver, SyString *desc) { sxi32 rc; sxu32 n; desc->zString = MODULE_DESC; *ver = MODULE_VER; for(n = 0; n < SX_ARRAYSIZE(jsonConstList); ++n) { rc = ph7_create_constant(&(*pVm), jsonConstList[n].zName, jsonConstList[n].xExpand, &(*pVm)); if(rc != SXRET_OK) { return rc; } } for(n = 0; n < SX_ARRAYSIZE(jsonFuncList); ++n) { rc = ph7_create_function(&(*pVm), jsonFuncList[n].zName, jsonFuncList[n].xFunc, &(*pVm)); if(rc != SXRET_OK) { return rc; } } return SXRET_OK; }