Skip to content

Commit 4e6a0b9

Browse files
committed
Copy progenitor vartable manager when extending Data
We walk up the hierarchy to get the actual Data definitions, but each concrete allocatable class should have its own variable table manager.
1 parent 6311f6e commit 4e6a0b9

2 files changed

Lines changed: 49 additions & 7 deletions

File tree

core/src/main/java/org/jruby/RubyClass.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ public static RubyClass newClass(ThreadContext context, RubyClass superClass, St
555555
baseName(name);
556556
clazz.makeMetaClass(context, superClass.getMetaClass());
557557
if (setParent) clazz.setParent(parent);
558+
clazz.copyVariableTableManager(context, superClass);
558559
parent.setConstant(context, name, clazz, file, line);
559560
superClass.invokeInherited(context, superClass, clazz);
560561
return clazz;
@@ -1077,6 +1078,7 @@ private RubyClass initializeCommon(ThreadContext context, RubyClass superClazz,
10771078
allocator = superClazz.allocator;
10781079
makeMetaClass(context, superClazz.getMetaClass());
10791080
superClazz.addSubclass(this);
1081+
copyVariableTableManager(context, superClazz);
10801082

10811083
marshal = superClazz.marshal;
10821084

@@ -1086,6 +1088,14 @@ private RubyClass initializeCommon(ThreadContext context, RubyClass superClazz,
10861088
return this;
10871089
}
10881090

1091+
private void copyVariableTableManager(ThreadContext context, RubyClass superClazz) {
1092+
VariableTableManager variableTableManager = superClazz.getVariableTableManager();
1093+
if (variableTableManager.getRealClass().superClass() == context.runtime.getData()) {
1094+
// duplicate data's variable table in subclasses
1095+
this.variableTableManager = variableTableManager.duplicate();
1096+
}
1097+
}
1098+
10891099
/** rb_class_init_copy
10901100
*
10911101
*/
@@ -3484,5 +3494,5 @@ private CallSite[] createCallSites() {
34843494
private final RubyClass realClass;
34853495

34863496
/** Variable table manager for this class */
3487-
private final VariableTableManager variableTableManager;
3497+
private VariableTableManager variableTableManager;
34883498
}

core/src/main/java/org/jruby/runtime/ivars/VariableTableManager.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.io.ObjectOutputStream;
3333
import java.lang.invoke.MethodHandle;
3434
import java.util.ArrayList;
35+
import java.util.Arrays;
3536
import java.util.Collections;
3637
import java.util.LinkedHashMap;
3738
import java.util.Map;
@@ -105,6 +106,27 @@ public VariableTableManager(RubyClass realClass) {
105106
this.realClass = realClass;
106107
}
107108

109+
/**
110+
* Copy constructor with deep cloning.
111+
*
112+
* @param original VariableTableManager to copy
113+
*/
114+
VariableTableManager(VariableTableManager original) {
115+
synchronized (original) {
116+
this.realClass = original.realClass;
117+
this.variableAccessors = copyVariableAccessors(original.variableAccessors);
118+
this.variableNames = original.variableNames.clone();
119+
this.hasObjectID = original.hasObjectID;
120+
this.hasFFI = original.hasFFI;
121+
this.hasObjectspaceGroup = original.hasObjectspaceGroup;
122+
this.fieldVariables = original.fieldVariables;
123+
}
124+
}
125+
126+
public RubyClass getRealClass() {
127+
return realClass;
128+
}
129+
108130
/**
109131
* Get the map of all current variable accessors with intent to read from it.
110132
*
@@ -253,18 +275,24 @@ VariableAccessor getVariableAccessorWithBuilder(String name, Function<Integer, V
253275
if (ivarAccessor == null) {
254276
// allocate a new accessor and populate a new table
255277
ivarAccessor = allocateVariableAccessors(name, defaultAccessorBuilder);
256-
Map<String, VariableAccessor> newVariableAccessors = new LinkedHashMap<>(myVariableAccessors.size() + 1);
257-
258-
newVariableAccessors.putAll(myVariableAccessors);
259-
newVariableAccessors.put(name, ivarAccessor);
260-
261-
variableAccessors = newVariableAccessors;
278+
variableAccessors = copyVariableAccessors(myVariableAccessors, name, ivarAccessor);
262279
}
263280
}
264281
}
265282
return ivarAccessor;
266283
}
267284

285+
private static Map<String, VariableAccessor> copyVariableAccessors(Map<String, VariableAccessor> myVariableAccessors) {
286+
return new LinkedHashMap<>(myVariableAccessors);
287+
}
288+
289+
private static Map<String, VariableAccessor> copyVariableAccessors(Map<String, VariableAccessor> myVariableAccessors, String name, VariableAccessor ivarAccessor) {
290+
LinkedHashMap<String, VariableAccessor> newVariableAccessors = new LinkedHashMap<>(myVariableAccessors.size() + 1);
291+
newVariableAccessors.putAll(myVariableAccessors);
292+
newVariableAccessors.put(name, ivarAccessor);
293+
return newVariableAccessors;
294+
}
295+
268296
/**
269297
* Get the variable accessor for the given name with intent to use it for
270298
* reading.
@@ -514,6 +542,10 @@ public Object clearVariable(RubyBasicObject object, String name) {
514542
}
515543
}
516544

545+
public VariableTableManager duplicate() {
546+
return new VariableTableManager(this);
547+
}
548+
517549
/**
518550
* We lazily stand up the object ID since it forces us to stand up
519551
* per-object state for a given object. We also check for ObjectSpace here,

0 commit comments

Comments
 (0)