Skip to content

Commit a762a64

Browse files
committed
Introduce ValueWrapper.
PullRequest: truffleruby/484
2 parents 0dec7f3 + 440b981 commit a762a64

File tree

8 files changed

+607
-0
lines changed

8 files changed

+607
-0
lines changed

src/main/c/cext/ruby.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4608,3 +4608,11 @@ VALUE rb_syserr_new_str(int n, VALUE arg) {
46084608
VALUE rb_yield_values2(int argc, const VALUE *argv) {
46094609
rb_tr_error("rb_yield_values2 not implemented");
46104610
}
4611+
4612+
VALUE rb_tr_wrap(VALUE object) {
4613+
return polyglot_invoke(RUBY_CEXT, "rb_tr_wrap", object);
4614+
}
4615+
4616+
VALUE rb_tr_unwrap(VALUE object) {
4617+
return polyglot_invoke(RUBY_CEXT, "rb_tr_unwrap", object);
4618+
}

src/main/java/org/truffleruby/Layouts.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
package org.truffleruby;
1111

1212
import com.oracle.truffle.api.object.HiddenKey;
13+
14+
import org.truffleruby.cext.ValueWrapperLayout;
15+
import org.truffleruby.cext.ValueWrapperLayoutImpl;
1316
import org.truffleruby.core.HandleLayout;
1417
import org.truffleruby.core.HandleLayoutImpl;
1518
import org.truffleruby.core.array.ArrayLayout;
@@ -102,6 +105,7 @@ public abstract class Layouts {
102105
public static final HiddenKey FROZEN_IDENTIFIER = new HiddenKey("frozen?");
103106
public static final HiddenKey ASSOCIATED_IDENTIFIER = new HiddenKey("associated");
104107
public static final HiddenKey FINALIZER_REF_IDENTIFIER = new HiddenKey("finalizerRef");
108+
public static final HiddenKey VALUE_WRAPPER_IDENTIFIER = new HiddenKey("value_wrapper");
105109

106110
// Generated layouts
107111

@@ -145,5 +149,6 @@ public abstract class Layouts {
145149
public static final TracePointLayout TRACE_POINT = TracePointLayoutImpl.INSTANCE;
146150
public static final DigestLayout DIGEST = DigestLayoutImpl.INSTANCE;
147151
public static final SystemCallErrorLayout SYSTEM_CALL_ERROR = SystemCallErrorLayoutImpl.INSTANCE;
152+
public static final ValueWrapperLayout VALUE_WRAPPER = ValueWrapperLayoutImpl.INSTANCE;
148153

149154
}

src/main/java/org/truffleruby/cext/CExtNodes.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,4 +1260,32 @@ public DynamicObject newManagedStruct(Object type) {
12601260

12611261
}
12621262

1263+
@CoreMethod(names = "rb_tr_wrap", onSingleton = true, required = 1)
1264+
public abstract static class WrapValueNode extends CoreMethodArrayArgumentsNode {
1265+
1266+
@Specialization
1267+
public TruffleObject wrapInt(Object value,
1268+
@Cached("createWrapNode()") WrapNode wrapNode) {
1269+
return wrapNode.execute(value);
1270+
}
1271+
1272+
protected WrapNode createWrapNode() {
1273+
return WrapNodeGen.create();
1274+
}
1275+
}
1276+
1277+
@CoreMethod(names = "rb_tr_unwrap", onSingleton = true, required = 1)
1278+
public abstract static class UnwrapValueNode extends CoreMethodArrayArgumentsNode {
1279+
1280+
@Specialization
1281+
public Object unwrap(Object value,
1282+
@Cached("createUnwrapNode()") UnwrapNode unwrapNode) {
1283+
return unwrapNode.execute(value);
1284+
}
1285+
1286+
protected UnwrapNode createUnwrapNode() {
1287+
return UnwrapNodeGen.create();
1288+
}
1289+
}
1290+
12631291
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.truffleruby.cext;
2+
3+
import org.truffleruby.Layouts;
4+
import org.truffleruby.language.RubyBaseNode;
5+
import org.truffleruby.language.control.RaiseException;
6+
7+
import com.oracle.truffle.api.dsl.Fallback;
8+
import com.oracle.truffle.api.dsl.Specialization;
9+
import com.oracle.truffle.api.interop.TruffleObject;
10+
import com.oracle.truffle.api.object.DynamicObject;
11+
12+
public abstract class UnwrapNode extends RubyBaseNode {
13+
14+
public abstract Object execute(Object value);
15+
16+
@Specialization(guards = "isWrapper(value)")
17+
public Object unwrapValue(DynamicObject value) {
18+
return Layouts.VALUE_WRAPPER.getObject(value);
19+
}
20+
21+
@Fallback
22+
public Object unwrapTypeCastObject(Object value) {
23+
throw new RaiseException(getContext(), coreExceptions().argumentError("Unwrapping something that isn't a wrapper", this));
24+
}
25+
26+
public static boolean isWrapper(TruffleObject value) {
27+
return ValueWrapperObjectType.isInstance(value);
28+
}
29+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) 2018 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 1.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.cext;
11+
12+
import com.oracle.truffle.api.object.DynamicObject;
13+
import com.oracle.truffle.api.object.dsl.Layout;
14+
15+
/**
16+
* This layout represents a VALUE in C which wraps the raw Ruby object. This allows foreign access
17+
* methods to be set up which convert these value wrappers to native pointers without affecting the
18+
* semantics of the wrapped objects.
19+
*/
20+
@Layout(objectTypeSuperclass = ValueWrapperObjectType.class)
21+
public interface ValueWrapperLayout {
22+
23+
DynamicObject createValueWrapper(Object object);
24+
25+
boolean isValueWrapper(Object object);
26+
27+
boolean isValueWrapper(DynamicObject object);
28+
29+
Object getObject(DynamicObject object);
30+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2018 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 1.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.cext;
11+
12+
import org.truffleruby.Layouts;
13+
import org.truffleruby.collections.LongHashMap;
14+
import org.truffleruby.language.NotProvided;
15+
16+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
17+
import com.oracle.truffle.api.interop.TruffleObject;
18+
import com.oracle.truffle.api.object.DynamicObject;
19+
import com.oracle.truffle.api.object.ObjectType;
20+
21+
public class ValueWrapperObjectType extends ObjectType {
22+
23+
private static DynamicObject UNDEF_WRAPPER = null;
24+
25+
private static DynamicObject TRUE_WRAPPER = null;
26+
private static DynamicObject FALSE_WRAPPER = null;
27+
28+
private static LongHashMap<DynamicObject> longMap = new LongHashMap<>(128);
29+
30+
public static DynamicObject createValueWrapper(Object value) {
31+
return Layouts.VALUE_WRAPPER.createValueWrapper(value);
32+
}
33+
34+
public static synchronized DynamicObject createUndefWrapper(NotProvided value) {
35+
return UNDEF_WRAPPER != null ? UNDEF_WRAPPER : (UNDEF_WRAPPER = Layouts.VALUE_WRAPPER.createValueWrapper(value));
36+
}
37+
38+
public static synchronized DynamicObject createBooleanWrapper(boolean value) {
39+
if (value) {
40+
return TRUE_WRAPPER != null ? TRUE_WRAPPER : (TRUE_WRAPPER = Layouts.VALUE_WRAPPER.createValueWrapper(true));
41+
} else {
42+
return FALSE_WRAPPER != null ? FALSE_WRAPPER : (FALSE_WRAPPER = createFalseWrapper());
43+
}
44+
}
45+
46+
private static DynamicObject createFalseWrapper() {
47+
// Ensure that Qfalse will by falsy in C.
48+
return Layouts.VALUE_WRAPPER.createValueWrapper(false);
49+
}
50+
51+
/*
52+
* We keep a map of long wrappers that have been generated because various C extensions assume
53+
* that any given fixnum will translate to a given VALUE.
54+
*/
55+
@TruffleBoundary
56+
public static synchronized DynamicObject createLongWrapper(long value) {
57+
DynamicObject wrapper = longMap.get(value);
58+
if (wrapper == null) {
59+
wrapper = Layouts.VALUE_WRAPPER.createValueWrapper(value);
60+
longMap.put(value, wrapper);
61+
}
62+
return wrapper;
63+
}
64+
65+
@TruffleBoundary
66+
public static synchronized DynamicObject createDoubleWrapper(double value) {
67+
return Layouts.VALUE_WRAPPER.createValueWrapper(value);
68+
}
69+
70+
public static boolean isInstance(TruffleObject receiver) {
71+
return Layouts.VALUE_WRAPPER.isValueWrapper(receiver);
72+
}
73+
74+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package org.truffleruby.cext;
2+
3+
import org.jcodings.specific.UTF8Encoding;
4+
import org.truffleruby.Layouts;
5+
import org.truffleruby.core.rope.RopeOperations;
6+
import org.truffleruby.language.NotProvided;
7+
import org.truffleruby.language.RubyBaseNode;
8+
import org.truffleruby.language.control.RaiseException;
9+
import org.truffleruby.language.objects.ReadObjectFieldNode;
10+
import org.truffleruby.language.objects.ReadObjectFieldNodeGen;
11+
import org.truffleruby.language.objects.WriteObjectFieldNode;
12+
import org.truffleruby.language.objects.WriteObjectFieldNodeGen;
13+
14+
import com.oracle.truffle.api.dsl.Cached;
15+
import com.oracle.truffle.api.dsl.Specialization;
16+
import com.oracle.truffle.api.interop.TruffleObject;
17+
import com.oracle.truffle.api.object.DynamicObject;
18+
19+
public abstract class WrapNode extends RubyBaseNode {
20+
21+
public abstract TruffleObject execute(Object value);
22+
23+
@Specialization
24+
public DynamicObject wrapInt(int value) {
25+
return ValueWrapperObjectType.createLongWrapper(value);
26+
}
27+
28+
@Specialization
29+
public DynamicObject wrapLong(long value) {
30+
return ValueWrapperObjectType.createLongWrapper(value);
31+
}
32+
33+
@Specialization
34+
public DynamicObject wrapDouble(double value) {
35+
return ValueWrapperObjectType.createDoubleWrapper(value);
36+
}
37+
38+
@Specialization
39+
public DynamicObject wrapBoolean(boolean value) {
40+
return ValueWrapperObjectType.createBooleanWrapper(value);
41+
}
42+
43+
@Specialization
44+
public DynamicObject wrapUndef(NotProvided value) {
45+
return ValueWrapperObjectType.createUndefWrapper(value);
46+
}
47+
48+
@Specialization(guards = "isWrapped(value)")
49+
public DynamicObject wrapWrappedValue(DynamicObject value) {
50+
throw new RaiseException(getContext(), coreExceptions().argumentError(RopeOperations.encodeAscii("Wrapping wrapped object", UTF8Encoding.INSTANCE), this));
51+
}
52+
53+
@Specialization(guards = "isRubyBasicObject(value)")
54+
public DynamicObject wrapValue(DynamicObject value,
55+
@Cached("createReader()") ReadObjectFieldNode readWrapperNode,
56+
@Cached("createWriter()") WriteObjectFieldNode writeWrapperNode) {
57+
DynamicObject wrapper = (DynamicObject) readWrapperNode.execute(value);
58+
if (wrapper == null) {
59+
synchronized (value) {
60+
wrapper = (DynamicObject) readWrapperNode.execute(value);
61+
if (wrapper == null) {
62+
wrapper = ValueWrapperObjectType.createValueWrapper(value);
63+
writeWrapperNode.write(value, wrapper);
64+
}
65+
}
66+
}
67+
return wrapper;
68+
}
69+
70+
@Specialization(guards = "!isRubyBasicObject(value)")
71+
public TruffleObject wrapNonRubyObject(TruffleObject value) {
72+
throw new RaiseException(getContext(), coreExceptions().argumentError("Attempt to wrap something that isn't an Ruby object", this));
73+
}
74+
75+
public ReadObjectFieldNode createReader() {
76+
return ReadObjectFieldNodeGen.create(Layouts.VALUE_WRAPPER_IDENTIFIER, null);
77+
}
78+
79+
public WriteObjectFieldNode createWriter() {
80+
return WriteObjectFieldNodeGen.create(Layouts.VALUE_WRAPPER_IDENTIFIER);
81+
}
82+
83+
public boolean isWrapped(TruffleObject value) {
84+
return ValueWrapperObjectType.isInstance(value);
85+
}
86+
87+
}

0 commit comments

Comments
 (0)