10
10
package org .truffleruby .language .constants ;
11
11
12
12
import com .oracle .truffle .api .CompilerDirectives ;
13
+ import com .oracle .truffle .api .CompilerDirectives .CompilationFinal ;
13
14
import com .oracle .truffle .api .frame .VirtualFrame ;
15
+ import com .oracle .truffle .api .profiles .ConditionProfile ;
14
16
import org .truffleruby .core .cast .BooleanCastNode ;
15
17
import org .truffleruby .core .cast .BooleanCastNodeGen ;
16
18
import org .truffleruby .core .module .RubyModule ;
@@ -29,6 +31,26 @@ public class OrAssignConstantNode extends RubyContextSourceNode {
29
31
@ Child protected WriteConstantNode writeConstant ;
30
32
@ Child private BooleanCastNode cast ;
31
33
34
+ private enum ExecuteCounter {
35
+ NEVER ,
36
+ ONCE ,
37
+ MANY ;
38
+
39
+ public ExecuteCounter next () {
40
+ if (this == NEVER ) {
41
+ return ONCE ;
42
+ } else {
43
+ return MANY ;
44
+ }
45
+ }
46
+
47
+ }
48
+
49
+ @ CompilationFinal private ExecuteCounter executeCounter = ExecuteCounter .NEVER ;
50
+ private final ConditionProfile triviallyUndefined = ConditionProfile .create ();
51
+ private final ConditionProfile defined = ConditionProfile .create ();
52
+ private final ConditionProfile truthy = ConditionProfile .create ();
53
+
32
54
public OrAssignConstantNode (ReadConstantNode readConstant , WriteConstantNode writeConstant ) {
33
55
this .readConstant = readConstant ;
34
56
this .writeConstant = writeConstant ;
@@ -37,7 +59,14 @@ public OrAssignConstantNode(ReadConstantNode readConstant, WriteConstantNode wri
37
59
@ Override
38
60
public Object execute (VirtualFrame frame ) {
39
61
40
- if (readConstant .isModuleTriviallyUndefined (frame , getLanguage (), getContext ())) {
62
+ if (executeCounter != ExecuteCounter .MANY ) {
63
+ // Make sure to reset the profiles after the first execution, as we expect the first execution to write
64
+ // the constant and subsequent execution to simply read it.
65
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
66
+ executeCounter = executeCounter .next ();
67
+ }
68
+
69
+ if (triviallyUndefined .profile (readConstant .isModuleTriviallyUndefined (frame , getLanguage (), getContext ()))) {
41
70
// It might not be defined because of autoloaded constants (maybe other reasons?),
42
71
// simply attempt writing (which will trigger autoloads if required).
43
72
// Since we didn't evaluate the module part yet, no side-effects can occur.
@@ -53,12 +82,12 @@ public Object execute(VirtualFrame frame) {
53
82
54
83
// Next we check if the constant itself is defined, and if it is, we get its value.
55
84
final RubyConstant constant = readConstant .getConstantIfDefined (module );
56
- final Object value = constant == null
57
- ? null
58
- : readConstant . getConstant ( module , constant ) ;
85
+ final Object value = defined . profile ( constant != null )
86
+ ? readConstant . getConstant ( module , constant )
87
+ : null ;
59
88
60
89
// Write if the constant is undefined or if its value is falsy.
61
- if (constant == null || !castToBoolean (value )) {
90
+ if (! defined . profile ( constant != null ) || !truthy . profile ( castToBoolean (value ) )) {
62
91
return writeConstant .execute (frame , module );
63
92
} else {
64
93
return value ;
0 commit comments