--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/compiler/directivesParser.cpp Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,724 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/directivesParser.hpp"
+#include "memory/allocation.inline.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/os.hpp"
+#include <string.h>
+
+void DirectivesParser::push_tmp(CompilerDirectives* dir) {
+ _tmp_depth++;
+ dir->set_next(_tmp_top);
+ _tmp_top = dir;
+}
+
+CompilerDirectives* DirectivesParser::pop_tmp() {
+ if (_tmp_top == NULL) {
+ return NULL;
+ }
+ CompilerDirectives* tmp = _tmp_top;
+ _tmp_top = _tmp_top->next();
+ tmp->set_next(NULL);
+ _tmp_depth--;
+ return tmp;
+}
+
+void DirectivesParser::clean_tmp() {
+ CompilerDirectives* tmp = pop_tmp();
+ while (tmp != NULL) {
+ delete tmp;
+ tmp = pop_tmp();
+ }
+ assert(_tmp_depth == 0, "Consistency");
+}
+
+int DirectivesParser::parse_string(const char* text, outputStream* st) {
+ DirectivesParser cd(text, st, false);
+ if (cd.valid()) {
+ return cd.install_directives();
+ } else {
+ cd.clean_tmp();
+ st->flush();
+ st->print_cr("Parsing of compiler directives failed");
+ return -1;
+ }
+}
+
+bool DirectivesParser::has_file() {
+ return CompilerDirectivesFile != NULL;
+}
+
+bool DirectivesParser::parse_from_flag() {
+ return parse_from_file(CompilerDirectivesFile, tty);
+}
+
+bool DirectivesParser::parse_from_file(const char* filename, outputStream* st) {
+ assert(filename != NULL, "Test before calling this");
+ if (!parse_from_file_inner(filename, st)) {
+ st->print_cr("Could not load file: %s", filename);
+ return false;
+ }
+ return true;
+}
+
+bool DirectivesParser::parse_from_file_inner(const char* filename, outputStream* stream) {
+ struct stat st;
+ ResourceMark rm;
+ if (os::stat(filename, &st) == 0) {
+ // found file, open it
+ int file_handle = os::open(filename, 0, 0);
+ if (file_handle != -1) {
+ // read contents into resource array
+ char* buffer = NEW_RESOURCE_ARRAY(char, st.st_size+1);
+ size_t num_read = os::read(file_handle, (char*) buffer, st.st_size);
+ buffer[num_read] = '\0';
+ // close file
+ os::close(file_handle);
+ return parse_string(buffer, stream) > 0;
+ }
+ }
+ return false;
+}
+
+int DirectivesParser::install_directives() {
+ // Check limit
+ if (!DirectivesStack::check_capacity(_tmp_depth, _st)) {
+ clean_tmp();
+ return 0;
+ }
+
+ // Pop from internal temporary stack and push to compileBroker.
+ CompilerDirectives* tmp = pop_tmp();
+ int i = 0;
+ while (tmp != NULL) {
+ i++;
+ DirectivesStack::push(tmp);
+ tmp = pop_tmp();
+ }
+ if (i == 0) {
+ _st->print_cr("No directives in file");
+ return 0;
+ } else {
+ _st->print_cr("%i compiler directives added", i);
+ if (CompilerDirectivesPrint) {
+ // Print entire directives stack after new has been pushed.
+ DirectivesStack::print(_st);
+ }
+ return i;
+ }
+}
+
+DirectivesParser::DirectivesParser(const char* text, outputStream* st, bool silent)
+: JSON(text, silent, st), depth(0), current_directive(NULL), current_directiveset(NULL), _tmp_top(NULL), _tmp_depth(0) {
+#ifndef PRODUCT
+ memset(stack, 0, MAX_DEPTH * sizeof(stack[0]));
+#endif
+ parse();
+}
+
+DirectivesParser::~DirectivesParser() {
+ assert(_tmp_top == NULL, "Consistency");
+ assert(_tmp_depth == 0, "Consistency");
+}
+
+const DirectivesParser::key DirectivesParser::keys[] = {
+ // name, keytype, allow_array, allowed_mask, set_function
+ { "c1", type_c1, 0, mask(type_directives), NULL, UnknownFlagType },
+ { "c2", type_c2, 0, mask(type_directives), NULL, UnknownFlagType },
+ { "match", type_match, 1, mask(type_directives), NULL, UnknownFlagType },
+ { "inline", type_inline, 1, mask(type_directives) | mask(type_c1) | mask(type_c2), NULL, UnknownFlagType },
+
+ // Global flags
+ #define common_flag_key(name, type, dvalue, compiler) \
+ { #name, type_flag, 0, mask(type_directives) | mask(type_c1) | mask(type_c2), &DirectiveSet::set_##name, type##Flag},
+ compilerdirectives_common_flags(common_flag_key)
+ compilerdirectives_c2_flags(common_flag_key)
+ compilerdirectives_c1_flags(common_flag_key)
+ #undef common_flag_key
+};
+
+const DirectivesParser::key DirectivesParser::dir_array_key = {
+ "top level directives array", type_dir_array, 0, 1 // Lowest bit means allow at top level
+};
+const DirectivesParser::key DirectivesParser::dir_key = {
+ "top level directive", type_directives, 0, mask(type_dir_array) | 1 // Lowest bit means allow at top level
+};
+const DirectivesParser::key DirectivesParser::value_array_key = {
+ "value array", type_value_array, 0, UINT_MAX // Allow all, checked by allow_array on other keys, not by allowed_mask from this key
+};
+
+const DirectivesParser::key* DirectivesParser::lookup_key(const char* str, size_t len) {
+ for (size_t i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
+ if (strncasecmp(keys[i].name, str, len) == 0) {
+ return &keys[i];
+ }
+ }
+ return NULL;
+}
+
+uint DirectivesParser::mask(keytype kt) {
+ return 1 << (kt + 1);
+}
+
+bool DirectivesParser::push_key(const char* str, size_t len) {
+ bool result = true;
+ const key* k = lookup_key(str, len);
+
+ if (k == NULL) {
+ // os::strdup
+ char* s = NEW_C_HEAP_ARRAY(char, len + 1, mtCompiler);
+ strncpy(s, str, len);
+ s[len] = '\0';
+ error(KEY_ERROR, "No such key: '%s'.", s);
+ FREE_C_HEAP_ARRAY(char, s);
+ return false;
+ }
+
+ return push_key(k);
+}
+
+bool DirectivesParser::push_key(const key* k) {
+ assert(k->allowedmask != 0, "not allowed anywhere?");
+
+ // Exceeding the stack should not be possible with a valid compiler directive,
+ // and an invalid should abort before this happens
+ assert(depth < MAX_DEPTH, "exceeded stack depth");
+ if (depth >= MAX_DEPTH) {
+ error(INTERNAL_ERROR, "Stack depth exceeded.");
+ return false;
+ }
+
+ assert(stack[depth] == NULL, "element not nulled, something is wrong");
+
+ if (depth == 0 && !(k->allowedmask & 1)) {
+ error(KEY_ERROR, "Key '%s' not allowed at top level.", k->name);
+ return false;
+ }
+
+ if (depth > 0) {
+ const key* prev = stack[depth - 1];
+ if (!(k->allowedmask & mask(prev->type))) {
+ error(KEY_ERROR, "Key '%s' not allowed after '%s' key.", k->name, prev->name);
+ return false;
+ }
+ }
+
+ stack[depth] = k;
+ depth++;
+ return true;
+}
+
+const DirectivesParser::key* DirectivesParser::current_key() {
+ assert(depth > 0, "getting key from empty stack");
+ if (depth == 0) {
+ return NULL;
+ }
+ return stack[depth - 1];
+}
+
+const DirectivesParser::key* DirectivesParser::pop_key() {
+ assert(depth > 0, "popping empty stack");
+ if (depth == 0) {
+ error(INTERNAL_ERROR, "Popping empty stack.");
+ return NULL;
+ }
+ depth--;
+
+ const key* k = stack[depth];
+#ifndef PRODUCT
+ stack[depth] = NULL;
+#endif
+
+ return k;
+}
+
+bool DirectivesParser::set_option_flag(JSON_TYPE t, JSON_VAL* v, const key* option_key, DirectiveSet* set) {
+
+ void (DirectiveSet::*test)(void *args);
+ test = option_key->set;
+
+ switch (t) {
+ case JSON_TRUE:
+ if (option_key->flag_type != boolFlag) {
+ error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ } else {
+ bool val = true;
+ (set->*test)((void *)&val);
+ }
+ break;
+
+ case JSON_FALSE:
+ if (option_key->flag_type != boolFlag) {
+ error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ } else {
+ bool val = false;
+ (set->*test)((void *)&val);
+ }
+ break;
+
+ case JSON_NUMBER_INT:
+ if (option_key->flag_type == intxFlag) {
+ intx ival = v->int_value;
+ (set->*test)((void *)&ival);
+ } else if (option_key->flag_type == uintxFlag) {
+ uintx ival = v->uint_value;
+ (set->*test)((void *)&ival);
+ } else if (option_key->flag_type == doubleFlag) {
+ double dval = (double)v->int_value;
+ (set->*test)((void *)&dval);
+ } else {
+ error(VALUE_ERROR, "Cannot use int value for an %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ }
+ break;
+
+ case JSON_NUMBER_FLOAT:
+ if (option_key->flag_type != doubleFlag) {
+ error(VALUE_ERROR, "Cannot use double value for an %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ } else {
+ double dval = v->double_value;
+ (set->*test)((void *)&dval);
+ }
+ break;
+
+ case JSON_STRING:
+ if (option_key->flag_type != ccstrFlag && option_key->flag_type != ccstrlistFlag) {
+ error(VALUE_ERROR, "Cannot use string value for a %s flag", flag_type_names[option_key->flag_type]);
+ return false;
+ } else {
+ char* s = NEW_C_HEAP_ARRAY(char, v->str.length+1, mtCompiler);
+ strncpy(s, v->str.start, v->str.length + 1);
+ s[v->str.length] = '\0';
+ (set->*test)((void *)&s);
+ }
+ break;
+
+ default:
+ assert(0, "Should not reach here.");
+ }
+ return true;
+}
+
+bool DirectivesParser::set_option(JSON_TYPE t, JSON_VAL* v) {
+
+ const key* option_key = pop_key();
+ const key* enclosing_key = current_key();
+
+ if (option_key->type == value_array_key.type) {
+ // Multi value array, we are really setting the value
+ // for the key one step further up.
+ option_key = pop_key();
+ enclosing_key = current_key();
+
+ // Repush option_key and multi value marker, since
+ // we need to keep them until all multi values are set.
+ push_key(option_key);
+ push_key(&value_array_key);
+ }
+
+ switch (option_key->type) {
+ case type_flag:
+ {
+ if (current_directiveset == NULL) {
+ assert(depth == 2, "Must not have active directive set");
+
+ if (!set_option_flag(t, v, option_key, current_directive->_c1_store)) {
+ return false;
+ }
+ if (!set_option_flag(t, v, option_key, current_directive->_c2_store)) {
+ return false;
+ }
+ } else {
+ assert(depth > 2, "Must have active current directive set");
+ if (!set_option_flag(t, v, option_key, current_directiveset)) {
+ return false;
+ }
+ }
+ break;
+ }
+
+ case type_match:
+ if (t != JSON_STRING) {
+ error(VALUE_ERROR, "Key of type %s needs a value of type string", option_key->name);
+ return false;
+ }
+ if (enclosing_key->type != type_directives) {
+ error(SYNTAX_ERROR, "Match keyword can only exist inside a directive");
+ return false;
+ }
+ {
+ char* s = NEW_C_HEAP_ARRAY(char, v->str.length + 1, mtCompiler);
+ strncpy(s, v->str.start, v->str.length);
+ s[v->str.length] = '\0';
+
+ const char* error_msg = NULL;
+ if (!current_directive->add_match(s, error_msg)) {
+ assert (error_msg != NULL, "Must have valid error message");
+ error(VALUE_ERROR, "Method pattern error: %s", error_msg);
+ }
+ FREE_C_HEAP_ARRAY(char, s);
+ }
+ break;
+
+ case type_inline:
+ if (t != JSON_STRING) {
+ error(VALUE_ERROR, "Key of type %s needs a value of type string", option_key->name);
+ return false;
+ }
+ {
+ //char* s = strndup(v->str.start, v->str.length);
+ char* s = NEW_C_HEAP_ARRAY(char, v->str.length + 1, mtCompiler);
+ strncpy(s, v->str.start, v->str.length);
+ s[v->str.length] = '\0';
+
+ const char* error_msg = NULL;
+ if (current_directiveset == NULL) {
+ if (current_directive->_c1_store->parse_and_add_inline(s, error_msg)) {
+ if (!current_directive->_c2_store->parse_and_add_inline(s, error_msg)) {
+ assert (error_msg != NULL, "Must have valid error message");
+ error(VALUE_ERROR, "Method pattern error: %s", error_msg);
+ }
+ } else {
+ assert (error_msg != NULL, "Must have valid error message");
+ error(VALUE_ERROR, "Method pattern error: %s", error_msg);
+ }
+ } else {
+ if (!current_directiveset->parse_and_add_inline(s, error_msg)) {
+ assert (error_msg != NULL, "Must have valid error message");
+ error(VALUE_ERROR, "Method pattern error: %s", error_msg);
+ }
+ }
+ FREE_C_HEAP_ARRAY(char, s);
+ }
+ break;
+
+ case type_c1:
+ current_directiveset = current_directive->_c1_store;
+ if (t != JSON_TRUE && t != JSON_FALSE) {
+ error(VALUE_ERROR, "Key of type %s needs a true or false value", option_key->name);
+ return false;
+ }
+ break;
+
+ case type_c2:
+ current_directiveset = current_directive->_c2_store;
+ if (t != JSON_TRUE && t != JSON_FALSE) {
+ error(VALUE_ERROR, "Key of type %s needs a true or false value", option_key->name);
+ return false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool DirectivesParser::callback(JSON_TYPE t, JSON_VAL* v, uint rlimit) {
+ const key* k;
+
+ if (depth == 0) {
+ switch (t) {
+ case JSON_ARRAY_BEGIN:
+ return push_key(&dir_array_key);
+
+ case JSON_OBJECT_BEGIN:
+ // push synthetic dir_array
+ push_key(&dir_array_key);
+ assert(depth == 1, "Make sure the stack are aligned with the directives");
+ break;
+
+ default:
+ error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive.");
+ return false;
+ }
+ }
+ if (depth == 1) {
+ switch (t) {
+ case JSON_OBJECT_BEGIN:
+ // Parsing a new directive.
+ current_directive = new CompilerDirectives();
+ return push_key(&dir_key);
+
+ case JSON_ARRAY_END:
+ k = pop_key();
+
+ if (k->type != type_dir_array) {
+ error(SYNTAX_ERROR, "Expected end of directives array");
+ return false;
+ }
+ return true;
+
+ default:
+ error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive.");
+ return false;
+ }
+ } else {
+ switch (t) {
+ case JSON_OBJECT_BEGIN:
+ k = current_key();
+ switch (k->type) {
+ case type_c1:
+ current_directiveset = current_directive->_c1_store;
+ return true;
+ case type_c2:
+ current_directiveset = current_directive->_c2_store;
+ return true;
+
+ case type_dir_array:
+ return push_key(&dir_key);
+
+ default:
+ error(SYNTAX_ERROR, "The key '%s' does not allow an object to follow.", k->name);
+ return false;
+ }
+ return false;
+
+ case JSON_OBJECT_END:
+ k = pop_key();
+ switch (k->type) {
+ case type_c1:
+ case type_c2:
+ // This is how we now if options apply to a single or both directive sets
+ current_directiveset = NULL;
+ break;
+
+ case type_directives:
+ // Check, finish and push to stack!
+ if (current_directive->match() == NULL) {
+ error(INTERNAL_ERROR, "Directive missing required match.");
+ return false;
+ }
+ current_directive->finalize(_st);
+ push_tmp(current_directive);
+ current_directive = NULL;
+ break;
+
+ default:
+ error(INTERNAL_ERROR, "Object end with wrong key type on stack: %s.", k->name);
+ ShouldNotReachHere();
+ return false;
+ }
+ return true;
+
+ case JSON_ARRAY_BEGIN:
+ k = current_key();
+ if (!(k->allow_array_value)) {
+ if (k->type == type_dir_array) {
+ error(SYNTAX_ERROR, "Array not allowed inside top level array, expected directive object.");
+ } else {
+ error(VALUE_ERROR, "The key '%s' does not allow an array of values.", k->name);
+ }
+ return false;
+ }
+ return push_key(&value_array_key);
+
+ case JSON_ARRAY_END:
+ k = pop_key(); // Pop multi value marker
+ assert(k->type == value_array_key.type, "array end for level != 0 should terminate multi value");
+ k = pop_key(); // Pop key for option that was set
+ return true;
+
+ case JSON_KEY:
+ return push_key(v->str.start, v->str.length);
+
+ case JSON_STRING:
+ case JSON_NUMBER_INT:
+ case JSON_NUMBER_FLOAT:
+ case JSON_TRUE:
+ case JSON_FALSE:
+ case JSON_NULL:
+ return set_option(t, v);
+
+ default:
+ error(INTERNAL_ERROR, "Unknown JSON type: %d.", t);
+ ShouldNotReachHere();
+ return false;
+ }
+ }
+}
+
+#ifndef PRODUCT
+void DirectivesParser::test(const char* text, bool should_pass) {
+ DirectivesParser cd(text, tty, !VerboseInternalVMTests);
+ if (should_pass) {
+ assert(cd.valid() == true, "failed on a valid DirectivesParser string");
+ if (VerboseInternalVMTests) {
+ tty->print("-- DirectivesParser test passed as expected --\n");
+ }
+ } else {
+ assert(cd.valid() == false, "succeeded on an invalid DirectivesParser string");
+ if (VerboseInternalVMTests) {
+ tty->print("-- DirectivesParser test failed as expected --\n");
+ }
+ }
+ cd.clean_tmp();
+}
+
+void DirectivesParser::test() {
+ DirectivesParser::test("{}", false);
+ DirectivesParser::test("[]", true);
+ DirectivesParser::test("[{}]", false);
+ DirectivesParser::test("[{},{}]", false);
+ DirectivesParser::test("{},{}", false);
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " inline : \"+java/util.*\"," "\n"
+ " PrintAssembly: true," "\n"
+ " BreakAtExecute: true," "\n"
+ " }" "\n"
+ "]" "\n", true);
+
+ DirectivesParser::test(
+ "[" "\n"
+ " [" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " inline : \"+java/util.*\"," "\n"
+ " PrintAssembly: true," "\n"
+ " BreakAtExecute: true," "\n"
+ " }" "\n"
+ " ]" "\n"
+ "]" "\n", false);
+
+ /*DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " c1: {"
+ " PrintIntrinsics: false," "\n"
+ " }" "\n"
+ " }" "\n"
+ "]" "\n", false);*/
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " c2: {" "\n"
+ " PrintInlining: false," "\n"
+ " }" "\n"
+ " }" "\n"
+ "]" "\n", true);
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " c2: {" "\n"
+ " VectorizeDebug: 1," "\n"
+ " VectorizeDebug: -1," "\n"
+ " }" "\n"
+ " }" "\n"
+ "]" "\n", COMPILER2_PRESENT(true) NOT_COMPILER2(false));
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ " PrintInlining: [" "\n"
+ " true," "\n"
+ " false" "\n"
+ " ]," "\n"
+ " }" "\n"
+ "]" "\n", false);
+
+ DirectivesParser::test(
+ "[" "\n"
+ " {"
+ " // pattern to match against class+method+signature" "\n"
+ " // leading and trailing wildcard (*) allowed" "\n"
+ " match: \"foo/bar.*\"," "\n"
+ "" "\n"
+ " // override defaults for specified compiler" "\n"
+ " // we may differentiate between levels too. TBD." "\n"
+ " c1: {" "\n"
+ " //override c1 presets " "\n"
+ " DumpReplay: false," "\n"
+ " BreakAtCompile: true," "\n"
+ " }," "\n"
+ "" "\n"
+ " c2: {" "\n"
+ " // control inlining of method" "\n"
+ " // + force inline, - dont inline" "\n"
+ " inline : \"+java/util.*\"," "\n"
+ " PrintInlining: true," "\n"
+ " }," "\n"
+ "" "\n"
+ " // directives outside a specific preset applies to all compilers" "\n"
+ " inline : [ \"+java/util.*\", \"-com/sun.*\"]," "\n"
+ " BreakAtExecute: true," "\n"
+ " Log: true," "\n"
+ " }," "\n"
+ " {" "\n"
+ " // matching several patterns require an array" "\n"
+ " match: [\"baz.*\",\"frob.*\"]," "\n"
+ "" "\n"
+ " // applies to all compilers" "\n"
+ " // + force inline, - dont inline" "\n"
+ " inline : [ \"+java/util.*\", \"-com/sun.*\" ]," "\n"
+ " PrintInlining: true," "\n"
+ "" "\n"
+ " // force matching compiles to be blocking/syncronous" "\n"
+ " PrintNMethods: true" "\n"
+ " }," "\n"
+ "]" "\n", true);
+
+ // Test max stack depth
+ DirectivesParser::test(
+ "[" "\n" // depth 1: type_dir_array
+ " {" "\n" // depth 2: type_directives
+ " match: \"*.*\"," // match required
+ " c1:" "\n" // depth 3: type_c1
+ " {" "\n"
+ " inline:" "\n" // depth 4: type_inline
+ " [" "\n" // depth 5: type_value_array
+ " \"foo\"," "\n"
+ " \"bar\"," "\n"
+ " ]" "\n" // depth 3: pop type_value_array and type_inline keys
+ " }" "\n" // depth 2: pop type_c1 key
+ " }" "\n" // depth 1: pop type_directives key
+ "]" "\n", true); // depth 0: pop type_dir_array key
+
+ // Test max stack depth
+ DirectivesParser::test(
+ "[{c1:{c1:{c1:{c1:{c1:{c1:{c1:{}}}}}}}}]", false);
+
+}
+
+void DirectivesParser_test() {
+ DirectivesParser::test();
+}
+
+#endif