/* * Javascript implementation for the Jansson JSON parser. * Source: https://github.com/akheron/jansson * * Implemented and tested with: * https://github.com/akheron/jansson/commit/cf1074e70ea42a1dea4d6e57b2b29532049bcd28 */ var LibraryJansson = { $JANSSON: { // Returns the node ID to which the pointer `nodePtr` points to getNodeIDByPtr: function(nodePtr) { if (nodePtr) return {{{ makeGetValue('nodePtr', '0', 'i32') }}}; else return 0; }, // Returns the Javascript node object which is referenced by `nodeID` getNodeByID: function(nodeID) { if (!g_json_context) { console.log("Jansson: No JSON context."); return null; } var node = g_json_context[nodeID]; if (!node) { console.log("Jansson: Node with ID `" + nodeID + "` not found. Context has `" + g_json_context.length + "` nodes."); return null; } return node; }, // Returns the Javascript node object to which is referenced by `nodePtr` (via node ID). getNodeByPtr: function(nodePtr) { var nodeID = JANSSON.getNodeIDByPtr(nodePtr); var node = JANSSON.getNodeByID(nodeID); return node; }, // Returns the pointer to the `nodeID` of the node that is referenced by `nodeID` getNodePtrByID: function(nodeID) { if (nodeID <= 0) return null; var node = JANSSON.getNodeByID(nodeID); if (!node) return null; return node['nodeIDPtr']; }, // Adds a Javascript node object to the Javascript JSON context allocNode: function(name, node) { var id = g_json_context.length; g_json_context[id] = {}; g_json_context[id]['name'] = name; g_json_context[id]['node'] = node; g_json_context[id]['nodeIDPtr'] = _malloc(32); {{{ makeSetValue('g_json_context[id][\'nodeIDPtr\']', '0', 'id', 'i32') }}} return id; }, // Frees all memory that might have been allocated for a given node freeNode: function(node) { node['name'] = null; node['node'] = null; _free(node['nodeIDPtr']); _free(node['keyPtr']); _free(node['stringValuePtr']); }, // Loads a string into the Javascript JSON context // Only the root node is loaded. Child nodes are loaded on demand via `loadChildNodes` method. load: function(string, flags, error) { // This is potentially a security problem. // TODO: Make sure everything is properly escaped var json_obj = JSON.parse(string); if (json_obj != null) { // The context is an array storing all child nodes. // These child nodes are added to the array on demand. // The root node is at index `1` (index `0` is intentionally `null`) g_json_context = [null]; var nodeID = JANSSON.allocNode('root', json_obj); return JANSSON.getNodePtrByID(nodeID); } else { g_json_context = null; return null; } }, // Loads the child nodes of a given node (if the node has any) into the Javascript JSON context. loadChildNodes: function(node) { if (!node) return null; // Add all child nodes of the parent node to the context. // Consequently we can access these child nodes with an ID (pointer) // TODO: Here is room for performance improvements if (!node.begin) { var childNodeID = g_json_context.length; for(var childNode in node['node']) { var childNodeID = JANSSON.allocNode(childNode, node['node'][childNode]); if (!node.begin) node.begin = childNodeID; }; node.end = childNodeID; } return node; }, // Returns the content type (object, array, string, number, null) of the node referenced by `nodePtr` type: function(nodePtr) { var node = JANSSON.getNodeByPtr(nodePtr); if (!node) return null; var content = node['node']; if (typeof(content) === 'object' && (content instanceof Array)) return 'array' else { return typeof(content); } } }, json_loads: function(string, flags, error) { return JANSSON.load(Pointer_stringify(string), flags, error); }, json_loadb: function(buffer, buflen, flags, error) { return JANSSON.load(Pointer_stringify(buffer).substr(0, buflen), flags, error); }, json_is_object: function(nodePtr) { return (JANSSON.type(nodePtr) === 'object'); }, json_is_array: function(nodePtr) { return (JANSSON.type(nodePtr) === 'array'); }, json_is_string: function(nodePtr) { return (JANSSON.type(nodePtr) === 'string'); }, json_is_integer: function(nodePtr) { return (JANSSON.type(nodePtr) === 'number'); }, json_is_real: function(nodePtr) { return (JANSSON.type(nodePtr) === 'number'); }, json_is_number: function(nodePtr) { return (JANSSON.type(nodePtr) === 'number'); }, json_is_null: function(nodePtr) { var nodeType = JANSSON.type(nodePtr); return (nodeType === 'undefined' || nodeType === 'null'); }, // Returns the pointer to the child node with the key `key` of the given parent node json_object_get: function(parentNodePtr, key) { var key = Pointer_stringify(key); var parentNode = JANSSON.getNodeByPtr(parentNodePtr); if (!parentNode) return null; JANSSON.loadChildNodes(parentNode); // Find the requested child node for (var i=parentNode.begin; i<=parentNode.end; i++) { if (g_json_context[i]['name'] === key) return JANSSON.getNodePtrByID(i); } return null; }, // Returns the pointer to the first child node of the given parent node json_object_iter: function(parentNodePtr) { var parentNode = JANSSON.getNodeByPtr(parentNodePtr); if (!parentNode) return null; JANSSON.loadChildNodes(parentNode); return JANSSON.getNodePtrByID(parentNode.begin); }, // Returns a pointer to the child node after the given child node of the given parent node json_object_iter_next: function(parentNodePtr, childNodePtr) { var parentNode = JANSSON.getNodeByPtr(parentNodePtr); var childNodeID = JANSSON.getNodeIDByPtr(childNodePtr); if (!parentNode) return null; if (childNodeID < parentNode.begin || childNodeID >= parentNode.end) return null; else return JANSSON.getNodePtrByID(childNodeID+1); }, // Returns a integer containing the size of an array json_array_size: function(parentNodePtr) { var parentNode = JANSSON.getNodeByPtr(parentNodePtr); if (!parentNode) return 0; JANSSON.loadChildNodes(parentNode); var size = parentNode.end - parentNode.begin + 1; if (size < 0) size = 0; return size; }, // Returns the pointer of the node with the index `index` in a given array parent node json_array_get: function(parentNodePtr, index) { var parentNode = JANSSON.getNodeByPtr(parentNodePtr); JANSSON.loadChildNodes(parentNode); var position = parentNode.begin + index; if (position < parentNode.begin || position > parentNode.end) return null; else return JANSSON.getNodePtrByID(position); }, json_object_iter_key: function(nodePtr) { var node = JANSSON.getNodeByPtr(nodePtr); if (!node) return null; if (!node['keyPtr']) { node['keyPtr'] = allocate(intArrayFromString(node['name']), 'i8', ALLOC_NORMAL); } return node['keyPtr']; }, json_object_iter_value: function(nodePtr) { return nodePtr; }, json_string_value: function(nodePtr) { var node = JANSSON.getNodeByPtr(nodePtr); if (!node) { console.log("Jansson: invalid node in `function json_string_value`"); return null; } if (!node['stringValuePtr']) { node['stringValuePtr'] = allocate(intArrayFromString(node['node']), 'i8', ALLOC_NORMAL); } return node['stringValuePtr']; }, json_integer_value: function(nodePtr) { var node = JANSSON.getNodeByPtr(nodePtr); if (!node) return null; // Convert JSON number to an integer // c.f. http://stackoverflow.com/questions/596467/how-do-i-convert-a-float-to-an-int-in-javascript if (node['node']<0) return Math.ceil(node['node']); else return Math.floor(node['node']); }, json_real_value: function(nodePtr) { var node = JANSSON.getNodeByPtr(nodePtr); if (!node) return null; return node['node']; }, json_number_value: function(nodePtr) { var node = JANSSON.getNodeByPtr(nodePtr); if (!node) return null; return node['node']; }, json_delete: function(nodePtr) { // We assume, if the root node is deleted, all nodes and the context are deleted. if (JANSSON.getNodeIDByPtr(nodePtr) == 1) { for (var node in g_json_context) { JANSSON.freeNode(node); } g_json_context = null; } } }; autoAddDeps(LibraryJansson, '$JANSSON'); mergeInto(LibraryManager.library, LibraryJansson);