1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25#include "precompiled.hpp"
26#include "compiler/compileBroker.hpp"
27#include "compiler/directivesParser.hpp"
28#include "memory/allocation.inline.hpp"
29#include "memory/resourceArea.hpp"
30#include "runtime/os.hpp"
31#include <string.h>
32
33void DirectivesParser::push_tmp(CompilerDirectives* dir) {
34  _tmp_depth++;
35  dir->set_next(_tmp_top);
36  _tmp_top = dir;
37}
38
39CompilerDirectives* DirectivesParser::pop_tmp() {
40  if (_tmp_top == NULL) {
41    return NULL;
42  }
43  CompilerDirectives* tmp = _tmp_top;
44  _tmp_top = _tmp_top->next();
45  tmp->set_next(NULL);
46  _tmp_depth--;
47  return tmp;
48}
49
50void DirectivesParser::clean_tmp() {
51  CompilerDirectives* tmp = pop_tmp();
52  while (tmp != NULL) {
53    delete tmp;
54    tmp = pop_tmp();
55  }
56  assert(_tmp_depth == 0, "Consistency");
57}
58
59int DirectivesParser::parse_string(const char* text, outputStream* st) {
60  DirectivesParser cd(text, st, false);
61  if (cd.valid()) {
62    return cd.install_directives();
63  } else {
64    cd.clean_tmp();
65    st->flush();
66    st->print_cr("Parsing of compiler directives failed");
67    return -1;
68  }
69}
70
71bool DirectivesParser::has_file() {
72  return CompilerDirectivesFile != NULL;
73}
74
75bool DirectivesParser::parse_from_flag() {
76  return parse_from_file(CompilerDirectivesFile, tty);
77}
78
79bool DirectivesParser::parse_from_file(const char* filename, outputStream* st) {
80  assert(filename != NULL, "Test before calling this");
81  if (!parse_from_file_inner(filename, st)) {
82    st->print_cr("Could not load file: %s", filename);
83    return false;
84  }
85  return true;
86}
87
88bool DirectivesParser::parse_from_file_inner(const char* filename, outputStream* stream) {
89  struct stat st;
90  ResourceMark rm;
91  if (os::stat(filename, &st) == 0) {
92    // found file, open it
93    int file_handle = os::open(filename, 0, 0);
94    if (file_handle != -1) {
95      // read contents into resource array
96      char* buffer = NEW_RESOURCE_ARRAY(char, st.st_size+1);
97      size_t num_read = os::read(file_handle, (char*) buffer, st.st_size);
98      buffer[num_read] = '\0';
99      // close file
100      os::close(file_handle);
101      return parse_string(buffer, stream) > 0;
102    }
103  }
104  return false;
105}
106
107int DirectivesParser::install_directives() {
108  // Check limit
109  if (!DirectivesStack::check_capacity(_tmp_depth, _st)) {
110    clean_tmp();
111    return 0;
112  }
113
114  // Pop from internal temporary stack and push to compileBroker.
115  CompilerDirectives* tmp = pop_tmp();
116  int i = 0;
117  while (tmp != NULL) {
118    i++;
119    DirectivesStack::push(tmp);
120    tmp = pop_tmp();
121  }
122  if (i == 0) {
123    _st->print_cr("No directives in file");
124    return 0;
125  } else {
126    _st->print_cr("%i compiler directives added", i);
127    if (CompilerDirectivesPrint) {
128      // Print entire directives stack after new has been pushed.
129      DirectivesStack::print(_st);
130    }
131    return i;
132  }
133}
134
135DirectivesParser::DirectivesParser(const char* text, outputStream* st, bool silent)
136: JSON(text, silent, st), depth(0), current_directive(NULL), current_directiveset(NULL), _tmp_top(NULL), _tmp_depth(0) {
137#ifndef PRODUCT
138  memset(stack, 0, MAX_DEPTH * sizeof(stack[0]));
139#endif
140  parse();
141}
142
143DirectivesParser::~DirectivesParser() {
144  assert(_tmp_top == NULL, "Consistency");
145  assert(_tmp_depth == 0, "Consistency");
146}
147
148const DirectivesParser::key DirectivesParser::keys[] = {
149    // name, keytype, allow_array, allowed_mask, set_function
150    { "c1",     type_c1,     0, mask(type_directives), NULL, UnknownFlagType },
151    { "c2",     type_c2,     0, mask(type_directives), NULL, UnknownFlagType },
152    { "match",  type_match,  1, mask(type_directives), NULL, UnknownFlagType },
153    { "inline", type_inline, 1, mask(type_directives) | mask(type_c1) | mask(type_c2), NULL, UnknownFlagType },
154
155    // Global flags
156    #define common_flag_key(name, type, dvalue, compiler) \
157    { #name, type_flag, 0, mask(type_directives) | mask(type_c1) | mask(type_c2), &DirectiveSet::set_##name, type##Flag},
158    compilerdirectives_common_flags(common_flag_key)
159    compilerdirectives_c2_flags(common_flag_key)
160    compilerdirectives_c1_flags(common_flag_key)
161    #undef common_flag_key
162};
163
164const DirectivesParser::key DirectivesParser::dir_array_key = {
165     "top level directives array", type_dir_array, 0, 1 // Lowest bit means allow at top level
166};
167const DirectivesParser::key DirectivesParser::dir_key = {
168   "top level directive", type_directives, 0, mask(type_dir_array) | 1 // Lowest bit means allow at top level
169};
170const DirectivesParser::key DirectivesParser::value_array_key = {
171   "value array", type_value_array, 0, UINT_MAX // Allow all, checked by allow_array on other keys, not by allowed_mask from this key
172};
173
174const DirectivesParser::key* DirectivesParser::lookup_key(const char* str, size_t len) {
175  for (size_t i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
176    if (strncasecmp(keys[i].name, str, len) == 0) {
177      return &keys[i];
178    }
179  }
180  return NULL;
181}
182
183uint DirectivesParser::mask(keytype kt) {
184  return 1 << (kt + 1);
185}
186
187bool DirectivesParser::push_key(const char* str, size_t len) {
188  bool result = true;
189  const key* k = lookup_key(str, len);
190
191  if (k == NULL) {
192    // os::strdup
193    char* s = NEW_C_HEAP_ARRAY(char, len + 1, mtCompiler);
194    strncpy(s, str, len);
195    s[len] = '\0';
196    error(KEY_ERROR, "No such key: '%s'.", s);
197    FREE_C_HEAP_ARRAY(char, s);
198    return false;
199  }
200
201  return push_key(k);
202}
203
204bool DirectivesParser::push_key(const key* k) {
205  assert(k->allowedmask != 0, "not allowed anywhere?");
206
207  // Exceeding the stack should not be possible with a valid compiler directive,
208  // and an invalid should abort before this happens
209  assert(depth < MAX_DEPTH, "exceeded stack depth");
210  if (depth >= MAX_DEPTH) {
211    error(INTERNAL_ERROR, "Stack depth exceeded.");
212    return false;
213  }
214
215  assert(stack[depth] == NULL, "element not nulled, something is wrong");
216
217  if (depth == 0 && !(k->allowedmask & 1)) {
218    error(KEY_ERROR, "Key '%s' not allowed at top level.", k->name);
219    return false;
220  }
221
222  if (depth > 0) {
223    const key* prev = stack[depth - 1];
224    if (!(k->allowedmask & mask(prev->type))) {
225      error(KEY_ERROR, "Key '%s' not allowed after '%s' key.", k->name, prev->name);
226      return false;
227    }
228  }
229
230  stack[depth] = k;
231  depth++;
232  return true;
233}
234
235const DirectivesParser::key* DirectivesParser::current_key() {
236  assert(depth > 0, "getting key from empty stack");
237  if (depth == 0) {
238    return NULL;
239  }
240  return stack[depth - 1];
241}
242
243const DirectivesParser::key* DirectivesParser::pop_key() {
244  assert(depth > 0, "popping empty stack");
245  if (depth == 0) {
246    error(INTERNAL_ERROR, "Popping empty stack.");
247    return NULL;
248  }
249  depth--;
250
251  const key* k = stack[depth];
252#ifndef PRODUCT
253  stack[depth] = NULL;
254#endif
255
256  return k;
257}
258
259bool DirectivesParser::set_option_flag(JSON_TYPE t, JSON_VAL* v, const key* option_key, DirectiveSet* set) {
260
261  void (DirectiveSet::*test)(void *args);
262  test = option_key->set;
263
264  switch (t) {
265    case JSON_TRUE:
266      if (option_key->flag_type != boolFlag) {
267        error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]);
268        return false;
269      } else {
270        bool val = true;
271        (set->*test)((void *)&val);
272      }
273      break;
274
275    case JSON_FALSE:
276      if (option_key->flag_type != boolFlag) {
277        error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]);
278        return false;
279      } else {
280        bool val = false;
281        (set->*test)((void *)&val);
282      }
283      break;
284
285    case JSON_NUMBER_INT:
286      if (option_key->flag_type == intxFlag) {
287        intx ival = v->int_value;
288        (set->*test)((void *)&ival);
289      } else if (option_key->flag_type == uintxFlag) {
290        uintx ival = v->uint_value;
291        (set->*test)((void *)&ival);
292      } else if (option_key->flag_type == doubleFlag) {
293        double dval = (double)v->int_value;
294        (set->*test)((void *)&dval);
295      } else {
296        error(VALUE_ERROR, "Cannot use int value for an %s flag", flag_type_names[option_key->flag_type]);
297        return false;
298      }
299      break;
300
301    case JSON_NUMBER_FLOAT:
302      if (option_key->flag_type != doubleFlag) {
303        error(VALUE_ERROR, "Cannot use double value for an %s flag", flag_type_names[option_key->flag_type]);
304        return false;
305      } else {
306        double dval = v->double_value;
307        (set->*test)((void *)&dval);
308      }
309      break;
310
311    case JSON_STRING:
312      if (option_key->flag_type != ccstrFlag && option_key->flag_type != ccstrlistFlag) {
313        error(VALUE_ERROR, "Cannot use string value for a %s flag", flag_type_names[option_key->flag_type]);
314        return false;
315      } else {
316        char* s = NEW_C_HEAP_ARRAY(char, v->str.length+1,  mtCompiler);
317        strncpy(s, v->str.start, v->str.length + 1);
318        s[v->str.length] = '\0';
319        (set->*test)((void *)&s);
320      }
321      break;
322
323    default:
324      assert(0, "Should not reach here.");
325    }
326  return true;
327}
328
329bool DirectivesParser::set_option(JSON_TYPE t, JSON_VAL* v) {
330
331  const key* option_key = pop_key();
332  const key* enclosing_key = current_key();
333
334  if (option_key->type == value_array_key.type) {
335    // Multi value array, we are really setting the value
336    // for the key one step further up.
337    option_key = pop_key();
338    enclosing_key = current_key();
339
340    // Repush option_key and multi value marker, since
341    // we need to keep them until all multi values are set.
342    push_key(option_key);
343    push_key(&value_array_key);
344  }
345
346  switch (option_key->type) {
347  case type_flag:
348  {
349    if (current_directiveset == NULL) {
350      assert(depth == 2, "Must not have active directive set");
351
352      if (!set_option_flag(t, v, option_key, current_directive->_c1_store)) {
353        return false;
354      }
355      if (!set_option_flag(t, v, option_key, current_directive->_c2_store)) {
356        return false;
357      }
358    } else {
359      assert(depth > 2, "Must have active current directive set");
360      if (!set_option_flag(t, v, option_key, current_directiveset)) {
361        return false;
362      }
363    }
364    break;
365  }
366
367  case type_match:
368    if (t != JSON_STRING) {
369      error(VALUE_ERROR, "Key of type %s needs a value of type string", option_key->name);
370      return false;
371    }
372    if (enclosing_key->type != type_directives) {
373      error(SYNTAX_ERROR, "Match keyword can only exist inside a directive");
374      return false;
375    }
376    {
377      char* s = NEW_C_HEAP_ARRAY(char, v->str.length + 1, mtCompiler);
378      strncpy(s, v->str.start, v->str.length);
379      s[v->str.length] = '\0';
380
381      const char* error_msg = NULL;
382      if (!current_directive->add_match(s, error_msg)) {
383        assert (error_msg != NULL, "Must have valid error message");
384        error(VALUE_ERROR, "Method pattern error: %s", error_msg);
385      }
386      FREE_C_HEAP_ARRAY(char, s);
387    }
388    break;
389
390  case type_inline:
391    if (t != JSON_STRING) {
392      error(VALUE_ERROR, "Key of type %s needs a value of type string", option_key->name);
393      return false;
394    }
395    {
396      //char* s = strndup(v->str.start, v->str.length);
397      char* s = NEW_C_HEAP_ARRAY(char, v->str.length + 1, mtCompiler);
398      strncpy(s, v->str.start, v->str.length);
399      s[v->str.length] = '\0';
400
401      const char* error_msg = NULL;
402      if (current_directiveset == NULL) {
403        if (current_directive->_c1_store->parse_and_add_inline(s, error_msg)) {
404          if (!current_directive->_c2_store->parse_and_add_inline(s, error_msg)) {
405            assert (error_msg != NULL, "Must have valid error message");
406            error(VALUE_ERROR, "Method pattern error: %s", error_msg);
407          }
408        } else {
409          assert (error_msg != NULL, "Must have valid error message");
410          error(VALUE_ERROR, "Method pattern error: %s", error_msg);
411        }
412      } else {
413        if (!current_directiveset->parse_and_add_inline(s, error_msg)) {
414          assert (error_msg != NULL, "Must have valid error message");
415          error(VALUE_ERROR, "Method pattern error: %s", error_msg);
416        }
417      }
418      FREE_C_HEAP_ARRAY(char, s);
419    }
420    break;
421
422  case type_c1:
423    current_directiveset = current_directive->_c1_store;
424    if (t != JSON_TRUE && t != JSON_FALSE) {
425      error(VALUE_ERROR, "Key of type %s needs a true or false value", option_key->name);
426      return false;
427    }
428    break;
429
430  case type_c2:
431    current_directiveset = current_directive->_c2_store;
432    if (t != JSON_TRUE && t != JSON_FALSE) {
433      error(VALUE_ERROR, "Key of type %s needs a true or false value", option_key->name);
434      return false;
435    }
436    break;
437
438  default:
439    break;
440  }
441
442  return true;
443}
444
445bool DirectivesParser::callback(JSON_TYPE t, JSON_VAL* v, uint rlimit) {
446  const key* k;
447
448  if (depth == 0) {
449    switch (t) {
450      case JSON_ARRAY_BEGIN:
451        return push_key(&dir_array_key);
452
453      case JSON_OBJECT_BEGIN:
454        // push synthetic dir_array
455        push_key(&dir_array_key);
456        assert(depth == 1, "Make sure the stack are aligned with the directives");
457        break;
458
459      default:
460        error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive.");
461        return false;
462      }
463  }
464  if (depth == 1) {
465    switch (t) {
466      case JSON_OBJECT_BEGIN:
467        // Parsing a new directive.
468        current_directive = new CompilerDirectives();
469        return push_key(&dir_key);
470
471      case JSON_ARRAY_END:
472        k = pop_key();
473
474        if (k->type != type_dir_array) {
475          error(SYNTAX_ERROR, "Expected end of directives array");
476          return false;
477        }
478        return true;
479
480    default:
481      error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive.");
482      return false;
483    }
484  } else {
485    switch (t) {
486    case JSON_OBJECT_BEGIN:
487      k = current_key();
488      switch (k->type) {
489      case type_c1:
490        current_directiveset = current_directive->_c1_store;
491        return true;
492      case type_c2:
493        current_directiveset = current_directive->_c2_store;
494        return true;
495
496      case type_dir_array:
497        return push_key(&dir_key);
498
499      default:
500        error(SYNTAX_ERROR, "The key '%s' does not allow an object to follow.", k->name);
501        return false;
502      }
503      return false;
504
505    case JSON_OBJECT_END:
506      k = pop_key();
507      switch (k->type) {
508      case type_c1:
509      case type_c2:
510        // This is how we now if options apply to a single or both directive sets
511        current_directiveset = NULL;
512        break;
513
514      case type_directives:
515        // Check, finish and push to stack!
516        if (current_directive->match() == NULL) {
517          error(INTERNAL_ERROR, "Directive missing required match.");
518          return false;
519        }
520        current_directive->finalize(_st);
521        push_tmp(current_directive);
522        current_directive = NULL;
523        break;
524
525      default:
526        error(INTERNAL_ERROR, "Object end with wrong key type on stack: %s.", k->name);
527        ShouldNotReachHere();
528        return false;
529      }
530      return true;
531
532    case JSON_ARRAY_BEGIN:
533      k = current_key();
534      if (!(k->allow_array_value)) {
535        if (k->type == type_dir_array) {
536          error(SYNTAX_ERROR, "Array not allowed inside top level array, expected directive object.");
537        } else {
538          error(VALUE_ERROR, "The key '%s' does not allow an array of values.", k->name);
539        }
540        return false;
541      }
542      return push_key(&value_array_key);
543
544    case JSON_ARRAY_END:
545      k = pop_key(); // Pop multi value marker
546      assert(k->type == value_array_key.type, "array end for level != 0 should terminate multi value");
547      k = pop_key(); // Pop key for option that was set
548      return true;
549
550    case JSON_KEY:
551      return push_key(v->str.start, v->str.length);
552
553    case JSON_STRING:
554    case JSON_NUMBER_INT:
555    case JSON_NUMBER_FLOAT:
556    case JSON_TRUE:
557    case JSON_FALSE:
558    case JSON_NULL:
559      return set_option(t, v);
560
561    default:
562      error(INTERNAL_ERROR, "Unknown JSON type: %d.", t);
563      ShouldNotReachHere();
564      return false;
565    }
566  }
567}
568
569#ifndef PRODUCT
570void DirectivesParser::test(const char* text, bool should_pass) {
571  DirectivesParser cd(text, tty, !VerboseInternalVMTests);
572  if (should_pass) {
573    assert(cd.valid() == true, "failed on a valid DirectivesParser string");
574    if (VerboseInternalVMTests) {
575      tty->print("-- DirectivesParser test passed as expected --\n");
576    }
577  } else {
578    assert(cd.valid() == false, "succeeded on an invalid DirectivesParser string");
579    if (VerboseInternalVMTests) {
580      tty->print("-- DirectivesParser test failed as expected --\n");
581    }
582  }
583  cd.clean_tmp();
584}
585
586void DirectivesParser::test() {
587  DirectivesParser::test("{}", false);
588  DirectivesParser::test("[]", true);
589  DirectivesParser::test("[{}]", false);
590  DirectivesParser::test("[{},{}]", false);
591  DirectivesParser::test("{},{}", false);
592
593  DirectivesParser::test(
594    "[" "\n"
595    "  {" "\n"
596    "    match: \"foo/bar.*\"," "\n"
597    "    inline : \"+java/util.*\"," "\n"
598    "    PrintAssembly: true," "\n"
599    "    BreakAtExecute: true," "\n"
600    "  }" "\n"
601    "]" "\n", true);
602
603  DirectivesParser::test(
604    "[" "\n"
605    "  [" "\n"
606    "    {" "\n"
607    "      match: \"foo/bar.*\"," "\n"
608    "      inline : \"+java/util.*\"," "\n"
609    "      PrintAssembly: true," "\n"
610    "      BreakAtExecute: true," "\n"
611    "    }" "\n"
612    "  ]" "\n"
613    "]" "\n", false);
614
615  /*DirectivesParser::test(
616    "[" "\n"
617    "  {" "\n"
618    "    match: \"foo/bar.*\"," "\n"
619    "    c1: {"
620    "      PrintIntrinsics: false," "\n"
621    "    }" "\n"
622    "  }" "\n"
623    "]" "\n", false);*/
624
625  DirectivesParser::test(
626    "[" "\n"
627    "  {" "\n"
628    "    match: \"foo/bar.*\"," "\n"
629    "    c2: {" "\n"
630    "      PrintInlining: false," "\n"
631    "    }" "\n"
632    "  }" "\n"
633    "]" "\n", true);
634
635  DirectivesParser::test(
636    "[" "\n"
637    "  {" "\n"
638    "    match: \"foo/bar.*\"," "\n"
639    "    c2: {" "\n"
640    "      VectorizeDebug: 1," "\n"
641    "      VectorizeDebug: -1," "\n"
642    "    }" "\n"
643    "  }" "\n"
644    "]" "\n", COMPILER2_PRESENT(true) NOT_COMPILER2(false));
645
646  DirectivesParser::test(
647    "[" "\n"
648    "  {" "\n"
649    "    match: \"foo/bar.*\"," "\n"
650    "    PrintInlining: [" "\n"
651    "      true," "\n"
652    "      false" "\n"
653    "    ]," "\n"
654    "  }" "\n"
655    "]" "\n", false);
656
657  DirectivesParser::test(
658    "[" "\n"
659    "  {"
660    "    // pattern to match against class+method+signature" "\n"
661    "    // leading and trailing wildcard (*) allowed" "\n"
662    "    match: \"foo/bar.*\"," "\n"
663    "" "\n"
664    "    // override defaults for specified compiler" "\n"
665    "    // we may differentiate between levels too. TBD." "\n"
666    "    c1:  {" "\n"
667    "      //override c1 presets " "\n"
668    "      DumpReplay: false," "\n"
669    "      BreakAtCompile: true," "\n"
670    "    }," "\n"
671    "" "\n"
672    "    c2: {" "\n"
673    "        // control inlining of method" "\n"
674    "        // + force inline, - dont inline" "\n"
675    "        inline : \"+java/util.*\"," "\n"
676    "        PrintInlining: true," "\n"
677    "    }," "\n"
678    "" "\n"
679    "    // directives outside a specific preset applies to all compilers" "\n"
680    "    inline : [ \"+java/util.*\", \"-com/sun.*\"]," "\n"
681    "    BreakAtExecute: true," "\n"
682    "    Log: true," "\n"
683    "  }," "\n"
684    "  {" "\n"
685    "    // matching several patterns require an array" "\n"
686    "    match: [\"baz.*\",\"frob.*\"]," "\n"
687    "" "\n"
688    "    // applies to all compilers" "\n"
689    "    // + force inline, - dont inline" "\n"
690    "    inline : [ \"+java/util.*\", \"-com/sun.*\" ]," "\n"
691    "    PrintInlining: true," "\n"
692    "" "\n"
693    "    // force matching compiles to be blocking/syncronous" "\n"
694    "    PrintNMethods: true" "\n"
695    "  }," "\n"
696    "]" "\n", true);
697
698  // Test max stack depth
699    DirectivesParser::test(
700      "[" "\n"             // depth 1: type_dir_array
701      "  {" "\n"           // depth 2: type_directives
702      "    match: \"*.*\"," // match required
703      "    c1:" "\n"       // depth 3: type_c1
704      "    {" "\n"
705      "      inline:" "\n" // depth 4: type_inline
706      "      [" "\n"       // depth 5: type_value_array
707      "        \"foo\"," "\n"
708      "        \"bar\"," "\n"
709      "      ]" "\n"       // depth 3: pop type_value_array and type_inline keys
710      "    }" "\n"         // depth 2: pop type_c1 key
711      "  }" "\n"           // depth 1: pop type_directives key
712      "]" "\n", true);     // depth 0: pop type_dir_array key
713
714    // Test max stack depth
715    DirectivesParser::test(
716      "[{c1:{c1:{c1:{c1:{c1:{c1:{c1:{}}}}}}}}]", false);
717
718}
719
720void DirectivesParser_test() {
721  DirectivesParser::test();
722}
723
724#endif
725