Skip to content

Commit b544bea

Browse files
aardvark179chrisseaton
authored andcommitted
Update Hash#merge and Hash#merge! to be 2.6 compatible
1 parent 6f3b602 commit b544bea

File tree

5 files changed

+55
-80
lines changed

5 files changed

+55
-80
lines changed

spec/tags/core/hash/merge_tags.txt

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/main/java/org/truffleruby/core/CoreLibrary.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,7 @@ public DynamicObject getWarningModule() {
14221422
"/core/truffle/debug.rb",
14231423
"/core/truffle/warnings.rb",
14241424
"/core/truffle/exception_operations.rb",
1425+
"/core/truffle/hash_operations.rb",
14251426
"/core/truffle/numeric_operations.rb",
14261427
"/core/truffle/proc_operations.rb",
14271428
"/core/truffle/range_operations.rb",

src/main/java/org/truffleruby/core/hash/HashNodes.java

Lines changed: 13 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import org.truffleruby.core.hash.HashNodesFactory.HashLookupOrExecuteDefaultNodeGen;
3535
import org.truffleruby.core.hash.HashNodesFactory.InitializeCopyNodeFactory;
3636
import org.truffleruby.core.hash.HashNodesFactory.InternalRehashNodeGen;
37-
import org.truffleruby.language.NotOptimizedWarningNode;
3837
import org.truffleruby.language.NotProvided;
3938
import org.truffleruby.language.RubyBaseNode;
4039
import org.truffleruby.language.RubyGuards;
@@ -683,8 +682,8 @@ private Object yieldPair(DynamicObject block, Object key, Object value) {
683682
}
684683

685684
@ImportStatic(HashGuards.class)
686-
@CoreMethod(names = "merge", required = 1, needsBlock = true)
687-
public abstract static class MergeNode extends YieldingCoreMethodNode {
685+
@Primitive(name = "hash_merge", needsSelf = false)
686+
public abstract static class MergeNode extends PrimitiveArrayArgumentsNode {
688687

689688
@Child private LookupEntryNode lookupEntryNode;
690689
@Child private SetNode setNode = SetNode.create();
@@ -694,38 +693,38 @@ public abstract static class MergeNode extends YieldingCoreMethodNode {
694693
// Merge with an empty hash, without a block
695694

696695
@Specialization(guards = {"isNullHash(hash)", "isRubyHash(other)", "isNullHash(other)"})
697-
public DynamicObject mergeEmptyEmpty(DynamicObject hash, DynamicObject other, NotProvided block) {
696+
public DynamicObject mergeEmptyEmpty(DynamicObject hash, DynamicObject other) {
698697
return newHash(hash, null, 0, Layouts.HASH.getCompareByIdentity(hash));
699698
}
700699

701700
@Specialization(guards = {"isEmptyHash(hash)", "isRubyHash(other)", "isPackedHash(other)"})
702-
public DynamicObject mergeEmptyPacked(DynamicObject hash, DynamicObject other, NotProvided block) {
701+
public DynamicObject mergeEmptyPacked(DynamicObject hash, DynamicObject other) {
703702
final Object[] store = (Object[]) Layouts.HASH.getStore(other);
704703
final Object[] copy = PackedArrayStrategy.copyStore(getContext(), store);
705704
return newHash(hash, copy, Layouts.HASH.getSize(other), Layouts.HASH.getCompareByIdentity(hash));
706705
}
707706

708707
@Specialization(guards = {"isPackedHash(hash)", "isRubyHash(other)", "isEmptyHash(other)"})
709-
public DynamicObject mergePackedEmpty(DynamicObject hash, DynamicObject other, NotProvided block) {
710-
return mergeEmptyPacked(other, hash, block);
708+
public DynamicObject mergePackedEmpty(DynamicObject hash, DynamicObject other) {
709+
return mergeEmptyPacked(other, hash);
711710
}
712711

713712
@Specialization(guards = {"isEmptyHash(hash)", "isRubyHash(other)", "isBucketHash(other)"})
714-
public DynamicObject mergeEmptyBuckets(DynamicObject hash, DynamicObject other, NotProvided block) {
713+
public DynamicObject mergeEmptyBuckets(DynamicObject hash, DynamicObject other) {
715714
final DynamicObject merged = newHash(hash, null, 0, Layouts.HASH.getCompareByIdentity(hash));
716715
BucketsStrategy.copyInto(getContext(), other, merged);
717716
return merged;
718717
}
719718

720719
@Specialization(guards = {"isBucketHash(hash)", "isRubyHash(other)", "isEmptyHash(other)"})
721-
public DynamicObject mergeBucketsEmpty(DynamicObject hash, DynamicObject other, NotProvided block) {
722-
return mergeEmptyBuckets(other, hash, block);
720+
public DynamicObject mergeBucketsEmpty(DynamicObject hash, DynamicObject other) {
721+
return mergeEmptyBuckets(other, hash);
723722
}
724723

725724
// Merge non-empty packed with non-empty packed, without a block
726725

727726
@Specialization(guards = {"isPackedHash(hash)", "!isEmptyHash(hash)", "isRubyHash(other)", "isPackedHash(other)", "!isEmptyHash(other)"})
728-
public DynamicObject mergePackedPacked(DynamicObject hash, DynamicObject other, NotProvided block,
727+
public DynamicObject mergePackedPacked(DynamicObject hash, DynamicObject other,
729728
@Cached("createBinaryProfile()") ConditionProfile byIdentityProfile,
730729
@Cached("createBinaryProfile()") ConditionProfile nothingFromFirstProfile,
731730
@Cached("createBinaryProfile()") ConditionProfile resultIsSmallProfile) {
@@ -834,7 +833,7 @@ private int mergedPackedHashes(boolean compareByIdentity, Object[] storeA, int s
834833
// Merge non-empty buckets with non-empty buckets, without a block
835834

836835
@Specialization(guards = {"isBucketHash(hash)", "!isEmptyHash(hash)", "isRubyHash(other)", "isBucketHash(other)", "!isEmptyHash(other)"})
837-
public DynamicObject mergeBucketsBuckets(DynamicObject hash, DynamicObject other, NotProvided block) {
836+
public DynamicObject mergeBucketsBuckets(DynamicObject hash, DynamicObject other) {
838837
final boolean compareByIdentity = Layouts.HASH.getCompareByIdentity(hash);
839838
final Entry[] newStore = new Entry[BucketsStrategy.capacityGreaterThan(Layouts.HASH.getSize(hash) + Layouts.HASH.getSize(other))];
840839
final DynamicObject merged = newHash(hash, newStore, 0, compareByIdentity);
@@ -854,7 +853,7 @@ public DynamicObject mergeBucketsBuckets(DynamicObject hash, DynamicObject other
854853
// Merge combinations of packed and buckets, without a block
855854

856855
@Specialization(guards = {"isPackedHash(hash)", "!isEmptyHash(hash)", "isRubyHash(other)", "isBucketHash(other)", "!isEmptyHash(other)"})
857-
public DynamicObject mergePackedBuckets(DynamicObject hash, DynamicObject other, NotProvided block) {
856+
public DynamicObject mergePackedBuckets(DynamicObject hash, DynamicObject other) {
858857
final boolean compareByIdentity = Layouts.HASH.getCompareByIdentity(hash);
859858
final Entry[] newStore = new Entry[BucketsStrategy.capacityGreaterThan(Layouts.HASH.getSize(hash) + Layouts.HASH.getSize(other))];
860859
final DynamicObject merged = newHash(hash, newStore, 0, compareByIdentity);
@@ -877,7 +876,7 @@ public DynamicObject mergePackedBuckets(DynamicObject hash, DynamicObject other,
877876
}
878877

879878
@Specialization(guards = {"isBucketHash(hash)", "!isEmptyHash(hash)", "isRubyHash(other)", "isPackedHash(other)", "!isEmptyHash(other)"})
880-
public DynamicObject mergeBucketsPacked(DynamicObject hash, DynamicObject other, NotProvided block) {
879+
public DynamicObject mergeBucketsPacked(DynamicObject hash, DynamicObject other) {
881880
final boolean compareByIdentity = Layouts.HASH.getCompareByIdentity(hash);
882881
final Entry[] newStore = new Entry[BucketsStrategy.capacityGreaterThan(Layouts.HASH.getSize(hash) + Layouts.HASH.getSize(other))];
883882
final DynamicObject merged = newHash(hash, newStore, 0, compareByIdentity);
@@ -899,66 +898,6 @@ public DynamicObject mergeBucketsPacked(DynamicObject hash, DynamicObject other,
899898
return merged;
900899
}
901900

902-
// Merge using a block
903-
904-
@Specialization(guards = "isRubyHash(other)")
905-
public DynamicObject merge(DynamicObject hash, DynamicObject other, DynamicObject block,
906-
@Cached("new()") NotOptimizedWarningNode notOptimizedWarningNode) {
907-
notOptimizedWarningNode.warn("Hash#merge with a block is not yet optimized");
908-
909-
final boolean compareByIdentity = Layouts.HASH.getCompareByIdentity(hash);
910-
911-
final int capacity = BucketsStrategy.capacityGreaterThan(Layouts.HASH.getSize(hash) + Layouts.HASH.getSize(other));
912-
final DynamicObject merged = newHash(hash, new Entry[capacity], 0, compareByIdentity);
913-
914-
int size = 0;
915-
916-
for (KeyValue keyValue : HashOperations.iterableKeyValues(hash)) {
917-
setNode.executeSet(merged, keyValue.getKey(), keyValue.getValue(), false);
918-
size++;
919-
}
920-
921-
if (lookupEntryNode == null) {
922-
CompilerDirectives.transferToInterpreterAndInvalidate();
923-
lookupEntryNode = insert(new LookupEntryNode());
924-
}
925-
926-
for (KeyValue keyValue : HashOperations.iterableKeyValues(other)) {
927-
final HashLookupResult searchResult = lookupEntryNode.lookup(merged, keyValue.getKey());
928-
929-
if (searchResult.getEntry() == null) {
930-
setNode.executeSet(merged, keyValue.getKey(), keyValue.getValue(), false);
931-
size++;
932-
} else {
933-
final Object oldValue = searchResult.getEntry().getValue();
934-
final Object newValue = keyValue.getValue();
935-
final Object mergedValue = yield(block, keyValue.getKey(), oldValue, newValue);
936-
937-
setNode.executeSet(merged, keyValue.getKey(), mergedValue, false);
938-
}
939-
}
940-
941-
Layouts.HASH.setSize(merged, size);
942-
943-
assert HashOperations.verifyStore(getContext(), hash);
944-
return merged;
945-
}
946-
947-
// Merge with something that wasn't a hash
948-
949-
@Specialization(guards = "!isRubyHash(other)")
950-
public Object merge(DynamicObject hash, Object other, Object maybeBlock,
951-
@Cached("createPrivate()") CallDispatchHeadNode fallbackCallNode) {
952-
final DynamicObject block;
953-
if (maybeBlock == NotProvided.INSTANCE) {
954-
block = null;
955-
} else {
956-
block = (DynamicObject) maybeBlock;
957-
}
958-
959-
return fallbackCallNode.callWithBlock(hash, "merge_fallback", block, other);
960-
}
961-
962901
private DynamicObject newHash(DynamicObject hash, Object[] store, int size, boolean compareByIdentity) {
963902
return allocateObjectNode.allocate(Layouts.BASIC_OBJECT.getLogicalClass(hash),
964903
Layouts.HASH.build(store, size, null, null, Layouts.HASH.getDefaultBlock(hash), Layouts.HASH.getDefaultValue(hash), compareByIdentity));

src/main/ruby/core/hash.rb

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,29 @@ def keep_if
242242
self
243243
end
244244

245+
def merge(*others)
246+
unless block_given?
247+
current = self
248+
others.each do |other|
249+
other = Truffle::Type.coerce_to(other, Hash, :to_hash)
250+
current = Truffle::HashOperations.hash_merge(current, other)
251+
end
252+
else
253+
current = self.dup
254+
others.each do |other|
255+
other.each do |k, v|
256+
other = Truffle::Type.coerce_to(other, Hash, :to_hash)
257+
if current.include?(k)
258+
current[k] = yield(k, current[k], v)
259+
else
260+
current[k] = v
261+
end
262+
end
263+
end
264+
end
265+
current
266+
end
267+
245268
def merge!(*others)
246269
Truffle.check_frozen
247270

@@ -467,10 +490,6 @@ def to_proc
467490
end
468491
end
469492

470-
def merge_fallback(other, &block)
471-
merge(Truffle::Type.coerce_to other, Hash, :to_hash, &block)
472-
end
473-
474493
def transform_values
475494
return to_enum(:transform_values) { size } unless block_given?
476495

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. This
4+
# code is released under a tri EPL/GPL/LGPL license. You can use it,
5+
# redistribute it and/or modify it under the terms of the:
6+
#
7+
# Eclipse Public License version 1.0, or
8+
# GNU General Public License version 2, or
9+
# GNU Lesser General Public License version 2.1.
10+
11+
module Truffle
12+
module HashOperations
13+
def self.hash_merge(current, other)
14+
Truffle.invoke_primitive(:hash_merge, current, other)
15+
end
16+
end
17+
end
18+

0 commit comments

Comments
 (0)