From 00dcccce979243fa9721e5be27eedc136ad439e5 Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Fri, 30 Jan 2015 14:12:32 +0100 Subject: some formatting stuff and cycle detection in include as well as repeated import detection. --- src/core/resource/ResourceManager.cpp | 182 +++++++++++++++++++++------------- src/core/resource/ResourceManager.hpp | 18 ++-- 2 files changed, 125 insertions(+), 75 deletions(-) (limited to 'src/core/resource') diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp index 1d32a4d..48d12b3 100644 --- a/src/core/resource/ResourceManager.cpp +++ b/src/core/resource/ResourceManager.cpp @@ -70,6 +70,25 @@ void ResourceManager::purgeResource(SourceId sourceId) contextReaders.erase(sourceId); } +template +class GuardedSetInsertion { +private: + std::unordered_set &set; + T value; + bool success; + +public: + GuardedSetInsertion(std::unordered_set &set, T value) + : set(set), value(value) + { + success = set.insert(value).second; + } + + ~GuardedSetInsertion() { set.erase(value); } + + bool isSuccess() { return success; } +}; + NodeVector ResourceManager::parse( ParserContext &ctx, const std::string &path, const std::string &mimetype, const std::string &rel, const RttiSet &supportedTypes, ParseMode mode) @@ -89,86 +108,111 @@ NodeVector ResourceManager::parse( return NodeVector{}; } - // Allocate a new SourceId handle for this Resource - SourceId sourceId = allocateSourceId(resource); - - // We can now try to parse the given file + // initialize the output vector. NodeVector parsedNodes; - // Set the current source id in the logger instance. Note that this - // modifies the logger instance -- the GuardedLogger is just used to - // make sure the default location is popped from the stack again. - GuardedLogger guardedLogger(logger, SourceLocation{sourceId}); + // Allocate a new SourceId handle for this Resource + bool newResource = false; + SourceId sourceId = getSourceId(resource); + if (sourceId == InvalidSourceId) { + newResource = true; + sourceId = allocateSourceId(resource); + } - try { - // Fetch the input stream and create a char reader - std::unique_ptr is = resource.stream(); - CharReader reader(*is, sourceId); - - // Actually parse the input stream, distinguish the IMPORT and the - // INCLUDE mode - switch (mode) { - case ParseMode::IMPORT: { - // Create a new, empty parser scope instance and a new parser - // context with this instance in place - ParserScope innerScope; - ParserContext childCtx = ctx.clone(innerScope, sourceId); - - // Run the parser - req.getParser()->parse(reader, childCtx); - - // Make sure the scope has been unwound and perform all - // deferred resolutions - innerScope.checkUnwound(logger); - innerScope.performDeferredResolution(logger); - - // Fetch the nodes that were parsed by this parser instance and - // validate them - parsedNodes = innerScope.getTopLevelNodes(); - for (auto parsedNode : parsedNodes) { - parsedNode->validate(logger); - } + if (!newResource && mode == ParseMode::IMPORT) { + // if a already imported resource should be imported we just use the + // cached node. + parsedNodes.push_back(getNode(ctx.getManager(), sourceId)); + } else { + // check for cycles. + GuardedSetInsertion cycleDetection{currentlyParsing, + sourceId}; + if (!cycleDetection.isSuccess()) { + throw LoggableException{ + std::string("Detected cyclic inclusion of ") + + resource.getLocation()}; + } - // Make sure the number of elements is exactly one -- we can - // only store one element per top-level node. - if (parsedNodes.empty()) { - throw LoggableException{"Module is empty."}; - } - if (parsedNodes.size() > 1) { - throw LoggableException{ - std::string( - "Expected exactly one top-level node but got ") + - std::to_string(parsedNodes.size())}; + // We can now try to parse the given file + + // Set the current source id in the logger instance. Note that this + // modifies the logger instance -- the GuardedLogger is just used to + // make sure the default location is popped from the stack again. + GuardedLogger guardedLogger(logger, SourceLocation{sourceId}); + + try { + // Fetch the input stream and create a char reader + std::unique_ptr is = resource.stream(); + CharReader reader(*is, sourceId); + + // Actually parse the input stream, distinguish the IMPORT and the + // INCLUDE mode + switch (mode) { + case ParseMode::IMPORT: { + // Create a new, empty parser scope instance and a new + // parser + // context with this instance in place + ParserScope innerScope; + ParserContext childCtx = ctx.clone(innerScope, sourceId); + + // Run the parser + req.getParser()->parse(reader, childCtx); + + // Make sure the scope has been unwound and perform all + // deferred resolutions + innerScope.checkUnwound(logger); + innerScope.performDeferredResolution(logger); + + // Fetch the nodes that were parsed by this parser instance + // and + // validate them + parsedNodes = innerScope.getTopLevelNodes(); + for (auto parsedNode : parsedNodes) { + parsedNode->validate(logger); + } + + // Make sure the number of elements is exactly one -- we can + // only store one element per top-level node. + if (parsedNodes.empty()) { + throw LoggableException{"Module is empty."}; + } + if (parsedNodes.size() > 1) { + throw LoggableException{ + std::string( + "Expected exactly one top-level node but " + "got ") + + std::to_string(parsedNodes.size())}; + } + + // Store the parsed node along with the sourceId + storeNode(sourceId, parsedNodes[0]); + + break; } + case ParseMode::INCLUDE: { + // Fork the scope instance and create a new parser context + // with this instance in place + ParserScope forkedScope = scope.fork(); + ParserContext childCtx = ctx.clone(forkedScope, sourceId); - // Store the parsed node along with the sourceId - storeNode(sourceId, parsedNodes[0]); - - break; - } - case ParseMode::INCLUDE: { - // Fork the scope instance and create a new parser context with - // this instanc ein place - ParserScope forkedScope = scope.fork(); - ParserContext childCtx = ctx.clone(forkedScope, sourceId); - - // Run the parser - req.getParser()->parse(reader, childCtx); + // Run the parser + req.getParser()->parse(reader, childCtx); - // Join the forked scope with the outer scope - scope.join(forkedScope, logger); + // Join the forked scope with the outer scope + scope.join(forkedScope, logger); - // Fetch the nodes that were parsed by this parser instance - parsedNodes = forkedScope.getTopLevelNodes(); + // Fetch the nodes that were parsed by this parser instance + parsedNodes = forkedScope.getTopLevelNodes(); - break; + break; + } } } - } - catch (LoggableException ex) { - // Log the exception and return nullptr - logger.log(ex); - return NodeVector{}; + catch (LoggableException ex) { + // Log the exception and return nullptr + logger.log(ex); + return NodeVector{}; + } } // Make sure the parsed nodes fulfill the "supportedTypes" constraint, diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp index 1279bee..186ce42 100644 --- a/src/core/resource/ResourceManager.hpp +++ b/src/core/resource/ResourceManager.hpp @@ -81,6 +81,11 @@ private: */ std::unordered_map nodes; + /** + * The set of SourceIds for which resources are currently being parsed. + */ + std::unordered_set currentlyParsing; + /** * Map containing SourceContextReader instances which are -- as their name * suggests -- used to produce SourceContext structures describing the @@ -132,8 +137,8 @@ private: * @return the parsed nodes or an empty list if something went wrong. */ NodeVector parse(ParserContext &ctx, const std::string &path, - const std::string &mimetype, const std::string &rel, - const RttiSet &supportedTypes, ParseMode mode); + const std::string &mimetype, const std::string &rel, + const RttiSet &supportedTypes, ParseMode mode); public: /** @@ -161,8 +166,8 @@ public: * @return the parsed node or nullptr if something went wrong. */ Rooted import(ParserContext &ctx, const std::string &path, - const std::string &mimetype, const std::string &rel, - const RttiSet &supportedTypes); + const std::string &mimetype, const std::string &rel, + const RttiSet &supportedTypes); /** * Resolves the reference to the file specified by the given path and parses @@ -192,8 +197,9 @@ public: * @return the parsed nodes or an empty list if something went wrong. */ NodeVector include(ParserContext &ctx, const std::string &path, - const std::string &mimetype, const std::string &rel, - const RttiSet &supportedTypes); + const std::string &mimetype, + const std::string &rel, + const RttiSet &supportedTypes); /** * Creates and returns a SourceContext structure containing information -- cgit v1.2.3