1 /* 2 * Copyright (c) 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 33 void DirectivesParser::push_tmp(CompilerDirectives* dir) { 34 _tmp_depth++; 35 dir->set_next(_tmp_top); 36 _tmp_top = dir; 37 } 38 39 CompilerDirectives* 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 50 void 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 59 bool DirectivesParser::parse_string(const char* text, outputStream* st) { 60 DirectivesParser cd(text, st); 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 false; 68 } 69 } 70 71 bool DirectivesParser::has_file() { 72 return CompilerDirectivesFile != NULL; 73 } 74 75 bool DirectivesParser::parse_from_flag() { 76 return parse_from_file(CompilerDirectivesFile, tty); 77 } 78 79 bool 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 88 bool 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); 102 } 103 } 104 return false; 105 } 106 107 bool DirectivesParser::install_directives() { 108 // Check limit 109 if (!DirectivesStack::check_capacity(_tmp_depth, _st)) { 110 clean_tmp(); 111 return false; 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 false; 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 true; 132 } 133 } 134 135 DirectivesParser::DirectivesParser(const char* text, outputStream* st) 136 : JSON(text, false, 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 143 DirectivesParser::~DirectivesParser() { 144 assert(_tmp_top == NULL, "Consistency"); 145 assert(_tmp_depth == 0, "Consistency"); 146 } 147 148 const 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 { "enable", type_enable, 1, mask(type_directives) | mask(type_c1) | mask(type_c2), NULL, UnknownFlagType }, 155 { "preset", type_preset, 0, mask(type_c1) | mask(type_c2), NULL, UnknownFlagType }, 156 157 // Global flags 158 #define common_flag_key(name, type, dvalue, compiler) \ 159 { #name, type_flag, 0, mask(type_directives) | mask(type_c1) | mask(type_c2), &DirectiveSet::set_##name, type##Flag}, 160 compilerdirectives_common_flags(common_flag_key) 161 compilerdirectives_c2_flags(common_flag_key) 162 compilerdirectives_c1_flags(common_flag_key) 163 #undef common_flag_key 164 }; 165 166 const DirectivesParser::key DirectivesParser::dir_array_key = { 167 "top level directives array", type_dir_array, 0, 1 // Lowest bit means allow at top level 168 }; 169 const DirectivesParser::key DirectivesParser::dir_key = { 170 "top level directive", type_directives, 0, mask(type_dir_array) | 1 // Lowest bit means allow at top level 171 }; 172 const DirectivesParser::key DirectivesParser::value_array_key = { 173 "value array", type_value_array, 0, UINT_MAX // Allow all, checked by allow_array on other keys, not by allowed_mask from this key 174 }; 175 176 const DirectivesParser::key* DirectivesParser::lookup_key(const char* str, size_t len) { 177 for (size_t i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) { 178 if (strncasecmp(keys[i].name, str, len) == 0) { 179 return &keys[i]; 180 } 181 } 182 return NULL; 183 } 184 185 uint DirectivesParser::mask(keytype kt) { 186 return 1 << (kt + 1); 187 } 188 189 bool DirectivesParser::push_key(const char* str, size_t len) { 190 bool result = true; 191 const key* k = lookup_key(str, len); 192 193 if (k == NULL) { 194 // os::strdup 195 char* s = NEW_C_HEAP_ARRAY(char, len + 1, mtCompiler); 196 strncpy(s, str, len); 197 s[len] = '\0'; 198 error(KEY_ERROR, "No such key: '%s'.", s); 199 FREE_C_HEAP_ARRAY(char, s); 200 return false; 201 } 202 203 return push_key(k); 204 } 205 206 bool DirectivesParser::push_key(const key* k) { 207 assert(k->allowedmask != 0, "not allowed anywhere?"); 208 209 // Exceeding the stack should not be possible with a valid compiler directive, 210 // and an invalid should abort before this happens 211 assert(depth < MAX_DEPTH, "exceeded stack depth"); 212 if (depth >= MAX_DEPTH) { 213 error(INTERNAL_ERROR, "Stack depth exceeded."); 214 return false; 215 } 216 217 assert(stack[depth] == NULL, "element not nulled, something is wrong"); 218 219 if (depth == 0 && !(k->allowedmask & 1)) { 220 error(KEY_ERROR, "Key '%s' not allowed at top level.", k->name); 221 return false; 222 } 223 224 if (depth > 0) { 225 const key* prev = stack[depth - 1]; 226 if (!(k->allowedmask & mask(prev->type))) { 227 error(KEY_ERROR, "Key '%s' not allowed after '%s' key.", k->name, prev->name); 228 return false; 229 } 230 } 231 232 stack[depth] = k; 233 depth++; 234 return true; 235 } 236 237 const DirectivesParser::key* DirectivesParser::current_key() { 238 assert(depth > 0, "getting key from empty stack"); 239 if (depth == 0) { 240 return NULL; 241 } 242 return stack[depth - 1]; 243 } 244 245 const DirectivesParser::key* DirectivesParser::pop_key() { 246 assert(depth > 0, "popping empty stack"); 247 if (depth == 0) { 248 error(INTERNAL_ERROR, "Popping empty stack."); 249 return NULL; 250 } 251 depth--; 252 253 const key* k = stack[depth]; 254 #ifndef PRODUCT 255 stack[depth] = NULL; 256 #endif 257 258 return k; 259 } 260 261 bool DirectivesParser::set_option_flag(JSON_TYPE t, JSON_VAL* v, const key* option_key, DirectiveSet* set) { 262 263 void (DirectiveSet::*test)(void *args); 264 test = option_key->set; 265 266 switch (t) { 267 case JSON_TRUE: 268 if (option_key->flag_type != boolFlag) { 269 error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]); 270 return false; 271 } else { 272 bool val = true; 273 (set->*test)((void *)&val); 274 } 275 break; 276 277 case JSON_FALSE: 278 if (option_key->flag_type != boolFlag) { 279 error(VALUE_ERROR, "Cannot use bool value for an %s flag", flag_type_names[option_key->flag_type]); 280 return false; 281 } else { 282 bool val = false; 283 (set->*test)((void *)&val); 284 } 285 break; 286 287 case JSON_NUMBER_INT: 288 if (option_key->flag_type != intxFlag) { 289 if (option_key->flag_type == doubleFlag) { 290 double dval = (double)v->int_value; 291 (set->*test)((void *)&dval); 292 break; 293 } 294 error(VALUE_ERROR, "Cannot use int value for an %s flag", flag_type_names[option_key->flag_type]); 295 return false; 296 } else { 297 intx ival = v->int_value; 298 (set->*test)((void *)&ival); 299 } 300 break; 301 302 case JSON_NUMBER_FLOAT: 303 if (option_key->flag_type != doubleFlag) { 304 error(VALUE_ERROR, "Cannot use double value for an %s flag", flag_type_names[option_key->flag_type]); 305 return false; 306 } else { 307 double dval = v->double_value; 308 (set->*test)((void *)&dval); 309 } 310 break; 311 312 case JSON_STRING: 313 if (option_key->flag_type != ccstrFlag && option_key->flag_type != ccstrlistFlag) { 314 error(VALUE_ERROR, "Cannot use string value for a %s flag", flag_type_names[option_key->flag_type]); 315 return false; 316 } else { 317 char* s = NEW_C_HEAP_ARRAY(char, v->str.length+1, mtCompiler); 318 strncpy(s, v->str.start, v->str.length + 1); 319 s[v->str.length] = '\0'; 320 (set->*test)((void *)&s); 321 } 322 break; 323 324 default: 325 assert(0, "Should not reach here."); 326 } 327 return true; 328 } 329 330 bool DirectivesParser::set_option(JSON_TYPE t, JSON_VAL* v) { 331 332 const key* option_key = pop_key(); 333 const key* enclosing_key = current_key(); 334 335 if (option_key->type == value_array_key.type) { 336 // Multi value array, we are really setting the value 337 // for the key one step further up. 338 option_key = pop_key(); 339 enclosing_key = current_key(); 340 341 // Repush option_key and multi value marker, since 342 // we need to keep them until all multi values are set. 343 push_key(option_key); 344 push_key(&value_array_key); 345 } 346 347 switch (option_key->type) { 348 case type_flag: 349 { 350 if (current_directiveset == NULL) { 351 assert(depth == 2, "Must not have active directive set"); 352 353 if (!set_option_flag(t, v, option_key, current_directive->_c1_store)) { 354 return false; 355 } 356 if(!set_option_flag(t, v, option_key, current_directive->_c2_store)) { 357 return false; 358 } 359 } else { 360 assert(depth > 2, "Must have active current directive set"); 361 if (!set_option_flag(t, v, option_key, current_directiveset)) { 362 return false; 363 } 364 } 365 break; 366 } 367 368 case type_match: 369 if (t != JSON_STRING) { 370 error(VALUE_ERROR, "Key of type %s needs a value of type string", option_key->name); 371 return false; 372 } 373 if (enclosing_key->type != type_directives) { 374 error(SYNTAX_ERROR, "Match keyword can only exist inside a directive"); 375 return false; 376 } 377 { 378 char* s = NEW_C_HEAP_ARRAY(char, v->str.length + 1, mtCompiler); 379 strncpy(s, v->str.start, v->str.length); 380 s[v->str.length] = '\0'; 381 382 const char* error_msg = NULL; 383 if (!current_directive->add_match(s, error_msg)) { 384 assert (error_msg != NULL, "Must have valid error message"); 385 error(VALUE_ERROR, "Method pattern error: %s", error_msg); 386 } 387 FREE_C_HEAP_ARRAY(char, s); 388 } 389 break; 390 391 case type_inline: 392 if (t != JSON_STRING) { 393 error(VALUE_ERROR, "Key of type %s needs a value of type string", option_key->name); 394 return false; 395 } 396 { 397 //char* s = strndup(v->str.start, v->str.length); 398 char* s = NEW_C_HEAP_ARRAY(char, v->str.length + 1, mtCompiler); 399 strncpy(s, v->str.start, v->str.length); 400 s[v->str.length] = '\0'; 401 402 const char* error_msg = NULL; 403 if (current_directiveset == NULL) { 404 if (current_directive->_c1_store->parse_and_add_inline(s, error_msg)) { 405 if (!current_directive->_c2_store->parse_and_add_inline(s, error_msg)) { 406 assert (error_msg != NULL, "Must have valid error message"); 407 error(VALUE_ERROR, "Method pattern error: %s", error_msg); 408 } 409 } else { 410 assert (error_msg != NULL, "Must have valid error message"); 411 error(VALUE_ERROR, "Method pattern error: %s", error_msg); 412 } 413 } else { 414 if (!current_directiveset->parse_and_add_inline(s, error_msg)) { 415 assert (error_msg != NULL, "Must have valid error message"); 416 error(VALUE_ERROR, "Method pattern error: %s", error_msg); 417 } 418 } 419 FREE_C_HEAP_ARRAY(char, s); 420 } 421 break; 422 423 case type_c1: 424 current_directiveset = current_directive->_c1_store; 425 if (t != JSON_TRUE && t != JSON_FALSE) { 426 error(VALUE_ERROR, "Key of type %s needs a true or false value", option_key->name); 427 return false; 428 } 429 break; 430 431 case type_c2: 432 current_directiveset = current_directive->_c2_store; 433 if (t != JSON_TRUE && t != JSON_FALSE) { 434 error(VALUE_ERROR, "Key of type %s needs a true or false value", option_key->name); 435 return false; 436 } 437 break; 438 439 case type_enable: 440 switch (enclosing_key->type) { 441 case type_c1: 442 case type_c2: 443 { 444 if (t != JSON_TRUE && t != JSON_FALSE) { 445 error(VALUE_ERROR, "Key of type %s enclosed in a %s key needs a true or false value", option_key->name, enclosing_key->name); 446 return false; 447 } 448 int val = (t == JSON_TRUE); 449 current_directiveset->set_Enable(&val); 450 break; 451 } 452 453 case type_directives: 454 error(VALUE_ERROR, "Enable keyword not available for generic directive"); 455 return false; 456 457 default: 458 error(INTERNAL_ERROR, "Unexpected enclosing type for key %s: %s", option_key->name, enclosing_key->name); 459 ShouldNotReachHere(); 460 return false; 461 } 462 break; 463 464 default: 465 break; 466 } 467 468 return true; 469 } 470 471 bool DirectivesParser::callback(JSON_TYPE t, JSON_VAL* v, uint rlimit) { 472 const key* k; 473 474 if (depth == 0) { 475 switch (t) { 476 case JSON_ARRAY_BEGIN: 477 return push_key(&dir_array_key); 478 479 case JSON_OBJECT_BEGIN: 480 // push synthetic dir_array 481 push_key(&dir_array_key); 482 assert(depth == 1, "Make sure the stack are aligned with the directives"); 483 break; 484 485 default: 486 error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive."); 487 return false; 488 } 489 } 490 if (depth == 1) { 491 switch (t) { 492 case JSON_OBJECT_BEGIN: 493 // Parsing a new directive. 494 current_directive = new CompilerDirectives(); 495 return push_key(&dir_key); 496 497 case JSON_ARRAY_END: 498 k = pop_key(); 499 500 if (k->type != type_dir_array) { 501 error(SYNTAX_ERROR, "Expected end of directives array"); 502 return false; 503 } 504 return true; 505 506 default: 507 error(SYNTAX_ERROR, "DirectivesParser can only start with an array containing directive objects, or one single directive."); 508 return false; 509 } 510 } else { 511 switch (t) { 512 case JSON_OBJECT_BEGIN: 513 k = current_key(); 514 switch (k->type) { 515 case type_c1: 516 current_directiveset = current_directive->_c1_store; 517 return true; 518 case type_c2: 519 current_directiveset = current_directive->_c2_store; 520 return true; 521 522 case type_dir_array: 523 return push_key(&dir_key); 524 525 default: 526 error(SYNTAX_ERROR, "The key '%s' does not allow an object to follow.", k->name); 527 return false; 528 } 529 return false; 530 531 case JSON_OBJECT_END: 532 k = pop_key(); 533 switch (k->type) { 534 case type_c1: 535 case type_c2: 536 // This is how we now if options apply to a single or both directive sets 537 current_directiveset = NULL; 538 break; 539 540 case type_directives: 541 // Check, finish and push to stack! 542 if (current_directive->match() == NULL) { 543 error(INTERNAL_ERROR, "Directive missing required match."); 544 return false; 545 } 546 current_directive->finalize(_st); 547 push_tmp(current_directive); 548 current_directive = NULL; 549 break; 550 551 default: 552 error(INTERNAL_ERROR, "Object end with wrong key type on stack: %s.", k->name); 553 ShouldNotReachHere(); 554 return false; 555 } 556 return true; 557 558 case JSON_ARRAY_BEGIN: 559 k = current_key(); 560 if (!(k->allow_array_value)) { 561 if (k->type == type_dir_array) { 562 error(SYNTAX_ERROR, "Array not allowed inside top level array, expected directive object."); 563 } else { 564 error(VALUE_ERROR, "The key '%s' does not allow an array of values.", k->name); 565 } 566 return false; 567 } 568 return push_key(&value_array_key); 569 570 case JSON_ARRAY_END: 571 k = pop_key(); // Pop multi value marker 572 assert(k->type == value_array_key.type, "array end for level != 0 should terminate multi value"); 573 k = pop_key(); // Pop key for option that was set 574 return true; 575 576 case JSON_KEY: 577 return push_key(v->str.start, v->str.length); 578 579 case JSON_STRING: 580 case JSON_NUMBER_INT: 581 case JSON_NUMBER_FLOAT: 582 case JSON_TRUE: 583 case JSON_FALSE: 584 case JSON_NULL: 585 return set_option(t, v); 586 587 default: 588 error(INTERNAL_ERROR, "Unknown JSON type: %d.", t); 589 ShouldNotReachHere(); 590 return false; 591 } 592 } 593 } 594 595 #ifndef PRODUCT 596 void DirectivesParser::test(const char* text, bool should_pass) { 597 DirectivesParser cd(text, tty); 598 if (should_pass) { 599 assert(cd.valid() == true, "failed on a valid DirectivesParser string"); 600 if (VerboseInternalVMTests) { 601 tty->print("-- DirectivesParser test passed as expected --\n"); 602 } 603 } else { 604 assert(cd.valid() == false, "succeeded on an invalid DirectivesParser string"); 605 if (VerboseInternalVMTests) { 606 tty->print("-- DirectivesParser test failed as expected --\n"); 607 } 608 } 609 cd.clean_tmp(); 610 } 611 612 void DirectivesParser::test() { 613 DirectivesParser::test("{}", false); 614 DirectivesParser::test("[]", true); 615 DirectivesParser::test("[{}]", false); 616 DirectivesParser::test("[{},{}]", false); 617 DirectivesParser::test("{},{}", false); 618 619 DirectivesParser::test( 620 "[" "\n" 621 " {" "\n" 622 " match: \"foo/bar.*\"," "\n" 623 " inline : \"+java/util.*\"," "\n" 624 " PrintAssembly: true," "\n" 625 " BreakAtExecute: true," "\n" 626 " }" "\n" 627 "]" "\n", true); 628 629 DirectivesParser::test( 630 "[" "\n" 631 " [" "\n" 632 " {" "\n" 633 " match: \"foo/bar.*\"," "\n" 634 " inline : \"+java/util.*\"," "\n" 635 " PrintAssembly: true," "\n" 636 " BreakAtExecute: true," "\n" 637 " }" "\n" 638 " ]" "\n" 639 "]" "\n", false); 640 641 /*DirectivesParser::test( 642 "[" "\n" 643 " {" "\n" 644 " match: \"foo/bar.*\"," "\n" 645 " c1: {" 646 " PrintIntrinsics: false," "\n" 647 " }" "\n" 648 " }" "\n" 649 "]" "\n", false);*/ 650 651 DirectivesParser::test( 652 "[" "\n" 653 " {" "\n" 654 " match: \"foo/bar.*\"," "\n" 655 " c2: {" "\n" 656 " PrintInlining: false," "\n" 657 " }" "\n" 658 " }" "\n" 659 "]" "\n", true); 660 661 DirectivesParser::test( 662 "[" "\n" 663 " {" "\n" 664 " match: \"foo/bar.*\"," "\n" 665 " PrintInlining: [" "\n" 666 " true," "\n" 667 " false" "\n" 668 " ]," "\n" 669 " }" "\n" 670 "]" "\n", false); 671 672 DirectivesParser::test( 673 "[" "\n" 674 " {" 675 " // pattern to match against class+method+signature" "\n" 676 " // leading and trailing wildcard (*) allowed" "\n" 677 " match: \"foo/bar.*\"," "\n" 678 "" "\n" 679 " // override defaults for specified compiler" "\n" 680 " // we may differentiate between levels too. TBD." "\n" 681 " c1: {" "\n" 682 " //override c1 presets " "\n" 683 " DumpReplay: false," "\n" 684 " BreakAtCompile: true," "\n" 685 " }," "\n" 686 "" "\n" 687 " c2: {" "\n" 688 " // control inlining of method" "\n" 689 " // + force inline, - dont inline" "\n" 690 " inline : \"+java/util.*\"," "\n" 691 " PrintInlining: true," "\n" 692 " }," "\n" 693 "" "\n" 694 " // directives outside a specific preset applies to all compilers" "\n" 695 " inline : [ \"+java/util.*\", \"-com/sun.*\"]," "\n" 696 " BreakAtExecute: true," "\n" 697 " Log: true," "\n" 698 " }," "\n" 699 " {" "\n" 700 " // matching several patterns require an array" "\n" 701 " match: [\"baz.*\",\"frob.*\"]," "\n" 702 "" "\n" 703 " // applies to all compilers" "\n" 704 " // + force inline, - dont inline" "\n" 705 " inline : [ \"+java/util.*\", \"-com/sun.*\" ]," "\n" 706 " PrintInlining: true," "\n" 707 "" "\n" 708 " // force matching compiles to be blocking/syncronous" "\n" 709 " PrintNMethods: true" "\n" 710 " }," "\n" 711 "]" "\n", true); 712 713 // Test max stack depth 714 DirectivesParser::test( 715 "[" "\n" // depth 1: type_dir_array 716 " {" "\n" // depth 2: type_directives 717 " match: \"*.*\"," // match required 718 " c1:" "\n" // depth 3: type_c1 719 " {" "\n" 720 " inline:" "\n" // depth 4: type_inline 721 " [" "\n" // depth 5: type_value_array 722 " \"foo\"," "\n" 723 " \"bar\"," "\n" 724 " ]" "\n" // depth 3: pop type_value_array and type_inline keys 725 " }" "\n" // depth 2: pop type_c1 key 726 " }" "\n" // depth 1: pop type_directives key 727 "]" "\n", true); // depth 0: pop type_dir_array key 728 729 // Test max stack depth 730 DirectivesParser::test( 731 "[{c1:{c1:{c1:{c1:{c1:{c1:{c1:{}}}}}}}}]", false); 732 733 DirectivesParser::test( 734 "[" "\n" 735 " {" "\n" 736 " c1: true," "\n" 737 " c2: true," "\n" 738 " match: true," "\n" 739 " inline: true," "\n" 740 " enable: true," "\n" 741 " c1: {" "\n" 742 " preset: true," "\n" 743 " }" "\n" 744 " }" "\n" 745 "]" "\n", false); 746 } 747 748 void DirectivesParser_test() { 749 DirectivesParser::test(); 750 } 751 752 #endif