Skip to content

Commit 9fa65a9

Browse files
committed
Implement FrozenError receiver
1 parent f6bd129 commit 9fa65a9

File tree

10 files changed

+149
-7
lines changed

10 files changed

+149
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Compatibility:
2222
* Implement `Enumerator#produce` (#2160, @zverok)
2323
* Implement `Complex#<=>` (#2004, @ssnickolay).
2424
* Add warning for `proc` without block (#2004, @ssnickolay).
25+
* Implemented `FrozenError#receiver`.
2526

2627
Performance:
2728

spec/tags/core/exception/frozen_error_tags.txt

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

src/main/java/org/truffleruby/RubyLanguage.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.truffleruby.core.encoding.RubyEncoding;
2727
import org.truffleruby.core.encoding.RubyEncodingConverter;
2828
import org.truffleruby.core.exception.RubyException;
29+
import org.truffleruby.core.exception.RubyFrozenError;
2930
import org.truffleruby.core.exception.RubyNameError;
3031
import org.truffleruby.core.exception.RubyNoMethodError;
3132
import org.truffleruby.core.exception.RubySystemCallError;
@@ -177,6 +178,7 @@ public final class RubyLanguage extends TruffleLanguage<RubyContext> {
177178
public final Shape encodingShape = createShape(RubyEncoding.class);
178179
public final Shape exceptionShape = createShape(RubyException.class);
179180
public final Shape fiberShape = createShape(RubyFiber.class);
181+
public final Shape frozenErrorShape = createShape(RubyFrozenError.class);
180182
public final Shape handleShape = createShape(RubyHandle.class);
181183
public final Shape hashShape = createShape(RubyHash.class);
182184
public final Shape intRangeShape = createShape(RubyIntRange.class);

src/main/java/org/truffleruby/builtins/BuiltinsClasses.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
import org.truffleruby.core.encoding.EncodingNodesFactory;
4949
import org.truffleruby.core.exception.ExceptionNodesBuiltins;
5050
import org.truffleruby.core.exception.ExceptionNodesFactory;
51+
import org.truffleruby.core.exception.FrozenErrorNodesBuiltins;
52+
import org.truffleruby.core.exception.FrozenErrorNodesFactory;
5153
import org.truffleruby.core.exception.NameErrorNodesBuiltins;
5254
import org.truffleruby.core.exception.NameErrorNodesFactory;
5355
import org.truffleruby.core.exception.NoMethodErrorNodesBuiltins;
@@ -182,6 +184,7 @@ public static void setupBuiltinsLazy(CoreMethodNodeManager coreManager) {
182184
FalseClassNodesBuiltins.setup(coreManager);
183185
FiberNodesBuiltins.setup(coreManager);
184186
FloatNodesBuiltins.setup(coreManager);
187+
FrozenErrorNodesBuiltins.setup(coreManager);
185188
GCNodesBuiltins.setup(coreManager);
186189
HashNodesBuiltins.setup(coreManager);
187190
IntegerNodesBuiltins.setup(coreManager);
@@ -257,6 +260,7 @@ public static void setupBuiltinsLazyPrimitives(PrimitiveManager primitiveManager
257260
FalseClassNodesBuiltins.setupPrimitives(primitiveManager);
258261
FiberNodesBuiltins.setupPrimitives(primitiveManager);
259262
FloatNodesBuiltins.setupPrimitives(primitiveManager);
263+
FrozenErrorNodesBuiltins.setupPrimitives(primitiveManager);
260264
GCNodesBuiltins.setupPrimitives(primitiveManager);
261265
HashNodesBuiltins.setupPrimitives(primitiveManager);
262266
IntegerNodesBuiltins.setupPrimitives(primitiveManager);
@@ -333,6 +337,7 @@ public static List<List<? extends NodeFactory<? extends RubyNode>>> getCoreNodeF
333337
FalseClassNodesFactory.getFactories(),
334338
FiberNodesFactory.getFactories(),
335339
FloatNodesFactory.getFactories(),
340+
FrozenErrorNodesFactory.getFactories(),
336341
GCNodesFactory.getFactories(),
337342
HashNodesFactory.getFactories(),
338343
IntegerNodesFactory.getFactories(),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ public CoreLibrary(RubyContext context, RubyLanguage language) {
358358

359359
// StandardError > RuntimeError
360360
runtimeErrorClass = defineClass(standardErrorClass, "RuntimeError");
361-
frozenErrorClass = defineClass(runtimeErrorClass, "FrozenError");
361+
frozenErrorClass = defineClass(runtimeErrorClass, "FrozenError", language.frozenErrorShape);
362362

363363
// StandardError > RangeError
364364
rangeErrorClass = defineClass(standardErrorClass, "RangeError");

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,15 +258,24 @@ public RubyException argumentErrorCantUnfreeze(Object self, Node currentNode) {
258258
@TruffleBoundary
259259
public RubyException frozenError(Object object, Node currentNode) {
260260
String className = LogicalClassNode.getUncached().execute(object).fields.getName();
261-
return frozenError(StringUtils.format("can't modify frozen %s", className), currentNode);
261+
return frozenError(StringUtils.format("can't modify frozen %s", className), currentNode, object);
262262
}
263263

264264
@TruffleBoundary
265-
public RubyException frozenError(String message, Node currentNode) {
265+
public RubyException frozenError(String message, Node currentNode, Object receiver) {
266266
RubyClass exceptionClass = context.getCoreLibrary().frozenErrorClass;
267267
RubyString errorMessage = StringOperations
268268
.createString(context, language, StringOperations.encodeRope(message, UTF8Encoding.INSTANCE));
269-
return ExceptionOperations.createRubyException(context, exceptionClass, errorMessage, currentNode, null);
269+
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);
270+
final Object cause = ThreadGetExceptionNode.getLastException(context);
271+
showExceptionIfDebug(exceptionClass, errorMessage, backtrace);
272+
return new RubyFrozenError(
273+
exceptionClass,
274+
language.frozenErrorShape,
275+
errorMessage,
276+
backtrace,
277+
cause,
278+
receiver);
270279
}
271280

272281
// RuntimeError
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2020 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.exception;
11+
12+
import com.oracle.truffle.api.dsl.Cached;
13+
import com.oracle.truffle.api.profiles.BranchProfile;
14+
import org.truffleruby.builtins.CoreMethod;
15+
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
16+
import org.truffleruby.builtins.CoreModule;
17+
import org.truffleruby.builtins.Primitive;
18+
import org.truffleruby.builtins.PrimitiveArrayArgumentsNode;
19+
import org.truffleruby.core.klass.RubyClass;
20+
import org.truffleruby.language.Visibility;
21+
import org.truffleruby.language.control.RaiseException;
22+
import org.truffleruby.language.objects.AllocateHelperNode;
23+
import org.truffleruby.language.objects.AllocationTracing;
24+
25+
import com.oracle.truffle.api.dsl.Specialization;
26+
import com.oracle.truffle.api.object.Shape;
27+
28+
@CoreModule(value = "FrozenError", isClass = true)
29+
public abstract class FrozenErrorNodes {
30+
31+
@CoreMethod(names = { "__allocate__", "__layout_allocate__" }, constructor = true, visibility = Visibility.PRIVATE)
32+
public abstract static class AllocateNode extends CoreMethodArrayArgumentsNode {
33+
34+
@Child private AllocateHelperNode allocateNode = AllocateHelperNode.create();
35+
36+
@Specialization
37+
protected RubyFrozenError allocateFrozenError(RubyClass rubyClass) {
38+
final Shape shape = allocateNode.getCachedShape(rubyClass);
39+
final RubyFrozenError instance = new RubyFrozenError(rubyClass, shape, nil, null, nil, null);
40+
AllocationTracing.trace(instance, this);
41+
return instance;
42+
}
43+
44+
}
45+
46+
@CoreMethod(names = "receiver")
47+
public abstract static class ReceiverNode extends CoreMethodArrayArgumentsNode {
48+
49+
@Specialization
50+
protected Object receiver(RubyFrozenError self,
51+
@Cached BranchProfile errorProfile) {
52+
final Object receiver = self.receiver;
53+
54+
if (receiver == null) {
55+
errorProfile.enter();
56+
throw new RaiseException(getContext(), coreExceptions().argumentErrorNoReceiver(this));
57+
}
58+
return receiver;
59+
}
60+
61+
}
62+
63+
@Primitive(name = "frozen_error_set_receiver")
64+
public abstract static class ReceiverSetNode extends PrimitiveArrayArgumentsNode {
65+
66+
@Specialization
67+
protected Object setReceiver(RubyFrozenError error, Object receiver) {
68+
error.receiver = receiver;
69+
return receiver;
70+
}
71+
72+
}
73+
74+
75+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2020 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.exception;
11+
12+
import java.util.Set;
13+
14+
import org.truffleruby.core.klass.RubyClass;
15+
import org.truffleruby.language.backtrace.Backtrace;
16+
import org.truffleruby.language.objects.ObjectGraph;
17+
import org.truffleruby.language.objects.ObjectGraphNode;
18+
19+
import com.oracle.truffle.api.object.Shape;
20+
21+
public class RubyFrozenError extends RubyException implements ObjectGraphNode {
22+
23+
public Object receiver;
24+
25+
public RubyFrozenError(
26+
RubyClass rubyClass,
27+
Shape shape,
28+
Object message,
29+
Backtrace backtrace,
30+
Object cause,
31+
Object receiver) {
32+
super(rubyClass, shape, message, backtrace, cause);
33+
this.receiver = receiver;
34+
}
35+
36+
@Override
37+
public void getAdjacentObjects(Set<Object> reachable) {
38+
super.getAdjacentObjects(reachable);
39+
ObjectGraph.addProperty(reachable, receiver);
40+
}
41+
42+
}

src/main/java/org/truffleruby/core/module/ModuleFields.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,12 @@ public void initCopy(RubyModule from) {
203203
public void checkFrozen(RubyContext context, Node currentNode) {
204204
if (context.getCoreLibrary() != null && RubyLibrary.getUncached().isFrozen(rubyModule)) {
205205
String name;
206+
Object receiver = rubyModule;
206207
if (rubyModule instanceof RubyClass) {
207208
final RubyClass cls = (RubyClass) rubyModule;
208209
name = "object";
209210
if (cls.isSingleton) {
211+
receiver = cls.attached;
210212
if (cls.attached instanceof RubyClass) {
211213
name = "Class";
212214
} else if (cls.attached instanceof RubyModule) {
@@ -222,7 +224,8 @@ public void checkFrozen(RubyContext context, Node currentNode) {
222224
context,
223225
context.getCoreExceptions().frozenError(
224226
StringUtils.format("can't modify frozen %s", name),
225-
currentNode));
227+
currentNode,
228+
receiver));
226229
}
227230
}
228231

src/main/ruby/truffleruby/core/exception.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,13 @@ def to_s
215215
end
216216
end
217217

218+
class FrozenError < RuntimeError
219+
def initialize(*args, receiver: undefined)
220+
super(*args)
221+
Primitive.frozen_error_set_receiver self, receiver unless Primitive.undefined?(receiver)
222+
end
223+
end
224+
218225
class IndexError < StandardError
219226
end
220227

0 commit comments

Comments
 (0)