|
13 | 13 | import java.util.Map;
|
14 | 14 | import java.util.Map.Entry;
|
15 | 15 | import java.util.Objects;
|
| 16 | +import java.util.Optional; |
16 | 17 | import java.util.Set;
|
17 | 18 | import java.util.concurrent.ConcurrentHashMap;
|
18 | 19 | import java.util.concurrent.atomic.AtomicInteger;
|
|
28 | 29 | import com.oracle.truffle.api.profiles.IntValueProfile;
|
29 | 30 | import com.oracle.truffle.api.profiles.LoopConditionProfile;
|
30 | 31 | import org.graalvm.collections.Pair;
|
31 |
| -import org.jcodings.Encoding; |
32 | 32 | import org.jcodings.specific.ASCIIEncoding;
|
33 | 33 | import org.jcodings.specific.USASCIIEncoding;
|
34 | 34 | import org.jcodings.specific.UTF8Encoding;
|
|
50 | 50 | import org.truffleruby.core.array.RubyArray;
|
51 | 51 | import org.truffleruby.core.encoding.Encodings;
|
52 | 52 | import org.truffleruby.core.encoding.RubyEncoding;
|
| 53 | +import org.truffleruby.core.hash.HashOperations; |
| 54 | +import org.truffleruby.core.hash.RubyHash; |
| 55 | +import org.truffleruby.core.hash.library.HashStoreLibrary; |
53 | 56 | import org.truffleruby.core.kernel.KernelNodes.SameOrEqualNode;
|
54 | 57 | import org.truffleruby.core.regexp.RegexpNodes.ToSNode;
|
55 | 58 | import org.truffleruby.core.regexp.TruffleRegexpNodesFactory.MatchNodeGen;
|
@@ -439,6 +442,175 @@ protected Object buildUnusedRegexpsArray(
|
439 | 442 | }
|
440 | 443 | }
|
441 | 444 |
|
| 445 | + @CoreMethod(names = "compiled_regexp_hash_array", onSingleton = true, required = 0) |
| 446 | + public abstract static class CompiledRegexpHashArray extends CoreMethodArrayArgumentsNode { |
| 447 | + |
| 448 | + @Specialization |
| 449 | + protected Object buildInfoArray( |
| 450 | + @Cached ArrayBuilderNode arrayBuilderNode, |
| 451 | + @CachedLibrary(limit = "3") HashStoreLibrary hashStoreLibrary) { |
| 452 | + final Set<RegexpCacheKey> compiledRegexps = new HashSet<>(); |
| 453 | + compiledRegexps.addAll(COMPILED_REGEXPS_DYNAMIC.keySet()); |
| 454 | + compiledRegexps.addAll(COMPILED_REGEXPS_LITERAL.keySet()); |
| 455 | + |
| 456 | + final Set<RegexpCacheKey> matchedRegexps = new HashSet<>(); |
| 457 | + matchedRegexps.addAll( |
| 458 | + MATCHED_REGEXPS_JONI |
| 459 | + .keySet() |
| 460 | + .stream() |
| 461 | + .map(matchInfo -> matchInfo.regexpInfo) |
| 462 | + .collect(Collectors.toSet())); |
| 463 | + matchedRegexps.addAll( |
| 464 | + MATCHED_REGEXPS_TREGEX |
| 465 | + .keySet() |
| 466 | + .stream() |
| 467 | + .map(matchInfo -> matchInfo.regexpInfo) |
| 468 | + .collect(Collectors.toSet())); |
| 469 | + |
| 470 | + final int arraySize = COMPILED_REGEXPS_LITERAL.size() + COMPILED_REGEXPS_DYNAMIC.size(); |
| 471 | + final BuilderState state = arrayBuilderNode.start(arraySize); |
| 472 | + |
| 473 | + processGroup(COMPILED_REGEXPS_LITERAL, matchedRegexps, true, hashStoreLibrary, arrayBuilderNode, state, 0); |
| 474 | + processGroup( |
| 475 | + COMPILED_REGEXPS_DYNAMIC, |
| 476 | + matchedRegexps, |
| 477 | + false, |
| 478 | + hashStoreLibrary, |
| 479 | + arrayBuilderNode, |
| 480 | + state, |
| 481 | + COMPILED_REGEXPS_LITERAL.size()); |
| 482 | + |
| 483 | + return createArray(arrayBuilderNode.finish(state, arraySize), arraySize); |
| 484 | + } |
| 485 | + |
| 486 | + private void processGroup(ConcurrentHashMap<RegexpCacheKey, AtomicInteger> group, |
| 487 | + Set<RegexpCacheKey> matchedRegexps, |
| 488 | + boolean isRegexpLiteral, |
| 489 | + HashStoreLibrary hashStoreLibrary, |
| 490 | + ArrayBuilderNode arrayBuilderNode, BuilderState state, int offset) { |
| 491 | + int n = 0; |
| 492 | + for (Entry<RegexpCacheKey, AtomicInteger> entry : group.entrySet()) { |
| 493 | + arrayBuilderNode |
| 494 | + .appendValue( |
| 495 | + state, |
| 496 | + offset + n, |
| 497 | + buildRegexInfoHash( |
| 498 | + getContext(), |
| 499 | + getLanguage(), |
| 500 | + hashStoreLibrary, |
| 501 | + entry.getKey(), |
| 502 | + matchedRegexps.contains(entry.getKey()), |
| 503 | + Optional.of(isRegexpLiteral), |
| 504 | + Optional.of(entry.getValue()))); |
| 505 | + n++; |
| 506 | + } |
| 507 | + } |
| 508 | + |
| 509 | + protected static RubyHash buildRegexInfoHash(RubyContext context, RubyLanguage language, |
| 510 | + HashStoreLibrary hashStoreLibrary, RegexpCacheKey regexpInfo, boolean isUsed, |
| 511 | + Optional<Boolean> isRegexpLiteral, |
| 512 | + Optional<AtomicInteger> count) { |
| 513 | + final RubyHash hash = HashOperations.newEmptyHash(context, language); |
| 514 | + |
| 515 | + hashStoreLibrary.set( |
| 516 | + hash.store, |
| 517 | + hash, |
| 518 | + language.getSymbol("value"), |
| 519 | + StringOperations.createUTF8String(context, language, regexpInfo.getRope()), |
| 520 | + true); |
| 521 | + |
| 522 | + if (count.isPresent()) { |
| 523 | + hashStoreLibrary.set(hash.store, hash, language.getSymbol("count"), count.get().get(), true); |
| 524 | + } |
| 525 | + |
| 526 | + if (isRegexpLiteral.isPresent()) { |
| 527 | + hashStoreLibrary.set(hash.store, hash, language.getSymbol("isLiteral"), isRegexpLiteral.get(), true); |
| 528 | + } |
| 529 | + |
| 530 | + hashStoreLibrary.set(hash.store, hash, language.getSymbol("isUsed"), isUsed, true); |
| 531 | + hashStoreLibrary.set(hash.store, hash, language.getSymbol("encoding"), regexpInfo.getEncoding(), true); |
| 532 | + hashStoreLibrary.set( |
| 533 | + hash.store, |
| 534 | + hash, |
| 535 | + language.getSymbol("options"), |
| 536 | + RegexpOptions.fromJoniOptions(regexpInfo.getJoniOptions()).toOptions(), |
| 537 | + true); |
| 538 | + |
| 539 | + assert hashStoreLibrary.verify(hash.store, hash); |
| 540 | + |
| 541 | + return hash; |
| 542 | + } |
| 543 | + } |
| 544 | + |
| 545 | + @CoreMethod(names = "matched_regexp_hash_array", onSingleton = true, required = 0) |
| 546 | + public abstract static class MatchedRegexpHashArray extends CoreMethodArrayArgumentsNode { |
| 547 | + |
| 548 | + @Specialization |
| 549 | + protected Object buildInfoArray( |
| 550 | + @Cached ArrayBuilderNode arrayBuilderNode, |
| 551 | + @CachedLibrary(limit = "3") HashStoreLibrary hashStoreLibrary) { |
| 552 | + final int arraySize = (MATCHED_REGEXPS_JONI.size() + MATCHED_REGEXPS_TREGEX.size()); |
| 553 | + |
| 554 | + final BuilderState state = arrayBuilderNode.start(arraySize); |
| 555 | + |
| 556 | + processGroup(MATCHED_REGEXPS_JONI, false, hashStoreLibrary, arrayBuilderNode, state, 0); |
| 557 | + processGroup( |
| 558 | + MATCHED_REGEXPS_TREGEX, |
| 559 | + true, |
| 560 | + hashStoreLibrary, |
| 561 | + arrayBuilderNode, |
| 562 | + state, |
| 563 | + MATCHED_REGEXPS_JONI.size()); |
| 564 | + |
| 565 | + return createArray(arrayBuilderNode.finish(state, arraySize), arraySize); |
| 566 | + } |
| 567 | + |
| 568 | + private void processGroup(ConcurrentHashMap<MatchInfo, AtomicInteger> group, |
| 569 | + boolean isTRegexMatch, |
| 570 | + HashStoreLibrary hashStoreLibrary, |
| 571 | + ArrayBuilderNode arrayBuilderNode, BuilderState state, int offset) { |
| 572 | + int n = 0; |
| 573 | + for (Entry<MatchInfo, AtomicInteger> entry : group.entrySet()) { |
| 574 | + arrayBuilderNode |
| 575 | + .appendValue( |
| 576 | + state, |
| 577 | + offset + n, |
| 578 | + buildHash(hashStoreLibrary, isTRegexMatch, entry.getKey(), entry.getValue())); |
| 579 | + n++; |
| 580 | + } |
| 581 | + } |
| 582 | + |
| 583 | + private RubyHash buildHash(HashStoreLibrary hashStoreLibrary, boolean isTRegexMatch, MatchInfo matchInfo, |
| 584 | + AtomicInteger count) { |
| 585 | + final RubyHash regexpInfoHash = CompiledRegexpHashArray.buildRegexInfoHash( |
| 586 | + getContext(), |
| 587 | + getLanguage(), |
| 588 | + hashStoreLibrary, |
| 589 | + matchInfo.regexpInfo, |
| 590 | + true, |
| 591 | + Optional.empty(), |
| 592 | + Optional.empty()); |
| 593 | + final RubyHash matchInfoHash = HashOperations.newEmptyHash(getContext(), getLanguage()); |
| 594 | + |
| 595 | + hashStoreLibrary |
| 596 | + .set(matchInfoHash.store, matchInfoHash, getLanguage().getSymbol("regexp"), regexpInfoHash, true); |
| 597 | + hashStoreLibrary |
| 598 | + .set(matchInfoHash.store, matchInfoHash, getLanguage().getSymbol("count"), count.get(), true); |
| 599 | + hashStoreLibrary |
| 600 | + .set(matchInfoHash.store, matchInfoHash, getLanguage().getSymbol("isTRegex"), isTRegexMatch, true); |
| 601 | + hashStoreLibrary.set( |
| 602 | + matchInfoHash.store, |
| 603 | + matchInfoHash, |
| 604 | + getLanguage().getSymbol("fromStart"), |
| 605 | + matchInfo.matchStart, |
| 606 | + true); |
| 607 | + |
| 608 | + assert hashStoreLibrary.verify(matchInfoHash.store, matchInfoHash); |
| 609 | + |
| 610 | + return matchInfoHash; |
| 611 | + } |
| 612 | + } |
| 613 | + |
442 | 614 | @Primitive(name = "regexp_initialized?")
|
443 | 615 | public abstract static class InitializedNode extends CoreMethodArrayArgumentsNode {
|
444 | 616 |
|
|
0 commit comments