1 /* 2 * Copyright (c) 2015, 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 "runtime/os.hpp" 30 #include <string.h> 31 32 void DirectivesParser::push_tmp(CompilerDirectives* dir) { 33 _tmp_depth++; 34 dir->set_next(_tmp_top); 35 _tmp_top = dir; 36 } 37 38 CompilerDirectives* DirectivesParser::pop_tmp() { 39 if (_tmp_top == NULL) { 40 return NULL; 41 } 42 CompilerDirectives* tmp = _tmp_top; 43 _tmp_top = _tmp_top->next(); 44 tmp->set_next(NULL); 45 _tmp_depth--; 46 return tmp; 47 } 48 49 void DirectivesParser::clean_tmp() { 50 CompilerDirectives* tmp = pop_tmp(); 51 while (tmp != NULL) { 52 delete tmp; 53 tmp = pop_tmp(); 54 } 55 assert(_tmp_depth == 0, "Consistency"); 56 } 57 58 bool DirectivesParser::parse_string(const char* text, outputStream* st) { 59 DirectivesParser cd(text, st); 60 if (cd.valid()) { 61 return cd.install_directives(); 62 } else { 63 cd.clean_tmp(); 64 st->flush(); 65 st->print_cr("Parsing of compiler directives failed"); 66 return false; 67 } 68 } 69 70 bool DirectivesParser::has_file() { 71 return CompilerDirectivesFile != NULL; 72 } 73 74 bool DirectivesParser::parse_from_flag() { 75 return parse_from_file(CompilerDirectivesFile, tty); 76 } 77 78 bool DirectivesParser::parse_from_file(const char* filename, outputStream* st) { 79 assert(filename != NULL, "Test before calling this"); 80 if (!parse_from_file_inner(filename, st)) { 81 st->print_cr("Could not load file: %s", filename); 82 return false; 83 } 84 return true; 85 } 86 87 bool DirectivesParser::parse_from_file_inner(const char* filename, outputStream* stream) { 88 struct stat st; 89 ResourceMark rm; 90 if (os::stat(filename, &st) == 0) { 91 // found file, open it 92 int file_handle = os::open(filename, 0, 0); 93 if (file_handle != -1) { 94 // read contents into resource array 95 char* buffer = NEW_RESOURCE_ARRAY(char, st.st_size+1); 96 size_t num_read = os::read(file_handle, (char*) buffer, st.st_size); 97 buffer[num_read] = '\0'; 98 // close file 99 os::close(file_handle); 100 return parse_string(buffer, stream); 101 } 102 } 103 return false; 104 } 105 106 bool DirectivesParser::install_directives() { 107 // Check limit 108 if (!DirectivesStack::check_capacity(_tmp_depth, _st)) { 109 clean_tmp(); 110 return false; 111 } 112 113 // Pop from internal temporary stack and push to compileBroker. 114 CompilerDirectives* tmp = pop_tmp(); 115 int i = 0; 116 while (tmp != NULL) { 117 i++; 118 DirectivesStack::push(tmp); 119 tmp = pop_tmp(); 120 } 121 if (i == 0) { 122 _st->print_cr("No directives in file"); 123 return false; 124 } else { 125 _st->print_cr("%i compiler directives added", i); 126 if (CompilerDirectivesPrint) { 127 // Print entire directives stack after new has been pushed. 128 DirectivesStack::print(_st); 129 } 130 return true; 131 } 132 } 133 134 DirectivesParser::DirectivesParser(const char* text, outputStream* st) 135 : JSON(text, false, st), depth(0), current_directive(NULL), current_directiveset(NULL), _tmp_top(NULL), _tmp_depth(0) { 136 #ifndef PRODUCT 137 memset(stack, 0, MAX_DEPTH * sizeof(stack[0])); 138 #endif 139 parse(); 140 } 141 142 DirectivesParser::~DirectivesParser() { 143 assert(_tmp_top == NULL, "Consistency"); 144 assert(_tmp_depth == 0, "Consistency"); 145 } 146 147 const DirectivesParser::key DirectivesParser::keys[] = { 148 // name, keytype, allow_array, allowed_mask, set_function 149 { "c1", type_c1, 0, mask(type_directives), NULL, UnknownFlagType }, 150 { "c2", type_c2, 0, mask(type_directives), NULL, UnknownFlagType }, 151 { "match", type_match, 1, mask(type_directives), NULL, UnknownFlagType }, 152 { "inline", type_inline, 1, mask(type_directives) | mask(type_c1) | mask(type_c2), NULL, UnknownFlagType }, 153 { "enable", type_enable, 1, mask(type_directives) | mask(type_c1) | mask(type_c2), NULL, UnknownFlagType }, 154 { "preset", type_preset, 0, mask(type_c1) | mask(type_c2), NULL, UnknownFlagType }, 155 156 // Global flags 157 #define common_flag_key(name, type, dvalue, compiler) \ 158 { #name, type_flag, 0, mask(type_directives) | mask(type_c1) | mask(type_c2), &DirectiveSet::set_##name, type##Flag}, 159 compilerdirectives_common_flags(common_flag_key) 160 compilerdirectives_c2_flags(common_flag_key) 161 compilerdirectives_c1_flags(common_flag_key) 162 #undef common_flag_key 163 }; 164 165 const DirectivesParser::key DirectivesParser::dir_array_key = { 166 "top level directives array", type_dir_array, 0, 1 // Lowest bit means allow at top level 167 }; 168 const DirectivesParser::key DirectivesParser::dir_key = { 169 "top level directive", type_directives, 0, mask(type_dir_array) | 1 // Lowest bit means allow at top level 170 }; 171 const DirectivesParser::key DirectivesParser::value_array_key = { 172 "value array", type_value_array, 0, UINT_MAX // Allow all, checked by allow_array on other keys, not by allowed_mask from this key 173 }; 174 175 const DirectivesParser::key* DirectivesParser::lookup_key(const char* str, size_t len) { 176 for (size_t i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) { 177 if (strncasecmp(keys[i].name, str, len) == 0) { 178 return &keys[i]; 179 } 180 } 181 return NULL; 182 } 183 184 uint DirectivesParser::mask(keytype kt) { 185 return 1 << (kt + 1); 186 } 187 188 bool DirectivesParser::push_key(const char* str, size_t len) { 189 bool result = true; 190 const key* k = lookup_key(str, len); 191 192 if (k == NULL) { 193 // os::strdup 194 char* s = NEW_C_HEAP_ARRAY(char, len + 1, mtCompiler); 195 strncpy(s, str, len); 196 s[len] = '\0'; 197 error(KEY_ERROR, "No such key: '%s'.", s); 198 FREE_C_HEAP_ARRAY(char, s); 199 return false; 200 } 201 202 return push_key(k); 203 } 204 205 bool DirectivesParser::push_key(const key* k) { 206 assert(k->allowedmask != 0, "not allowed anywhere?"); 207 208 // Exceeding the stack should not be possible with a valid compiler directive, 209 // and an invalid should abort before this happens 210 assert(depth < MAX_DEPTH, "exceeded stack depth"); 211 if (depth >= MAX_DEPTH) { 212 error(INTERNAL_ERROR, "Stack depth exceeded."); 213 return false; 214 } 215 216 assert(stack[depth] == NULL, "element not nulled, something is wrong"); 217 218 if (depth == 0 && !(k->allowedmask & 1)) { 219 error(KEY_ERROR, "Key '%s' not allowed at top level.", k->name); 220 return false; 221 } 222 223 if (depth > 0) { 224 const key* prev = stack[depth - 1]; 225 if (!(k->allowedmask & mask(prev->type))) { 226 error(KEY_ERROR, "Key '%s' not allowed after '%s' key.", k->name, prev->name); 227 return false; 228 } 229 } 230 231 stack[depth] = k; 232 depth++; 233 return true; 234 } 235 236 const DirectivesParser::key* DirectivesParser::current_key() { 237 assert(depth > 0, "getting key from empty stack"); 238 if (depth == 0) { 239 return NULL; 240 } 241 return stack[depth - 1]; 242 } 243 244 const DirectivesParser::key* DirectivesParser::pop_key() { 245 assert(depth > 0, "popping empty stack"); 246 if (depth == 0) { 247 error(INTERNAL_ERROR, "Popping empty stack."); 248 return NULL; 249 } 250 depth--; 251 252 const key* k = stack[depth]; 253 #ifndef PRODUCT 254 stack[depth] = NULL; 255 #endif 256 257 return k; 258 } 259 260 bool DirectivesParser::set_option_flag(JSON_TYPE t, JSON_VAL* v, const key* option_key, DirectiveSet* set) { 261 262 void (DirectiveSet::*test)(void *args); 263 test = option_key->set; 264 265 switch (t) { 266 case JSON_TRUE: 267 if (option_key->flag_type != boolFlag) { 268 error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]); 269 return false; 270 } else { 271 bool val = true; 272 (set->*test)((void *)&val); 273 } 274 break; 275 276 case JSON_FALSE: 277 if (option_key->flag_type != boolFlag) { 278 error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]); 279 return false; 280 } else { 281 bool val = false; 282 (set->*test)((void *)&val); 283 } 284 break; 285 286 case JSON_NUMBER_INT: 287 if (option_key->flag_type != intxFlag) { 288 if (option_key->flag_type == doubleFlag) { 289 double dval = (double)v->int_value; 290 (set->*test)((void *)&dval); 291 break; 292 } 293 error(VALUE_ERROR, "Cannot use int value for an %s flag", flag_type_names[option_key->flag_type]); 294 return false; 295 } else { 296 intx ival = v->int_value; 297 (set->*test)((void *)&ival); 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 329 bool 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 case type_enable: 439 switch (enclosing_key->type) { 440 case type_c1: 441 case type_c2: 442 { 443 if (t != JSON_TRUE && t != JSON_FALSE) { 444 error(VALUE_ERROR, "Key of type %s enclosed in a %s key needs a true or false value", option_key->name, enclosing_key->name); 445 return false; 446 } 447 int val = (t == JSON_TRUE); 448 current_directiveset->set_Enable(&val); 449 break; 450 } 451 452 case type_directives: 453 error(VALUE_ERROR, "Enable keyword not available for generic directive"); 454 return false; 455 456 default: 457 error(INTERNAL_ERROR, "Unexpected enclosing type for key %s: %s", option_key->name, enclosing_key->name); 458 ShouldNotReachHere(); 459 return false; 460 } 461 break; 462 463 default: 464 break; 465 } 466 467 return true; 468 } 469 470 bool DirectivesParser::callback(JSON_TYPE t, JSON_VAL* v, uint rlimit) { 471 const key* k; 472 473 if (depth == 0) { 474 switch (t) { 475 case JSON_ARRAY_BEGIN: 476 return push_key(&dir_array_key); 477 478 case JSON_OBJECT_BEGIN: 479 // push synthetic dir_array 480 push_key(&dir_array_key); 481 assert(depth == 1, "Make sure the stack are aligned with the directives"); 482 break; 483 484 default: 485 error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive."); 486 return false; 487 } 488 } 489 if (depth == 1) { 490 switch (t) { 491 case JSON_OBJECT_BEGIN: 492 // Parsing a new directive. 493 current_directive = new CompilerDirectives(); 494 return push_key(&dir_key); 495 496 case JSON_ARRAY_END: 497 k = pop_key(); 498 499 if (k->type != type_dir_array) { 500 error(SYNTAX_ERROR, "Expected end of directives array"); 501 return false; 502 } 503 return true; 504 505 default: 506 error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive."); 507 return false; 508 } 509 } else { 510 switch (t) { 511 case JSON_OBJECT_BEGIN: 512 k = current_key(); 513 switch (k->type) { 514 case type_c1: 515 current_directiveset = current_directive->_c1_store; 516 return true; 517 case type_c2: 518 current_directiveset = current_directive->_c2_store; 519 return true; 520 521 case type_dir_array: 522 return push_key(&dir_key); 523 524 default: 525 error(SYNTAX_ERROR, "The key '%s' does not allow an object to follow.", k->name); 526 return false; 527 } 528 return false; 529 530 case JSON_OBJECT_END: 531 k = pop_key(); 532 switch (k->type) { 533 case type_c1: 534 case type_c2: 535 // This is how we now if options apply to a single or both directive sets 536 current_directiveset = NULL; 537 break; 538 539 case type_directives: 540 // Check, finish and push to stack! 541 if (current_directive->match() == NULL) { 542 error(INTERNAL_ERROR, "Directive missing required match."); 543 return false; 544 } 545 current_directive->finalize(_st); 546 push_tmp(current_directive); 547 current_directive = NULL; 548 break; 549 550 default: 551 error(INTERNAL_ERROR, "Object end with wrong key type on stack: %s.", k->name); 552 ShouldNotReachHere(); 553 return false; 554 } 555 return true; 556 557 case JSON_ARRAY_BEGIN: 558 k = current_key(); 559 if (!(k->allow_array_value)) { 560 if (k->type == type_dir_array) { 561 error(SYNTAX_ERROR, "Array not allowed inside top level array, expected directive object."); 562 } else { 563 error(VALUE_ERROR, "The key '%s' does not allow an array of values.", k->name); 564 } 565 return false; 566 } 567 return push_key(&value_array_key); 568 569 case JSON_ARRAY_END: 570 k = pop_key(); // Pop multi value marker 571 assert(k->type == value_array_key.type, "array end for level != 0 should terminate multi value"); 572 k = pop_key(); // Pop key for option that was set 573 return true; 574 575 case JSON_KEY: 576 return push_key(v->str.start, v->str.length); 577 578 case JSON_STRING: 579 case JSON_NUMBER_INT: 580 case JSON_NUMBER_FLOAT: 581 case JSON_TRUE: 582 case JSON_FALSE: 583 case JSON_NULL: 584 return set_option(t, v); 585 586 default: 587 error(INTERNAL_ERROR, "Unknown JSON type: %d.", t); 588 ShouldNotReachHere(); 589 return false; 590 } 591 } 592 } 593 594 #ifndef PRODUCT 595 void DirectivesParser::test(const char* text, bool should_pass) { 596 DirectivesParser cd(text, tty); 597 if (should_pass) { 598 assert(cd.valid() == true, "failed on a valid DirectivesParser string"); 599 if (VerboseInternalVMTests) { 600 tty->print("-- DirectivesParser test passed as expected --\n"); 601 } 602 } else { 603 assert(cd.valid() == false, "succeeded on an invalid DirectivesParser string"); 604 if (VerboseInternalVMTests) { 605 tty->print("-- DirectivesParser test failed as expected --\n"); 606 } 607 } 608 cd.clean_tmp(); 609 } 610 611 void DirectivesParser::test() { 612 DirectivesParser::test("{}", false); 613 DirectivesParser::test("[]", true); 614 DirectivesParser::test("[{}]", false); 615 DirectivesParser::test("[{},{}]", false); 616 DirectivesParser::test("{},{}", false); 617 618 DirectivesParser::test( 619 "[" "\n" 620 " {" "\n" 621 " match: \"foo/bar.*\"," "\n" 622 " inline : \"+java/util.*\"," "\n" 623 " PrintAssembly: true," "\n" 624 " BreakAtExecute: true," "\n" 625 " }" "\n" 626 "]" "\n", true); 627 628 DirectivesParser::test( 629 "[" "\n" 630 " [" "\n" 631 " {" "\n" 632 " match: \"foo/bar.*\"," "\n" 633 " inline : \"+java/util.*\"," "\n" 634 " PrintAssembly: true," "\n" 635 " BreakAtExecute: true," "\n" 636 " }" "\n" 637 " ]" "\n" 638 "]" "\n", false); 639 640 /*DirectivesParser::test( 641 "[" "\n" 642 " {" "\n" 643 " match: \"foo/bar.*\"," "\n" 644 " c1: {" 645 " PrintIntrinsics: false," "\n" 646 " }" "\n" 647 " }" "\n" 648 "]" "\n", false);*/ 649 650 DirectivesParser::test( 651 "[" "\n" 652 " {" "\n" 653 " match: \"foo/bar.*\"," "\n" 654 " c2: {" "\n" 655 " PrintInlining: false," "\n" 656 " }" "\n" 657 " }" "\n" 658 "]" "\n", true); 659 660 DirectivesParser::test( 661 "[" "\n" 662 " {" "\n" 663 " match: \"foo/bar.*\"," "\n" 664 " PrintInlining: [" "\n" 665 " true," "\n" 666 " false" "\n" 667 " ]," "\n" 668 " }" "\n" 669 "]" "\n", false); 670 671 DirectivesParser::test( 672 "[" "\n" 673 " {" 674 " // pattern to match against class+method+signature" "\n" 675 " // leading and trailing wildcard (*) allowed" "\n" 676 " match: \"foo/bar.*\"," "\n" 677 "" "\n" 678 " // override defaults for specified compiler" "\n" 679 " // we may differentiate between levels too. TBD." "\n" 680 " c1: {" "\n" 681 " //override c1 presets " "\n" 682 " DumpReplay: false," "\n" 683 " BreakAtCompile: true," "\n" 684 " }," "\n" 685 "" "\n" 686 " c2: {" "\n" 687 " // control inlining of method" "\n" 688 " // + force inline, - dont inline" "\n" 689 " inline : \"+java/util.*\"," "\n" 690 " PrintInlining: true," "\n" 691 " }," "\n" 692 "" "\n" 693 " // directives outside a specific preset applies to all compilers" "\n" 694 " inline : [ \"+java/util.*\", \"-com/sun.*\"]," "\n" 695 " BreakAtExecute: true," "\n" 696 " Log: true," "\n" 697 " }," "\n" 698 " {" "\n" 699 " // matching several patterns require an array" "\n" 700 " match: [\"baz.*\",\"frob.*\"]," "\n" 701 "" "\n" 702 " // applies to all compilers" "\n" 703 " // + force inline, - dont inline" "\n" 704 " inline : [ \"+java/util.*\", \"-com/sun.*\" ]," "\n" 705 " PrintInlining: true," "\n" 706 "" "\n" 707 " // force matching compiles to be blocking/syncronous" "\n" 708 " PrintNMethods: true" "\n" 709 " }," "\n" 710 "]" "\n", true); 711 712 // Test max stack depth 713 DirectivesParser::test( 714 "[" "\n" // depth 1: type_dir_array 715 " {" "\n" // depth 2: type_directives 716 " match: \"*.*\"," // match required 717 " c1:" "\n" // depth 3: type_c1 718 " {" "\n" 719 " inline:" "\n" // depth 4: type_inline 720 " [" "\n" // depth 5: type_value_array 721 " \"foo\"," "\n" 722 " \"bar\"," "\n" 723 " ]" "\n" // depth 3: pop type_value_array and type_inline keys 724 " }" "\n" // depth 2: pop type_c1 key 725 " }" "\n" // depth 1: pop type_directives key 726 "]" "\n", true); // depth 0: pop type_dir_array key 727 728 // Test max stack depth 729 DirectivesParser::test( 730 "[{c1:{c1:{c1:{c1:{c1:{c1:{c1:{}}}}}}}}]", false); 731 732 DirectivesParser::test( 733 "[" "\n" 734 " {" "\n" 735 " c1: true," "\n" 736 " c2: true," "\n" 737 " match: true," "\n" 738 " inline: true," "\n" 739 " enable: true," "\n" 740 " c1: {" "\n" 741 " preset: true," "\n" 742 " }" "\n" 743 " }" "\n" 744 "]" "\n", false); 745 } 746 747 void DirectivesParser_test() { 748 DirectivesParser::test(); 749 } 750 751 #endif