Skip to content

Commit c877bb4

Browse files
committed
Add translation to native pointers.
PullRequest: truffleruby/492
2 parents 2ba8c27 + 9bd325c commit c877bb4

File tree

8 files changed

+276
-54
lines changed

8 files changed

+276
-54
lines changed

src/main/java/org/truffleruby/RubyContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.oracle.truffle.api.source.SourceSection;
2424
import org.joni.Regex;
2525
import org.truffleruby.builtins.PrimitiveManager;
26+
import org.truffleruby.cext.ValueWrapperManager;
2627
import org.truffleruby.collections.WeakValueCache;
2728
import org.truffleruby.core.CoreLibrary;
2829
import org.truffleruby.core.FinalizationService;
@@ -117,6 +118,7 @@ public class RubyContext {
117118
private final WeakValueCache<RegexpCacheKey, Regex> regexpCache = new WeakValueCache<>();
118119
private final PreInitializationManager preInitializationManager;
119120
private final NativeConfiguration nativeConfiguration;
121+
private final ValueWrapperManager valueWrapperManager = new ValueWrapperManager(this);
120122

121123
private final CompilerOptions compilerOptions = Truffle.getRuntime().createCompilerOptions();
122124

@@ -778,6 +780,10 @@ public NativeConfiguration getNativeConfiguration() {
778780
return nativeConfiguration;
779781
}
780782

783+
public ValueWrapperManager getValueWrapperManager() {
784+
return valueWrapperManager;
785+
}
786+
781787
private static SecureRandom createRandomInstance() {
782788
try {
783789
/*

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1279,8 +1279,15 @@ public abstract static class UnwrapValueNode extends CoreMethodArrayArgumentsNod
12791279

12801280
@Specialization
12811281
public Object unwrap(Object value,
1282+
@Cached("create()") BranchProfile exceptionProfile,
12821283
@Cached("createUnwrapNode()") UnwrapNode unwrapNode) {
1283-
return unwrapNode.execute(value);
1284+
Object object = unwrapNode.execute(value);
1285+
if (object == null) {
1286+
exceptionProfile.enter();
1287+
throw new RaiseException(getContext(), coreExceptions().runtimeError("native handle not found", this));
1288+
} else {
1289+
return object;
1290+
}
12841291
}
12851292

12861293
protected UnwrapNode createUnwrapNode() {

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@
44
import org.truffleruby.language.RubyBaseNode;
55
import org.truffleruby.language.control.RaiseException;
66

7-
import com.oracle.truffle.api.dsl.Fallback;
7+
import com.oracle.truffle.api.dsl.Cached;
8+
import com.oracle.truffle.api.dsl.ImportStatic;
89
import com.oracle.truffle.api.dsl.Specialization;
10+
import com.oracle.truffle.api.interop.ForeignAccess;
11+
import com.oracle.truffle.api.interop.Message;
912
import com.oracle.truffle.api.interop.TruffleObject;
13+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
14+
import com.oracle.truffle.api.nodes.Node;
1015
import com.oracle.truffle.api.object.DynamicObject;
16+
import com.oracle.truffle.api.profiles.BranchProfile;
1117

18+
@ImportStatic(Message.class)
1219
public abstract class UnwrapNode extends RubyBaseNode {
1320

1421
public abstract Object execute(Object value);
@@ -18,9 +25,29 @@ public Object unwrapValue(DynamicObject value) {
1825
return Layouts.VALUE_WRAPPER.getObject(value);
1926
}
2027

21-
@Fallback
22-
public Object unwrapTypeCastObject(Object value) {
23-
throw new RaiseException(getContext(), coreExceptions().argumentError("Unwrapping something that isn't a wrapper", this));
28+
@Specialization(guards = "!isWrapper(value)")
29+
public Object unwrapTypeCastObject(TruffleObject value,
30+
@Cached("IS_POINTER.createNode()") Node isPointerNode,
31+
@Cached("AS_POINTER.createNode()") Node asPointerNode,
32+
@Cached("create()") BranchProfile unsupportedProfile,
33+
@Cached("create()") BranchProfile nonPointerProfile) {
34+
if (ForeignAccess.sendIsPointer(isPointerNode, value)) {
35+
long handle = 0;
36+
try {
37+
handle = ForeignAccess.sendAsPointer(asPointerNode, value);
38+
} catch (UnsupportedMessageException e) {
39+
unsupportedProfile.enter();
40+
throw new RaiseException(getContext(), coreExceptions().argumentError(e.getMessage(), this, e));
41+
}
42+
return unwrapHandle(handle);
43+
} else {
44+
nonPointerProfile.enter();
45+
throw new RaiseException(getContext(), coreExceptions().argumentError("Not a handle or a pointer", this));
46+
}
47+
}
48+
49+
private Object unwrapHandle(long handle) {
50+
return getContext().getValueWrapperManager().getFromHandleMap(handle);
2451
}
2552

2653
public static boolean isWrapper(TruffleObject value) {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@
2020
@Layout(objectTypeSuperclass = ValueWrapperObjectType.class)
2121
public interface ValueWrapperLayout {
2222

23-
DynamicObject createValueWrapper(Object object);
23+
DynamicObject createValueWrapper(Object object,
24+
long handle);
2425

2526
boolean isValueWrapper(Object object);
2627

2728
boolean isValueWrapper(DynamicObject object);
2829

2930
Object getObject(DynamicObject object);
31+
32+
long getHandle(DynamicObject object);
33+
34+
void setHandle(DynamicObject object, long value);
3035
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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 java.lang.ref.WeakReference;
13+
14+
import org.truffleruby.Layouts;
15+
import org.truffleruby.RubyContext;
16+
import org.truffleruby.collections.LongHashMap;
17+
import org.truffleruby.extra.ffi.Pointer;
18+
import org.truffleruby.language.NotProvided;
19+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
20+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
21+
import com.oracle.truffle.api.object.DynamicObject;
22+
23+
public class ValueWrapperManager {
24+
25+
static final int UNSET_HANDLE = -1;
26+
27+
@CompilationFinal private DynamicObject undefWrapper = null;
28+
@CompilationFinal private DynamicObject trueWrapper = null;
29+
@CompilationFinal private DynamicObject falseWrapper = null;
30+
31+
private final LongHashMap<DynamicObject> longMap = new LongHashMap<>(128);
32+
private final LongHashMap<WeakReference<DynamicObject>> handleMap = new LongHashMap<>(1024);
33+
34+
private final RubyContext context;
35+
36+
public ValueWrapperManager(RubyContext context) {
37+
this.context = context;
38+
}
39+
40+
public DynamicObject undefWrapper() {
41+
if (undefWrapper == null) {
42+
undefWrapper = Layouts.VALUE_WRAPPER.createValueWrapper(NotProvided.INSTANCE, UNSET_HANDLE);
43+
}
44+
return undefWrapper;
45+
}
46+
47+
public DynamicObject booleanWrapper(boolean value) {
48+
if (value) {
49+
if (trueWrapper == null) {
50+
trueWrapper = Layouts.VALUE_WRAPPER.createValueWrapper(true, UNSET_HANDLE);
51+
}
52+
return trueWrapper;
53+
} else {
54+
if (falseWrapper == null) {
55+
falseWrapper = createFalseWrapper();
56+
}
57+
return falseWrapper;
58+
}
59+
}
60+
61+
private DynamicObject createFalseWrapper() {
62+
// Ensure that Qfalse will by falsy in C.
63+
return Layouts.VALUE_WRAPPER.createValueWrapper(false, 0);
64+
}
65+
66+
/*
67+
* We keep a map of long wrappers that have been generated because various C extensions assume
68+
* that any given fixnum will translate to a given VALUE.
69+
*/
70+
@TruffleBoundary
71+
public synchronized DynamicObject longWrapper(long value) {
72+
DynamicObject wrapper = longMap.get(value);
73+
if (wrapper == null) {
74+
wrapper = Layouts.VALUE_WRAPPER.createValueWrapper(value, ValueWrapperManager.UNSET_HANDLE);
75+
longMap.put(value, wrapper);
76+
}
77+
return wrapper;
78+
}
79+
80+
public DynamicObject doubleWrapper(double value) {
81+
return Layouts.VALUE_WRAPPER.createValueWrapper(value, ValueWrapperManager.UNSET_HANDLE);
82+
}
83+
84+
@TruffleBoundary
85+
public synchronized void addToHandleMap(long handle, DynamicObject wrapper) {
86+
handleMap.put(handle, new WeakReference<>(wrapper));
87+
}
88+
89+
@TruffleBoundary
90+
public synchronized Object getFromHandleMap(long handle) {
91+
WeakReference<DynamicObject> ref = handleMap.get(handle);
92+
DynamicObject object;
93+
if (ref == null) {
94+
return null;
95+
}
96+
if ((object = ref.get()) == null) {
97+
return null;
98+
}
99+
return Layouts.VALUE_WRAPPER.getObject(object);
100+
}
101+
102+
@TruffleBoundary
103+
public synchronized void removeFromHandleMap(long handle) {
104+
handleMap.remove(handle);
105+
}
106+
107+
@TruffleBoundary
108+
public synchronized long createNativeHandle(DynamicObject wrapper) {
109+
Pointer handlePointer = Pointer.malloc(1);
110+
long handleAddress = handlePointer.getAddress();
111+
if ((handleAddress & 0x7) != 0) {
112+
throw new RuntimeException("unaligned malloc for native handle");
113+
}
114+
Layouts.VALUE_WRAPPER.setHandle(wrapper, handleAddress);
115+
addToHandleMap(handleAddress, wrapper);
116+
addFinalizer(wrapper, handlePointer);
117+
return handleAddress;
118+
}
119+
120+
public void addFinalizer(DynamicObject wrapper, Pointer handle) {
121+
context.getFinalizationService().addFinalizer(
122+
wrapper, null, ValueWrapperObjectType.class,
123+
createFinalizer(handle), null);
124+
}
125+
126+
private Runnable createFinalizer(Pointer handle) {
127+
return () -> {
128+
this.removeFromHandleMap(handle.getAddress());
129+
handle.free();
130+
};
131+
132+
}
133+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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.RubyContext;
14+
import org.truffleruby.RubyLanguage;
15+
16+
import com.oracle.truffle.api.CompilerDirectives;
17+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
18+
import com.oracle.truffle.api.frame.VirtualFrame;
19+
import com.oracle.truffle.api.interop.MessageResolution;
20+
import com.oracle.truffle.api.interop.Resolve;
21+
import com.oracle.truffle.api.nodes.Node;
22+
import com.oracle.truffle.api.object.DynamicObject;
23+
import com.oracle.truffle.api.profiles.BranchProfile;
24+
25+
@MessageResolution(receiverType = ValueWrapperObjectType.class)
26+
public class ValueWrapperMessageResolution {
27+
28+
@Resolve(message = "IS_POINTER")
29+
public static abstract class ForeignIsPointerNode extends Node {
30+
31+
protected boolean access(VirtualFrame frame, DynamicObject wrapper) {
32+
return true;
33+
}
34+
}
35+
36+
@Resolve(message = "TO_NATIVE")
37+
public static abstract class ForeignToNativeNode extends Node {
38+
protected Object access(VirtualFrame frame, DynamicObject receiver) {
39+
return receiver;
40+
}
41+
}
42+
43+
@Resolve(message = "AS_POINTER")
44+
public static abstract class ForeignAsPointerNode extends Node {
45+
46+
@CompilationFinal private RubyContext context;
47+
private final BranchProfile createHandleProfile = BranchProfile.create();
48+
49+
protected long access(VirtualFrame frame, DynamicObject wrapper) {
50+
long handle = Layouts.VALUE_WRAPPER.getHandle(wrapper);
51+
if (handle == ValueWrapperManager.UNSET_HANDLE) {
52+
createHandleProfile.enter();
53+
handle = getContext().getValueWrapperManager().createNativeHandle(wrapper);
54+
}
55+
return handle;
56+
}
57+
58+
public RubyContext getContext() {
59+
if (context == null) {
60+
CompilerDirectives.transferToInterpreterAndInvalidate();
61+
context = RubyLanguage.getCurrentContext();
62+
}
63+
64+
return context;
65+
}
66+
67+
}
68+
69+
}

0 commit comments

Comments
 (0)