IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
JNIEnvContext.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2014 Jeroen Frijters
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
19
20 Jeroen Frijters
21 jeroen@frijters.net
22
23*/
24using System;
25using System.Diagnostics;
27
28namespace IKVM.Runtime.JNI
29{
30 using jboolean = System.SByte;
31 using jint = System.Int32;
32 using jobject = System.IntPtr;
33
34#if FIRST_PASS == false && IMPORTER == false && EXPORTER == false
35
39 internal sealed unsafe class JNIEnvContext
40 {
41
42 [ThreadStatic]
43 static JNIEnvContext current;
44
48 internal static JNIEnvContext Current
49 {
50 [MethodImpl(MethodImplOptions.NoOptimization)] get => current;
51 [MethodImpl(MethodImplOptions.NoOptimization)] set => current = value;
52 }
53
54 internal const int JNI_OK = 0;
55 internal const int JNI_ERR = -1;
56 internal const int JNI_EDETACHED = -2;
57 internal const int JNI_EVERSION = -3;
58 internal const int JNI_COMMIT = 1;
59 internal const int JNI_ABORT = 2;
60 internal const jboolean JNI_TRUE = 1;
61 internal const jboolean JNI_FALSE = 0;
62
63 // NOTE the initial bucket size must be a power of two < LOCAL_REF_MAX_BUCKET_SIZE,
64 // because each time we grow it, we double the size and it must eventually reach
65 // exactly LOCAL_REF_MAX_BUCKET_SIZE
66 const int LOCAL_REF_INITIAL_BUCKET_SIZE = 32;
67 const int LOCAL_REF_SHIFT = 10;
68 const int LOCAL_REF_MAX_BUCKET_SIZE = (1 << LOCAL_REF_SHIFT);
69 const int LOCAL_REF_MASK = (LOCAL_REF_MAX_BUCKET_SIZE - 1);
70
71 internal readonly RuntimeContext context;
72 internal readonly JNIEnv* pJNIEnv;
73 internal RuntimeClassLoader classLoader;
74 internal ikvm.@internal.CallerID callerID;
75
76 object[][] localRefs;
77 int localRefSlot;
78 int localRefIndex;
79 object[] active;
80
84 internal JNIEnvContext(RuntimeContext context)
85 {
86 this.context = context ?? throw new ArgumentNullException(nameof(context));
87
88 pJNIEnv = (JNIEnv*)JNIMemory.Alloc(sizeof(JNIEnv));
89 localRefs = new object[32][];
90 active = localRefs[0] = new object[LOCAL_REF_INITIAL_BUCKET_SIZE];
91 // stuff something in the first entry to make sure we don't hand out a zero handle
92 // (a zero handle corresponds to a null reference)
93 active[0] = "";
94 localRefIndex = 1;
95 }
96
100 ~JNIEnvContext()
101 {
102 if (Environment.HasShutdownStarted == false)
103 {
104 if (pJNIEnv->context.IsAllocated)
105 pJNIEnv->context.Free();
106
107 for (int i = 0; i < pJNIEnv->pinHandleMaxCount; i++)
108 if (pJNIEnv->pinHandles[i].IsAllocated)
109 pJNIEnv->pinHandles[i].Free();
110
111 JNIMemory.Free((nint)(void*)pJNIEnv);
112 }
113 }
114
115 internal readonly struct FrameState
116 {
117
118 internal readonly ikvm.@internal.CallerID callerID;
119 internal readonly int localRefSlot;
120 internal readonly int localRefIndex;
121
128 internal FrameState(ikvm.@internal.CallerID callerID, int localRefSlot, int localRefIndex)
129 {
130 this.callerID = callerID;
131 this.localRefSlot = localRefSlot;
132 this.localRefIndex = localRefIndex;
133 }
134
135 }
136
137 internal FrameState Enter(ikvm.@internal.CallerID newCallerID)
138 {
139 var prev = new FrameState(callerID, localRefSlot, localRefIndex);
140 callerID = newCallerID;
141 localRefSlot++;
142 if (localRefSlot >= localRefs.Length)
143 {
144 var tmp = new object[localRefs.Length * 2][];
145 Array.Copy(localRefs, 0, tmp, 0, localRefs.Length);
146 localRefs = tmp;
147 }
148
149 localRefIndex = 0;
150 active = localRefs[localRefSlot];
151 active ??= localRefs[localRefSlot] = new object[LOCAL_REF_INITIAL_BUCKET_SIZE];
152
153 return prev;
154 }
155
156 internal void Leave(FrameState prev)
157 {
158 for (int i = 0; i < localRefIndex; i++)
159 active[i] = null;
160
161 while (--localRefSlot != prev.localRefSlot)
162 {
163 if (localRefs[localRefSlot] != null)
164 {
165 if (localRefs[localRefSlot].Length == LOCAL_REF_MAX_BUCKET_SIZE)
166 {
167 // if the bucket is totally allocated, we're assuming a leaky method so we throw the bucket away
168 localRefs[localRefSlot] = null;
169 }
170 else
171 {
172 Array.Clear(localRefs[localRefSlot], 0, localRefs[localRefSlot].Length);
173 }
174 }
175 }
176
177 active = localRefs[localRefSlot];
178 localRefIndex = prev.localRefIndex;
179 callerID = prev.callerID;
180 }
181
182 internal nint MakeLocalRef(object obj)
183 {
184 if (obj == null)
185 return 0;
186
187 int index;
188 if (localRefIndex == active.Length)
189 {
190 index = FindFreeIndex();
191 }
192 else
193 {
194 index = localRefIndex++;
195 }
196
197 active[index] = obj;
198 return (nint)((localRefSlot << LOCAL_REF_SHIFT) + index);
199 }
200
201 int FindFreeIndex()
202 {
203 for (int i = 0; i < active.Length; i++)
204 {
205 if (active[i] == null)
206 {
207 while (localRefIndex - 1 > i && active[localRefIndex - 1] == null)
208 {
209 localRefIndex--;
210 }
211
212 return i;
213 }
214 }
215
216 GrowActiveSlot();
217 return localRefIndex++;
218 }
219
220 void GrowActiveSlot()
221 {
222 if (active.Length < LOCAL_REF_MAX_BUCKET_SIZE)
223 {
224 var tmp = new object[active.Length * 2];
225 Array.Copy(active, tmp, active.Length);
226 active = localRefs[localRefSlot] = tmp;
227 return;
228 }
229
230 // if we get here, we're in a native method that most likely is leaking locals refs,
231 // so we're going to allocate a new bucket and increment localRefSlot, this means that
232 // any slots that become available in the previous bucket are not going to be reused,
233 // but since we're assuming that the method is leaking anyway, that isn't a problem
234 // (it's never a correctness issue, just a resource consumption issue)
235 localRefSlot++;
236 localRefIndex = 0;
237 if (localRefSlot == localRefs.Length)
238 {
239 var tmp = new object[localRefSlot * 2][];
240 Array.Copy(localRefs, 0, tmp, 0, localRefSlot);
241 localRefs = tmp;
242 }
243
244 active = localRefs[localRefSlot];
245 active ??= localRefs[localRefSlot] = new object[LOCAL_REF_MAX_BUCKET_SIZE];
246 }
247
248 internal object UnwrapLocalRef(nint i)
249 {
250 return localRefs[i >> LOCAL_REF_SHIFT][i & LOCAL_REF_MASK];
251 }
252
253 internal int PushLocalFrame(jint capacity)
254 {
255 localRefSlot += 2;
256 if (localRefSlot >= localRefs.Length)
257 {
258 var tmp = new object[localRefs.Length * 2][];
259 Array.Copy(localRefs, 0, tmp, 0, localRefs.Length);
260 localRefs = tmp;
261 }
262
263 // we use a null slot to mark the fact that we used PushLocalFrame
264 localRefs[localRefSlot - 1] = null;
265 if (localRefs[localRefSlot] == null)
266 {
267 // we can't use capacity directly, because the array length must be a power of two
268 // and it can't be bigger than LOCAL_REF_MAX_BUCKET_SIZE
269 int r = 1;
270 capacity = Math.Min(capacity, LOCAL_REF_MAX_BUCKET_SIZE);
271 while (r < capacity)
272 r *= 2;
273
274 localRefs[localRefSlot] = new object[r];
275 }
276
277 localRefIndex = 0;
278 active = localRefs[localRefSlot];
279 return JNI_OK;
280 }
281
282 internal jobject PopLocalFrame(object res)
283 {
284 while (localRefs[localRefSlot] != null)
285 {
286 localRefs[localRefSlot] = null;
287 localRefSlot--;
288 }
289
290 localRefSlot--;
291 localRefIndex = localRefs[localRefSlot].Length;
292 active = localRefs[localRefSlot];
293 return MakeLocalRef(res);
294 }
295
296 internal void DeleteLocalRef(jobject obj)
297 {
298 int i = obj.ToInt32();
299 if (i > 0)
300 {
301 localRefs[i >> LOCAL_REF_SHIFT][i & LOCAL_REF_MASK] = null;
302 return;
303 }
304
305 if (i < 0)
306 {
307 Debug.Assert(false, "bogus localref in DeleteLocalRef");
308 }
309 }
310
311 }
312
313#endif
314
315}
System.SByte jboolean
System.Int32 jint
Definition JavaVM.cs:35
System.IntPtr jobject