Skip to content

Commit 568a4ed

Browse files
committed
Use Kernel#dup and Kernel#clone for Range
* Add RubyIntOrLongRange superclass to deduplicate a bit.
1 parent 1cc9c7b commit 568a4ed

17 files changed

+175
-260
lines changed

src/main/java/org/truffleruby/core/encoding/RubyEncoding.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import org.truffleruby.core.rope.RopeConstants;
2424
import org.truffleruby.core.string.FrozenStringLiterals;
2525
import org.truffleruby.core.string.ImmutableRubyString;
26-
import org.truffleruby.language.ImmutableRubyObject;
2726

27+
import org.truffleruby.language.ImmutableRubyObjectNotCopyable;
2828
import org.truffleruby.language.dispatch.DispatchNode;
2929
import org.truffleruby.language.objects.ObjectGraph;
3030
import org.truffleruby.language.objects.ObjectGraphNode;
@@ -33,7 +33,7 @@
3333
import java.util.Set;
3434

3535
@ExportLibrary(InteropLibrary.class)
36-
public class RubyEncoding extends ImmutableRubyObject implements ObjectGraphNode, Comparable<RubyEncoding> {
36+
public class RubyEncoding extends ImmutableRubyObjectNotCopyable implements ObjectGraphNode, Comparable<RubyEncoding> {
3737

3838
public final Encoding jcoding;
3939
public final ImmutableRubyString name;

src/main/java/org/truffleruby/core/kernel/KernelNodes.java

Lines changed: 30 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
import org.truffleruby.core.proc.ProcNodes.ProcNewNode;
7272
import org.truffleruby.core.proc.ProcOperations;
7373
import org.truffleruby.core.proc.RubyProc;
74+
import org.truffleruby.core.range.RubyIntOrLongRange;
75+
import org.truffleruby.core.range.RubyObjectRange;
7476
import org.truffleruby.core.rope.CodeRange;
7577
import org.truffleruby.core.rope.Rope;
7678
import org.truffleruby.core.rope.RopeNodes;
@@ -125,7 +127,8 @@
125127
import org.truffleruby.language.objects.AllocationTracing;
126128
import org.truffleruby.language.objects.CheckIVarNameNode;
127129
import org.truffleruby.language.objects.IsANode;
128-
import org.truffleruby.language.objects.IsImmutableObjectNode;
130+
import org.truffleruby.language.objects.IsCopyableObjectNode;
131+
import org.truffleruby.language.objects.IsCopyableObjectNodeGen;
129132
import org.truffleruby.language.objects.LogicalClassNode;
130133
import org.truffleruby.language.objects.MetaClassNode;
131134
import org.truffleruby.language.objects.ShapeCachingGuards;
@@ -522,7 +525,7 @@ protected RubyDynamicObject copyRubyDynamicObject(RubyDynamicObject self,
522525
}
523526

524527
@Specialization
525-
protected RubyClass copyRubyClass(RubyClass self,
528+
protected RubyDynamicObject copyRubyClass(RubyClass self,
526529
@Cached CopyInstanceVariablesNode copyInstanceVariablesNode) {
527530
var newClass = new RubyClass(coreLibrary().classClass, getLanguage(), getEncapsulatingSourceSection(),
528531
null, null, false, null, self.superclass);
@@ -531,33 +534,40 @@ protected RubyClass copyRubyClass(RubyClass self,
531534
}
532535

533536
@Specialization
534-
protected RubyString copyImmutableString(ImmutableRubyString string,
537+
protected RubyDynamicObject copy(ImmutableRubyString string,
535538
@Cached DispatchNode allocateStringNode) {
536539
return (RubyString) allocateStringNode.call(coreLibrary().stringClass, "__allocate__");
537540
}
541+
542+
@Specialization
543+
protected RubyDynamicObject copy(RubyIntOrLongRange range,
544+
@Cached DispatchNode allocateRangeNode) {
545+
return (RubyObjectRange) allocateRangeNode.call(coreLibrary().rangeClass, "__allocate__");
546+
}
538547
}
539548

540-
@Primitive(name = "object_clone")
549+
@Primitive(name = "object_clone") // "clone"
541550
@NodeChild(value = "object", type = RubyNode.class)
542551
@NodeChild(value = "freeze", type = RubyBaseNodeWithExecute.class)
543552
public abstract static class CloneNode extends PrimitiveNode {
544553

545-
@Child private SingletonClassNode singletonClassNode;
554+
@Child IsCopyableObjectNode isCopyableObjectNode = IsCopyableObjectNodeGen.create();
555+
@Child SingletonClassNode singletonClassNode;
546556
private final BranchProfile cantUnfreezeErrorProfile = BranchProfile.create();
547557

548-
@Specialization(limit = "getRubyLibraryCacheLimit()")
549-
protected RubyDynamicObject clone(RubyDynamicObject object, Object freeze,
558+
@Specialization(guards = "isCopyableObjectNode.execute(object)", limit = "getRubyLibraryCacheLimit()")
559+
protected RubyDynamicObject copyable(Object object, Object freeze,
560+
@Cached MetaClassNode metaClassNode,
550561
@Cached CopyNode copyNode,
551562
@Cached DispatchNode initializeCloneNode,
552563
@Cached ConditionProfile isSingletonProfile,
553-
@Cached ConditionProfile isRubyClass,
554564
@Cached HashingNodes.ToHashByHashCode hashNode,
555565
@CachedLibrary("object") RubyLibrary rubyLibrary,
556566
@CachedLibrary(limit = "getRubyLibraryCacheLimit()") RubyLibrary rubyLibraryFreeze) {
557567
final RubyDynamicObject newObject = copyNode.executeCopy(object);
558568

559569
// Copy the singleton class if any.
560-
final RubyClass selfMetaClass = object.getMetaClass();
570+
final RubyClass selfMetaClass = metaClassNode.execute(object);
561571
if (isSingletonProfile.profile(selfMetaClass.isSingleton)) {
562572
final RubyClass newObjectMetaClass = executeSingletonClass(newObject);
563573
newObjectMetaClass.fields.initCopy(selfMetaClass);
@@ -581,58 +591,14 @@ protected RubyDynamicObject clone(RubyDynamicObject object, Object freeze,
581591
return newObject;
582592
}
583593

584-
@Specialization
585-
protected boolean cloneBoolean(boolean object, Object freeze) {
586-
if (forceNotFrozen(freeze)) {
587-
raiseCantUnfreezeError(object);
588-
}
589-
return object;
590-
}
591-
592-
@Specialization
593-
protected int cloneInteger(int object, Object freeze) {
594-
if (forceNotFrozen(freeze)) {
595-
raiseCantUnfreezeError(object);
596-
}
597-
return object;
598-
}
599-
600-
@Specialization
601-
protected long cloneLong(long object, Object freeze) {
594+
@Specialization(guards = "!isCopyableObjectNode.execute(object)")
595+
protected Object notCopyable(Object object, Object freeze) {
602596
if (forceNotFrozen(freeze)) {
603597
raiseCantUnfreezeError(object);
604598
}
605599
return object;
606600
}
607601

608-
@Specialization
609-
protected double cloneFloat(double object, Object freeze) {
610-
if (forceNotFrozen(freeze)) {
611-
raiseCantUnfreezeError(object);
612-
}
613-
return object;
614-
}
615-
616-
@Specialization(guards = "!isImmutableRubyString(object)")
617-
protected ImmutableRubyObject cloneImmutableObject(ImmutableRubyObject object, Object freeze) {
618-
if (forceNotFrozen(freeze)) {
619-
raiseCantUnfreezeError(object);
620-
}
621-
return object;
622-
}
623-
624-
@Specialization
625-
protected RubyDynamicObject cloneImmutableRubyString(ImmutableRubyString object, Object freeze,
626-
@CachedLibrary(limit = "getRubyLibraryCacheLimit()") RubyLibrary rubyLibraryFreeze,
627-
@Cached MakeStringNode makeStringNode) {
628-
final RubyDynamicObject newObject = makeStringNode.fromRope(object.rope, object.encoding);
629-
if (!forceNotFrozen(freeze)) {
630-
rubyLibraryFreeze.freeze(newObject);
631-
}
632-
633-
return newObject;
634-
}
635-
636602
private RubyHash createFreezeBooleanHash(boolean freeze, HashingNodes.ToHashByHashCode hashNode) {
637603
final RubySymbol key = coreSymbols().FREEZE;
638604

@@ -677,20 +643,19 @@ private RubyClass executeSingletonClass(RubyDynamicObject newObject) {
677643
public abstract static class DupNode extends AlwaysInlinedMethodNode {
678644
@Specialization
679645
protected Object dup(Frame callerFrame, Object self, Object[] rubyArgs, RootCallTarget target,
680-
@Cached IsImmutableObjectNode isImmutableObjectNode,
681-
@Cached ConditionProfile immutableProfile,
646+
@Cached IsCopyableObjectNode isCopyableObjectNode,
647+
@Cached ConditionProfile isCopyableProfile,
682648
@Cached CopyNode copyNode,
683649
@Cached DispatchNode initializeDupNode) {
684-
if (immutableProfile
685-
.profile(!(self instanceof ImmutableRubyString) && isImmutableObjectNode.execute(self))) {
686-
return self;
687-
}
650+
if (isCopyableProfile.profile(isCopyableObjectNode.execute(self))) {
651+
final RubyDynamicObject copy = copyNode.executeCopy(self);
688652

689-
final RubyDynamicObject newObject = copyNode.executeCopy(self);
653+
initializeDupNode.call(copy, "initialize_dup", self);
690654

691-
initializeDupNode.call(newObject, "initialize_dup", self);
692-
693-
return newObject;
655+
return copy;
656+
} else {
657+
return self;
658+
}
694659
}
695660
}
696661

src/main/java/org/truffleruby/core/numeric/RubyBignum.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
import com.oracle.truffle.api.library.ExportMessage;
1717
import org.truffleruby.RubyContext;
1818
import org.truffleruby.core.klass.RubyClass;
19-
import org.truffleruby.language.ImmutableRubyObject;
19+
import org.truffleruby.language.ImmutableRubyObjectNotCopyable;
2020

2121
import java.math.BigInteger;
2222

2323
@ExportLibrary(InteropLibrary.class)
24-
public class RubyBignum extends ImmutableRubyObject {
24+
public class RubyBignum extends ImmutableRubyObjectNotCopyable {
2525

2626
public final BigInteger value;
2727

src/main/java/org/truffleruby/core/range/RangeNodes.java

Lines changed: 29 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,7 @@ protected boolean excludeEnd(RubyObjectRange range) {
161161
}
162162

163163
@Specialization
164-
protected boolean excludeEnd(RubyIntRange range) {
165-
return range.excludedEnd;
166-
}
167-
168-
@Specialization
169-
protected boolean excludeEnd(RubyLongRange range) {
164+
protected boolean excludeEnd(RubyIntOrLongRange range) {
170165
return range.excludedEnd;
171166
}
172167

@@ -192,86 +187,6 @@ protected Object eachObject(RubyObjectRange range) {
192187

193188
}
194189

195-
@CoreMethod(names = "dup")
196-
public abstract static class DupNode extends UnaryCoreMethodNode {
197-
198-
// NOTE(norswap): This is a hack, as it doesn't copy the ivars.
199-
// We do copy the logical class (but not the singleton class, to be MRI compatible).
200-
201-
@Specialization
202-
protected RubyObjectRange dupIntRange(RubyIntRange range) {
203-
// RubyIntRange means this isn't a Range subclass (cf. NewNode), we can use the shape directly.
204-
final RubyObjectRange copy = new RubyObjectRange(
205-
coreLibrary().rangeClass,
206-
getLanguage().objectRangeShape,
207-
range.excludedEnd,
208-
range.begin,
209-
range.end,
210-
false);
211-
AllocationTracing.trace(copy, this);
212-
return copy;
213-
}
214-
215-
@Specialization
216-
protected RubyObjectRange dupLongRange(RubyLongRange range) {
217-
// RubyLongRange means this isn't a Range subclass (cf. NewNode), we can use the shape directly.
218-
final RubyObjectRange copy = new RubyObjectRange(
219-
coreLibrary().rangeClass,
220-
getLanguage().objectRangeShape,
221-
range.excludedEnd,
222-
range.begin,
223-
range.end,
224-
false);
225-
AllocationTracing.trace(copy, this);
226-
return copy;
227-
}
228-
229-
@Specialization
230-
protected RubyObjectRange dup(RubyObjectRange range) {
231-
final RubyClass logicalClass = range.getLogicalClass();
232-
final RubyObjectRange copy = new RubyObjectRange(
233-
logicalClass,
234-
getLanguage().objectRangeShape,
235-
range.excludedEnd,
236-
range.begin,
237-
range.end,
238-
false);
239-
AllocationTracing.trace(copy, this);
240-
return copy;
241-
}
242-
}
243-
244-
@CoreMethod(names = "clone")
245-
public abstract static class CloneNode extends UnaryCoreMethodNode {
246-
247-
// NOTE(norswap): This is a hack, as it doesn't copy the ivars.
248-
// We do copy the logical class (but not the singleton class, to be MRI compatible).
249-
250-
@Specialization
251-
protected RubyIntRange cloneIntRange(RubyIntRange range) {
252-
return new RubyIntRange(range);
253-
}
254-
255-
@Specialization
256-
protected RubyLongRange dupLongRange(RubyLongRange range) {
257-
return new RubyLongRange(range);
258-
}
259-
260-
@Specialization
261-
protected RubyObjectRange dup(RubyObjectRange range) {
262-
final RubyClass logicalClass = range.getLogicalClass();
263-
final RubyObjectRange copy = new RubyObjectRange(
264-
logicalClass,
265-
getLanguage().objectRangeShape,
266-
range.excludedEnd,
267-
range.begin,
268-
range.end,
269-
range.frozen);
270-
AllocationTracing.trace(copy, this);
271-
return copy;
272-
}
273-
}
274-
275190
@CoreMethod(names = "end")
276191
public abstract static class EndNode extends CoreMethodArrayArgumentsNode {
277192

@@ -517,6 +432,34 @@ protected RubyObjectRange allocate(RubyClass rubyClass) {
517432
}
518433
}
519434

435+
@CoreMethod(names = "initialize_copy", required = 1, raiseIfFrozenSelf = true)
436+
public abstract static class InitializeCopyNode extends CoreMethodArrayArgumentsNode {
437+
438+
@Specialization
439+
protected RubyObjectRange initializeCopy(RubyObjectRange self, RubyIntRange from) {
440+
self.begin = from.begin;
441+
self.end = from.end;
442+
self.excludedEnd = from.excludedEnd;
443+
return self;
444+
}
445+
446+
@Specialization
447+
protected RubyObjectRange initializeCopy(RubyObjectRange self, RubyLongRange from) {
448+
self.begin = from.begin;
449+
self.end = from.end;
450+
self.excludedEnd = from.excludedEnd;
451+
return self;
452+
}
453+
454+
@Specialization
455+
protected RubyObjectRange initializeCopy(RubyObjectRange self, RubyObjectRange from) {
456+
self.begin = from.begin;
457+
self.end = from.end;
458+
self.excludedEnd = from.excludedEnd;
459+
return self;
460+
}
461+
}
462+
520463
/** Returns an array containing normalized int range parameters {@code [start, length]}, such that both are 32-bits
521464
* java ints (if conversion is impossible, an error is raised). The method attempts to make the values positive, by
522465
* adding {@code size} to them if they are negative. They may still be negative after the operation however, as
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 2.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.core.range;
11+
12+
import org.truffleruby.RubyContext;
13+
import org.truffleruby.language.ImmutableRubyObjectCopyable;
14+
import org.truffleruby.language.dispatch.DispatchNode;
15+
16+
import com.oracle.truffle.api.dsl.Cached;
17+
import com.oracle.truffle.api.dsl.Cached.Exclusive;
18+
import com.oracle.truffle.api.interop.InteropLibrary;
19+
import com.oracle.truffle.api.library.CachedLibrary;
20+
import com.oracle.truffle.api.library.ExportLibrary;
21+
import com.oracle.truffle.api.library.ExportMessage;
22+
23+
@ExportLibrary(InteropLibrary.class)
24+
public abstract class RubyIntOrLongRange extends ImmutableRubyObjectCopyable {
25+
26+
public final boolean excludedEnd;
27+
28+
public RubyIntOrLongRange(boolean excludedEnd) {
29+
this.excludedEnd = excludedEnd;
30+
}
31+
32+
@ExportMessage
33+
public boolean hasIterator() {
34+
return true;
35+
}
36+
37+
@ExportMessage
38+
public Object getIterator(
39+
@CachedLibrary("this") InteropLibrary node,
40+
@Exclusive @Cached DispatchNode dispatchNode) {
41+
final RubyContext context = RubyContext.get(node);
42+
return dispatchNode.call(context.getCoreLibrary().truffleInteropOperationsModule, "get_iterator", this);
43+
}
44+
45+
}

0 commit comments

Comments
 (0)