|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "ctypes/CTypes.h" |
|
8 |
|
9 #include "mozilla/FloatingPoint.h" |
|
10 #include "mozilla/MemoryReporting.h" |
|
11 #include "mozilla/NumericLimits.h" |
|
12 |
|
13 #include <math.h> |
|
14 #include <stdint.h> |
|
15 |
|
16 #if defined(XP_WIN) |
|
17 #include <float.h> |
|
18 #endif |
|
19 |
|
20 #if defined(SOLARIS) |
|
21 #include <ieeefp.h> |
|
22 #endif |
|
23 |
|
24 #ifdef HAVE_SSIZE_T |
|
25 #include <sys/types.h> |
|
26 #endif |
|
27 |
|
28 #if defined(XP_UNIX) |
|
29 #include <errno.h> |
|
30 #elif defined(XP_WIN) |
|
31 #include <windows.h> |
|
32 #endif |
|
33 |
|
34 #include "jscntxt.h" |
|
35 #include "jsfun.h" |
|
36 #include "jsnum.h" |
|
37 #include "jsprf.h" |
|
38 |
|
39 #include "builtin/TypedObject.h" |
|
40 #include "ctypes/Library.h" |
|
41 |
|
42 using namespace std; |
|
43 using mozilla::NumericLimits; |
|
44 |
|
45 namespace js { |
|
46 namespace ctypes { |
|
47 |
|
48 size_t |
|
49 GetDeflatedUTF8StringLength(JSContext *maybecx, const jschar *chars, |
|
50 size_t nchars) |
|
51 { |
|
52 size_t nbytes; |
|
53 const jschar *end; |
|
54 unsigned c, c2; |
|
55 char buffer[10]; |
|
56 |
|
57 nbytes = nchars; |
|
58 for (end = chars + nchars; chars != end; chars++) { |
|
59 c = *chars; |
|
60 if (c < 0x80) |
|
61 continue; |
|
62 if (0xD800 <= c && c <= 0xDFFF) { |
|
63 /* Surrogate pair. */ |
|
64 chars++; |
|
65 |
|
66 /* nbytes sets 1 length since this is surrogate pair. */ |
|
67 nbytes--; |
|
68 if (c >= 0xDC00 || chars == end) |
|
69 goto bad_surrogate; |
|
70 c2 = *chars; |
|
71 if (c2 < 0xDC00 || c2 > 0xDFFF) |
|
72 goto bad_surrogate; |
|
73 c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; |
|
74 } |
|
75 c >>= 11; |
|
76 nbytes++; |
|
77 while (c) { |
|
78 c >>= 5; |
|
79 nbytes++; |
|
80 } |
|
81 } |
|
82 return nbytes; |
|
83 |
|
84 bad_surrogate: |
|
85 if (maybecx) { |
|
86 JS_snprintf(buffer, 10, "0x%x", c); |
|
87 JS_ReportErrorFlagsAndNumber(maybecx, JSREPORT_ERROR, js_GetErrorMessage, |
|
88 nullptr, JSMSG_BAD_SURROGATE_CHAR, buffer); |
|
89 } |
|
90 return (size_t) -1; |
|
91 } |
|
92 |
|
93 bool |
|
94 DeflateStringToUTF8Buffer(JSContext *maybecx, const jschar *src, size_t srclen, |
|
95 char *dst, size_t *dstlenp) |
|
96 { |
|
97 size_t i, utf8Len; |
|
98 jschar c, c2; |
|
99 uint32_t v; |
|
100 uint8_t utf8buf[6]; |
|
101 |
|
102 size_t dstlen = *dstlenp; |
|
103 size_t origDstlen = dstlen; |
|
104 |
|
105 while (srclen) { |
|
106 c = *src++; |
|
107 srclen--; |
|
108 if (c >= 0xDC00 && c <= 0xDFFF) |
|
109 goto badSurrogate; |
|
110 if (c < 0xD800 || c > 0xDBFF) { |
|
111 v = c; |
|
112 } else { |
|
113 if (srclen < 1) |
|
114 goto badSurrogate; |
|
115 c2 = *src; |
|
116 if ((c2 < 0xDC00) || (c2 > 0xDFFF)) |
|
117 goto badSurrogate; |
|
118 src++; |
|
119 srclen--; |
|
120 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; |
|
121 } |
|
122 if (v < 0x0080) { |
|
123 /* no encoding necessary - performance hack */ |
|
124 if (dstlen == 0) |
|
125 goto bufferTooSmall; |
|
126 *dst++ = (char) v; |
|
127 utf8Len = 1; |
|
128 } else { |
|
129 utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); |
|
130 if (utf8Len > dstlen) |
|
131 goto bufferTooSmall; |
|
132 for (i = 0; i < utf8Len; i++) |
|
133 *dst++ = (char) utf8buf[i]; |
|
134 } |
|
135 dstlen -= utf8Len; |
|
136 } |
|
137 *dstlenp = (origDstlen - dstlen); |
|
138 return true; |
|
139 |
|
140 badSurrogate: |
|
141 *dstlenp = (origDstlen - dstlen); |
|
142 /* Delegate error reporting to the measurement function. */ |
|
143 if (maybecx) |
|
144 GetDeflatedUTF8StringLength(maybecx, src - 1, srclen + 1); |
|
145 return false; |
|
146 |
|
147 bufferTooSmall: |
|
148 *dstlenp = (origDstlen - dstlen); |
|
149 if (maybecx) { |
|
150 JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr, |
|
151 JSMSG_BUFFER_TOO_SMALL); |
|
152 } |
|
153 return false; |
|
154 } |
|
155 |
|
156 /******************************************************************************* |
|
157 ** JSAPI function prototypes |
|
158 *******************************************************************************/ |
|
159 |
|
160 // We use an enclosing struct here out of paranoia about the ability of gcc 4.4 |
|
161 // (and maybe 4.5) to correctly compile this if it were a template function. |
|
162 // See also the comments in dom/workers/Events.cpp (and other adjacent files) by |
|
163 // the |struct Property| there. |
|
164 template<JS::IsAcceptableThis Test, JS::NativeImpl Impl> |
|
165 struct Property |
|
166 { |
|
167 static bool |
|
168 Fun(JSContext* cx, unsigned argc, JS::Value* vp) |
|
169 { |
|
170 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
|
171 return JS::CallNonGenericMethod<Test, Impl>(cx, args); |
|
172 } |
|
173 }; |
|
174 |
|
175 static bool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp); |
|
176 |
|
177 namespace CType { |
|
178 static bool ConstructData(JSContext* cx, unsigned argc, jsval* vp); |
|
179 static bool ConstructBasic(JSContext* cx, HandleObject obj, const CallArgs& args); |
|
180 |
|
181 static void Trace(JSTracer* trc, JSObject* obj); |
|
182 static void Finalize(JSFreeOp *fop, JSObject* obj); |
|
183 |
|
184 bool IsCType(HandleValue v); |
|
185 bool IsCTypeOrProto(HandleValue v); |
|
186 |
|
187 bool PrototypeGetter(JSContext* cx, JS::CallArgs args); |
|
188 bool NameGetter(JSContext* cx, JS::CallArgs args); |
|
189 bool SizeGetter(JSContext* cx, JS::CallArgs args); |
|
190 bool PtrGetter(JSContext* cx, JS::CallArgs args); |
|
191 |
|
192 static bool CreateArray(JSContext* cx, unsigned argc, jsval* vp); |
|
193 static bool ToString(JSContext* cx, unsigned argc, jsval* vp); |
|
194 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
|
195 static bool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp); |
|
196 |
|
197 |
|
198 /* |
|
199 * Get the global "ctypes" object. |
|
200 * |
|
201 * |obj| must be a CType object. |
|
202 * |
|
203 * This function never returns nullptr. |
|
204 */ |
|
205 static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj); |
|
206 |
|
207 } |
|
208 |
|
209 namespace ABI { |
|
210 bool IsABI(JSObject* obj); |
|
211 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
|
212 } |
|
213 |
|
214 namespace PointerType { |
|
215 static bool Create(JSContext* cx, unsigned argc, jsval* vp); |
|
216 static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); |
|
217 |
|
218 bool IsPointerType(HandleValue v); |
|
219 bool IsPointer(HandleValue v); |
|
220 |
|
221 bool TargetTypeGetter(JSContext* cx, JS::CallArgs args); |
|
222 bool ContentsGetter(JSContext* cx, JS::CallArgs args); |
|
223 bool ContentsSetter(JSContext* cx, JS::CallArgs args); |
|
224 |
|
225 static bool IsNull(JSContext* cx, unsigned argc, jsval* vp); |
|
226 static bool Increment(JSContext* cx, unsigned argc, jsval* vp); |
|
227 static bool Decrement(JSContext* cx, unsigned argc, jsval* vp); |
|
228 // The following is not an instance function, since we don't want to expose arbitrary |
|
229 // pointer arithmetic at this moment. |
|
230 static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset); |
|
231 } |
|
232 |
|
233 namespace ArrayType { |
|
234 bool IsArrayType(HandleValue v); |
|
235 bool IsArrayOrArrayType(HandleValue v); |
|
236 |
|
237 static bool Create(JSContext* cx, unsigned argc, jsval* vp); |
|
238 static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); |
|
239 |
|
240 bool ElementTypeGetter(JSContext* cx, JS::CallArgs args); |
|
241 bool LengthGetter(JSContext* cx, JS::CallArgs args); |
|
242 |
|
243 static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp); |
|
244 static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp); |
|
245 static bool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp); |
|
246 } |
|
247 |
|
248 namespace StructType { |
|
249 bool IsStruct(HandleValue v); |
|
250 |
|
251 static bool Create(JSContext* cx, unsigned argc, jsval* vp); |
|
252 static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); |
|
253 |
|
254 bool FieldsArrayGetter(JSContext* cx, JS::CallArgs args); |
|
255 |
|
256 static bool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, |
|
257 MutableHandleValue vp); |
|
258 static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, |
|
259 MutableHandleValue vp); |
|
260 static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp); |
|
261 static bool Define(JSContext* cx, unsigned argc, jsval* vp); |
|
262 } |
|
263 |
|
264 namespace FunctionType { |
|
265 static bool Create(JSContext* cx, unsigned argc, jsval* vp); |
|
266 static bool ConstructData(JSContext* cx, HandleObject typeObj, |
|
267 HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, jsval errVal); |
|
268 |
|
269 static bool Call(JSContext* cx, unsigned argc, jsval* vp); |
|
270 |
|
271 bool IsFunctionType(HandleValue v); |
|
272 |
|
273 bool ArgTypesGetter(JSContext* cx, JS::CallArgs args); |
|
274 bool ReturnTypeGetter(JSContext* cx, JS::CallArgs args); |
|
275 bool ABIGetter(JSContext* cx, JS::CallArgs args); |
|
276 bool IsVariadicGetter(JSContext* cx, JS::CallArgs args); |
|
277 } |
|
278 |
|
279 namespace CClosure { |
|
280 static void Trace(JSTracer* trc, JSObject* obj); |
|
281 static void Finalize(JSFreeOp *fop, JSObject* obj); |
|
282 |
|
283 // libffi callback |
|
284 static void ClosureStub(ffi_cif* cif, void* result, void** args, |
|
285 void* userData); |
|
286 } |
|
287 |
|
288 namespace CData { |
|
289 static void Finalize(JSFreeOp *fop, JSObject* obj); |
|
290 |
|
291 bool ValueGetter(JSContext* cx, JS::CallArgs args); |
|
292 bool ValueSetter(JSContext* cx, JS::CallArgs args); |
|
293 |
|
294 static bool Address(JSContext* cx, unsigned argc, jsval* vp); |
|
295 static bool ReadString(JSContext* cx, unsigned argc, jsval* vp); |
|
296 static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp); |
|
297 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
|
298 static JSString *GetSourceString(JSContext *cx, HandleObject typeObj, |
|
299 void *data); |
|
300 |
|
301 bool ErrnoGetter(JSContext* cx, JS::CallArgs args); |
|
302 |
|
303 #if defined(XP_WIN) |
|
304 bool LastErrorGetter(JSContext* cx, JS::CallArgs args); |
|
305 #endif // defined(XP_WIN) |
|
306 } |
|
307 |
|
308 namespace CDataFinalizer { |
|
309 /* |
|
310 * Attach a C function as a finalizer to a JS object. |
|
311 * |
|
312 * This function is available from JS as |ctypes.withFinalizer|. |
|
313 * |
|
314 * JavaScript signature: |
|
315 * function(CData, CData): CDataFinalizer |
|
316 * value finalizer finalizable |
|
317 * |
|
318 * Where |finalizer| is a one-argument function taking a value |
|
319 * with the same type as |value|. |
|
320 */ |
|
321 static bool Construct(JSContext* cx, unsigned argc, jsval *vp); |
|
322 |
|
323 /* |
|
324 * Private data held by |CDataFinalizer|. |
|
325 * |
|
326 * See also |enum CDataFinalizerSlot| for the slots of |
|
327 * |CDataFinalizer|. |
|
328 * |
|
329 * Note: the private data may be nullptr, if |dispose|, |forget| or the |
|
330 * finalizer has already been called. |
|
331 */ |
|
332 struct Private { |
|
333 /* |
|
334 * The C data to pass to the code. |
|
335 * Finalization/|dispose|/|forget| release this memory. |
|
336 */ |
|
337 void *cargs; |
|
338 |
|
339 /* |
|
340 * The total size of the buffer pointed by |cargs| |
|
341 */ |
|
342 size_t cargs_size; |
|
343 |
|
344 /* |
|
345 * Low-level signature information. |
|
346 * Finalization/|dispose|/|forget| release this memory. |
|
347 */ |
|
348 ffi_cif CIF; |
|
349 |
|
350 /* |
|
351 * The C function to invoke during finalization. |
|
352 * Do not deallocate this. |
|
353 */ |
|
354 uintptr_t code; |
|
355 |
|
356 /* |
|
357 * A buffer for holding the return value. |
|
358 * Finalization/|dispose|/|forget| release this memory. |
|
359 */ |
|
360 void *rvalue; |
|
361 }; |
|
362 |
|
363 /* |
|
364 * Methods of instances of |CDataFinalizer| |
|
365 */ |
|
366 namespace Methods { |
|
367 static bool Dispose(JSContext* cx, unsigned argc, jsval *vp); |
|
368 static bool Forget(JSContext* cx, unsigned argc, jsval *vp); |
|
369 static bool ToSource(JSContext* cx, unsigned argc, jsval *vp); |
|
370 static bool ToString(JSContext* cx, unsigned argc, jsval *vp); |
|
371 } |
|
372 |
|
373 /* |
|
374 * Utility functions |
|
375 * |
|
376 * @return true if |obj| is a CDataFinalizer, false otherwise. |
|
377 */ |
|
378 static bool IsCDataFinalizer(JSObject *obj); |
|
379 |
|
380 /* |
|
381 * Clean up the finalization information of a CDataFinalizer. |
|
382 * |
|
383 * Used by |Finalize|, |Dispose| and |Forget|. |
|
384 * |
|
385 * @param p The private information of the CDataFinalizer. If nullptr, |
|
386 * this function does nothing. |
|
387 * @param obj Either nullptr, if the object should not be cleaned up (i.e. |
|
388 * during finalization) or a CDataFinalizer JSObject. Always use nullptr |
|
389 * if you are calling from a finalizer. |
|
390 */ |
|
391 static void Cleanup(Private *p, JSObject *obj); |
|
392 |
|
393 /* |
|
394 * Perform the actual call to the finalizer code. |
|
395 */ |
|
396 static void CallFinalizer(CDataFinalizer::Private *p, |
|
397 int* errnoStatus, |
|
398 int32_t* lastErrorStatus); |
|
399 |
|
400 /* |
|
401 * Return the CType of a CDataFinalizer object, or nullptr if the object |
|
402 * has been cleaned-up already. |
|
403 */ |
|
404 static JSObject *GetCType(JSContext *cx, JSObject *obj); |
|
405 |
|
406 /* |
|
407 * Perform finalization of a |CDataFinalizer| |
|
408 */ |
|
409 static void Finalize(JSFreeOp *fop, JSObject *obj); |
|
410 |
|
411 /* |
|
412 * Return the jsval contained by this finalizer. |
|
413 * |
|
414 * Note that the jsval is actually not recorded, but converted back from C. |
|
415 */ |
|
416 static bool GetValue(JSContext *cx, JSObject *obj, jsval *result); |
|
417 |
|
418 static JSObject* GetCData(JSContext *cx, JSObject *obj); |
|
419 } |
|
420 |
|
421 |
|
422 // Int64Base provides functions common to Int64 and UInt64. |
|
423 namespace Int64Base { |
|
424 JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data, |
|
425 bool isUnsigned); |
|
426 |
|
427 uint64_t GetInt(JSObject* obj); |
|
428 |
|
429 bool ToString(JSContext* cx, JSObject* obj, const CallArgs& args, |
|
430 bool isUnsigned); |
|
431 |
|
432 bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args, |
|
433 bool isUnsigned); |
|
434 |
|
435 static void Finalize(JSFreeOp *fop, JSObject* obj); |
|
436 } |
|
437 |
|
438 namespace Int64 { |
|
439 static bool Construct(JSContext* cx, unsigned argc, jsval* vp); |
|
440 |
|
441 static bool ToString(JSContext* cx, unsigned argc, jsval* vp); |
|
442 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
|
443 |
|
444 static bool Compare(JSContext* cx, unsigned argc, jsval* vp); |
|
445 static bool Lo(JSContext* cx, unsigned argc, jsval* vp); |
|
446 static bool Hi(JSContext* cx, unsigned argc, jsval* vp); |
|
447 static bool Join(JSContext* cx, unsigned argc, jsval* vp); |
|
448 } |
|
449 |
|
450 namespace UInt64 { |
|
451 static bool Construct(JSContext* cx, unsigned argc, jsval* vp); |
|
452 |
|
453 static bool ToString(JSContext* cx, unsigned argc, jsval* vp); |
|
454 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
|
455 |
|
456 static bool Compare(JSContext* cx, unsigned argc, jsval* vp); |
|
457 static bool Lo(JSContext* cx, unsigned argc, jsval* vp); |
|
458 static bool Hi(JSContext* cx, unsigned argc, jsval* vp); |
|
459 static bool Join(JSContext* cx, unsigned argc, jsval* vp); |
|
460 } |
|
461 |
|
462 /******************************************************************************* |
|
463 ** JSClass definitions and initialization functions |
|
464 *******************************************************************************/ |
|
465 |
|
466 // Class representing the 'ctypes' object itself. This exists to contain the |
|
467 // JSCTypesCallbacks set of function pointers. |
|
468 static const JSClass sCTypesGlobalClass = { |
|
469 "ctypes", |
|
470 JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS), |
|
471 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
472 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
|
473 }; |
|
474 |
|
475 static const JSClass sCABIClass = { |
|
476 "CABI", |
|
477 JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS), |
|
478 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
479 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
|
480 }; |
|
481 |
|
482 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype. |
|
483 // This exists to give said prototypes a class of "CType", and to provide |
|
484 // reserved slots for stashing various other prototype objects. |
|
485 static const JSClass sCTypeProtoClass = { |
|
486 "CType", |
|
487 JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS), |
|
488 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
489 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, |
|
490 ConstructAbstract, nullptr, ConstructAbstract |
|
491 }; |
|
492 |
|
493 // Class representing ctypes.CData.prototype and the 'prototype' properties |
|
494 // of CTypes. This exists to give said prototypes a class of "CData". |
|
495 static const JSClass sCDataProtoClass = { |
|
496 "CData", |
|
497 0, |
|
498 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
499 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
|
500 }; |
|
501 |
|
502 static const JSClass sCTypeClass = { |
|
503 "CType", |
|
504 JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS), |
|
505 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
506 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize, |
|
507 CType::ConstructData, CType::HasInstance, CType::ConstructData, |
|
508 CType::Trace |
|
509 }; |
|
510 |
|
511 static const JSClass sCDataClass = { |
|
512 "CData", |
|
513 JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS), |
|
514 JS_PropertyStub, JS_DeletePropertyStub, ArrayType::Getter, ArrayType::Setter, |
|
515 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CData::Finalize, |
|
516 FunctionType::Call, nullptr, FunctionType::Call |
|
517 }; |
|
518 |
|
519 static const JSClass sCClosureClass = { |
|
520 "CClosure", |
|
521 JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS), |
|
522 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
523 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize, |
|
524 nullptr, nullptr, nullptr, CClosure::Trace |
|
525 }; |
|
526 |
|
527 /* |
|
528 * Class representing the prototype of CDataFinalizer. |
|
529 */ |
|
530 static const JSClass sCDataFinalizerProtoClass = { |
|
531 "CDataFinalizer", |
|
532 0, |
|
533 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
534 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
|
535 }; |
|
536 |
|
537 /* |
|
538 * Class representing instances of CDataFinalizer. |
|
539 * |
|
540 * Instances of CDataFinalizer have both private data (with type |
|
541 * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|). |
|
542 */ |
|
543 static const JSClass sCDataFinalizerClass = { |
|
544 "CDataFinalizer", |
|
545 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS), |
|
546 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
547 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CDataFinalizer::Finalize, |
|
548 }; |
|
549 |
|
550 |
|
551 #define CTYPESFN_FLAGS \ |
|
552 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) |
|
553 |
|
554 #define CTYPESCTOR_FLAGS \ |
|
555 (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR) |
|
556 |
|
557 #define CTYPESACC_FLAGS \ |
|
558 (JSPROP_ENUMERATE | JSPROP_PERMANENT) |
|
559 |
|
560 #define CABIFN_FLAGS \ |
|
561 (JSPROP_READONLY | JSPROP_PERMANENT) |
|
562 |
|
563 #define CDATAFN_FLAGS \ |
|
564 (JSPROP_READONLY | JSPROP_PERMANENT) |
|
565 |
|
566 #define CDATAFINALIZERFN_FLAGS \ |
|
567 (JSPROP_READONLY | JSPROP_PERMANENT) |
|
568 |
|
569 static const JSPropertySpec sCTypeProps[] = { |
|
570 JS_PSG("name", |
|
571 (Property<CType::IsCType, CType::NameGetter>::Fun), |
|
572 CTYPESACC_FLAGS), |
|
573 JS_PSG("size", |
|
574 (Property<CType::IsCType, CType::SizeGetter>::Fun), |
|
575 CTYPESACC_FLAGS), |
|
576 JS_PSG("ptr", |
|
577 (Property<CType::IsCType, CType::PtrGetter>::Fun), |
|
578 CTYPESACC_FLAGS), |
|
579 JS_PSG("prototype", |
|
580 (Property<CType::IsCTypeOrProto, CType::PrototypeGetter>::Fun), |
|
581 CTYPESACC_FLAGS), |
|
582 JS_PS_END |
|
583 }; |
|
584 |
|
585 static const JSFunctionSpec sCTypeFunctions[] = { |
|
586 JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS), |
|
587 JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS), |
|
588 JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS), |
|
589 JS_FS_END |
|
590 }; |
|
591 |
|
592 static const JSFunctionSpec sCABIFunctions[] = { |
|
593 JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS), |
|
594 JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS), |
|
595 JS_FS_END |
|
596 }; |
|
597 |
|
598 static const JSPropertySpec sCDataProps[] = { |
|
599 JS_PSGS("value", |
|
600 (Property<CData::IsCData, CData::ValueGetter>::Fun), |
|
601 (Property<CData::IsCData, CData::ValueSetter>::Fun), |
|
602 JSPROP_PERMANENT), |
|
603 JS_PS_END |
|
604 }; |
|
605 |
|
606 static const JSFunctionSpec sCDataFunctions[] = { |
|
607 JS_FN("address", CData::Address, 0, CDATAFN_FLAGS), |
|
608 JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS), |
|
609 JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0, CDATAFN_FLAGS), |
|
610 JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS), |
|
611 JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS), |
|
612 JS_FS_END |
|
613 }; |
|
614 |
|
615 static const JSFunctionSpec sCDataFinalizerFunctions[] = { |
|
616 JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0, CDATAFINALIZERFN_FLAGS), |
|
617 JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS), |
|
618 JS_FN("readString",CData::ReadString, 0, CDATAFINALIZERFN_FLAGS), |
|
619 JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS), |
|
620 JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS), |
|
621 JS_FS_END |
|
622 }; |
|
623 |
|
624 static const JSFunctionSpec sPointerFunction = |
|
625 JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS); |
|
626 |
|
627 static const JSPropertySpec sPointerProps[] = { |
|
628 JS_PSG("targetType", |
|
629 (Property<PointerType::IsPointerType, PointerType::TargetTypeGetter>::Fun), |
|
630 CTYPESACC_FLAGS), |
|
631 JS_PS_END |
|
632 }; |
|
633 |
|
634 static const JSFunctionSpec sPointerInstanceFunctions[] = { |
|
635 JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS), |
|
636 JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS), |
|
637 JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS), |
|
638 JS_FS_END |
|
639 }; |
|
640 |
|
641 static const JSPropertySpec sPointerInstanceProps[] = { |
|
642 JS_PSGS("contents", |
|
643 (Property<PointerType::IsPointer, PointerType::ContentsGetter>::Fun), |
|
644 (Property<PointerType::IsPointer, PointerType::ContentsSetter>::Fun), |
|
645 JSPROP_PERMANENT), |
|
646 JS_PS_END |
|
647 }; |
|
648 |
|
649 static const JSFunctionSpec sArrayFunction = |
|
650 JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS); |
|
651 |
|
652 static const JSPropertySpec sArrayProps[] = { |
|
653 JS_PSG("elementType", |
|
654 (Property<ArrayType::IsArrayType, ArrayType::ElementTypeGetter>::Fun), |
|
655 CTYPESACC_FLAGS), |
|
656 JS_PSG("length", |
|
657 (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun), |
|
658 CTYPESACC_FLAGS), |
|
659 JS_PS_END |
|
660 }; |
|
661 |
|
662 static const JSFunctionSpec sArrayInstanceFunctions[] = { |
|
663 JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS), |
|
664 JS_FS_END |
|
665 }; |
|
666 |
|
667 static const JSPropertySpec sArrayInstanceProps[] = { |
|
668 JS_PSG("length", |
|
669 (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun), |
|
670 JSPROP_PERMANENT), |
|
671 JS_PS_END |
|
672 }; |
|
673 |
|
674 static const JSFunctionSpec sStructFunction = |
|
675 JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS); |
|
676 |
|
677 static const JSPropertySpec sStructProps[] = { |
|
678 JS_PSG("fields", |
|
679 (Property<StructType::IsStruct, StructType::FieldsArrayGetter>::Fun), |
|
680 CTYPESACC_FLAGS), |
|
681 JS_PS_END |
|
682 }; |
|
683 |
|
684 static const JSFunctionSpec sStructFunctions[] = { |
|
685 JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS), |
|
686 JS_FS_END |
|
687 }; |
|
688 |
|
689 static const JSFunctionSpec sStructInstanceFunctions[] = { |
|
690 JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS), |
|
691 JS_FS_END |
|
692 }; |
|
693 |
|
694 static const JSFunctionSpec sFunctionFunction = |
|
695 JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS); |
|
696 |
|
697 static const JSPropertySpec sFunctionProps[] = { |
|
698 JS_PSG("argTypes", |
|
699 (Property<FunctionType::IsFunctionType, FunctionType::ArgTypesGetter>::Fun), |
|
700 CTYPESACC_FLAGS), |
|
701 JS_PSG("returnType", |
|
702 (Property<FunctionType::IsFunctionType, FunctionType::ReturnTypeGetter>::Fun), |
|
703 CTYPESACC_FLAGS), |
|
704 JS_PSG("abi", |
|
705 (Property<FunctionType::IsFunctionType, FunctionType::ABIGetter>::Fun), |
|
706 CTYPESACC_FLAGS), |
|
707 JS_PSG("isVariadic", |
|
708 (Property<FunctionType::IsFunctionType, FunctionType::IsVariadicGetter>::Fun), |
|
709 CTYPESACC_FLAGS), |
|
710 JS_PS_END |
|
711 }; |
|
712 |
|
713 static const JSFunctionSpec sFunctionInstanceFunctions[] = { |
|
714 JS_FN("call", js_fun_call, 1, CDATAFN_FLAGS), |
|
715 JS_FN("apply", js_fun_apply, 2, CDATAFN_FLAGS), |
|
716 JS_FS_END |
|
717 }; |
|
718 |
|
719 static const JSClass sInt64ProtoClass = { |
|
720 "Int64", |
|
721 0, |
|
722 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
723 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
|
724 }; |
|
725 |
|
726 static const JSClass sUInt64ProtoClass = { |
|
727 "UInt64", |
|
728 0, |
|
729 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
730 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
|
731 }; |
|
732 |
|
733 static const JSClass sInt64Class = { |
|
734 "Int64", |
|
735 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), |
|
736 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
737 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize |
|
738 }; |
|
739 |
|
740 static const JSClass sUInt64Class = { |
|
741 "UInt64", |
|
742 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), |
|
743 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
744 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize |
|
745 }; |
|
746 |
|
747 static const JSFunctionSpec sInt64StaticFunctions[] = { |
|
748 JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS), |
|
749 JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS), |
|
750 JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS), |
|
751 JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS), |
|
752 JS_FS_END |
|
753 }; |
|
754 |
|
755 static const JSFunctionSpec sUInt64StaticFunctions[] = { |
|
756 JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS), |
|
757 JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS), |
|
758 JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS), |
|
759 JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS), |
|
760 JS_FS_END |
|
761 }; |
|
762 |
|
763 static const JSFunctionSpec sInt64Functions[] = { |
|
764 JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS), |
|
765 JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS), |
|
766 JS_FS_END |
|
767 }; |
|
768 |
|
769 static const JSFunctionSpec sUInt64Functions[] = { |
|
770 JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS), |
|
771 JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS), |
|
772 JS_FS_END |
|
773 }; |
|
774 |
|
775 static const JSPropertySpec sModuleProps[] = { |
|
776 JS_PSG("errno", |
|
777 (Property<IsCTypesGlobal, CData::ErrnoGetter>::Fun), |
|
778 JSPROP_PERMANENT), |
|
779 #if defined(XP_WIN) |
|
780 JS_PSG("winLastError", |
|
781 (Property<IsCTypesGlobal, CData::LastErrorGetter>::Fun), |
|
782 JSPROP_PERMANENT), |
|
783 #endif // defined(XP_WIN) |
|
784 JS_PS_END |
|
785 }; |
|
786 |
|
787 static const JSFunctionSpec sModuleFunctions[] = { |
|
788 JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS), |
|
789 JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS), |
|
790 JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS), |
|
791 JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS), |
|
792 JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS), |
|
793 JS_FS_END |
|
794 }; |
|
795 |
|
796 static MOZ_ALWAYS_INLINE JSString* |
|
797 NewUCString(JSContext* cx, const AutoString& from) |
|
798 { |
|
799 return JS_NewUCStringCopyN(cx, from.begin(), from.length()); |
|
800 } |
|
801 |
|
802 /* |
|
803 * Return a size rounded up to a multiple of a power of two. |
|
804 * |
|
805 * Note: |align| must be a power of 2. |
|
806 */ |
|
807 static MOZ_ALWAYS_INLINE size_t |
|
808 Align(size_t val, size_t align) |
|
809 { |
|
810 // Ensure that align is a power of two. |
|
811 MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0); |
|
812 return ((val - 1) | (align - 1)) + 1; |
|
813 } |
|
814 |
|
815 static ABICode |
|
816 GetABICode(JSObject* obj) |
|
817 { |
|
818 // make sure we have an object representing a CABI class, |
|
819 // and extract the enumerated class type from the reserved slot. |
|
820 if (JS_GetClass(obj) != &sCABIClass) |
|
821 return INVALID_ABI; |
|
822 |
|
823 jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE); |
|
824 return ABICode(JSVAL_TO_INT(result)); |
|
825 } |
|
826 |
|
827 static const JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = { |
|
828 #define MSG_DEF(name, number, count, exception, format) \ |
|
829 { format, count, exception } , |
|
830 #include "ctypes/ctypes.msg" |
|
831 #undef MSG_DEF |
|
832 }; |
|
833 |
|
834 static const JSErrorFormatString* |
|
835 GetErrorMessage(void* userRef, const char* locale, const unsigned errorNumber) |
|
836 { |
|
837 if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT) |
|
838 return &ErrorFormatString[errorNumber]; |
|
839 return nullptr; |
|
840 } |
|
841 |
|
842 static bool |
|
843 TypeError(JSContext* cx, const char* expected, HandleValue actual) |
|
844 { |
|
845 JSString* str = JS_ValueToSource(cx, actual); |
|
846 JSAutoByteString bytes; |
|
847 |
|
848 const char* src; |
|
849 if (str) { |
|
850 src = bytes.encodeLatin1(cx, str); |
|
851 if (!src) |
|
852 return false; |
|
853 } else { |
|
854 JS_ClearPendingException(cx); |
|
855 src = "<<error converting value to string>>"; |
|
856 } |
|
857 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, |
|
858 CTYPESMSG_TYPE_ERROR, expected, src); |
|
859 return false; |
|
860 } |
|
861 |
|
862 static JSObject* |
|
863 InitCTypeClass(JSContext* cx, HandleObject parent) |
|
864 { |
|
865 JSFunction *fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0, |
|
866 CTYPESCTOR_FLAGS); |
|
867 if (!fun) |
|
868 return nullptr; |
|
869 |
|
870 RootedObject ctor(cx, JS_GetFunctionObject(fun)); |
|
871 RootedObject fnproto(cx); |
|
872 if (!JS_GetPrototype(cx, ctor, &fnproto)) |
|
873 return nullptr; |
|
874 JS_ASSERT(ctor); |
|
875 JS_ASSERT(fnproto); |
|
876 |
|
877 // Set up ctypes.CType.prototype. |
|
878 RootedObject prototype(cx, JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent)); |
|
879 if (!prototype) |
|
880 return nullptr; |
|
881 |
|
882 if (!JS_DefineProperty(cx, ctor, "prototype", prototype, |
|
883 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
884 return nullptr; |
|
885 |
|
886 if (!JS_DefineProperty(cx, prototype, "constructor", ctor, |
|
887 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
888 return nullptr; |
|
889 |
|
890 // Define properties and functions common to all CTypes. |
|
891 if (!JS_DefineProperties(cx, prototype, sCTypeProps) || |
|
892 !JS_DefineFunctions(cx, prototype, sCTypeFunctions)) |
|
893 return nullptr; |
|
894 |
|
895 if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype)) |
|
896 return nullptr; |
|
897 |
|
898 return prototype; |
|
899 } |
|
900 |
|
901 static JSObject* |
|
902 InitABIClass(JSContext* cx, JSObject* parent) |
|
903 { |
|
904 RootedObject obj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr())); |
|
905 |
|
906 if (!obj) |
|
907 return nullptr; |
|
908 |
|
909 if (!JS_DefineFunctions(cx, obj, sCABIFunctions)) |
|
910 return nullptr; |
|
911 |
|
912 return obj; |
|
913 } |
|
914 |
|
915 |
|
916 static JSObject* |
|
917 InitCDataClass(JSContext* cx, HandleObject parent, HandleObject CTypeProto) |
|
918 { |
|
919 JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0, |
|
920 CTYPESCTOR_FLAGS); |
|
921 if (!fun) |
|
922 return nullptr; |
|
923 |
|
924 RootedObject ctor(cx, JS_GetFunctionObject(fun)); |
|
925 JS_ASSERT(ctor); |
|
926 |
|
927 // Set up ctypes.CData.__proto__ === ctypes.CType.prototype. |
|
928 // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the |
|
929 // prototype chain.) |
|
930 if (!JS_SetPrototype(cx, ctor, CTypeProto)) |
|
931 return nullptr; |
|
932 |
|
933 // Set up ctypes.CData.prototype. |
|
934 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, NullPtr(), parent)); |
|
935 if (!prototype) |
|
936 return nullptr; |
|
937 |
|
938 if (!JS_DefineProperty(cx, ctor, "prototype", prototype, |
|
939 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
940 return nullptr; |
|
941 |
|
942 if (!JS_DefineProperty(cx, prototype, "constructor", ctor, |
|
943 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
944 return nullptr; |
|
945 |
|
946 // Define properties and functions common to all CDatas. |
|
947 if (!JS_DefineProperties(cx, prototype, sCDataProps) || |
|
948 !JS_DefineFunctions(cx, prototype, sCDataFunctions)) |
|
949 return nullptr; |
|
950 |
|
951 if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212! |
|
952 !JS_FreezeObject(cx, ctor)) |
|
953 return nullptr; |
|
954 |
|
955 return prototype; |
|
956 } |
|
957 |
|
958 static bool |
|
959 DefineABIConstant(JSContext* cx, |
|
960 HandleObject parent, |
|
961 const char* name, |
|
962 ABICode code, |
|
963 HandleObject prototype) |
|
964 { |
|
965 RootedObject obj(cx, JS_DefineObject(cx, parent, name, &sCABIClass, prototype, |
|
966 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)); |
|
967 if (!obj) |
|
968 return false; |
|
969 JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code)); |
|
970 return JS_FreezeObject(cx, obj); |
|
971 } |
|
972 |
|
973 // Set up a single type constructor for |
|
974 // ctypes.{Pointer,Array,Struct,Function}Type. |
|
975 static bool |
|
976 InitTypeConstructor(JSContext* cx, |
|
977 HandleObject parent, |
|
978 HandleObject CTypeProto, |
|
979 HandleObject CDataProto, |
|
980 const JSFunctionSpec spec, |
|
981 const JSFunctionSpec* fns, |
|
982 const JSPropertySpec* props, |
|
983 const JSFunctionSpec* instanceFns, |
|
984 const JSPropertySpec* instanceProps, |
|
985 MutableHandleObject typeProto, |
|
986 MutableHandleObject dataProto) |
|
987 { |
|
988 JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call.op, |
|
989 spec.nargs, spec.flags); |
|
990 if (!fun) |
|
991 return false; |
|
992 |
|
993 RootedObject obj(cx, JS_GetFunctionObject(fun)); |
|
994 if (!obj) |
|
995 return false; |
|
996 |
|
997 // Set up the .prototype and .prototype.constructor properties. |
|
998 typeProto.set(JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent)); |
|
999 if (!typeProto) |
|
1000 return false; |
|
1001 |
|
1002 // Define property before proceeding, for GC safety. |
|
1003 if (!JS_DefineProperty(cx, obj, "prototype", typeProto, |
|
1004 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
1005 return false; |
|
1006 |
|
1007 if (fns && !JS_DefineFunctions(cx, typeProto, fns)) |
|
1008 return false; |
|
1009 |
|
1010 if (!JS_DefineProperties(cx, typeProto, props)) |
|
1011 return false; |
|
1012 |
|
1013 if (!JS_DefineProperty(cx, typeProto, "constructor", obj, |
|
1014 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
1015 return false; |
|
1016 |
|
1017 // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of |
|
1018 // the type constructor, for faster lookup. |
|
1019 js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto)); |
|
1020 |
|
1021 // Create an object to serve as the common ancestor for all CData objects |
|
1022 // created from the given type constructor. This has ctypes.CData.prototype |
|
1023 // as its prototype, such that it inherits the properties and functions |
|
1024 // common to all CDatas. |
|
1025 dataProto.set(JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent)); |
|
1026 if (!dataProto) |
|
1027 return false; |
|
1028 |
|
1029 // Define functions and properties on the 'dataProto' object that are common |
|
1030 // to all CData objects created from this type constructor. (These will |
|
1031 // become functions and properties on CData objects created from this type.) |
|
1032 if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns)) |
|
1033 return false; |
|
1034 |
|
1035 if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps)) |
|
1036 return false; |
|
1037 |
|
1038 // Link the type prototype to the data prototype. |
|
1039 JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto)); |
|
1040 |
|
1041 if (!JS_FreezeObject(cx, obj) || |
|
1042 //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212! |
|
1043 !JS_FreezeObject(cx, typeProto)) |
|
1044 return false; |
|
1045 |
|
1046 return true; |
|
1047 } |
|
1048 |
|
1049 static JSObject* |
|
1050 InitInt64Class(JSContext* cx, |
|
1051 HandleObject parent, |
|
1052 const JSClass* clasp, |
|
1053 JSNative construct, |
|
1054 const JSFunctionSpec* fs, |
|
1055 const JSFunctionSpec* static_fs) |
|
1056 { |
|
1057 // Init type class and constructor |
|
1058 RootedObject prototype(cx, JS_InitClass(cx, parent, js::NullPtr(), clasp, construct, |
|
1059 0, nullptr, fs, nullptr, static_fs)); |
|
1060 if (!prototype) |
|
1061 return nullptr; |
|
1062 |
|
1063 RootedObject ctor(cx, JS_GetConstructor(cx, prototype)); |
|
1064 if (!ctor) |
|
1065 return nullptr; |
|
1066 if (!JS_FreezeObject(cx, ctor)) |
|
1067 return nullptr; |
|
1068 |
|
1069 // Redefine the 'join' function as an extended native and stash |
|
1070 // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function. |
|
1071 JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass); |
|
1072 JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join; |
|
1073 JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native, |
|
1074 2, CTYPESFN_FLAGS); |
|
1075 if (!fun) |
|
1076 return nullptr; |
|
1077 |
|
1078 js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO, |
|
1079 OBJECT_TO_JSVAL(prototype)); |
|
1080 |
|
1081 if (!JS_FreezeObject(cx, prototype)) |
|
1082 return nullptr; |
|
1083 |
|
1084 return prototype; |
|
1085 } |
|
1086 |
|
1087 static void |
|
1088 AttachProtos(JSObject* proto, const AutoObjectVector& protos) |
|
1089 { |
|
1090 // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos' |
|
1091 // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot |
|
1092 // of [[Class]] "CTypeProto" that we fill in this automated manner.) |
|
1093 for (uint32_t i = 0; i <= SLOT_CTYPES; ++i) |
|
1094 JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i])); |
|
1095 } |
|
1096 |
|
1097 static bool |
|
1098 InitTypeClasses(JSContext* cx, HandleObject parent) |
|
1099 { |
|
1100 // Initialize the ctypes.CType class. This acts as an abstract base class for |
|
1101 // the various types, and provides the common API functions. It has: |
|
1102 // * [[Class]] "Function" |
|
1103 // * __proto__ === Function.prototype |
|
1104 // * A constructor that throws a TypeError. (You can't construct an |
|
1105 // abstract type!) |
|
1106 // * 'prototype' property: |
|
1107 // * [[Class]] "CTypeProto" |
|
1108 // * __proto__ === Function.prototype |
|
1109 // * A constructor that throws a TypeError. (You can't construct an |
|
1110 // abstract type instance!) |
|
1111 // * 'constructor' property === ctypes.CType |
|
1112 // * Provides properties and functions common to all CTypes. |
|
1113 RootedObject CTypeProto(cx, InitCTypeClass(cx, parent)); |
|
1114 if (!CTypeProto) |
|
1115 return false; |
|
1116 |
|
1117 // Initialize the ctypes.CData class. This acts as an abstract base class for |
|
1118 // instances of the various types, and provides the common API functions. |
|
1119 // It has: |
|
1120 // * [[Class]] "Function" |
|
1121 // * __proto__ === Function.prototype |
|
1122 // * A constructor that throws a TypeError. (You can't construct an |
|
1123 // abstract type instance!) |
|
1124 // * 'prototype' property: |
|
1125 // * [[Class]] "CDataProto" |
|
1126 // * 'constructor' property === ctypes.CData |
|
1127 // * Provides properties and functions common to all CDatas. |
|
1128 RootedObject CDataProto(cx, InitCDataClass(cx, parent, CTypeProto)); |
|
1129 if (!CDataProto) |
|
1130 return false; |
|
1131 |
|
1132 // Link CTypeProto to CDataProto. |
|
1133 JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(CDataProto)); |
|
1134 |
|
1135 // Create and attach the special class constructors: ctypes.PointerType, |
|
1136 // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType. |
|
1137 // Each of these constructors 'c' has, respectively: |
|
1138 // * [[Class]] "Function" |
|
1139 // * __proto__ === Function.prototype |
|
1140 // * A constructor that creates a user-defined type. |
|
1141 // * 'prototype' property: |
|
1142 // * [[Class]] "CTypeProto" |
|
1143 // * __proto__ === ctypes.CType.prototype |
|
1144 // * 'constructor' property === 'c' |
|
1145 // We also construct an object 'p' to serve, given a type object 't' |
|
1146 // constructed from one of these type constructors, as |
|
1147 // 't.prototype.__proto__'. This object has: |
|
1148 // * [[Class]] "CDataProto" |
|
1149 // * __proto__ === ctypes.CData.prototype |
|
1150 // * Properties and functions common to all CDatas. |
|
1151 // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type |
|
1152 // will have, resp.: |
|
1153 // * [[Class]] "CType" |
|
1154 // * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype |
|
1155 // * A constructor which creates and returns a CData object, containing |
|
1156 // binary data of the given type. |
|
1157 // * 'prototype' property: |
|
1158 // * [[Class]] "CDataProto" |
|
1159 // * __proto__ === 'p', the prototype object from above |
|
1160 // * 'constructor' property === 't' |
|
1161 AutoObjectVector protos(cx); |
|
1162 protos.resize(CTYPEPROTO_SLOTS); |
|
1163 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, |
|
1164 sPointerFunction, nullptr, sPointerProps, |
|
1165 sPointerInstanceFunctions, sPointerInstanceProps, |
|
1166 protos.handleAt(SLOT_POINTERPROTO), protos.handleAt(SLOT_POINTERDATAPROTO))) |
|
1167 return false; |
|
1168 |
|
1169 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, |
|
1170 sArrayFunction, nullptr, sArrayProps, |
|
1171 sArrayInstanceFunctions, sArrayInstanceProps, |
|
1172 protos.handleAt(SLOT_ARRAYPROTO), protos.handleAt(SLOT_ARRAYDATAPROTO))) |
|
1173 return false; |
|
1174 |
|
1175 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, |
|
1176 sStructFunction, sStructFunctions, sStructProps, |
|
1177 sStructInstanceFunctions, nullptr, |
|
1178 protos.handleAt(SLOT_STRUCTPROTO), protos.handleAt(SLOT_STRUCTDATAPROTO))) |
|
1179 return false; |
|
1180 |
|
1181 if (!InitTypeConstructor(cx, parent, CTypeProto, protos.handleAt(SLOT_POINTERDATAPROTO), |
|
1182 sFunctionFunction, nullptr, sFunctionProps, sFunctionInstanceFunctions, nullptr, |
|
1183 protos.handleAt(SLOT_FUNCTIONPROTO), protos.handleAt(SLOT_FUNCTIONDATAPROTO))) |
|
1184 return false; |
|
1185 |
|
1186 protos[SLOT_CDATAPROTO] = CDataProto; |
|
1187 |
|
1188 // Create and attach the ctypes.{Int64,UInt64} constructors. |
|
1189 // Each of these has, respectively: |
|
1190 // * [[Class]] "Function" |
|
1191 // * __proto__ === Function.prototype |
|
1192 // * A constructor that creates a ctypes.{Int64,UInt64} object, respectively. |
|
1193 // * 'prototype' property: |
|
1194 // * [[Class]] {"Int64Proto","UInt64Proto"} |
|
1195 // * 'constructor' property === ctypes.{Int64,UInt64} |
|
1196 protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass, |
|
1197 Int64::Construct, sInt64Functions, sInt64StaticFunctions); |
|
1198 if (!protos[SLOT_INT64PROTO]) |
|
1199 return false; |
|
1200 protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass, |
|
1201 UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions); |
|
1202 if (!protos[SLOT_UINT64PROTO]) |
|
1203 return false; |
|
1204 |
|
1205 // Finally, store a pointer to the global ctypes object. |
|
1206 // Note that there is no other reliable manner of locating this object. |
|
1207 protos[SLOT_CTYPES] = parent; |
|
1208 |
|
1209 // Attach the prototypes just created to each of ctypes.CType.prototype, |
|
1210 // and the special type constructors, so we can access them when constructing |
|
1211 // instances of those types. |
|
1212 AttachProtos(CTypeProto, protos); |
|
1213 AttachProtos(protos[SLOT_POINTERPROTO], protos); |
|
1214 AttachProtos(protos[SLOT_ARRAYPROTO], protos); |
|
1215 AttachProtos(protos[SLOT_STRUCTPROTO], protos); |
|
1216 AttachProtos(protos[SLOT_FUNCTIONPROTO], protos); |
|
1217 |
|
1218 RootedObject ABIProto(cx, InitABIClass(cx, parent)); |
|
1219 if (!ABIProto) |
|
1220 return false; |
|
1221 |
|
1222 // Attach objects representing ABI constants. |
|
1223 if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT, ABIProto) || |
|
1224 !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL, ABIProto) || |
|
1225 !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI, ABIProto)) |
|
1226 return false; |
|
1227 |
|
1228 // Create objects representing the builtin types, and attach them to the |
|
1229 // ctypes object. Each type object 't' has: |
|
1230 // * [[Class]] "CType" |
|
1231 // * __proto__ === ctypes.CType.prototype |
|
1232 // * A constructor which creates and returns a CData object, containing |
|
1233 // binary data of the given type. |
|
1234 // * 'prototype' property: |
|
1235 // * [[Class]] "CDataProto" |
|
1236 // * __proto__ === ctypes.CData.prototype |
|
1237 // * 'constructor' property === 't' |
|
1238 #define DEFINE_TYPE(name, type, ffiType) \ |
|
1239 RootedObject typeObj_##name(cx, \ |
|
1240 CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name, \ |
|
1241 TYPE_##name, INT_TO_JSVAL(sizeof(type)), \ |
|
1242 INT_TO_JSVAL(ffiType.alignment), &ffiType)); \ |
|
1243 if (!typeObj_##name) \ |
|
1244 return false; |
|
1245 #include "ctypes/typedefs.h" |
|
1246 |
|
1247 // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent |
|
1248 // the same type in C. |
|
1249 if (!JS_DefineProperty(cx, parent, "unsigned", typeObj_unsigned_int, |
|
1250 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
1251 return false; |
|
1252 |
|
1253 // Create objects representing the special types void_t and voidptr_t. |
|
1254 RootedObject typeObj(cx, |
|
1255 CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void", |
|
1256 TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void)); |
|
1257 if (!typeObj) |
|
1258 return false; |
|
1259 |
|
1260 typeObj = PointerType::CreateInternal(cx, typeObj); |
|
1261 if (!typeObj) |
|
1262 return false; |
|
1263 if (!JS_DefineProperty(cx, parent, "voidptr_t", typeObj, |
|
1264 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
1265 return false; |
|
1266 |
|
1267 return true; |
|
1268 } |
|
1269 |
|
1270 bool |
|
1271 IsCTypesGlobal(JSObject* obj) |
|
1272 { |
|
1273 return JS_GetClass(obj) == &sCTypesGlobalClass; |
|
1274 } |
|
1275 |
|
1276 bool |
|
1277 IsCTypesGlobal(HandleValue v) |
|
1278 { |
|
1279 return v.isObject() && IsCTypesGlobal(&v.toObject()); |
|
1280 } |
|
1281 |
|
1282 // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'. |
|
1283 JSCTypesCallbacks* |
|
1284 GetCallbacks(JSObject* obj) |
|
1285 { |
|
1286 JS_ASSERT(IsCTypesGlobal(obj)); |
|
1287 |
|
1288 jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS); |
|
1289 if (JSVAL_IS_VOID(result)) |
|
1290 return nullptr; |
|
1291 |
|
1292 return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result)); |
|
1293 } |
|
1294 |
|
1295 // Utility function to access a property of an object as an object |
|
1296 // returns false and sets the error if the property does not exist |
|
1297 // or is not an object |
|
1298 static bool GetObjectProperty(JSContext *cx, HandleObject obj, |
|
1299 const char *property, MutableHandleObject result) |
|
1300 { |
|
1301 RootedValue val(cx); |
|
1302 if (!JS_GetProperty(cx, obj, property, &val)) { |
|
1303 return false; |
|
1304 } |
|
1305 |
|
1306 if (JSVAL_IS_PRIMITIVE(val)) { |
|
1307 JS_ReportError(cx, "missing or non-object field"); |
|
1308 return false; |
|
1309 } |
|
1310 |
|
1311 result.set(JSVAL_TO_OBJECT(val)); |
|
1312 return true; |
|
1313 } |
|
1314 |
|
1315 } /* namespace ctypes */ |
|
1316 } /* namespace js */ |
|
1317 |
|
1318 using namespace js; |
|
1319 using namespace js::ctypes; |
|
1320 |
|
1321 JS_PUBLIC_API(bool) |
|
1322 JS_InitCTypesClass(JSContext* cx, HandleObject global) |
|
1323 { |
|
1324 // attach ctypes property to global object |
|
1325 RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass, NullPtr(), NullPtr())); |
|
1326 if (!ctypes) |
|
1327 return false; |
|
1328 |
|
1329 if (!JS_DefineProperty(cx, global, "ctypes", ctypes, JSPROP_READONLY | JSPROP_PERMANENT, |
|
1330 JS_PropertyStub, JS_StrictPropertyStub)){ |
|
1331 return false; |
|
1332 } |
|
1333 |
|
1334 if (!InitTypeClasses(cx, ctypes)) |
|
1335 return false; |
|
1336 |
|
1337 // attach API functions and properties |
|
1338 if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) || |
|
1339 !JS_DefineProperties(cx, ctypes, sModuleProps)) |
|
1340 return false; |
|
1341 |
|
1342 // Set up ctypes.CDataFinalizer.prototype. |
|
1343 RootedObject ctor(cx); |
|
1344 if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor)) |
|
1345 return false; |
|
1346 |
|
1347 RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass, NullPtr(), ctypes)); |
|
1348 if (!prototype) |
|
1349 return false; |
|
1350 |
|
1351 if (!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions)) |
|
1352 return false; |
|
1353 |
|
1354 if (!JS_DefineProperty(cx, ctor, "prototype", prototype, |
|
1355 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
1356 return false; |
|
1357 |
|
1358 if (!JS_DefineProperty(cx, prototype, "constructor", ctor, |
|
1359 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
1360 return false; |
|
1361 |
|
1362 |
|
1363 // Seal the ctypes object, to prevent modification. |
|
1364 return JS_FreezeObject(cx, ctypes); |
|
1365 } |
|
1366 |
|
1367 JS_PUBLIC_API(void) |
|
1368 JS_SetCTypesCallbacks(JSObject *ctypesObj, JSCTypesCallbacks* callbacks) |
|
1369 { |
|
1370 JS_ASSERT(callbacks); |
|
1371 JS_ASSERT(IsCTypesGlobal(ctypesObj)); |
|
1372 |
|
1373 // Set the callbacks on a reserved slot. |
|
1374 JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks)); |
|
1375 } |
|
1376 |
|
1377 namespace js { |
|
1378 |
|
1379 JS_FRIEND_API(size_t) |
|
1380 SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject *obj) |
|
1381 { |
|
1382 if (!CData::IsCData(obj)) |
|
1383 return 0; |
|
1384 |
|
1385 size_t n = 0; |
|
1386 jsval slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS); |
|
1387 if (!JSVAL_IS_VOID(slot)) { |
|
1388 bool owns = JSVAL_TO_BOOLEAN(slot); |
|
1389 slot = JS_GetReservedSlot(obj, ctypes::SLOT_DATA); |
|
1390 if (!JSVAL_IS_VOID(slot)) { |
|
1391 char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot)); |
|
1392 n += mallocSizeOf(buffer); |
|
1393 if (owns) |
|
1394 n += mallocSizeOf(*buffer); |
|
1395 } |
|
1396 } |
|
1397 return n; |
|
1398 } |
|
1399 |
|
1400 namespace ctypes { |
|
1401 |
|
1402 /******************************************************************************* |
|
1403 ** Type conversion functions |
|
1404 *******************************************************************************/ |
|
1405 |
|
1406 // Enforce some sanity checks on type widths and properties. |
|
1407 // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int |
|
1408 // autoconverts to a primitive JS number; to support ILP64 architectures, it |
|
1409 // would need to autoconvert to an Int64 object instead. Therefore we enforce |
|
1410 // this invariant here.) |
|
1411 JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4); |
|
1412 JS_STATIC_ASSERT(sizeof(char) == 1); |
|
1413 JS_STATIC_ASSERT(sizeof(short) == 2); |
|
1414 JS_STATIC_ASSERT(sizeof(int) == 4); |
|
1415 JS_STATIC_ASSERT(sizeof(unsigned) == 4); |
|
1416 JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8); |
|
1417 JS_STATIC_ASSERT(sizeof(long long) == 8); |
|
1418 JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t)); |
|
1419 JS_STATIC_ASSERT(sizeof(float) == 4); |
|
1420 JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*)); |
|
1421 JS_STATIC_ASSERT(NumericLimits<double>::is_signed); |
|
1422 |
|
1423 // Templated helper to convert FromType to TargetType, for the default case |
|
1424 // where the trivial POD constructor will do. |
|
1425 template<class TargetType, class FromType> |
|
1426 struct ConvertImpl { |
|
1427 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) { |
|
1428 return TargetType(d); |
|
1429 } |
|
1430 }; |
|
1431 |
|
1432 #ifdef _MSC_VER |
|
1433 // MSVC can't perform double to unsigned __int64 conversion when the |
|
1434 // double is greater than 2^63 - 1. Help it along a little. |
|
1435 template<> |
|
1436 struct ConvertImpl<uint64_t, double> { |
|
1437 static MOZ_ALWAYS_INLINE uint64_t Convert(double d) { |
|
1438 return d > 0x7fffffffffffffffui64 ? |
|
1439 uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 : |
|
1440 uint64_t(d); |
|
1441 } |
|
1442 }; |
|
1443 #endif |
|
1444 |
|
1445 // C++ doesn't guarantee that exact values are the only ones that will |
|
1446 // round-trip. In fact, on some platforms, including SPARC, there are pairs of |
|
1447 // values, a uint64_t and a double, such that neither value is exactly |
|
1448 // representable in the other type, but they cast to each other. |
|
1449 #if defined(SPARC) || defined(__powerpc__) |
|
1450 // Simulate x86 overflow behavior |
|
1451 template<> |
|
1452 struct ConvertImpl<uint64_t, double> { |
|
1453 static MOZ_ALWAYS_INLINE uint64_t Convert(double d) { |
|
1454 return d >= 0xffffffffffffffff ? |
|
1455 0x8000000000000000 : uint64_t(d); |
|
1456 } |
|
1457 }; |
|
1458 |
|
1459 template<> |
|
1460 struct ConvertImpl<int64_t, double> { |
|
1461 static MOZ_ALWAYS_INLINE int64_t Convert(double d) { |
|
1462 return d >= 0x7fffffffffffffff ? |
|
1463 0x8000000000000000 : int64_t(d); |
|
1464 } |
|
1465 }; |
|
1466 #endif |
|
1467 |
|
1468 template<class TargetType, class FromType> |
|
1469 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) |
|
1470 { |
|
1471 return ConvertImpl<TargetType, FromType>::Convert(d); |
|
1472 } |
|
1473 |
|
1474 template<class TargetType, class FromType> |
|
1475 static MOZ_ALWAYS_INLINE bool IsAlwaysExact() |
|
1476 { |
|
1477 // Return 'true' if TargetType can always exactly represent FromType. |
|
1478 // This means that: |
|
1479 // 1) TargetType must be the same or more bits wide as FromType. For integers |
|
1480 // represented in 'n' bits, unsigned variants will have 'n' digits while |
|
1481 // signed will have 'n - 1'. For floating point types, 'digits' is the |
|
1482 // mantissa width. |
|
1483 // 2) If FromType is signed, TargetType must also be signed. (Floating point |
|
1484 // types are always signed.) |
|
1485 // 3) If TargetType is an exact integral type, FromType must be also. |
|
1486 if (NumericLimits<TargetType>::digits < NumericLimits<FromType>::digits) |
|
1487 return false; |
|
1488 |
|
1489 if (NumericLimits<FromType>::is_signed && |
|
1490 !NumericLimits<TargetType>::is_signed) |
|
1491 return false; |
|
1492 |
|
1493 if (!NumericLimits<FromType>::is_exact && |
|
1494 NumericLimits<TargetType>::is_exact) |
|
1495 return false; |
|
1496 |
|
1497 return true; |
|
1498 } |
|
1499 |
|
1500 // Templated helper to determine if FromType 'i' converts losslessly to |
|
1501 // TargetType 'j'. Default case where both types are the same signedness. |
|
1502 template<class TargetType, class FromType, bool TargetSigned, bool FromSigned> |
|
1503 struct IsExactImpl { |
|
1504 static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) { |
|
1505 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact); |
|
1506 return FromType(j) == i; |
|
1507 } |
|
1508 }; |
|
1509 |
|
1510 // Specialization where TargetType is unsigned, FromType is signed. |
|
1511 template<class TargetType, class FromType> |
|
1512 struct IsExactImpl<TargetType, FromType, false, true> { |
|
1513 static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) { |
|
1514 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact); |
|
1515 return i >= 0 && FromType(j) == i; |
|
1516 } |
|
1517 }; |
|
1518 |
|
1519 // Specialization where TargetType is signed, FromType is unsigned. |
|
1520 template<class TargetType, class FromType> |
|
1521 struct IsExactImpl<TargetType, FromType, true, false> { |
|
1522 static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) { |
|
1523 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact); |
|
1524 return TargetType(i) >= 0 && FromType(j) == i; |
|
1525 } |
|
1526 }; |
|
1527 |
|
1528 // Convert FromType 'i' to TargetType 'result', returning true iff 'result' |
|
1529 // is an exact representation of 'i'. |
|
1530 template<class TargetType, class FromType> |
|
1531 static MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result) |
|
1532 { |
|
1533 // Require that TargetType is integral, to simplify conversion. |
|
1534 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact); |
|
1535 |
|
1536 *result = Convert<TargetType>(i); |
|
1537 |
|
1538 // See if we can avoid a dynamic check. |
|
1539 if (IsAlwaysExact<TargetType, FromType>()) |
|
1540 return true; |
|
1541 |
|
1542 // Return 'true' if 'i' is exactly representable in 'TargetType'. |
|
1543 return IsExactImpl<TargetType, |
|
1544 FromType, |
|
1545 NumericLimits<TargetType>::is_signed, |
|
1546 NumericLimits<FromType>::is_signed>::Test(i, *result); |
|
1547 } |
|
1548 |
|
1549 // Templated helper to determine if Type 'i' is negative. Default case |
|
1550 // where IntegerType is unsigned. |
|
1551 template<class Type, bool IsSigned> |
|
1552 struct IsNegativeImpl { |
|
1553 static MOZ_ALWAYS_INLINE bool Test(Type i) { |
|
1554 return false; |
|
1555 } |
|
1556 }; |
|
1557 |
|
1558 // Specialization where Type is signed. |
|
1559 template<class Type> |
|
1560 struct IsNegativeImpl<Type, true> { |
|
1561 static MOZ_ALWAYS_INLINE bool Test(Type i) { |
|
1562 return i < 0; |
|
1563 } |
|
1564 }; |
|
1565 |
|
1566 // Determine whether Type 'i' is negative. |
|
1567 template<class Type> |
|
1568 static MOZ_ALWAYS_INLINE bool IsNegative(Type i) |
|
1569 { |
|
1570 return IsNegativeImpl<Type, NumericLimits<Type>::is_signed>::Test(i); |
|
1571 } |
|
1572 |
|
1573 // Implicitly convert val to bool, allowing bool, int, and double |
|
1574 // arguments numerically equal to 0 or 1. |
|
1575 static bool |
|
1576 jsvalToBool(JSContext* cx, jsval val, bool* result) |
|
1577 { |
|
1578 if (JSVAL_IS_BOOLEAN(val)) { |
|
1579 *result = JSVAL_TO_BOOLEAN(val); |
|
1580 return true; |
|
1581 } |
|
1582 if (JSVAL_IS_INT(val)) { |
|
1583 int32_t i = JSVAL_TO_INT(val); |
|
1584 *result = i != 0; |
|
1585 return i == 0 || i == 1; |
|
1586 } |
|
1587 if (JSVAL_IS_DOUBLE(val)) { |
|
1588 double d = JSVAL_TO_DOUBLE(val); |
|
1589 *result = d != 0; |
|
1590 // Allow -0. |
|
1591 return d == 1 || d == 0; |
|
1592 } |
|
1593 // Don't silently convert null to bool. It's probably a mistake. |
|
1594 return false; |
|
1595 } |
|
1596 |
|
1597 // Implicitly convert val to IntegerType, allowing bool, int, double, |
|
1598 // Int64, UInt64, and CData integer types 't' where all values of 't' are |
|
1599 // representable by IntegerType. |
|
1600 template<class IntegerType> |
|
1601 static bool |
|
1602 jsvalToInteger(JSContext* cx, jsval val, IntegerType* result) |
|
1603 { |
|
1604 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); |
|
1605 |
|
1606 if (JSVAL_IS_INT(val)) { |
|
1607 // Make sure the integer fits in the alotted precision, and has the right |
|
1608 // sign. |
|
1609 int32_t i = JSVAL_TO_INT(val); |
|
1610 return ConvertExact(i, result); |
|
1611 } |
|
1612 if (JSVAL_IS_DOUBLE(val)) { |
|
1613 // Don't silently lose bits here -- check that val really is an |
|
1614 // integer value, and has the right sign. |
|
1615 double d = JSVAL_TO_DOUBLE(val); |
|
1616 return ConvertExact(d, result); |
|
1617 } |
|
1618 if (!JSVAL_IS_PRIMITIVE(val)) { |
|
1619 JSObject* obj = JSVAL_TO_OBJECT(val); |
|
1620 if (CData::IsCData(obj)) { |
|
1621 JSObject* typeObj = CData::GetCType(obj); |
|
1622 void* data = CData::GetData(obj); |
|
1623 |
|
1624 // Check whether the source type is always representable, with exact |
|
1625 // precision, by the target type. If it is, convert the value. |
|
1626 switch (CType::GetTypeCode(typeObj)) { |
|
1627 #define DEFINE_INT_TYPE(name, fromType, ffiType) \ |
|
1628 case TYPE_##name: \ |
|
1629 if (!IsAlwaysExact<IntegerType, fromType>()) \ |
|
1630 return false; \ |
|
1631 *result = IntegerType(*static_cast<fromType*>(data)); \ |
|
1632 return true; |
|
1633 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
1634 #include "ctypes/typedefs.h" |
|
1635 case TYPE_void_t: |
|
1636 case TYPE_bool: |
|
1637 case TYPE_float: |
|
1638 case TYPE_double: |
|
1639 case TYPE_float32_t: |
|
1640 case TYPE_float64_t: |
|
1641 case TYPE_char: |
|
1642 case TYPE_signed_char: |
|
1643 case TYPE_unsigned_char: |
|
1644 case TYPE_jschar: |
|
1645 case TYPE_pointer: |
|
1646 case TYPE_function: |
|
1647 case TYPE_array: |
|
1648 case TYPE_struct: |
|
1649 // Not a compatible number type. |
|
1650 return false; |
|
1651 } |
|
1652 } |
|
1653 |
|
1654 if (Int64::IsInt64(obj)) { |
|
1655 // Make sure the integer fits in IntegerType. |
|
1656 int64_t i = Int64Base::GetInt(obj); |
|
1657 return ConvertExact(i, result); |
|
1658 } |
|
1659 |
|
1660 if (UInt64::IsUInt64(obj)) { |
|
1661 // Make sure the integer fits in IntegerType. |
|
1662 uint64_t i = Int64Base::GetInt(obj); |
|
1663 return ConvertExact(i, result); |
|
1664 } |
|
1665 |
|
1666 if (CDataFinalizer::IsCDataFinalizer(obj)) { |
|
1667 RootedValue innerData(cx); |
|
1668 if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) { |
|
1669 return false; // Nothing to convert |
|
1670 } |
|
1671 return jsvalToInteger(cx, innerData, result); |
|
1672 } |
|
1673 |
|
1674 return false; |
|
1675 } |
|
1676 if (JSVAL_IS_BOOLEAN(val)) { |
|
1677 // Implicitly promote boolean values to 0 or 1, like C. |
|
1678 *result = JSVAL_TO_BOOLEAN(val); |
|
1679 JS_ASSERT(*result == 0 || *result == 1); |
|
1680 return true; |
|
1681 } |
|
1682 // Don't silently convert null to an integer. It's probably a mistake. |
|
1683 return false; |
|
1684 } |
|
1685 |
|
1686 // Implicitly convert val to FloatType, allowing int, double, |
|
1687 // Int64, UInt64, and CData numeric types 't' where all values of 't' are |
|
1688 // representable by FloatType. |
|
1689 template<class FloatType> |
|
1690 static bool |
|
1691 jsvalToFloat(JSContext *cx, jsval val, FloatType* result) |
|
1692 { |
|
1693 JS_STATIC_ASSERT(!NumericLimits<FloatType>::is_exact); |
|
1694 |
|
1695 // The following casts may silently throw away some bits, but there's |
|
1696 // no good way around it. Sternly requiring that the 64-bit double |
|
1697 // argument be exactly representable as a 32-bit float is |
|
1698 // unrealistic: it would allow 1/2 to pass but not 1/3. |
|
1699 if (JSVAL_IS_INT(val)) { |
|
1700 *result = FloatType(JSVAL_TO_INT(val)); |
|
1701 return true; |
|
1702 } |
|
1703 if (JSVAL_IS_DOUBLE(val)) { |
|
1704 *result = FloatType(JSVAL_TO_DOUBLE(val)); |
|
1705 return true; |
|
1706 } |
|
1707 if (!JSVAL_IS_PRIMITIVE(val)) { |
|
1708 JSObject* obj = JSVAL_TO_OBJECT(val); |
|
1709 if (CData::IsCData(obj)) { |
|
1710 JSObject* typeObj = CData::GetCType(obj); |
|
1711 void* data = CData::GetData(obj); |
|
1712 |
|
1713 // Check whether the source type is always representable, with exact |
|
1714 // precision, by the target type. If it is, convert the value. |
|
1715 switch (CType::GetTypeCode(typeObj)) { |
|
1716 #define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \ |
|
1717 case TYPE_##name: \ |
|
1718 if (!IsAlwaysExact<FloatType, fromType>()) \ |
|
1719 return false; \ |
|
1720 *result = FloatType(*static_cast<fromType*>(data)); \ |
|
1721 return true; |
|
1722 #define DEFINE_INT_TYPE(x, y, z) DEFINE_FLOAT_TYPE(x, y, z) |
|
1723 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
1724 #include "ctypes/typedefs.h" |
|
1725 case TYPE_void_t: |
|
1726 case TYPE_bool: |
|
1727 case TYPE_char: |
|
1728 case TYPE_signed_char: |
|
1729 case TYPE_unsigned_char: |
|
1730 case TYPE_jschar: |
|
1731 case TYPE_pointer: |
|
1732 case TYPE_function: |
|
1733 case TYPE_array: |
|
1734 case TYPE_struct: |
|
1735 // Not a compatible number type. |
|
1736 return false; |
|
1737 } |
|
1738 } |
|
1739 } |
|
1740 // Don't silently convert true to 1.0 or false to 0.0, even though C/C++ |
|
1741 // does it. It's likely to be a mistake. |
|
1742 return false; |
|
1743 } |
|
1744 |
|
1745 template<class IntegerType> |
|
1746 static bool |
|
1747 StringToInteger(JSContext* cx, JSString* string, IntegerType* result) |
|
1748 { |
|
1749 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); |
|
1750 |
|
1751 const jschar* cp = string->getChars(nullptr); |
|
1752 if (!cp) |
|
1753 return false; |
|
1754 |
|
1755 const jschar* end = cp + string->length(); |
|
1756 if (cp == end) |
|
1757 return false; |
|
1758 |
|
1759 IntegerType sign = 1; |
|
1760 if (cp[0] == '-') { |
|
1761 if (!NumericLimits<IntegerType>::is_signed) |
|
1762 return false; |
|
1763 |
|
1764 sign = -1; |
|
1765 ++cp; |
|
1766 } |
|
1767 |
|
1768 // Assume base-10, unless the string begins with '0x' or '0X'. |
|
1769 IntegerType base = 10; |
|
1770 if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) { |
|
1771 cp += 2; |
|
1772 base = 16; |
|
1773 } |
|
1774 |
|
1775 // Scan the string left to right and build the number, |
|
1776 // checking for valid characters 0 - 9, a - f, A - F and overflow. |
|
1777 IntegerType i = 0; |
|
1778 while (cp != end) { |
|
1779 jschar c = *cp++; |
|
1780 if (c >= '0' && c <= '9') |
|
1781 c -= '0'; |
|
1782 else if (base == 16 && c >= 'a' && c <= 'f') |
|
1783 c = c - 'a' + 10; |
|
1784 else if (base == 16 && c >= 'A' && c <= 'F') |
|
1785 c = c - 'A' + 10; |
|
1786 else |
|
1787 return false; |
|
1788 |
|
1789 IntegerType ii = i; |
|
1790 i = ii * base + sign * c; |
|
1791 if (i / base != ii) // overflow |
|
1792 return false; |
|
1793 } |
|
1794 |
|
1795 *result = i; |
|
1796 return true; |
|
1797 } |
|
1798 |
|
1799 // Implicitly convert val to IntegerType, allowing int, double, |
|
1800 // Int64, UInt64, and optionally a decimal or hexadecimal string argument. |
|
1801 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.) |
|
1802 template<class IntegerType> |
|
1803 static bool |
|
1804 jsvalToBigInteger(JSContext* cx, |
|
1805 jsval val, |
|
1806 bool allowString, |
|
1807 IntegerType* result) |
|
1808 { |
|
1809 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); |
|
1810 |
|
1811 if (JSVAL_IS_INT(val)) { |
|
1812 // Make sure the integer fits in the alotted precision, and has the right |
|
1813 // sign. |
|
1814 int32_t i = JSVAL_TO_INT(val); |
|
1815 return ConvertExact(i, result); |
|
1816 } |
|
1817 if (JSVAL_IS_DOUBLE(val)) { |
|
1818 // Don't silently lose bits here -- check that val really is an |
|
1819 // integer value, and has the right sign. |
|
1820 double d = JSVAL_TO_DOUBLE(val); |
|
1821 return ConvertExact(d, result); |
|
1822 } |
|
1823 if (allowString && JSVAL_IS_STRING(val)) { |
|
1824 // Allow conversion from base-10 or base-16 strings, provided the result |
|
1825 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed |
|
1826 // to the JS array element operator, which will automatically call |
|
1827 // toString() on the object for us.) |
|
1828 return StringToInteger(cx, JSVAL_TO_STRING(val), result); |
|
1829 } |
|
1830 if (!JSVAL_IS_PRIMITIVE(val)) { |
|
1831 // Allow conversion from an Int64 or UInt64 object directly. |
|
1832 JSObject* obj = JSVAL_TO_OBJECT(val); |
|
1833 |
|
1834 if (UInt64::IsUInt64(obj)) { |
|
1835 // Make sure the integer fits in IntegerType. |
|
1836 uint64_t i = Int64Base::GetInt(obj); |
|
1837 return ConvertExact(i, result); |
|
1838 } |
|
1839 |
|
1840 if (Int64::IsInt64(obj)) { |
|
1841 // Make sure the integer fits in IntegerType. |
|
1842 int64_t i = Int64Base::GetInt(obj); |
|
1843 return ConvertExact(i, result); |
|
1844 } |
|
1845 |
|
1846 if (CDataFinalizer::IsCDataFinalizer(obj)) { |
|
1847 RootedValue innerData(cx); |
|
1848 if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) { |
|
1849 return false; // Nothing to convert |
|
1850 } |
|
1851 return jsvalToBigInteger(cx, innerData, allowString, result); |
|
1852 } |
|
1853 |
|
1854 } |
|
1855 return false; |
|
1856 } |
|
1857 |
|
1858 // Implicitly convert val to a size value, where the size value is represented |
|
1859 // by size_t but must also fit in a double. |
|
1860 static bool |
|
1861 jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result) |
|
1862 { |
|
1863 if (!jsvalToBigInteger(cx, val, allowString, result)) |
|
1864 return false; |
|
1865 |
|
1866 // Also check that the result fits in a double. |
|
1867 return Convert<size_t>(double(*result)) == *result; |
|
1868 } |
|
1869 |
|
1870 // Implicitly convert val to IntegerType, allowing int, double, |
|
1871 // Int64, UInt64, and optionally a decimal or hexadecimal string argument. |
|
1872 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.) |
|
1873 template<class IntegerType> |
|
1874 static bool |
|
1875 jsidToBigInteger(JSContext* cx, |
|
1876 jsid val, |
|
1877 bool allowString, |
|
1878 IntegerType* result) |
|
1879 { |
|
1880 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); |
|
1881 |
|
1882 if (JSID_IS_INT(val)) { |
|
1883 // Make sure the integer fits in the alotted precision, and has the right |
|
1884 // sign. |
|
1885 int32_t i = JSID_TO_INT(val); |
|
1886 return ConvertExact(i, result); |
|
1887 } |
|
1888 if (allowString && JSID_IS_STRING(val)) { |
|
1889 // Allow conversion from base-10 or base-16 strings, provided the result |
|
1890 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed |
|
1891 // to the JS array element operator, which will automatically call |
|
1892 // toString() on the object for us.) |
|
1893 return StringToInteger(cx, JSID_TO_STRING(val), result); |
|
1894 } |
|
1895 if (JSID_IS_OBJECT(val)) { |
|
1896 // Allow conversion from an Int64 or UInt64 object directly. |
|
1897 JSObject* obj = JSID_TO_OBJECT(val); |
|
1898 |
|
1899 if (UInt64::IsUInt64(obj)) { |
|
1900 // Make sure the integer fits in IntegerType. |
|
1901 uint64_t i = Int64Base::GetInt(obj); |
|
1902 return ConvertExact(i, result); |
|
1903 } |
|
1904 |
|
1905 if (Int64::IsInt64(obj)) { |
|
1906 // Make sure the integer fits in IntegerType. |
|
1907 int64_t i = Int64Base::GetInt(obj); |
|
1908 return ConvertExact(i, result); |
|
1909 } |
|
1910 } |
|
1911 return false; |
|
1912 } |
|
1913 |
|
1914 // Implicitly convert val to a size value, where the size value is represented |
|
1915 // by size_t but must also fit in a double. |
|
1916 static bool |
|
1917 jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result) |
|
1918 { |
|
1919 if (!jsidToBigInteger(cx, val, allowString, result)) |
|
1920 return false; |
|
1921 |
|
1922 // Also check that the result fits in a double. |
|
1923 return Convert<size_t>(double(*result)) == *result; |
|
1924 } |
|
1925 |
|
1926 // Implicitly convert a size value to a jsval, ensuring that the size_t value |
|
1927 // fits in a double. |
|
1928 static bool |
|
1929 SizeTojsval(JSContext* cx, size_t size, jsval* result) |
|
1930 { |
|
1931 if (Convert<size_t>(double(size)) != size) { |
|
1932 JS_ReportError(cx, "size overflow"); |
|
1933 return false; |
|
1934 } |
|
1935 |
|
1936 *result = JS_NumberValue(double(size)); |
|
1937 return true; |
|
1938 } |
|
1939 |
|
1940 // Forcefully convert val to IntegerType when explicitly requested. |
|
1941 template<class IntegerType> |
|
1942 static bool |
|
1943 jsvalToIntegerExplicit(jsval val, IntegerType* result) |
|
1944 { |
|
1945 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); |
|
1946 |
|
1947 if (JSVAL_IS_DOUBLE(val)) { |
|
1948 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast. |
|
1949 double d = JSVAL_TO_DOUBLE(val); |
|
1950 *result = mozilla::IsFinite(d) ? IntegerType(d) : 0; |
|
1951 return true; |
|
1952 } |
|
1953 if (!JSVAL_IS_PRIMITIVE(val)) { |
|
1954 // Convert Int64 and UInt64 values by C-style cast. |
|
1955 JSObject* obj = JSVAL_TO_OBJECT(val); |
|
1956 if (Int64::IsInt64(obj)) { |
|
1957 int64_t i = Int64Base::GetInt(obj); |
|
1958 *result = IntegerType(i); |
|
1959 return true; |
|
1960 } |
|
1961 if (UInt64::IsUInt64(obj)) { |
|
1962 uint64_t i = Int64Base::GetInt(obj); |
|
1963 *result = IntegerType(i); |
|
1964 return true; |
|
1965 } |
|
1966 } |
|
1967 return false; |
|
1968 } |
|
1969 |
|
1970 // Forcefully convert val to a pointer value when explicitly requested. |
|
1971 static bool |
|
1972 jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result) |
|
1973 { |
|
1974 if (JSVAL_IS_INT(val)) { |
|
1975 // int32_t always fits in intptr_t. If the integer is negative, cast through |
|
1976 // an intptr_t intermediate to sign-extend. |
|
1977 int32_t i = JSVAL_TO_INT(val); |
|
1978 *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i); |
|
1979 return true; |
|
1980 } |
|
1981 if (JSVAL_IS_DOUBLE(val)) { |
|
1982 double d = JSVAL_TO_DOUBLE(val); |
|
1983 if (d < 0) { |
|
1984 // Cast through an intptr_t intermediate to sign-extend. |
|
1985 intptr_t i = Convert<intptr_t>(d); |
|
1986 if (double(i) != d) |
|
1987 return false; |
|
1988 |
|
1989 *result = uintptr_t(i); |
|
1990 return true; |
|
1991 } |
|
1992 |
|
1993 // Don't silently lose bits here -- check that val really is an |
|
1994 // integer value, and has the right sign. |
|
1995 *result = Convert<uintptr_t>(d); |
|
1996 return double(*result) == d; |
|
1997 } |
|
1998 if (!JSVAL_IS_PRIMITIVE(val)) { |
|
1999 JSObject* obj = JSVAL_TO_OBJECT(val); |
|
2000 if (Int64::IsInt64(obj)) { |
|
2001 int64_t i = Int64Base::GetInt(obj); |
|
2002 intptr_t p = intptr_t(i); |
|
2003 |
|
2004 // Make sure the integer fits in the alotted precision. |
|
2005 if (int64_t(p) != i) |
|
2006 return false; |
|
2007 *result = uintptr_t(p); |
|
2008 return true; |
|
2009 } |
|
2010 |
|
2011 if (UInt64::IsUInt64(obj)) { |
|
2012 uint64_t i = Int64Base::GetInt(obj); |
|
2013 |
|
2014 // Make sure the integer fits in the alotted precision. |
|
2015 *result = uintptr_t(i); |
|
2016 return uint64_t(*result) == i; |
|
2017 } |
|
2018 } |
|
2019 return false; |
|
2020 } |
|
2021 |
|
2022 template<class IntegerType, class CharType, size_t N, class AP> |
|
2023 void |
|
2024 IntegerToString(IntegerType i, int radix, Vector<CharType, N, AP>& result) |
|
2025 { |
|
2026 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); |
|
2027 |
|
2028 // The buffer must be big enough for all the bits of IntegerType to fit, |
|
2029 // in base-2, including '-'. |
|
2030 CharType buffer[sizeof(IntegerType) * 8 + 1]; |
|
2031 CharType* end = buffer + sizeof(buffer) / sizeof(CharType); |
|
2032 CharType* cp = end; |
|
2033 |
|
2034 // Build the string in reverse. We use multiplication and subtraction |
|
2035 // instead of modulus because that's much faster. |
|
2036 const bool isNegative = IsNegative(i); |
|
2037 size_t sign = isNegative ? -1 : 1; |
|
2038 do { |
|
2039 IntegerType ii = i / IntegerType(radix); |
|
2040 size_t index = sign * size_t(i - ii * IntegerType(radix)); |
|
2041 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index]; |
|
2042 i = ii; |
|
2043 } while (i != 0); |
|
2044 |
|
2045 if (isNegative) |
|
2046 *--cp = '-'; |
|
2047 |
|
2048 JS_ASSERT(cp >= buffer); |
|
2049 result.append(cp, end); |
|
2050 } |
|
2051 |
|
2052 template<class CharType> |
|
2053 static size_t |
|
2054 strnlen(const CharType* begin, size_t max) |
|
2055 { |
|
2056 for (const CharType* s = begin; s != begin + max; ++s) |
|
2057 if (*s == 0) |
|
2058 return s - begin; |
|
2059 |
|
2060 return max; |
|
2061 } |
|
2062 |
|
2063 // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where |
|
2064 // possible; otherwise, construct and return a CData object. The following |
|
2065 // semantics apply when constructing a CData object for return: |
|
2066 // * If 'wantPrimitive' is true, the caller indicates that 'result' must be |
|
2067 // a JS primitive, and ConvertToJS will fail if 'result' would be a CData |
|
2068 // object. Otherwise: |
|
2069 // * If a CData object 'parentObj' is supplied, the new CData object is |
|
2070 // dependent on the given parent and its buffer refers to a slice of the |
|
2071 // parent's buffer. |
|
2072 // * If 'parentObj' is null, the new CData object may or may not own its |
|
2073 // resulting buffer depending on the 'ownResult' argument. |
|
2074 static bool |
|
2075 ConvertToJS(JSContext* cx, |
|
2076 HandleObject typeObj, |
|
2077 HandleObject parentObj, |
|
2078 void* data, |
|
2079 bool wantPrimitive, |
|
2080 bool ownResult, |
|
2081 jsval* result) |
|
2082 { |
|
2083 JS_ASSERT(!parentObj || CData::IsCData(parentObj)); |
|
2084 JS_ASSERT(!parentObj || !ownResult); |
|
2085 JS_ASSERT(!wantPrimitive || !ownResult); |
|
2086 |
|
2087 TypeCode typeCode = CType::GetTypeCode(typeObj); |
|
2088 |
|
2089 switch (typeCode) { |
|
2090 case TYPE_void_t: |
|
2091 *result = JSVAL_VOID; |
|
2092 break; |
|
2093 case TYPE_bool: |
|
2094 *result = *static_cast<bool*>(data) ? JSVAL_TRUE : JSVAL_FALSE; |
|
2095 break; |
|
2096 #define DEFINE_INT_TYPE(name, type, ffiType) \ |
|
2097 case TYPE_##name: { \ |
|
2098 type value = *static_cast<type*>(data); \ |
|
2099 if (sizeof(type) < 4) \ |
|
2100 *result = INT_TO_JSVAL(int32_t(value)); \ |
|
2101 else \ |
|
2102 *result = JS_NumberValue(double(value)); \ |
|
2103 break; \ |
|
2104 } |
|
2105 #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \ |
|
2106 case TYPE_##name: { \ |
|
2107 /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \ |
|
2108 uint64_t value; \ |
|
2109 RootedObject proto(cx); \ |
|
2110 if (!NumericLimits<type>::is_signed) { \ |
|
2111 value = *static_cast<type*>(data); \ |
|
2112 /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \ |
|
2113 proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \ |
|
2114 if (!proto) \ |
|
2115 return false; \ |
|
2116 } else { \ |
|
2117 value = int64_t(*static_cast<type*>(data)); \ |
|
2118 /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \ |
|
2119 proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \ |
|
2120 if (!proto) \ |
|
2121 return false; \ |
|
2122 } \ |
|
2123 \ |
|
2124 JSObject* obj = Int64Base::Construct(cx, proto, value, \ |
|
2125 !NumericLimits<type>::is_signed); \ |
|
2126 if (!obj) \ |
|
2127 return false; \ |
|
2128 *result = OBJECT_TO_JSVAL(obj); \ |
|
2129 break; \ |
|
2130 } |
|
2131 #define DEFINE_FLOAT_TYPE(name, type, ffiType) \ |
|
2132 case TYPE_##name: { \ |
|
2133 type value = *static_cast<type*>(data); \ |
|
2134 *result = JS_NumberValue(double(value)); \ |
|
2135 break; \ |
|
2136 } |
|
2137 #define DEFINE_CHAR_TYPE(name, type, ffiType) \ |
|
2138 case TYPE_##name: \ |
|
2139 /* Convert to an integer. We have no idea what character encoding to */ \ |
|
2140 /* use, if any. */ \ |
|
2141 *result = INT_TO_JSVAL(*static_cast<type*>(data)); \ |
|
2142 break; |
|
2143 #include "ctypes/typedefs.h" |
|
2144 case TYPE_jschar: { |
|
2145 // Convert the jschar to a 1-character string. |
|
2146 JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1); |
|
2147 if (!str) |
|
2148 return false; |
|
2149 |
|
2150 *result = STRING_TO_JSVAL(str); |
|
2151 break; |
|
2152 } |
|
2153 case TYPE_pointer: |
|
2154 case TYPE_array: |
|
2155 case TYPE_struct: { |
|
2156 // We're about to create a new CData object to return. If the caller doesn't |
|
2157 // want this, return early. |
|
2158 if (wantPrimitive) { |
|
2159 JS_ReportError(cx, "cannot convert to primitive value"); |
|
2160 return false; |
|
2161 } |
|
2162 |
|
2163 JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult); |
|
2164 if (!obj) |
|
2165 return false; |
|
2166 |
|
2167 *result = OBJECT_TO_JSVAL(obj); |
|
2168 break; |
|
2169 } |
|
2170 case TYPE_function: |
|
2171 MOZ_ASSUME_UNREACHABLE("cannot return a FunctionType"); |
|
2172 } |
|
2173 |
|
2174 return true; |
|
2175 } |
|
2176 |
|
2177 // Determine if the contents of a typed array can be converted without |
|
2178 // ambiguity to a C type. Elements of a Int8Array are converted to |
|
2179 // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc. |
|
2180 bool CanConvertTypedArrayItemTo(JSObject *baseType, JSObject *valObj, JSContext *cx) { |
|
2181 TypeCode baseTypeCode = CType::GetTypeCode(baseType); |
|
2182 if (baseTypeCode == TYPE_void_t) { |
|
2183 return true; |
|
2184 } |
|
2185 TypeCode elementTypeCode; |
|
2186 switch (JS_GetArrayBufferViewType(valObj)) { |
|
2187 case ScalarTypeDescr::TYPE_INT8: |
|
2188 elementTypeCode = TYPE_int8_t; |
|
2189 break; |
|
2190 case ScalarTypeDescr::TYPE_UINT8: |
|
2191 case ScalarTypeDescr::TYPE_UINT8_CLAMPED: |
|
2192 elementTypeCode = TYPE_uint8_t; |
|
2193 break; |
|
2194 case ScalarTypeDescr::TYPE_INT16: |
|
2195 elementTypeCode = TYPE_int16_t; |
|
2196 break; |
|
2197 case ScalarTypeDescr::TYPE_UINT16: |
|
2198 elementTypeCode = TYPE_uint16_t; |
|
2199 break; |
|
2200 case ScalarTypeDescr::TYPE_INT32: |
|
2201 elementTypeCode = TYPE_int32_t; |
|
2202 break; |
|
2203 case ScalarTypeDescr::TYPE_UINT32: |
|
2204 elementTypeCode = TYPE_uint32_t; |
|
2205 break; |
|
2206 case ScalarTypeDescr::TYPE_FLOAT32: |
|
2207 elementTypeCode = TYPE_float32_t; |
|
2208 break; |
|
2209 case ScalarTypeDescr::TYPE_FLOAT64: |
|
2210 elementTypeCode = TYPE_float64_t; |
|
2211 break; |
|
2212 default: |
|
2213 return false; |
|
2214 } |
|
2215 return elementTypeCode == baseTypeCode; |
|
2216 } |
|
2217 |
|
2218 // Implicitly convert jsval 'val' to a C binary representation of CType |
|
2219 // 'targetType', storing the result in 'buffer'. Adequate space must be |
|
2220 // provided in 'buffer' by the caller. This function generally does minimal |
|
2221 // coercion between types. There are two cases in which this function is used: |
|
2222 // 1) The target buffer is internal to a CData object; we simply write data |
|
2223 // into it. |
|
2224 // 2) We are converting an argument for an ffi call, in which case 'isArgument' |
|
2225 // will be true. This allows us to handle a special case: if necessary, |
|
2226 // we can autoconvert a JS string primitive to a pointer-to-character type. |
|
2227 // In this case, ownership of the allocated string is handed off to the |
|
2228 // caller; 'freePointer' will be set to indicate this. |
|
2229 static bool |
|
2230 ImplicitConvert(JSContext* cx, |
|
2231 HandleValue val, |
|
2232 JSObject* targetType_, |
|
2233 void* buffer, |
|
2234 bool isArgument, |
|
2235 bool* freePointer) |
|
2236 { |
|
2237 RootedObject targetType(cx, targetType_); |
|
2238 JS_ASSERT(CType::IsSizeDefined(targetType)); |
|
2239 |
|
2240 // First, check if val is either a CData object or a CDataFinalizer |
|
2241 // of type targetType. |
|
2242 JSObject* sourceData = nullptr; |
|
2243 JSObject* sourceType = nullptr; |
|
2244 RootedObject valObj(cx, nullptr); |
|
2245 if (!JSVAL_IS_PRIMITIVE(val)) { |
|
2246 valObj = JSVAL_TO_OBJECT(val); |
|
2247 if (CData::IsCData(valObj)) { |
|
2248 sourceData = valObj; |
|
2249 sourceType = CData::GetCType(sourceData); |
|
2250 |
|
2251 // If the types are equal, copy the buffer contained within the CData. |
|
2252 // (Note that the buffers may overlap partially or completely.) |
|
2253 if (CType::TypesEqual(sourceType, targetType)) { |
|
2254 size_t size = CType::GetSize(sourceType); |
|
2255 memmove(buffer, CData::GetData(sourceData), size); |
|
2256 return true; |
|
2257 } |
|
2258 } else if (CDataFinalizer::IsCDataFinalizer(valObj)) { |
|
2259 sourceData = valObj; |
|
2260 sourceType = CDataFinalizer::GetCType(cx, sourceData); |
|
2261 |
|
2262 CDataFinalizer::Private *p = (CDataFinalizer::Private *) |
|
2263 JS_GetPrivate(sourceData); |
|
2264 |
|
2265 if (!p) { |
|
2266 // We have called |dispose| or |forget| already. |
|
2267 JS_ReportError(cx, "Attempting to convert an empty CDataFinalizer"); |
|
2268 return false; |
|
2269 } |
|
2270 |
|
2271 // If the types are equal, copy the buffer contained within the CData. |
|
2272 if (CType::TypesEqual(sourceType, targetType)) { |
|
2273 memmove(buffer, p->cargs, p->cargs_size); |
|
2274 return true; |
|
2275 } |
|
2276 } |
|
2277 } |
|
2278 |
|
2279 TypeCode targetCode = CType::GetTypeCode(targetType); |
|
2280 |
|
2281 switch (targetCode) { |
|
2282 case TYPE_bool: { |
|
2283 // Do not implicitly lose bits, but allow the values 0, 1, and -0. |
|
2284 // Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`. |
|
2285 bool result; |
|
2286 if (!jsvalToBool(cx, val, &result)) |
|
2287 return TypeError(cx, "boolean", val); |
|
2288 *static_cast<bool*>(buffer) = result; |
|
2289 break; |
|
2290 } |
|
2291 #define DEFINE_INT_TYPE(name, type, ffiType) \ |
|
2292 case TYPE_##name: { \ |
|
2293 /* Do not implicitly lose bits. */ \ |
|
2294 type result; \ |
|
2295 if (!jsvalToInteger(cx, val, &result)) \ |
|
2296 return TypeError(cx, #name, val); \ |
|
2297 *static_cast<type*>(buffer) = result; \ |
|
2298 break; \ |
|
2299 } |
|
2300 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
2301 #define DEFINE_FLOAT_TYPE(name, type, ffiType) \ |
|
2302 case TYPE_##name: { \ |
|
2303 type result; \ |
|
2304 if (!jsvalToFloat(cx, val, &result)) \ |
|
2305 return TypeError(cx, #name, val); \ |
|
2306 *static_cast<type*>(buffer) = result; \ |
|
2307 break; \ |
|
2308 } |
|
2309 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
2310 #define DEFINE_JSCHAR_TYPE(name, type, ffiType) \ |
|
2311 case TYPE_##name: { \ |
|
2312 /* Convert from a 1-character string, regardless of encoding, */ \ |
|
2313 /* or from an integer, provided the result fits in 'type'. */ \ |
|
2314 type result; \ |
|
2315 if (JSVAL_IS_STRING(val)) { \ |
|
2316 JSString* str = JSVAL_TO_STRING(val); \ |
|
2317 if (str->length() != 1) \ |
|
2318 return TypeError(cx, #name, val); \ |
|
2319 const jschar *chars = str->getChars(cx); \ |
|
2320 if (!chars) \ |
|
2321 return false; \ |
|
2322 result = chars[0]; \ |
|
2323 } else if (!jsvalToInteger(cx, val, &result)) { \ |
|
2324 return TypeError(cx, #name, val); \ |
|
2325 } \ |
|
2326 *static_cast<type*>(buffer) = result; \ |
|
2327 break; \ |
|
2328 } |
|
2329 #include "ctypes/typedefs.h" |
|
2330 case TYPE_pointer: { |
|
2331 if (JSVAL_IS_NULL(val)) { |
|
2332 // Convert to a null pointer. |
|
2333 *static_cast<void**>(buffer) = nullptr; |
|
2334 break; |
|
2335 } |
|
2336 |
|
2337 JS::Rooted<JSObject*> baseType(cx, PointerType::GetBaseType(targetType)); |
|
2338 if (sourceData) { |
|
2339 // First, determine if the targetType is ctypes.void_t.ptr. |
|
2340 TypeCode sourceCode = CType::GetTypeCode(sourceType); |
|
2341 void* sourceBuffer = CData::GetData(sourceData); |
|
2342 bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t; |
|
2343 |
|
2344 if (sourceCode == TYPE_pointer && voidptrTarget) { |
|
2345 // Autoconvert if targetType is ctypes.voidptr_t. |
|
2346 *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer); |
|
2347 break; |
|
2348 } |
|
2349 if (sourceCode == TYPE_array) { |
|
2350 // Autoconvert an array to a ctypes.void_t.ptr or to |
|
2351 // sourceType.elementType.ptr, just like C. |
|
2352 JSObject* elementType = ArrayType::GetBaseType(sourceType); |
|
2353 if (voidptrTarget || CType::TypesEqual(baseType, elementType)) { |
|
2354 *static_cast<void**>(buffer) = sourceBuffer; |
|
2355 break; |
|
2356 } |
|
2357 } |
|
2358 |
|
2359 } else if (isArgument && JSVAL_IS_STRING(val)) { |
|
2360 // Convert the string for the ffi call. This requires allocating space |
|
2361 // which the caller assumes ownership of. |
|
2362 // TODO: Extend this so we can safely convert strings at other times also. |
|
2363 JSString* sourceString = JSVAL_TO_STRING(val); |
|
2364 size_t sourceLength = sourceString->length(); |
|
2365 const jschar* sourceChars = sourceString->getChars(cx); |
|
2366 if (!sourceChars) |
|
2367 return false; |
|
2368 |
|
2369 switch (CType::GetTypeCode(baseType)) { |
|
2370 case TYPE_char: |
|
2371 case TYPE_signed_char: |
|
2372 case TYPE_unsigned_char: { |
|
2373 // Convert from UTF-16 to UTF-8. |
|
2374 size_t nbytes = |
|
2375 GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); |
|
2376 if (nbytes == (size_t) -1) |
|
2377 return false; |
|
2378 |
|
2379 char** charBuffer = static_cast<char**>(buffer); |
|
2380 *charBuffer = cx->pod_malloc<char>(nbytes + 1); |
|
2381 if (!*charBuffer) { |
|
2382 JS_ReportAllocationOverflow(cx); |
|
2383 return false; |
|
2384 } |
|
2385 |
|
2386 ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, |
|
2387 *charBuffer, &nbytes)); |
|
2388 (*charBuffer)[nbytes] = 0; |
|
2389 *freePointer = true; |
|
2390 break; |
|
2391 } |
|
2392 case TYPE_jschar: { |
|
2393 // Copy the jschar string data. (We could provide direct access to the |
|
2394 // JSString's buffer, but this approach is safer if the caller happens |
|
2395 // to modify the string.) |
|
2396 jschar** jscharBuffer = static_cast<jschar**>(buffer); |
|
2397 *jscharBuffer = cx->pod_malloc<jschar>(sourceLength + 1); |
|
2398 if (!*jscharBuffer) { |
|
2399 JS_ReportAllocationOverflow(cx); |
|
2400 return false; |
|
2401 } |
|
2402 |
|
2403 *freePointer = true; |
|
2404 memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar)); |
|
2405 (*jscharBuffer)[sourceLength] = 0; |
|
2406 break; |
|
2407 } |
|
2408 default: |
|
2409 return TypeError(cx, "string pointer", val); |
|
2410 } |
|
2411 break; |
|
2412 } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayBufferObject(valObj)) { |
|
2413 // Convert ArrayBuffer to pointer without any copy. |
|
2414 // Just as with C arrays, we make no effort to |
|
2415 // keep the ArrayBuffer alive. |
|
2416 void* p = JS_GetStableArrayBufferData(cx, valObj); |
|
2417 if (!p) |
|
2418 return false; |
|
2419 *static_cast<void**>(buffer) = p; |
|
2420 break; |
|
2421 } if (!JSVAL_IS_PRIMITIVE(val) && JS_IsTypedArrayObject(valObj)) { |
|
2422 if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) { |
|
2423 return TypeError(cx, "typed array with the appropriate type", val); |
|
2424 } |
|
2425 |
|
2426 // Convert TypedArray to pointer without any copy. |
|
2427 // Just as with C arrays, we make no effort to |
|
2428 // keep the TypedArray alive. |
|
2429 *static_cast<void**>(buffer) = JS_GetArrayBufferViewData(valObj); |
|
2430 break; |
|
2431 } |
|
2432 return TypeError(cx, "pointer", val); |
|
2433 } |
|
2434 case TYPE_array: { |
|
2435 RootedObject baseType(cx, ArrayType::GetBaseType(targetType)); |
|
2436 size_t targetLength = ArrayType::GetLength(targetType); |
|
2437 |
|
2438 if (JSVAL_IS_STRING(val)) { |
|
2439 JSString* sourceString = JSVAL_TO_STRING(val); |
|
2440 size_t sourceLength = sourceString->length(); |
|
2441 const jschar* sourceChars = sourceString->getChars(cx); |
|
2442 if (!sourceChars) |
|
2443 return false; |
|
2444 |
|
2445 switch (CType::GetTypeCode(baseType)) { |
|
2446 case TYPE_char: |
|
2447 case TYPE_signed_char: |
|
2448 case TYPE_unsigned_char: { |
|
2449 // Convert from UTF-16 to UTF-8. |
|
2450 size_t nbytes = |
|
2451 GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); |
|
2452 if (nbytes == (size_t) -1) |
|
2453 return false; |
|
2454 |
|
2455 if (targetLength < nbytes) { |
|
2456 JS_ReportError(cx, "ArrayType has insufficient length"); |
|
2457 return false; |
|
2458 } |
|
2459 |
|
2460 char* charBuffer = static_cast<char*>(buffer); |
|
2461 ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, |
|
2462 charBuffer, &nbytes)); |
|
2463 |
|
2464 if (targetLength > nbytes) |
|
2465 charBuffer[nbytes] = 0; |
|
2466 |
|
2467 break; |
|
2468 } |
|
2469 case TYPE_jschar: { |
|
2470 // Copy the string data, jschar for jschar, including the terminator |
|
2471 // if there's space. |
|
2472 if (targetLength < sourceLength) { |
|
2473 JS_ReportError(cx, "ArrayType has insufficient length"); |
|
2474 return false; |
|
2475 } |
|
2476 |
|
2477 memcpy(buffer, sourceChars, sourceLength * sizeof(jschar)); |
|
2478 if (targetLength > sourceLength) |
|
2479 static_cast<jschar*>(buffer)[sourceLength] = 0; |
|
2480 |
|
2481 break; |
|
2482 } |
|
2483 default: |
|
2484 return TypeError(cx, "array", val); |
|
2485 } |
|
2486 |
|
2487 } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayObject(cx, valObj)) { |
|
2488 // Convert each element of the array by calling ImplicitConvert. |
|
2489 uint32_t sourceLength; |
|
2490 if (!JS_GetArrayLength(cx, valObj, &sourceLength) || |
|
2491 targetLength != size_t(sourceLength)) { |
|
2492 JS_ReportError(cx, "ArrayType length does not match source array length"); |
|
2493 return false; |
|
2494 } |
|
2495 |
|
2496 // Convert into an intermediate, in case of failure. |
|
2497 size_t elementSize = CType::GetSize(baseType); |
|
2498 size_t arraySize = elementSize * targetLength; |
|
2499 AutoPtr<char> intermediate(cx->pod_malloc<char>(arraySize)); |
|
2500 if (!intermediate) { |
|
2501 JS_ReportAllocationOverflow(cx); |
|
2502 return false; |
|
2503 } |
|
2504 |
|
2505 for (uint32_t i = 0; i < sourceLength; ++i) { |
|
2506 RootedValue item(cx); |
|
2507 if (!JS_GetElement(cx, valObj, i, &item)) |
|
2508 return false; |
|
2509 |
|
2510 char* data = intermediate.get() + elementSize * i; |
|
2511 if (!ImplicitConvert(cx, item, baseType, data, false, nullptr)) |
|
2512 return false; |
|
2513 } |
|
2514 |
|
2515 memcpy(buffer, intermediate.get(), arraySize); |
|
2516 |
|
2517 } else if (!JSVAL_IS_PRIMITIVE(val) && |
|
2518 JS_IsArrayBufferObject(valObj)) { |
|
2519 // Check that array is consistent with type, then |
|
2520 // copy the array. |
|
2521 uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj); |
|
2522 size_t elementSize = CType::GetSize(baseType); |
|
2523 size_t arraySize = elementSize * targetLength; |
|
2524 if (arraySize != size_t(sourceLength)) { |
|
2525 JS_ReportError(cx, "ArrayType length does not match source ArrayBuffer length"); |
|
2526 return false; |
|
2527 } |
|
2528 memcpy(buffer, JS_GetArrayBufferData(valObj), sourceLength); |
|
2529 break; |
|
2530 } else if (!JSVAL_IS_PRIMITIVE(val) && |
|
2531 JS_IsTypedArrayObject(valObj)) { |
|
2532 // Check that array is consistent with type, then |
|
2533 // copy the array. |
|
2534 if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) { |
|
2535 return TypeError(cx, "typed array with the appropriate type", val); |
|
2536 } |
|
2537 |
|
2538 uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj); |
|
2539 size_t elementSize = CType::GetSize(baseType); |
|
2540 size_t arraySize = elementSize * targetLength; |
|
2541 if (arraySize != size_t(sourceLength)) { |
|
2542 JS_ReportError(cx, "typed array length does not match source TypedArray length"); |
|
2543 return false; |
|
2544 } |
|
2545 memcpy(buffer, JS_GetArrayBufferViewData(valObj), sourceLength); |
|
2546 break; |
|
2547 } else { |
|
2548 // Don't implicitly convert to string. Users can implicitly convert |
|
2549 // with `String(x)` or `""+x`. |
|
2550 return TypeError(cx, "array", val); |
|
2551 } |
|
2552 break; |
|
2553 } |
|
2554 case TYPE_struct: { |
|
2555 if (!JSVAL_IS_PRIMITIVE(val) && !sourceData) { |
|
2556 // Enumerate the properties of the object; if they match the struct |
|
2557 // specification, convert the fields. |
|
2558 RootedObject iter(cx, JS_NewPropertyIterator(cx, valObj)); |
|
2559 if (!iter) |
|
2560 return false; |
|
2561 |
|
2562 // Convert into an intermediate, in case of failure. |
|
2563 size_t structSize = CType::GetSize(targetType); |
|
2564 AutoPtr<char> intermediate(cx->pod_malloc<char>(structSize)); |
|
2565 if (!intermediate) { |
|
2566 JS_ReportAllocationOverflow(cx); |
|
2567 return false; |
|
2568 } |
|
2569 |
|
2570 RootedId id(cx); |
|
2571 size_t i = 0; |
|
2572 while (1) { |
|
2573 if (!JS_NextProperty(cx, iter, id.address())) |
|
2574 return false; |
|
2575 if (JSID_IS_VOID(id)) |
|
2576 break; |
|
2577 |
|
2578 if (!JSID_IS_STRING(id)) { |
|
2579 JS_ReportError(cx, "property name is not a string"); |
|
2580 return false; |
|
2581 } |
|
2582 |
|
2583 JSFlatString *name = JSID_TO_FLAT_STRING(id); |
|
2584 const FieldInfo* field = StructType::LookupField(cx, targetType, name); |
|
2585 if (!field) |
|
2586 return false; |
|
2587 |
|
2588 RootedValue prop(cx); |
|
2589 if (!JS_GetPropertyById(cx, valObj, id, &prop)) |
|
2590 return false; |
|
2591 |
|
2592 // Convert the field via ImplicitConvert(). |
|
2593 char* fieldData = intermediate.get() + field->mOffset; |
|
2594 if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, nullptr)) |
|
2595 return false; |
|
2596 |
|
2597 ++i; |
|
2598 } |
|
2599 |
|
2600 const FieldInfoHash* fields = StructType::GetFieldInfo(targetType); |
|
2601 if (i != fields->count()) { |
|
2602 JS_ReportError(cx, "missing fields"); |
|
2603 return false; |
|
2604 } |
|
2605 |
|
2606 memcpy(buffer, intermediate.get(), structSize); |
|
2607 break; |
|
2608 } |
|
2609 |
|
2610 return TypeError(cx, "struct", val); |
|
2611 } |
|
2612 case TYPE_void_t: |
|
2613 case TYPE_function: |
|
2614 MOZ_ASSUME_UNREACHABLE("invalid type"); |
|
2615 } |
|
2616 |
|
2617 return true; |
|
2618 } |
|
2619 |
|
2620 // Convert jsval 'val' to a C binary representation of CType 'targetType', |
|
2621 // storing the result in 'buffer'. This function is more forceful than |
|
2622 // ImplicitConvert. |
|
2623 static bool |
|
2624 ExplicitConvert(JSContext* cx, HandleValue val, HandleObject targetType, void* buffer) |
|
2625 { |
|
2626 // If ImplicitConvert succeeds, use that result. |
|
2627 if (ImplicitConvert(cx, val, targetType, buffer, false, nullptr)) |
|
2628 return true; |
|
2629 |
|
2630 // If ImplicitConvert failed, and there is no pending exception, then assume |
|
2631 // hard failure (out of memory, or some other similarly serious condition). |
|
2632 // We store any pending exception in case we need to re-throw it. |
|
2633 RootedValue ex(cx); |
|
2634 if (!JS_GetPendingException(cx, &ex)) |
|
2635 return false; |
|
2636 |
|
2637 // Otherwise, assume soft failure. Clear the pending exception so that we |
|
2638 // can throw a different one as required. |
|
2639 JS_ClearPendingException(cx); |
|
2640 |
|
2641 TypeCode type = CType::GetTypeCode(targetType); |
|
2642 |
|
2643 switch (type) { |
|
2644 case TYPE_bool: { |
|
2645 *static_cast<bool*>(buffer) = ToBoolean(val); |
|
2646 break; |
|
2647 } |
|
2648 #define DEFINE_INT_TYPE(name, type, ffiType) \ |
|
2649 case TYPE_##name: { \ |
|
2650 /* Convert numeric values with a C-style cast, and */ \ |
|
2651 /* allow conversion from a base-10 or base-16 string. */ \ |
|
2652 type result; \ |
|
2653 if (!jsvalToIntegerExplicit(val, &result) && \ |
|
2654 (!JSVAL_IS_STRING(val) || \ |
|
2655 !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \ |
|
2656 return TypeError(cx, #name, val); \ |
|
2657 *static_cast<type*>(buffer) = result; \ |
|
2658 break; \ |
|
2659 } |
|
2660 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
2661 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
2662 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_CHAR_TYPE(x, y, z) |
|
2663 #include "ctypes/typedefs.h" |
|
2664 case TYPE_pointer: { |
|
2665 // Convert a number, Int64 object, or UInt64 object to a pointer. |
|
2666 uintptr_t result; |
|
2667 if (!jsvalToPtrExplicit(cx, val, &result)) |
|
2668 return TypeError(cx, "pointer", val); |
|
2669 *static_cast<uintptr_t*>(buffer) = result; |
|
2670 break; |
|
2671 } |
|
2672 case TYPE_float32_t: |
|
2673 case TYPE_float64_t: |
|
2674 case TYPE_float: |
|
2675 case TYPE_double: |
|
2676 case TYPE_array: |
|
2677 case TYPE_struct: |
|
2678 // ImplicitConvert is sufficient. Re-throw the exception it generated. |
|
2679 JS_SetPendingException(cx, ex); |
|
2680 return false; |
|
2681 case TYPE_void_t: |
|
2682 case TYPE_function: |
|
2683 MOZ_ASSUME_UNREACHABLE("invalid type"); |
|
2684 } |
|
2685 return true; |
|
2686 } |
|
2687 |
|
2688 // Given a CType 'typeObj', generate a string describing the C type declaration |
|
2689 // corresponding to 'typeObj'. For instance, the CType constructed from |
|
2690 // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string |
|
2691 // 'int32_t*(**)[4]'. |
|
2692 static JSString* |
|
2693 BuildTypeName(JSContext* cx, JSObject* typeObj_) |
|
2694 { |
|
2695 AutoString result; |
|
2696 RootedObject typeObj(cx, typeObj_); |
|
2697 |
|
2698 // Walk the hierarchy of types, outermost to innermost, building up the type |
|
2699 // string. This consists of the base type, which goes on the left. |
|
2700 // Derived type modifiers (* and []) build from the inside outward, with |
|
2701 // pointers on the left and arrays on the right. An excellent description |
|
2702 // of the rules for building C type declarations can be found at: |
|
2703 // http://unixwiz.net/techtips/reading-cdecl.html |
|
2704 TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping; |
|
2705 while (1) { |
|
2706 currentGrouping = CType::GetTypeCode(typeObj); |
|
2707 switch (currentGrouping) { |
|
2708 case TYPE_pointer: { |
|
2709 // Pointer types go on the left. |
|
2710 PrependString(result, "*"); |
|
2711 |
|
2712 typeObj = PointerType::GetBaseType(typeObj); |
|
2713 prevGrouping = currentGrouping; |
|
2714 continue; |
|
2715 } |
|
2716 case TYPE_array: { |
|
2717 if (prevGrouping == TYPE_pointer) { |
|
2718 // Outer type is pointer, inner type is array. Grouping is required. |
|
2719 PrependString(result, "("); |
|
2720 AppendString(result, ")"); |
|
2721 } |
|
2722 |
|
2723 // Array types go on the right. |
|
2724 AppendString(result, "["); |
|
2725 size_t length; |
|
2726 if (ArrayType::GetSafeLength(typeObj, &length)) |
|
2727 IntegerToString(length, 10, result); |
|
2728 |
|
2729 AppendString(result, "]"); |
|
2730 |
|
2731 typeObj = ArrayType::GetBaseType(typeObj); |
|
2732 prevGrouping = currentGrouping; |
|
2733 continue; |
|
2734 } |
|
2735 case TYPE_function: { |
|
2736 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); |
|
2737 |
|
2738 // Add in the calling convention, if it's not cdecl. |
|
2739 // There's no trailing or leading space needed here, as none of the |
|
2740 // modifiers can produce a string beginning with an identifier --- |
|
2741 // except for TYPE_function itself, which is fine because functions |
|
2742 // can't return functions. |
|
2743 ABICode abi = GetABICode(fninfo->mABI); |
|
2744 if (abi == ABI_STDCALL) |
|
2745 PrependString(result, "__stdcall"); |
|
2746 else if (abi == ABI_WINAPI) |
|
2747 PrependString(result, "WINAPI"); |
|
2748 |
|
2749 // Function application binds more tightly than dereferencing, so |
|
2750 // wrap pointer types in parens. Functions can't return functions |
|
2751 // (only pointers to them), and arrays can't hold functions |
|
2752 // (similarly), so we don't need to address those cases. |
|
2753 if (prevGrouping == TYPE_pointer) { |
|
2754 PrependString(result, "("); |
|
2755 AppendString(result, ")"); |
|
2756 } |
|
2757 |
|
2758 // Argument list goes on the right. |
|
2759 AppendString(result, "("); |
|
2760 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { |
|
2761 RootedObject argType(cx, fninfo->mArgTypes[i]); |
|
2762 JSString* argName = CType::GetName(cx, argType); |
|
2763 AppendString(result, argName); |
|
2764 if (i != fninfo->mArgTypes.length() - 1 || |
|
2765 fninfo->mIsVariadic) |
|
2766 AppendString(result, ", "); |
|
2767 } |
|
2768 if (fninfo->mIsVariadic) |
|
2769 AppendString(result, "..."); |
|
2770 AppendString(result, ")"); |
|
2771 |
|
2772 // Set 'typeObj' to the return type, and let the loop process it. |
|
2773 // 'prevGrouping' doesn't matter here, because functions cannot return |
|
2774 // arrays -- thus the parenthetical rules don't get tickled. |
|
2775 typeObj = fninfo->mReturnType; |
|
2776 continue; |
|
2777 } |
|
2778 default: |
|
2779 // Either a basic or struct type. Use the type's name as the base type. |
|
2780 break; |
|
2781 } |
|
2782 break; |
|
2783 } |
|
2784 |
|
2785 // If prepending the base type name directly would splice two |
|
2786 // identifiers, insert a space. |
|
2787 if (('a' <= result[0] && result[0] <= 'z') || |
|
2788 ('A' <= result[0] && result[0] <= 'Z') || |
|
2789 (result[0] == '_')) |
|
2790 PrependString(result, " "); |
|
2791 |
|
2792 // Stick the base type and derived type parts together. |
|
2793 JSString* baseName = CType::GetName(cx, typeObj); |
|
2794 PrependString(result, baseName); |
|
2795 return NewUCString(cx, result); |
|
2796 } |
|
2797 |
|
2798 // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)' |
|
2799 // would construct the same CType. If 'makeShort' is true, assume that any |
|
2800 // StructType 't' is bound to an in-scope variable of name 't.name', and use |
|
2801 // that variable in place of generating a string to construct the type 't'. |
|
2802 // (This means the type comparison function CType::TypesEqual will return true |
|
2803 // when comparing the input and output of BuildTypeSource, since struct |
|
2804 // equality is determined by strict JSObject pointer equality.) |
|
2805 static void |
|
2806 BuildTypeSource(JSContext* cx, |
|
2807 JSObject* typeObj_, |
|
2808 bool makeShort, |
|
2809 AutoString& result) |
|
2810 { |
|
2811 RootedObject typeObj(cx, typeObj_); |
|
2812 |
|
2813 // Walk the types, building up the toSource() string. |
|
2814 switch (CType::GetTypeCode(typeObj)) { |
|
2815 case TYPE_void_t: |
|
2816 #define DEFINE_TYPE(name, type, ffiType) \ |
|
2817 case TYPE_##name: |
|
2818 #include "ctypes/typedefs.h" |
|
2819 { |
|
2820 AppendString(result, "ctypes."); |
|
2821 JSString* nameStr = CType::GetName(cx, typeObj); |
|
2822 AppendString(result, nameStr); |
|
2823 break; |
|
2824 } |
|
2825 case TYPE_pointer: { |
|
2826 RootedObject baseType(cx, PointerType::GetBaseType(typeObj)); |
|
2827 |
|
2828 // Specialcase ctypes.voidptr_t. |
|
2829 if (CType::GetTypeCode(baseType) == TYPE_void_t) { |
|
2830 AppendString(result, "ctypes.voidptr_t"); |
|
2831 break; |
|
2832 } |
|
2833 |
|
2834 // Recursively build the source string, and append '.ptr'. |
|
2835 BuildTypeSource(cx, baseType, makeShort, result); |
|
2836 AppendString(result, ".ptr"); |
|
2837 break; |
|
2838 } |
|
2839 case TYPE_function: { |
|
2840 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); |
|
2841 |
|
2842 AppendString(result, "ctypes.FunctionType("); |
|
2843 |
|
2844 switch (GetABICode(fninfo->mABI)) { |
|
2845 case ABI_DEFAULT: |
|
2846 AppendString(result, "ctypes.default_abi, "); |
|
2847 break; |
|
2848 case ABI_STDCALL: |
|
2849 AppendString(result, "ctypes.stdcall_abi, "); |
|
2850 break; |
|
2851 case ABI_WINAPI: |
|
2852 AppendString(result, "ctypes.winapi_abi, "); |
|
2853 break; |
|
2854 case INVALID_ABI: |
|
2855 MOZ_ASSUME_UNREACHABLE("invalid abi"); |
|
2856 } |
|
2857 |
|
2858 // Recursively build the source string describing the function return and |
|
2859 // argument types. |
|
2860 BuildTypeSource(cx, fninfo->mReturnType, true, result); |
|
2861 |
|
2862 if (fninfo->mArgTypes.length() > 0) { |
|
2863 AppendString(result, ", ["); |
|
2864 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { |
|
2865 BuildTypeSource(cx, fninfo->mArgTypes[i], true, result); |
|
2866 if (i != fninfo->mArgTypes.length() - 1 || |
|
2867 fninfo->mIsVariadic) |
|
2868 AppendString(result, ", "); |
|
2869 } |
|
2870 if (fninfo->mIsVariadic) |
|
2871 AppendString(result, "\"...\""); |
|
2872 AppendString(result, "]"); |
|
2873 } |
|
2874 |
|
2875 AppendString(result, ")"); |
|
2876 break; |
|
2877 } |
|
2878 case TYPE_array: { |
|
2879 // Recursively build the source string, and append '.array(n)', |
|
2880 // where n is the array length, or the empty string if the array length |
|
2881 // is undefined. |
|
2882 JSObject* baseType = ArrayType::GetBaseType(typeObj); |
|
2883 BuildTypeSource(cx, baseType, makeShort, result); |
|
2884 AppendString(result, ".array("); |
|
2885 |
|
2886 size_t length; |
|
2887 if (ArrayType::GetSafeLength(typeObj, &length)) |
|
2888 IntegerToString(length, 10, result); |
|
2889 |
|
2890 AppendString(result, ")"); |
|
2891 break; |
|
2892 } |
|
2893 case TYPE_struct: { |
|
2894 JSString* name = CType::GetName(cx, typeObj); |
|
2895 |
|
2896 if (makeShort) { |
|
2897 // Shorten the type declaration by assuming that StructType 't' is bound |
|
2898 // to an in-scope variable of name 't.name'. |
|
2899 AppendString(result, name); |
|
2900 break; |
|
2901 } |
|
2902 |
|
2903 // Write the full struct declaration. |
|
2904 AppendString(result, "ctypes.StructType(\""); |
|
2905 AppendString(result, name); |
|
2906 AppendString(result, "\""); |
|
2907 |
|
2908 // If it's an opaque struct, we're done. |
|
2909 if (!CType::IsSizeDefined(typeObj)) { |
|
2910 AppendString(result, ")"); |
|
2911 break; |
|
2912 } |
|
2913 |
|
2914 AppendString(result, ", ["); |
|
2915 |
|
2916 const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); |
|
2917 size_t length = fields->count(); |
|
2918 Array<const FieldInfoHash::Entry*, 64> fieldsArray; |
|
2919 if (!fieldsArray.resize(length)) |
|
2920 break; |
|
2921 |
|
2922 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) |
|
2923 fieldsArray[r.front().value().mIndex] = &r.front(); |
|
2924 |
|
2925 for (size_t i = 0; i < length; ++i) { |
|
2926 const FieldInfoHash::Entry* entry = fieldsArray[i]; |
|
2927 AppendString(result, "{ \""); |
|
2928 AppendString(result, entry->key()); |
|
2929 AppendString(result, "\": "); |
|
2930 BuildTypeSource(cx, entry->value().mType, true, result); |
|
2931 AppendString(result, " }"); |
|
2932 if (i != length - 1) |
|
2933 AppendString(result, ", "); |
|
2934 } |
|
2935 |
|
2936 AppendString(result, "])"); |
|
2937 break; |
|
2938 } |
|
2939 } |
|
2940 } |
|
2941 |
|
2942 // Given a CData object of CType 'typeObj' with binary value 'data', generate a |
|
2943 // string 'result' such that 'eval(result)' would construct a CData object with |
|
2944 // the same CType and containing the same binary value. This assumes that any |
|
2945 // StructType 't' is bound to an in-scope variable of name 't.name'. (This means |
|
2946 // the type comparison function CType::TypesEqual will return true when |
|
2947 // comparing the types, since struct equality is determined by strict JSObject |
|
2948 // pointer equality.) Further, if 'isImplicit' is true, ensure that the |
|
2949 // resulting string can ImplicitConvert successfully if passed to another data |
|
2950 // constructor. (This is important when called recursively, since fields of |
|
2951 // structs and arrays are converted with ImplicitConvert.) |
|
2952 static bool |
|
2953 BuildDataSource(JSContext* cx, |
|
2954 HandleObject typeObj, |
|
2955 void* data, |
|
2956 bool isImplicit, |
|
2957 AutoString& result) |
|
2958 { |
|
2959 TypeCode type = CType::GetTypeCode(typeObj); |
|
2960 switch (type) { |
|
2961 case TYPE_bool: |
|
2962 if (*static_cast<bool*>(data)) |
|
2963 AppendString(result, "true"); |
|
2964 else |
|
2965 AppendString(result, "false"); |
|
2966 break; |
|
2967 #define DEFINE_INT_TYPE(name, type, ffiType) \ |
|
2968 case TYPE_##name: \ |
|
2969 /* Serialize as a primitive decimal integer. */ \ |
|
2970 IntegerToString(*static_cast<type*>(data), 10, result); \ |
|
2971 break; |
|
2972 #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \ |
|
2973 case TYPE_##name: \ |
|
2974 /* Serialize as a wrapped decimal integer. */ \ |
|
2975 if (!NumericLimits<type>::is_signed) \ |
|
2976 AppendString(result, "ctypes.UInt64(\""); \ |
|
2977 else \ |
|
2978 AppendString(result, "ctypes.Int64(\""); \ |
|
2979 \ |
|
2980 IntegerToString(*static_cast<type*>(data), 10, result); \ |
|
2981 AppendString(result, "\")"); \ |
|
2982 break; |
|
2983 #define DEFINE_FLOAT_TYPE(name, type, ffiType) \ |
|
2984 case TYPE_##name: { \ |
|
2985 /* Serialize as a primitive double. */ \ |
|
2986 double fp = *static_cast<type*>(data); \ |
|
2987 ToCStringBuf cbuf; \ |
|
2988 char* str = NumberToCString(cx, &cbuf, fp); \ |
|
2989 if (!str) { \ |
|
2990 JS_ReportOutOfMemory(cx); \ |
|
2991 return false; \ |
|
2992 } \ |
|
2993 \ |
|
2994 result.append(str, strlen(str)); \ |
|
2995 break; \ |
|
2996 } |
|
2997 #define DEFINE_CHAR_TYPE(name, type, ffiType) \ |
|
2998 case TYPE_##name: \ |
|
2999 /* Serialize as an integer. */ \ |
|
3000 IntegerToString(*static_cast<type*>(data), 10, result); \ |
|
3001 break; |
|
3002 #include "ctypes/typedefs.h" |
|
3003 case TYPE_jschar: { |
|
3004 // Serialize as a 1-character JS string. |
|
3005 JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1); |
|
3006 if (!str) |
|
3007 return false; |
|
3008 |
|
3009 // Escape characters, and quote as necessary. |
|
3010 RootedValue valStr(cx, StringValue(str)); |
|
3011 JSString* src = JS_ValueToSource(cx, valStr); |
|
3012 if (!src) |
|
3013 return false; |
|
3014 |
|
3015 AppendString(result, src); |
|
3016 break; |
|
3017 } |
|
3018 case TYPE_pointer: |
|
3019 case TYPE_function: { |
|
3020 if (isImplicit) { |
|
3021 // The result must be able to ImplicitConvert successfully. |
|
3022 // Wrap in a type constructor, then serialize for ExplicitConvert. |
|
3023 BuildTypeSource(cx, typeObj, true, result); |
|
3024 AppendString(result, "("); |
|
3025 } |
|
3026 |
|
3027 // Serialize the pointer value as a wrapped hexadecimal integer. |
|
3028 uintptr_t ptr = *static_cast<uintptr_t*>(data); |
|
3029 AppendString(result, "ctypes.UInt64(\"0x"); |
|
3030 IntegerToString(ptr, 16, result); |
|
3031 AppendString(result, "\")"); |
|
3032 |
|
3033 if (isImplicit) |
|
3034 AppendString(result, ")"); |
|
3035 |
|
3036 break; |
|
3037 } |
|
3038 case TYPE_array: { |
|
3039 // Serialize each element of the array recursively. Each element must |
|
3040 // be able to ImplicitConvert successfully. |
|
3041 RootedObject baseType(cx, ArrayType::GetBaseType(typeObj)); |
|
3042 AppendString(result, "["); |
|
3043 |
|
3044 size_t length = ArrayType::GetLength(typeObj); |
|
3045 size_t elementSize = CType::GetSize(baseType); |
|
3046 for (size_t i = 0; i < length; ++i) { |
|
3047 char* element = static_cast<char*>(data) + elementSize * i; |
|
3048 if (!BuildDataSource(cx, baseType, element, true, result)) |
|
3049 return false; |
|
3050 |
|
3051 if (i + 1 < length) |
|
3052 AppendString(result, ", "); |
|
3053 } |
|
3054 AppendString(result, "]"); |
|
3055 break; |
|
3056 } |
|
3057 case TYPE_struct: { |
|
3058 if (isImplicit) { |
|
3059 // The result must be able to ImplicitConvert successfully. |
|
3060 // Serialize the data as an object with properties, rather than |
|
3061 // a sequence of arguments to the StructType constructor. |
|
3062 AppendString(result, "{"); |
|
3063 } |
|
3064 |
|
3065 // Serialize each field of the struct recursively. Each field must |
|
3066 // be able to ImplicitConvert successfully. |
|
3067 const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); |
|
3068 size_t length = fields->count(); |
|
3069 Array<const FieldInfoHash::Entry*, 64> fieldsArray; |
|
3070 if (!fieldsArray.resize(length)) |
|
3071 return false; |
|
3072 |
|
3073 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) |
|
3074 fieldsArray[r.front().value().mIndex] = &r.front(); |
|
3075 |
|
3076 for (size_t i = 0; i < length; ++i) { |
|
3077 const FieldInfoHash::Entry* entry = fieldsArray[i]; |
|
3078 |
|
3079 if (isImplicit) { |
|
3080 AppendString(result, "\""); |
|
3081 AppendString(result, entry->key()); |
|
3082 AppendString(result, "\": "); |
|
3083 } |
|
3084 |
|
3085 char* fieldData = static_cast<char*>(data) + entry->value().mOffset; |
|
3086 RootedObject entryType(cx, entry->value().mType); |
|
3087 if (!BuildDataSource(cx, entryType, fieldData, true, result)) |
|
3088 return false; |
|
3089 |
|
3090 if (i + 1 != length) |
|
3091 AppendString(result, ", "); |
|
3092 } |
|
3093 |
|
3094 if (isImplicit) |
|
3095 AppendString(result, "}"); |
|
3096 |
|
3097 break; |
|
3098 } |
|
3099 case TYPE_void_t: |
|
3100 MOZ_ASSUME_UNREACHABLE("invalid type"); |
|
3101 } |
|
3102 |
|
3103 return true; |
|
3104 } |
|
3105 |
|
3106 /******************************************************************************* |
|
3107 ** JSAPI callback function implementations |
|
3108 *******************************************************************************/ |
|
3109 |
|
3110 bool |
|
3111 ConstructAbstract(JSContext* cx, |
|
3112 unsigned argc, |
|
3113 jsval* vp) |
|
3114 { |
|
3115 // Calling an abstract base class constructor is disallowed. |
|
3116 JS_ReportError(cx, "cannot construct from abstract type"); |
|
3117 return false; |
|
3118 } |
|
3119 |
|
3120 /******************************************************************************* |
|
3121 ** CType implementation |
|
3122 *******************************************************************************/ |
|
3123 |
|
3124 bool |
|
3125 CType::ConstructData(JSContext* cx, |
|
3126 unsigned argc, |
|
3127 jsval* vp) |
|
3128 { |
|
3129 CallArgs args = CallArgsFromVp(argc, vp); |
|
3130 // get the callee object... |
|
3131 RootedObject obj(cx, &args.callee()); |
|
3132 if (!CType::IsCType(obj)) { |
|
3133 JS_ReportError(cx, "not a CType"); |
|
3134 return false; |
|
3135 } |
|
3136 |
|
3137 // How we construct the CData object depends on what type we represent. |
|
3138 // An instance 'd' of a CData object of type 't' has: |
|
3139 // * [[Class]] "CData" |
|
3140 // * __proto__ === t.prototype |
|
3141 switch (GetTypeCode(obj)) { |
|
3142 case TYPE_void_t: |
|
3143 JS_ReportError(cx, "cannot construct from void_t"); |
|
3144 return false; |
|
3145 case TYPE_function: |
|
3146 JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead"); |
|
3147 return false; |
|
3148 case TYPE_pointer: |
|
3149 return PointerType::ConstructData(cx, obj, args); |
|
3150 case TYPE_array: |
|
3151 return ArrayType::ConstructData(cx, obj, args); |
|
3152 case TYPE_struct: |
|
3153 return StructType::ConstructData(cx, obj, args); |
|
3154 default: |
|
3155 return ConstructBasic(cx, obj, args); |
|
3156 } |
|
3157 } |
|
3158 |
|
3159 bool |
|
3160 CType::ConstructBasic(JSContext* cx, |
|
3161 HandleObject obj, |
|
3162 const CallArgs& args) |
|
3163 { |
|
3164 if (args.length() > 1) { |
|
3165 JS_ReportError(cx, "CType constructor takes zero or one argument"); |
|
3166 return false; |
|
3167 } |
|
3168 |
|
3169 // construct a CData object |
|
3170 RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true)); |
|
3171 if (!result) |
|
3172 return false; |
|
3173 |
|
3174 if (args.length() == 1) { |
|
3175 if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result))) |
|
3176 return false; |
|
3177 } |
|
3178 |
|
3179 args.rval().setObject(*result); |
|
3180 return true; |
|
3181 } |
|
3182 |
|
3183 JSObject* |
|
3184 CType::Create(JSContext* cx, |
|
3185 HandleObject typeProto, |
|
3186 HandleObject dataProto, |
|
3187 TypeCode type, |
|
3188 JSString* name_, |
|
3189 jsval size_, |
|
3190 jsval align_, |
|
3191 ffi_type* ffiType) |
|
3192 { |
|
3193 RootedString name(cx, name_); |
|
3194 RootedValue size(cx, size_); |
|
3195 RootedValue align(cx, align_); |
|
3196 RootedObject parent(cx, JS_GetParent(typeProto)); |
|
3197 JS_ASSERT(parent); |
|
3198 |
|
3199 // Create a CType object with the properties and slots common to all CTypes. |
|
3200 // Each type object 't' has: |
|
3201 // * [[Class]] "CType" |
|
3202 // * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType, |
|
3203 // StructType}.prototype |
|
3204 // * A constructor which creates and returns a CData object, containing |
|
3205 // binary data of the given type. |
|
3206 // * 'prototype' property: |
|
3207 // * [[Class]] "CDataProto" |
|
3208 // * __proto__ === 'dataProto'; an object containing properties and |
|
3209 // functions common to all CData objects of types derived from |
|
3210 // 'typeProto'. (For instance, this could be ctypes.CData.prototype |
|
3211 // for simple types, or something representing structs for StructTypes.) |
|
3212 // * 'constructor' property === 't' |
|
3213 // * Additional properties specified by 'ps', as appropriate for the |
|
3214 // specific type instance 't'. |
|
3215 RootedObject typeObj(cx, JS_NewObject(cx, &sCTypeClass, typeProto, parent)); |
|
3216 if (!typeObj) |
|
3217 return nullptr; |
|
3218 |
|
3219 // Set up the reserved slots. |
|
3220 JS_SetReservedSlot(typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)); |
|
3221 if (ffiType) |
|
3222 JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType)); |
|
3223 if (name) |
|
3224 JS_SetReservedSlot(typeObj, SLOT_NAME, STRING_TO_JSVAL(name)); |
|
3225 JS_SetReservedSlot(typeObj, SLOT_SIZE, size); |
|
3226 JS_SetReservedSlot(typeObj, SLOT_ALIGN, align); |
|
3227 |
|
3228 if (dataProto) { |
|
3229 // Set up the 'prototype' and 'prototype.constructor' properties. |
|
3230 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, parent)); |
|
3231 if (!prototype) |
|
3232 return nullptr; |
|
3233 |
|
3234 if (!JS_DefineProperty(cx, prototype, "constructor", typeObj, |
|
3235 JSPROP_READONLY | JSPROP_PERMANENT)) |
|
3236 return nullptr; |
|
3237 |
|
3238 // Set the 'prototype' object. |
|
3239 //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212! |
|
3240 // return nullptr; |
|
3241 JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); |
|
3242 } |
|
3243 |
|
3244 if (!JS_FreezeObject(cx, typeObj)) |
|
3245 return nullptr; |
|
3246 |
|
3247 // Assert a sanity check on size and alignment: size % alignment should always |
|
3248 // be zero. |
|
3249 JS_ASSERT_IF(IsSizeDefined(typeObj), |
|
3250 GetSize(typeObj) % GetAlignment(typeObj) == 0); |
|
3251 |
|
3252 return typeObj; |
|
3253 } |
|
3254 |
|
3255 JSObject* |
|
3256 CType::DefineBuiltin(JSContext* cx, |
|
3257 JSObject* parent_, |
|
3258 const char* propName, |
|
3259 JSObject* typeProto_, |
|
3260 JSObject* dataProto_, |
|
3261 const char* name, |
|
3262 TypeCode type, |
|
3263 jsval size_, |
|
3264 jsval align_, |
|
3265 ffi_type* ffiType) |
|
3266 { |
|
3267 RootedObject parent(cx, parent_); |
|
3268 RootedObject typeProto(cx, typeProto_); |
|
3269 RootedObject dataProto(cx, dataProto_); |
|
3270 RootedValue size(cx, size_); |
|
3271 RootedValue align(cx, align_); |
|
3272 |
|
3273 RootedString nameStr(cx, JS_NewStringCopyZ(cx, name)); |
|
3274 if (!nameStr) |
|
3275 return nullptr; |
|
3276 |
|
3277 // Create a new CType object with the common properties and slots. |
|
3278 RootedObject typeObj(cx, Create(cx, typeProto, dataProto, type, nameStr, size, align, ffiType)); |
|
3279 if (!typeObj) |
|
3280 return nullptr; |
|
3281 |
|
3282 // Define the CType as a 'propName' property on 'parent'. |
|
3283 if (!JS_DefineProperty(cx, parent, propName, typeObj, |
|
3284 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
3285 return nullptr; |
|
3286 |
|
3287 return typeObj; |
|
3288 } |
|
3289 |
|
3290 void |
|
3291 CType::Finalize(JSFreeOp *fop, JSObject* obj) |
|
3292 { |
|
3293 // Make sure our TypeCode slot is legit. If it's not, bail. |
|
3294 jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE); |
|
3295 if (JSVAL_IS_VOID(slot)) |
|
3296 return; |
|
3297 |
|
3298 // The contents of our slots depends on what kind of type we are. |
|
3299 switch (TypeCode(JSVAL_TO_INT(slot))) { |
|
3300 case TYPE_function: { |
|
3301 // Free the FunctionInfo. |
|
3302 slot = JS_GetReservedSlot(obj, SLOT_FNINFO); |
|
3303 if (!JSVAL_IS_VOID(slot)) |
|
3304 FreeOp::get(fop)->delete_(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot))); |
|
3305 break; |
|
3306 } |
|
3307 |
|
3308 case TYPE_struct: { |
|
3309 // Free the FieldInfoHash table. |
|
3310 slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO); |
|
3311 if (!JSVAL_IS_VOID(slot)) { |
|
3312 void* info = JSVAL_TO_PRIVATE(slot); |
|
3313 FreeOp::get(fop)->delete_(static_cast<FieldInfoHash*>(info)); |
|
3314 } |
|
3315 } |
|
3316 |
|
3317 // Fall through. |
|
3318 case TYPE_array: { |
|
3319 // Free the ffi_type info. |
|
3320 slot = JS_GetReservedSlot(obj, SLOT_FFITYPE); |
|
3321 if (!JSVAL_IS_VOID(slot)) { |
|
3322 ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot)); |
|
3323 FreeOp::get(fop)->free_(ffiType->elements); |
|
3324 FreeOp::get(fop)->delete_(ffiType); |
|
3325 } |
|
3326 |
|
3327 break; |
|
3328 } |
|
3329 default: |
|
3330 // Nothing to do here. |
|
3331 break; |
|
3332 } |
|
3333 } |
|
3334 |
|
3335 void |
|
3336 CType::Trace(JSTracer* trc, JSObject* obj) |
|
3337 { |
|
3338 // Make sure our TypeCode slot is legit. If it's not, bail. |
|
3339 jsval slot = obj->getSlot(SLOT_TYPECODE); |
|
3340 if (JSVAL_IS_VOID(slot)) |
|
3341 return; |
|
3342 |
|
3343 // The contents of our slots depends on what kind of type we are. |
|
3344 switch (TypeCode(JSVAL_TO_INT(slot))) { |
|
3345 case TYPE_struct: { |
|
3346 slot = obj->getReservedSlot(SLOT_FIELDINFO); |
|
3347 if (JSVAL_IS_VOID(slot)) |
|
3348 return; |
|
3349 |
|
3350 FieldInfoHash* fields = |
|
3351 static_cast<FieldInfoHash*>(JSVAL_TO_PRIVATE(slot)); |
|
3352 for (FieldInfoHash::Enum e(*fields); !e.empty(); e.popFront()) { |
|
3353 JSString *key = e.front().key(); |
|
3354 JS_CallStringTracer(trc, &key, "fieldName"); |
|
3355 if (key != e.front().key()) |
|
3356 e.rekeyFront(JS_ASSERT_STRING_IS_FLAT(key)); |
|
3357 JS_CallHeapObjectTracer(trc, &e.front().value().mType, "fieldType"); |
|
3358 } |
|
3359 |
|
3360 break; |
|
3361 } |
|
3362 case TYPE_function: { |
|
3363 // Check if we have a FunctionInfo. |
|
3364 slot = obj->getReservedSlot(SLOT_FNINFO); |
|
3365 if (JSVAL_IS_VOID(slot)) |
|
3366 return; |
|
3367 |
|
3368 FunctionInfo* fninfo = static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)); |
|
3369 JS_ASSERT(fninfo); |
|
3370 |
|
3371 // Identify our objects to the tracer. |
|
3372 JS_CallHeapObjectTracer(trc, &fninfo->mABI, "abi"); |
|
3373 JS_CallHeapObjectTracer(trc, &fninfo->mReturnType, "returnType"); |
|
3374 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) |
|
3375 JS_CallHeapObjectTracer(trc, &fninfo->mArgTypes[i], "argType"); |
|
3376 |
|
3377 break; |
|
3378 } |
|
3379 default: |
|
3380 // Nothing to do here. |
|
3381 break; |
|
3382 } |
|
3383 } |
|
3384 |
|
3385 bool |
|
3386 CType::IsCType(JSObject* obj) |
|
3387 { |
|
3388 return JS_GetClass(obj) == &sCTypeClass; |
|
3389 } |
|
3390 |
|
3391 bool |
|
3392 CType::IsCTypeProto(JSObject* obj) |
|
3393 { |
|
3394 return JS_GetClass(obj) == &sCTypeProtoClass; |
|
3395 } |
|
3396 |
|
3397 TypeCode |
|
3398 CType::GetTypeCode(JSObject* typeObj) |
|
3399 { |
|
3400 JS_ASSERT(IsCType(typeObj)); |
|
3401 |
|
3402 jsval result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE); |
|
3403 return TypeCode(JSVAL_TO_INT(result)); |
|
3404 } |
|
3405 |
|
3406 bool |
|
3407 CType::TypesEqual(JSObject* t1, JSObject* t2) |
|
3408 { |
|
3409 JS_ASSERT(IsCType(t1) && IsCType(t2)); |
|
3410 |
|
3411 // Fast path: check for object equality. |
|
3412 if (t1 == t2) |
|
3413 return true; |
|
3414 |
|
3415 // First, perform shallow comparison. |
|
3416 TypeCode c1 = GetTypeCode(t1); |
|
3417 TypeCode c2 = GetTypeCode(t2); |
|
3418 if (c1 != c2) |
|
3419 return false; |
|
3420 |
|
3421 // Determine whether the types require shallow or deep comparison. |
|
3422 switch (c1) { |
|
3423 case TYPE_pointer: { |
|
3424 // Compare base types. |
|
3425 JSObject* b1 = PointerType::GetBaseType(t1); |
|
3426 JSObject* b2 = PointerType::GetBaseType(t2); |
|
3427 return TypesEqual(b1, b2); |
|
3428 } |
|
3429 case TYPE_function: { |
|
3430 FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1); |
|
3431 FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2); |
|
3432 |
|
3433 // Compare abi, return type, and argument types. |
|
3434 if (f1->mABI != f2->mABI) |
|
3435 return false; |
|
3436 |
|
3437 if (!TypesEqual(f1->mReturnType, f2->mReturnType)) |
|
3438 return false; |
|
3439 |
|
3440 if (f1->mArgTypes.length() != f2->mArgTypes.length()) |
|
3441 return false; |
|
3442 |
|
3443 if (f1->mIsVariadic != f2->mIsVariadic) |
|
3444 return false; |
|
3445 |
|
3446 for (size_t i = 0; i < f1->mArgTypes.length(); ++i) { |
|
3447 if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i])) |
|
3448 return false; |
|
3449 } |
|
3450 |
|
3451 return true; |
|
3452 } |
|
3453 case TYPE_array: { |
|
3454 // Compare length, then base types. |
|
3455 // An undefined length array matches other undefined length arrays. |
|
3456 size_t s1 = 0, s2 = 0; |
|
3457 bool d1 = ArrayType::GetSafeLength(t1, &s1); |
|
3458 bool d2 = ArrayType::GetSafeLength(t2, &s2); |
|
3459 if (d1 != d2 || (d1 && s1 != s2)) |
|
3460 return false; |
|
3461 |
|
3462 JSObject* b1 = ArrayType::GetBaseType(t1); |
|
3463 JSObject* b2 = ArrayType::GetBaseType(t2); |
|
3464 return TypesEqual(b1, b2); |
|
3465 } |
|
3466 case TYPE_struct: |
|
3467 // Require exact type object equality. |
|
3468 return false; |
|
3469 default: |
|
3470 // Shallow comparison is sufficient. |
|
3471 return true; |
|
3472 } |
|
3473 } |
|
3474 |
|
3475 bool |
|
3476 CType::GetSafeSize(JSObject* obj, size_t* result) |
|
3477 { |
|
3478 JS_ASSERT(CType::IsCType(obj)); |
|
3479 |
|
3480 jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); |
|
3481 |
|
3482 // The "size" property can be an int, a double, or JSVAL_VOID |
|
3483 // (for arrays of undefined length), and must always fit in a size_t. |
|
3484 if (JSVAL_IS_INT(size)) { |
|
3485 *result = JSVAL_TO_INT(size); |
|
3486 return true; |
|
3487 } |
|
3488 if (JSVAL_IS_DOUBLE(size)) { |
|
3489 *result = Convert<size_t>(JSVAL_TO_DOUBLE(size)); |
|
3490 return true; |
|
3491 } |
|
3492 |
|
3493 JS_ASSERT(JSVAL_IS_VOID(size)); |
|
3494 return false; |
|
3495 } |
|
3496 |
|
3497 size_t |
|
3498 CType::GetSize(JSObject* obj) |
|
3499 { |
|
3500 JS_ASSERT(CType::IsCType(obj)); |
|
3501 |
|
3502 jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); |
|
3503 |
|
3504 JS_ASSERT(!JSVAL_IS_VOID(size)); |
|
3505 |
|
3506 // The "size" property can be an int, a double, or JSVAL_VOID |
|
3507 // (for arrays of undefined length), and must always fit in a size_t. |
|
3508 // For callers who know it can never be JSVAL_VOID, return a size_t directly. |
|
3509 if (JSVAL_IS_INT(size)) |
|
3510 return JSVAL_TO_INT(size); |
|
3511 return Convert<size_t>(JSVAL_TO_DOUBLE(size)); |
|
3512 } |
|
3513 |
|
3514 bool |
|
3515 CType::IsSizeDefined(JSObject* obj) |
|
3516 { |
|
3517 JS_ASSERT(CType::IsCType(obj)); |
|
3518 |
|
3519 jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); |
|
3520 |
|
3521 // The "size" property can be an int, a double, or JSVAL_VOID |
|
3522 // (for arrays of undefined length), and must always fit in a size_t. |
|
3523 JS_ASSERT(JSVAL_IS_INT(size) || JSVAL_IS_DOUBLE(size) || JSVAL_IS_VOID(size)); |
|
3524 return !JSVAL_IS_VOID(size); |
|
3525 } |
|
3526 |
|
3527 size_t |
|
3528 CType::GetAlignment(JSObject* obj) |
|
3529 { |
|
3530 JS_ASSERT(CType::IsCType(obj)); |
|
3531 |
|
3532 jsval slot = JS_GetReservedSlot(obj, SLOT_ALIGN); |
|
3533 return static_cast<size_t>(JSVAL_TO_INT(slot)); |
|
3534 } |
|
3535 |
|
3536 ffi_type* |
|
3537 CType::GetFFIType(JSContext* cx, JSObject* obj) |
|
3538 { |
|
3539 JS_ASSERT(CType::IsCType(obj)); |
|
3540 |
|
3541 jsval slot = JS_GetReservedSlot(obj, SLOT_FFITYPE); |
|
3542 |
|
3543 if (!JSVAL_IS_VOID(slot)) { |
|
3544 return static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot)); |
|
3545 } |
|
3546 |
|
3547 AutoPtr<ffi_type> result; |
|
3548 switch (CType::GetTypeCode(obj)) { |
|
3549 case TYPE_array: |
|
3550 result = ArrayType::BuildFFIType(cx, obj); |
|
3551 break; |
|
3552 |
|
3553 case TYPE_struct: |
|
3554 result = StructType::BuildFFIType(cx, obj); |
|
3555 break; |
|
3556 |
|
3557 default: |
|
3558 MOZ_ASSUME_UNREACHABLE("simple types must have an ffi_type"); |
|
3559 } |
|
3560 |
|
3561 if (!result) |
|
3562 return nullptr; |
|
3563 JS_SetReservedSlot(obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get())); |
|
3564 return result.forget(); |
|
3565 } |
|
3566 |
|
3567 JSString* |
|
3568 CType::GetName(JSContext* cx, HandleObject obj) |
|
3569 { |
|
3570 JS_ASSERT(CType::IsCType(obj)); |
|
3571 |
|
3572 jsval string = JS_GetReservedSlot(obj, SLOT_NAME); |
|
3573 if (!JSVAL_IS_VOID(string)) |
|
3574 return JSVAL_TO_STRING(string); |
|
3575 |
|
3576 // Build the type name lazily. |
|
3577 JSString* name = BuildTypeName(cx, obj); |
|
3578 if (!name) |
|
3579 return nullptr; |
|
3580 JS_SetReservedSlot(obj, SLOT_NAME, STRING_TO_JSVAL(name)); |
|
3581 return name; |
|
3582 } |
|
3583 |
|
3584 JSObject* |
|
3585 CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot) |
|
3586 { |
|
3587 // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot |
|
3588 // on the type constructor. |
|
3589 jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO); |
|
3590 JSObject* proto = &protoslot.toObject(); |
|
3591 JS_ASSERT(proto); |
|
3592 JS_ASSERT(CType::IsCTypeProto(proto)); |
|
3593 |
|
3594 // Get the desired prototype. |
|
3595 jsval result = JS_GetReservedSlot(proto, slot); |
|
3596 return &result.toObject(); |
|
3597 } |
|
3598 |
|
3599 JSObject* |
|
3600 CType::GetProtoFromType(JSContext* cx, JSObject* objArg, CTypeProtoSlot slot) |
|
3601 { |
|
3602 JS_ASSERT(IsCType(objArg)); |
|
3603 RootedObject obj(cx, objArg); |
|
3604 |
|
3605 // Get the prototype of the type object. |
|
3606 RootedObject proto(cx); |
|
3607 if (!JS_GetPrototype(cx, obj, &proto)) |
|
3608 return nullptr; |
|
3609 JS_ASSERT(proto); |
|
3610 JS_ASSERT(CType::IsCTypeProto(proto)); |
|
3611 |
|
3612 // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype. |
|
3613 jsval result = JS_GetReservedSlot(proto, slot); |
|
3614 JS_ASSERT(!JSVAL_IS_PRIMITIVE(result)); |
|
3615 return JSVAL_TO_OBJECT(result); |
|
3616 } |
|
3617 |
|
3618 bool |
|
3619 CType::IsCTypeOrProto(HandleValue v) |
|
3620 { |
|
3621 if (!v.isObject()) |
|
3622 return false; |
|
3623 JSObject* obj = &v.toObject(); |
|
3624 return CType::IsCType(obj) || CType::IsCTypeProto(obj); |
|
3625 } |
|
3626 |
|
3627 bool |
|
3628 CType::PrototypeGetter(JSContext* cx, JS::CallArgs args) |
|
3629 { |
|
3630 RootedObject obj(cx, &args.thisv().toObject()); |
|
3631 unsigned slot = CType::IsCTypeProto(obj) ? (unsigned) SLOT_OURDATAPROTO |
|
3632 : (unsigned) SLOT_PROTO; |
|
3633 args.rval().set(JS_GetReservedSlot(obj, slot)); |
|
3634 MOZ_ASSERT(args.rval().isObject() || args.rval().isUndefined()); |
|
3635 return true; |
|
3636 } |
|
3637 |
|
3638 bool |
|
3639 CType::IsCType(HandleValue v) |
|
3640 { |
|
3641 return v.isObject() && CType::IsCType(&v.toObject()); |
|
3642 } |
|
3643 |
|
3644 bool |
|
3645 CType::NameGetter(JSContext* cx, JS::CallArgs args) |
|
3646 { |
|
3647 RootedObject obj(cx, &args.thisv().toObject()); |
|
3648 JSString* name = CType::GetName(cx, obj); |
|
3649 if (!name) |
|
3650 return false; |
|
3651 |
|
3652 args.rval().setString(name); |
|
3653 return true; |
|
3654 } |
|
3655 |
|
3656 bool |
|
3657 CType::SizeGetter(JSContext* cx, JS::CallArgs args) |
|
3658 { |
|
3659 RootedObject obj(cx, &args.thisv().toObject()); |
|
3660 args.rval().set(JS_GetReservedSlot(obj, SLOT_SIZE)); |
|
3661 MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined()); |
|
3662 return true; |
|
3663 } |
|
3664 |
|
3665 bool |
|
3666 CType::PtrGetter(JSContext* cx, JS::CallArgs args) |
|
3667 { |
|
3668 RootedObject obj(cx, &args.thisv().toObject()); |
|
3669 JSObject* pointerType = PointerType::CreateInternal(cx, obj); |
|
3670 if (!pointerType) |
|
3671 return false; |
|
3672 |
|
3673 args.rval().setObject(*pointerType); |
|
3674 return true; |
|
3675 } |
|
3676 |
|
3677 bool |
|
3678 CType::CreateArray(JSContext* cx, unsigned argc, jsval* vp) |
|
3679 { |
|
3680 CallArgs args = CallArgsFromVp(argc, vp); |
|
3681 RootedObject baseType(cx, JS_THIS_OBJECT(cx, vp)); |
|
3682 if (!baseType) |
|
3683 return false; |
|
3684 if (!CType::IsCType(baseType)) { |
|
3685 JS_ReportError(cx, "not a CType"); |
|
3686 return false; |
|
3687 } |
|
3688 |
|
3689 // Construct and return a new ArrayType object. |
|
3690 if (args.length() > 1) { |
|
3691 JS_ReportError(cx, "array takes zero or one argument"); |
|
3692 return false; |
|
3693 } |
|
3694 |
|
3695 // Convert the length argument to a size_t. |
|
3696 size_t length = 0; |
|
3697 if (args.length() == 1 && !jsvalToSize(cx, args[0], false, &length)) { |
|
3698 JS_ReportError(cx, "argument must be a nonnegative integer"); |
|
3699 return false; |
|
3700 } |
|
3701 |
|
3702 JSObject* result = ArrayType::CreateInternal(cx, baseType, length, args.length() == 1); |
|
3703 if (!result) |
|
3704 return false; |
|
3705 |
|
3706 args.rval().setObject(*result); |
|
3707 return true; |
|
3708 } |
|
3709 |
|
3710 bool |
|
3711 CType::ToString(JSContext* cx, unsigned argc, jsval* vp) |
|
3712 { |
|
3713 CallArgs args = CallArgsFromVp(argc, vp); |
|
3714 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); |
|
3715 if (!obj) |
|
3716 return false; |
|
3717 if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) { |
|
3718 JS_ReportError(cx, "not a CType"); |
|
3719 return false; |
|
3720 } |
|
3721 |
|
3722 // Create the appropriate string depending on whether we're sCTypeClass or |
|
3723 // sCTypeProtoClass. |
|
3724 JSString* result; |
|
3725 if (CType::IsCType(obj)) { |
|
3726 AutoString type; |
|
3727 AppendString(type, "type "); |
|
3728 AppendString(type, GetName(cx, obj)); |
|
3729 result = NewUCString(cx, type); |
|
3730 } |
|
3731 else { |
|
3732 result = JS_NewStringCopyZ(cx, "[CType proto object]"); |
|
3733 } |
|
3734 if (!result) |
|
3735 return false; |
|
3736 |
|
3737 args.rval().setString(result); |
|
3738 return true; |
|
3739 } |
|
3740 |
|
3741 bool |
|
3742 CType::ToSource(JSContext* cx, unsigned argc, jsval* vp) |
|
3743 { |
|
3744 CallArgs args = CallArgsFromVp(argc, vp); |
|
3745 JSObject* obj = JS_THIS_OBJECT(cx, vp); |
|
3746 if (!obj) |
|
3747 return false; |
|
3748 if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) |
|
3749 { |
|
3750 JS_ReportError(cx, "not a CType"); |
|
3751 return false; |
|
3752 } |
|
3753 |
|
3754 // Create the appropriate string depending on whether we're sCTypeClass or |
|
3755 // sCTypeProtoClass. |
|
3756 JSString* result; |
|
3757 if (CType::IsCType(obj)) { |
|
3758 AutoString source; |
|
3759 BuildTypeSource(cx, obj, false, source); |
|
3760 result = NewUCString(cx, source); |
|
3761 } else { |
|
3762 result = JS_NewStringCopyZ(cx, "[CType proto object]"); |
|
3763 } |
|
3764 if (!result) |
|
3765 return false; |
|
3766 |
|
3767 args.rval().setString(result); |
|
3768 return true; |
|
3769 } |
|
3770 |
|
3771 bool |
|
3772 CType::HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp) |
|
3773 { |
|
3774 JS_ASSERT(CType::IsCType(obj)); |
|
3775 |
|
3776 jsval slot = JS_GetReservedSlot(obj, SLOT_PROTO); |
|
3777 JS::Rooted<JSObject*> prototype(cx, &slot.toObject()); |
|
3778 JS_ASSERT(prototype); |
|
3779 JS_ASSERT(CData::IsCDataProto(prototype)); |
|
3780 |
|
3781 *bp = false; |
|
3782 if (JSVAL_IS_PRIMITIVE(v)) |
|
3783 return true; |
|
3784 |
|
3785 RootedObject proto(cx, &v.toObject()); |
|
3786 for (;;) { |
|
3787 if (!JS_GetPrototype(cx, proto, &proto)) |
|
3788 return false; |
|
3789 if (!proto) |
|
3790 break; |
|
3791 if (proto == prototype) { |
|
3792 *bp = true; |
|
3793 break; |
|
3794 } |
|
3795 } |
|
3796 return true; |
|
3797 } |
|
3798 |
|
3799 static JSObject* |
|
3800 CType::GetGlobalCTypes(JSContext* cx, JSObject* objArg) |
|
3801 { |
|
3802 JS_ASSERT(CType::IsCType(objArg)); |
|
3803 |
|
3804 RootedObject obj(cx, objArg); |
|
3805 RootedObject objTypeProto(cx); |
|
3806 if (!JS_GetPrototype(cx, obj, &objTypeProto)) |
|
3807 return nullptr; |
|
3808 JS_ASSERT(objTypeProto); |
|
3809 JS_ASSERT(CType::IsCTypeProto(objTypeProto)); |
|
3810 |
|
3811 jsval valCTypes = JS_GetReservedSlot(objTypeProto, SLOT_CTYPES); |
|
3812 JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCTypes)); |
|
3813 |
|
3814 JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCTypes)); |
|
3815 return &valCTypes.toObject(); |
|
3816 } |
|
3817 |
|
3818 /******************************************************************************* |
|
3819 ** ABI implementation |
|
3820 *******************************************************************************/ |
|
3821 |
|
3822 bool |
|
3823 ABI::IsABI(JSObject* obj) |
|
3824 { |
|
3825 return JS_GetClass(obj) == &sCABIClass; |
|
3826 } |
|
3827 |
|
3828 bool |
|
3829 ABI::ToSource(JSContext* cx, unsigned argc, jsval* vp) |
|
3830 { |
|
3831 CallArgs args = CallArgsFromVp(argc, vp); |
|
3832 if (args.length() != 0) { |
|
3833 JS_ReportError(cx, "toSource takes zero arguments"); |
|
3834 return false; |
|
3835 } |
|
3836 |
|
3837 JSObject* obj = JS_THIS_OBJECT(cx, vp); |
|
3838 if (!obj) |
|
3839 return false; |
|
3840 if (!ABI::IsABI(obj)) { |
|
3841 JS_ReportError(cx, "not an ABI"); |
|
3842 return false; |
|
3843 } |
|
3844 |
|
3845 JSString* result; |
|
3846 switch (GetABICode(obj)) { |
|
3847 case ABI_DEFAULT: |
|
3848 result = JS_NewStringCopyZ(cx, "ctypes.default_abi"); |
|
3849 break; |
|
3850 case ABI_STDCALL: |
|
3851 result = JS_NewStringCopyZ(cx, "ctypes.stdcall_abi"); |
|
3852 break; |
|
3853 case ABI_WINAPI: |
|
3854 result = JS_NewStringCopyZ(cx, "ctypes.winapi_abi"); |
|
3855 break; |
|
3856 default: |
|
3857 JS_ReportError(cx, "not a valid ABICode"); |
|
3858 return false; |
|
3859 } |
|
3860 if (!result) |
|
3861 return false; |
|
3862 |
|
3863 args.rval().setString(result); |
|
3864 return true; |
|
3865 } |
|
3866 |
|
3867 |
|
3868 /******************************************************************************* |
|
3869 ** PointerType implementation |
|
3870 *******************************************************************************/ |
|
3871 |
|
3872 bool |
|
3873 PointerType::Create(JSContext* cx, unsigned argc, jsval* vp) |
|
3874 { |
|
3875 CallArgs args = CallArgsFromVp(argc, vp); |
|
3876 // Construct and return a new PointerType object. |
|
3877 if (args.length() != 1) { |
|
3878 JS_ReportError(cx, "PointerType takes one argument"); |
|
3879 return false; |
|
3880 } |
|
3881 |
|
3882 jsval arg = args[0]; |
|
3883 RootedObject obj(cx); |
|
3884 if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(obj = &arg.toObject())) { |
|
3885 JS_ReportError(cx, "first argument must be a CType"); |
|
3886 return false; |
|
3887 } |
|
3888 |
|
3889 JSObject* result = CreateInternal(cx, obj); |
|
3890 if (!result) |
|
3891 return false; |
|
3892 |
|
3893 args.rval().setObject(*result); |
|
3894 return true; |
|
3895 } |
|
3896 |
|
3897 JSObject* |
|
3898 PointerType::CreateInternal(JSContext* cx, HandleObject baseType) |
|
3899 { |
|
3900 // check if we have a cached PointerType on our base CType. |
|
3901 jsval slot = JS_GetReservedSlot(baseType, SLOT_PTR); |
|
3902 if (!slot.isUndefined()) |
|
3903 return &slot.toObject(); |
|
3904 |
|
3905 // Get ctypes.PointerType.prototype and the common prototype for CData objects |
|
3906 // of this type, or ctypes.FunctionType.prototype for function pointers. |
|
3907 CTypeProtoSlot slotId = CType::GetTypeCode(baseType) == TYPE_function ? |
|
3908 SLOT_FUNCTIONDATAPROTO : SLOT_POINTERDATAPROTO; |
|
3909 RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, slotId)); |
|
3910 if (!dataProto) |
|
3911 return nullptr; |
|
3912 RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO)); |
|
3913 if (!typeProto) |
|
3914 return nullptr; |
|
3915 |
|
3916 // Create a new CType object with the common properties and slots. |
|
3917 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer, |
|
3918 nullptr, INT_TO_JSVAL(sizeof(void*)), |
|
3919 INT_TO_JSVAL(ffi_type_pointer.alignment), |
|
3920 &ffi_type_pointer); |
|
3921 if (!typeObj) |
|
3922 return nullptr; |
|
3923 |
|
3924 // Set the target type. (This will be 'null' for an opaque pointer type.) |
|
3925 JS_SetReservedSlot(typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType)); |
|
3926 |
|
3927 // Finally, cache our newly-created PointerType on our pointed-to CType. |
|
3928 JS_SetReservedSlot(baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)); |
|
3929 |
|
3930 return typeObj; |
|
3931 } |
|
3932 |
|
3933 bool |
|
3934 PointerType::ConstructData(JSContext* cx, |
|
3935 HandleObject obj, |
|
3936 const CallArgs& args) |
|
3937 { |
|
3938 if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) { |
|
3939 JS_ReportError(cx, "not a PointerType"); |
|
3940 return false; |
|
3941 } |
|
3942 |
|
3943 if (args.length() > 3) { |
|
3944 JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments"); |
|
3945 return false; |
|
3946 } |
|
3947 |
|
3948 RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true)); |
|
3949 if (!result) |
|
3950 return false; |
|
3951 |
|
3952 // Set return value early, must not observe *vp after |
|
3953 args.rval().setObject(*result); |
|
3954 |
|
3955 // There are 3 things that we might be creating here: |
|
3956 // 1 - A null pointer (no arguments) |
|
3957 // 2 - An initialized pointer (1 argument) |
|
3958 // 3 - A closure (1-3 arguments) |
|
3959 // |
|
3960 // The API doesn't give us a perfect way to distinguish 2 and 3, but the |
|
3961 // heuristics we use should be fine. |
|
3962 |
|
3963 // |
|
3964 // Case 1 - Null pointer |
|
3965 // |
|
3966 if (args.length() == 0) |
|
3967 return true; |
|
3968 |
|
3969 // Analyze the arguments a bit to decide what to do next. |
|
3970 RootedObject baseObj(cx, PointerType::GetBaseType(obj)); |
|
3971 bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function && |
|
3972 args[0].isObject() && |
|
3973 JS_ObjectIsCallable(cx, &args[0].toObject()); |
|
3974 |
|
3975 // |
|
3976 // Case 2 - Initialized pointer |
|
3977 // |
|
3978 if (!looksLikeClosure) { |
|
3979 if (args.length() != 1) { |
|
3980 JS_ReportError(cx, "first argument must be a function"); |
|
3981 return false; |
|
3982 } |
|
3983 return ExplicitConvert(cx, args[0], obj, CData::GetData(result)); |
|
3984 } |
|
3985 |
|
3986 // |
|
3987 // Case 3 - Closure |
|
3988 // |
|
3989 |
|
3990 // The second argument is an optional 'this' parameter with which to invoke |
|
3991 // the given js function. Callers may leave this blank, or pass null if they |
|
3992 // wish to pass the third argument. |
|
3993 RootedObject thisObj(cx, nullptr); |
|
3994 if (args.length() >= 2) { |
|
3995 if (args[1].isNull()) { |
|
3996 thisObj = nullptr; |
|
3997 } else if (!JSVAL_IS_PRIMITIVE(args[1])) { |
|
3998 thisObj = &args[1].toObject(); |
|
3999 } else if (!JS_ValueToObject(cx, args[1], &thisObj)) { |
|
4000 return false; |
|
4001 } |
|
4002 } |
|
4003 |
|
4004 // The third argument is an optional error sentinel that js-ctypes will return |
|
4005 // if an exception is raised while executing the closure. The type must match |
|
4006 // the return type of the callback. |
|
4007 jsval errVal = JSVAL_VOID; |
|
4008 if (args.length() == 3) |
|
4009 errVal = args[2]; |
|
4010 |
|
4011 RootedObject fnObj(cx, &args[0].toObject()); |
|
4012 return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal); |
|
4013 } |
|
4014 |
|
4015 JSObject* |
|
4016 PointerType::GetBaseType(JSObject* obj) |
|
4017 { |
|
4018 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer); |
|
4019 |
|
4020 jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T); |
|
4021 JS_ASSERT(!type.isNull()); |
|
4022 return &type.toObject(); |
|
4023 } |
|
4024 |
|
4025 bool |
|
4026 PointerType::IsPointerType(HandleValue v) |
|
4027 { |
|
4028 if (!v.isObject()) |
|
4029 return false; |
|
4030 JSObject* obj = &v.toObject(); |
|
4031 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_pointer; |
|
4032 } |
|
4033 |
|
4034 bool |
|
4035 PointerType::IsPointer(HandleValue v) |
|
4036 { |
|
4037 if (!v.isObject()) |
|
4038 return false; |
|
4039 JSObject* obj = &v.toObject(); |
|
4040 return CData::IsCData(obj) && CType::GetTypeCode(CData::GetCType(obj)) == TYPE_pointer; |
|
4041 } |
|
4042 |
|
4043 bool |
|
4044 PointerType::TargetTypeGetter(JSContext* cx, JS::CallArgs args) |
|
4045 { |
|
4046 RootedObject obj(cx, &args.thisv().toObject()); |
|
4047 args.rval().set(JS_GetReservedSlot(obj, SLOT_TARGET_T)); |
|
4048 MOZ_ASSERT(args.rval().isObject()); |
|
4049 return true; |
|
4050 } |
|
4051 |
|
4052 bool |
|
4053 PointerType::IsNull(JSContext* cx, unsigned argc, jsval* vp) |
|
4054 { |
|
4055 CallArgs args = CallArgsFromVp(argc, vp); |
|
4056 JSObject* obj = JS_THIS_OBJECT(cx, vp); |
|
4057 if (!obj) |
|
4058 return false; |
|
4059 if (!CData::IsCData(obj)) { |
|
4060 JS_ReportError(cx, "not a CData"); |
|
4061 return false; |
|
4062 } |
|
4063 |
|
4064 // Get pointer type and base type. |
|
4065 JSObject* typeObj = CData::GetCType(obj); |
|
4066 if (CType::GetTypeCode(typeObj) != TYPE_pointer) { |
|
4067 JS_ReportError(cx, "not a PointerType"); |
|
4068 return false; |
|
4069 } |
|
4070 |
|
4071 void* data = *static_cast<void**>(CData::GetData(obj)); |
|
4072 args.rval().setBoolean(data == nullptr); |
|
4073 return true; |
|
4074 } |
|
4075 |
|
4076 bool |
|
4077 PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset) |
|
4078 { |
|
4079 JSObject* obj = JS_THIS_OBJECT(cx, args.base()); |
|
4080 if (!obj) |
|
4081 return false; |
|
4082 if (!CData::IsCData(obj)) { |
|
4083 JS_ReportError(cx, "not a CData"); |
|
4084 return false; |
|
4085 } |
|
4086 |
|
4087 RootedObject typeObj(cx, CData::GetCType(obj)); |
|
4088 if (CType::GetTypeCode(typeObj) != TYPE_pointer) { |
|
4089 JS_ReportError(cx, "not a PointerType"); |
|
4090 return false; |
|
4091 } |
|
4092 |
|
4093 RootedObject baseType(cx, PointerType::GetBaseType(typeObj)); |
|
4094 if (!CType::IsSizeDefined(baseType)) { |
|
4095 JS_ReportError(cx, "cannot modify pointer of undefined size"); |
|
4096 return false; |
|
4097 } |
|
4098 |
|
4099 size_t elementSize = CType::GetSize(baseType); |
|
4100 char* data = static_cast<char*>(*static_cast<void**>(CData::GetData(obj))); |
|
4101 void* address = data + offset * elementSize; |
|
4102 |
|
4103 // Create a PointerType CData object containing the new address. |
|
4104 JSObject* result = CData::Create(cx, typeObj, NullPtr(), &address, true); |
|
4105 if (!result) |
|
4106 return false; |
|
4107 |
|
4108 args.rval().setObject(*result); |
|
4109 return true; |
|
4110 } |
|
4111 |
|
4112 bool |
|
4113 PointerType::Increment(JSContext* cx, unsigned argc, jsval* vp) |
|
4114 { |
|
4115 CallArgs args = CallArgsFromVp(argc, vp); |
|
4116 return OffsetBy(cx, args, 1); |
|
4117 } |
|
4118 |
|
4119 bool |
|
4120 PointerType::Decrement(JSContext* cx, unsigned argc, jsval* vp) |
|
4121 { |
|
4122 CallArgs args = CallArgsFromVp(argc, vp); |
|
4123 return OffsetBy(cx, args, -1); |
|
4124 } |
|
4125 |
|
4126 bool |
|
4127 PointerType::ContentsGetter(JSContext* cx, JS::CallArgs args) |
|
4128 { |
|
4129 RootedObject obj(cx, &args.thisv().toObject()); |
|
4130 RootedObject baseType(cx, GetBaseType(CData::GetCType(obj))); |
|
4131 if (!CType::IsSizeDefined(baseType)) { |
|
4132 JS_ReportError(cx, "cannot get contents of undefined size"); |
|
4133 return false; |
|
4134 } |
|
4135 |
|
4136 void* data = *static_cast<void**>(CData::GetData(obj)); |
|
4137 if (data == nullptr) { |
|
4138 JS_ReportError(cx, "cannot read contents of null pointer"); |
|
4139 return false; |
|
4140 } |
|
4141 |
|
4142 RootedValue result(cx); |
|
4143 if (!ConvertToJS(cx, baseType, NullPtr(), data, false, false, result.address())) |
|
4144 return false; |
|
4145 |
|
4146 args.rval().set(result); |
|
4147 return true; |
|
4148 } |
|
4149 |
|
4150 bool |
|
4151 PointerType::ContentsSetter(JSContext* cx, JS::CallArgs args) |
|
4152 { |
|
4153 RootedObject obj(cx, &args.thisv().toObject()); |
|
4154 RootedObject baseType(cx, GetBaseType(CData::GetCType(obj))); |
|
4155 if (!CType::IsSizeDefined(baseType)) { |
|
4156 JS_ReportError(cx, "cannot set contents of undefined size"); |
|
4157 return false; |
|
4158 } |
|
4159 |
|
4160 void* data = *static_cast<void**>(CData::GetData(obj)); |
|
4161 if (data == nullptr) { |
|
4162 JS_ReportError(cx, "cannot write contents to null pointer"); |
|
4163 return false; |
|
4164 } |
|
4165 |
|
4166 args.rval().setUndefined(); |
|
4167 return ImplicitConvert(cx, args.get(0), baseType, data, false, nullptr); |
|
4168 } |
|
4169 |
|
4170 /******************************************************************************* |
|
4171 ** ArrayType implementation |
|
4172 *******************************************************************************/ |
|
4173 |
|
4174 bool |
|
4175 ArrayType::Create(JSContext* cx, unsigned argc, jsval* vp) |
|
4176 { |
|
4177 CallArgs args = CallArgsFromVp(argc, vp); |
|
4178 // Construct and return a new ArrayType object. |
|
4179 if (args.length() < 1 || args.length() > 2) { |
|
4180 JS_ReportError(cx, "ArrayType takes one or two arguments"); |
|
4181 return false; |
|
4182 } |
|
4183 |
|
4184 if (JSVAL_IS_PRIMITIVE(args[0]) || |
|
4185 !CType::IsCType(&args[0].toObject())) { |
|
4186 JS_ReportError(cx, "first argument must be a CType"); |
|
4187 return false; |
|
4188 } |
|
4189 |
|
4190 // Convert the length argument to a size_t. |
|
4191 size_t length = 0; |
|
4192 if (args.length() == 2 && !jsvalToSize(cx, args[1], false, &length)) { |
|
4193 JS_ReportError(cx, "second argument must be a nonnegative integer"); |
|
4194 return false; |
|
4195 } |
|
4196 |
|
4197 RootedObject baseType(cx, &args[0].toObject()); |
|
4198 JSObject* result = CreateInternal(cx, baseType, length, args.length() == 2); |
|
4199 if (!result) |
|
4200 return false; |
|
4201 |
|
4202 args.rval().setObject(*result); |
|
4203 return true; |
|
4204 } |
|
4205 |
|
4206 JSObject* |
|
4207 ArrayType::CreateInternal(JSContext* cx, |
|
4208 HandleObject baseType, |
|
4209 size_t length, |
|
4210 bool lengthDefined) |
|
4211 { |
|
4212 // Get ctypes.ArrayType.prototype and the common prototype for CData objects |
|
4213 // of this type, from ctypes.CType.prototype. |
|
4214 RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO)); |
|
4215 if (!typeProto) |
|
4216 return nullptr; |
|
4217 RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO)); |
|
4218 if (!dataProto) |
|
4219 return nullptr; |
|
4220 |
|
4221 // Determine the size of the array from the base type, if possible. |
|
4222 // The size of the base type must be defined. |
|
4223 // If our length is undefined, both our size and length will be undefined. |
|
4224 size_t baseSize; |
|
4225 if (!CType::GetSafeSize(baseType, &baseSize)) { |
|
4226 JS_ReportError(cx, "base size must be defined"); |
|
4227 return nullptr; |
|
4228 } |
|
4229 |
|
4230 RootedValue sizeVal(cx, JSVAL_VOID); |
|
4231 RootedValue lengthVal(cx, JSVAL_VOID); |
|
4232 if (lengthDefined) { |
|
4233 // Check for overflow, and convert to an int or double as required. |
|
4234 size_t size = length * baseSize; |
|
4235 if (length > 0 && size / length != baseSize) { |
|
4236 JS_ReportError(cx, "size overflow"); |
|
4237 return nullptr; |
|
4238 } |
|
4239 if (!SizeTojsval(cx, size, sizeVal.address()) || |
|
4240 !SizeTojsval(cx, length, lengthVal.address())) |
|
4241 return nullptr; |
|
4242 } |
|
4243 |
|
4244 size_t align = CType::GetAlignment(baseType); |
|
4245 |
|
4246 // Create a new CType object with the common properties and slots. |
|
4247 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, nullptr, |
|
4248 sizeVal, INT_TO_JSVAL(align), nullptr); |
|
4249 if (!typeObj) |
|
4250 return nullptr; |
|
4251 |
|
4252 // Set the element type. |
|
4253 JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType)); |
|
4254 |
|
4255 // Set the length. |
|
4256 JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal); |
|
4257 |
|
4258 return typeObj; |
|
4259 } |
|
4260 |
|
4261 bool |
|
4262 ArrayType::ConstructData(JSContext* cx, |
|
4263 HandleObject obj_, |
|
4264 const CallArgs& args) |
|
4265 { |
|
4266 RootedObject obj(cx, obj_); // Make a mutable version |
|
4267 |
|
4268 if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) { |
|
4269 JS_ReportError(cx, "not an ArrayType"); |
|
4270 return false; |
|
4271 } |
|
4272 |
|
4273 // Decide whether we have an object to initialize from. We'll override this |
|
4274 // if we get a length argument instead. |
|
4275 bool convertObject = args.length() == 1; |
|
4276 |
|
4277 // Check if we're an array of undefined length. If we are, allow construction |
|
4278 // with a length argument, or with an actual JS array. |
|
4279 if (CType::IsSizeDefined(obj)) { |
|
4280 if (args.length() > 1) { |
|
4281 JS_ReportError(cx, "constructor takes zero or one argument"); |
|
4282 return false; |
|
4283 } |
|
4284 |
|
4285 } else { |
|
4286 if (args.length() != 1) { |
|
4287 JS_ReportError(cx, "constructor takes one argument"); |
|
4288 return false; |
|
4289 } |
|
4290 |
|
4291 RootedObject baseType(cx, GetBaseType(obj)); |
|
4292 |
|
4293 size_t length; |
|
4294 if (jsvalToSize(cx, args[0], false, &length)) { |
|
4295 // Have a length, rather than an object to initialize from. |
|
4296 convertObject = false; |
|
4297 |
|
4298 } else if (!JSVAL_IS_PRIMITIVE(args[0])) { |
|
4299 // We were given an object with a .length property. |
|
4300 // This could be a JS array, or a CData array. |
|
4301 RootedObject arg(cx, &args[0].toObject()); |
|
4302 RootedValue lengthVal(cx); |
|
4303 if (!JS_GetProperty(cx, arg, "length", &lengthVal) || |
|
4304 !jsvalToSize(cx, lengthVal, false, &length)) { |
|
4305 JS_ReportError(cx, "argument must be an array object or length"); |
|
4306 return false; |
|
4307 } |
|
4308 |
|
4309 } else if (args[0].isString()) { |
|
4310 // We were given a string. Size the array to the appropriate length, |
|
4311 // including space for the terminator. |
|
4312 JSString* sourceString = args[0].toString(); |
|
4313 size_t sourceLength = sourceString->length(); |
|
4314 const jschar* sourceChars = sourceString->getChars(cx); |
|
4315 if (!sourceChars) |
|
4316 return false; |
|
4317 |
|
4318 switch (CType::GetTypeCode(baseType)) { |
|
4319 case TYPE_char: |
|
4320 case TYPE_signed_char: |
|
4321 case TYPE_unsigned_char: { |
|
4322 // Determine the UTF-8 length. |
|
4323 length = GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); |
|
4324 if (length == (size_t) -1) |
|
4325 return false; |
|
4326 |
|
4327 ++length; |
|
4328 break; |
|
4329 } |
|
4330 case TYPE_jschar: |
|
4331 length = sourceLength + 1; |
|
4332 break; |
|
4333 default: |
|
4334 return TypeError(cx, "array", args[0]); |
|
4335 } |
|
4336 |
|
4337 } else { |
|
4338 JS_ReportError(cx, "argument must be an array object or length"); |
|
4339 return false; |
|
4340 } |
|
4341 |
|
4342 // Construct a new ArrayType of defined length, for the new CData object. |
|
4343 obj = CreateInternal(cx, baseType, length, true); |
|
4344 if (!obj) |
|
4345 return false; |
|
4346 } |
|
4347 |
|
4348 JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true); |
|
4349 if (!result) |
|
4350 return false; |
|
4351 |
|
4352 args.rval().setObject(*result); |
|
4353 |
|
4354 if (convertObject) { |
|
4355 if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result))) |
|
4356 return false; |
|
4357 } |
|
4358 |
|
4359 return true; |
|
4360 } |
|
4361 |
|
4362 JSObject* |
|
4363 ArrayType::GetBaseType(JSObject* obj) |
|
4364 { |
|
4365 JS_ASSERT(CType::IsCType(obj)); |
|
4366 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); |
|
4367 |
|
4368 jsval type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T); |
|
4369 JS_ASSERT(!JSVAL_IS_NULL(type)); |
|
4370 return &type.toObject(); |
|
4371 } |
|
4372 |
|
4373 bool |
|
4374 ArrayType::GetSafeLength(JSObject* obj, size_t* result) |
|
4375 { |
|
4376 JS_ASSERT(CType::IsCType(obj)); |
|
4377 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); |
|
4378 |
|
4379 jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH); |
|
4380 |
|
4381 // The "length" property can be an int, a double, or JSVAL_VOID |
|
4382 // (for arrays of undefined length), and must always fit in a size_t. |
|
4383 if (length.isInt32()) { |
|
4384 *result = length.toInt32();; |
|
4385 return true; |
|
4386 } |
|
4387 if (length.isDouble()) { |
|
4388 *result = Convert<size_t>(length.toDouble()); |
|
4389 return true; |
|
4390 } |
|
4391 |
|
4392 JS_ASSERT(length.isUndefined()); |
|
4393 return false; |
|
4394 } |
|
4395 |
|
4396 size_t |
|
4397 ArrayType::GetLength(JSObject* obj) |
|
4398 { |
|
4399 JS_ASSERT(CType::IsCType(obj)); |
|
4400 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); |
|
4401 |
|
4402 jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH); |
|
4403 |
|
4404 JS_ASSERT(!length.isUndefined()); |
|
4405 |
|
4406 // The "length" property can be an int, a double, or JSVAL_VOID |
|
4407 // (for arrays of undefined length), and must always fit in a size_t. |
|
4408 // For callers who know it can never be JSVAL_VOID, return a size_t directly. |
|
4409 if (length.isInt32()) |
|
4410 return length.toInt32();; |
|
4411 return Convert<size_t>(length.toDouble()); |
|
4412 } |
|
4413 |
|
4414 ffi_type* |
|
4415 ArrayType::BuildFFIType(JSContext* cx, JSObject* obj) |
|
4416 { |
|
4417 JS_ASSERT(CType::IsCType(obj)); |
|
4418 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); |
|
4419 JS_ASSERT(CType::IsSizeDefined(obj)); |
|
4420 |
|
4421 JSObject* baseType = ArrayType::GetBaseType(obj); |
|
4422 ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType); |
|
4423 if (!ffiBaseType) |
|
4424 return nullptr; |
|
4425 |
|
4426 size_t length = ArrayType::GetLength(obj); |
|
4427 |
|
4428 // Create an ffi_type to represent the array. This is necessary for the case |
|
4429 // where the array is part of a struct. Since libffi has no intrinsic |
|
4430 // support for array types, we approximate it by creating a struct type |
|
4431 // with elements of type 'baseType' and with appropriate size and alignment |
|
4432 // values. It would be nice to not do all the work of setting up 'elements', |
|
4433 // but some libffi platforms currently require that it be meaningful. I'm |
|
4434 // looking at you, x86_64. |
|
4435 AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>()); |
|
4436 if (!ffiType) { |
|
4437 JS_ReportOutOfMemory(cx); |
|
4438 return nullptr; |
|
4439 } |
|
4440 |
|
4441 ffiType->type = FFI_TYPE_STRUCT; |
|
4442 ffiType->size = CType::GetSize(obj); |
|
4443 ffiType->alignment = CType::GetAlignment(obj); |
|
4444 ffiType->elements = cx->pod_malloc<ffi_type*>(length + 1); |
|
4445 if (!ffiType->elements) { |
|
4446 JS_ReportAllocationOverflow(cx); |
|
4447 return nullptr; |
|
4448 } |
|
4449 |
|
4450 for (size_t i = 0; i < length; ++i) |
|
4451 ffiType->elements[i] = ffiBaseType; |
|
4452 ffiType->elements[length] = nullptr; |
|
4453 |
|
4454 return ffiType.forget(); |
|
4455 } |
|
4456 |
|
4457 bool |
|
4458 ArrayType::IsArrayType(HandleValue v) |
|
4459 { |
|
4460 if (!v.isObject()) |
|
4461 return false; |
|
4462 JSObject* obj = &v.toObject(); |
|
4463 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array; |
|
4464 } |
|
4465 |
|
4466 bool |
|
4467 ArrayType::IsArrayOrArrayType(HandleValue v) |
|
4468 { |
|
4469 if (!v.isObject()) |
|
4470 return false; |
|
4471 JSObject* obj = &v.toObject(); |
|
4472 |
|
4473 // Allow both CTypes and CDatas of the ArrayType persuasion by extracting the |
|
4474 // CType if we're dealing with a CData. |
|
4475 if (CData::IsCData(obj)) { |
|
4476 obj = CData::GetCType(obj); |
|
4477 } |
|
4478 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array; |
|
4479 } |
|
4480 |
|
4481 bool |
|
4482 ArrayType::ElementTypeGetter(JSContext* cx, JS::CallArgs args) |
|
4483 { |
|
4484 RootedObject obj(cx, &args.thisv().toObject()); |
|
4485 args.rval().set(JS_GetReservedSlot(obj, SLOT_ELEMENT_T)); |
|
4486 MOZ_ASSERT(args.rval().isObject()); |
|
4487 return true; |
|
4488 } |
|
4489 |
|
4490 bool |
|
4491 ArrayType::LengthGetter(JSContext* cx, JS::CallArgs args) |
|
4492 { |
|
4493 JSObject *obj = &args.thisv().toObject(); |
|
4494 |
|
4495 // This getter exists for both CTypes and CDatas of the ArrayType persuasion. |
|
4496 // If we're dealing with a CData, get the CType from it. |
|
4497 if (CData::IsCData(obj)) |
|
4498 obj = CData::GetCType(obj); |
|
4499 |
|
4500 args.rval().set(JS_GetReservedSlot(obj, SLOT_LENGTH)); |
|
4501 JS_ASSERT(args.rval().isNumber() || args.rval().isUndefined()); |
|
4502 return true; |
|
4503 } |
|
4504 |
|
4505 bool |
|
4506 ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp) |
|
4507 { |
|
4508 // This should never happen, but we'll check to be safe. |
|
4509 if (!CData::IsCData(obj)) { |
|
4510 JS_ReportError(cx, "not a CData"); |
|
4511 return false; |
|
4512 } |
|
4513 |
|
4514 // Bail early if we're not an ArrayType. (This setter is present for all |
|
4515 // CData, regardless of CType.) |
|
4516 JSObject* typeObj = CData::GetCType(obj); |
|
4517 if (CType::GetTypeCode(typeObj) != TYPE_array) |
|
4518 return true; |
|
4519 |
|
4520 // Convert the index to a size_t and bounds-check it. |
|
4521 size_t index; |
|
4522 size_t length = GetLength(typeObj); |
|
4523 bool ok = jsidToSize(cx, idval, true, &index); |
|
4524 int32_t dummy; |
|
4525 if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) { |
|
4526 // String either isn't a number, or doesn't fit in size_t. |
|
4527 // Chances are it's a regular property lookup, so return. |
|
4528 return true; |
|
4529 } |
|
4530 if (!ok || index >= length) { |
|
4531 JS_ReportError(cx, "invalid index"); |
|
4532 return false; |
|
4533 } |
|
4534 |
|
4535 RootedObject baseType(cx, GetBaseType(typeObj)); |
|
4536 size_t elementSize = CType::GetSize(baseType); |
|
4537 char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index; |
|
4538 return ConvertToJS(cx, baseType, obj, data, false, false, vp.address()); |
|
4539 } |
|
4540 |
|
4541 bool |
|
4542 ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp) |
|
4543 { |
|
4544 // This should never happen, but we'll check to be safe. |
|
4545 if (!CData::IsCData(obj)) { |
|
4546 JS_ReportError(cx, "not a CData"); |
|
4547 return false; |
|
4548 } |
|
4549 |
|
4550 // Bail early if we're not an ArrayType. (This setter is present for all |
|
4551 // CData, regardless of CType.) |
|
4552 JSObject* typeObj = CData::GetCType(obj); |
|
4553 if (CType::GetTypeCode(typeObj) != TYPE_array) |
|
4554 return true; |
|
4555 |
|
4556 // Convert the index to a size_t and bounds-check it. |
|
4557 size_t index; |
|
4558 size_t length = GetLength(typeObj); |
|
4559 bool ok = jsidToSize(cx, idval, true, &index); |
|
4560 int32_t dummy; |
|
4561 if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) { |
|
4562 // String either isn't a number, or doesn't fit in size_t. |
|
4563 // Chances are it's a regular property lookup, so return. |
|
4564 return true; |
|
4565 } |
|
4566 if (!ok || index >= length) { |
|
4567 JS_ReportError(cx, "invalid index"); |
|
4568 return false; |
|
4569 } |
|
4570 |
|
4571 JSObject* baseType = GetBaseType(typeObj); |
|
4572 size_t elementSize = CType::GetSize(baseType); |
|
4573 char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index; |
|
4574 return ImplicitConvert(cx, vp, baseType, data, false, nullptr); |
|
4575 } |
|
4576 |
|
4577 bool |
|
4578 ArrayType::AddressOfElement(JSContext* cx, unsigned argc, jsval* vp) |
|
4579 { |
|
4580 CallArgs args = CallArgsFromVp(argc, vp); |
|
4581 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); |
|
4582 if (!obj) |
|
4583 return false; |
|
4584 if (!CData::IsCData(obj)) { |
|
4585 JS_ReportError(cx, "not a CData"); |
|
4586 return false; |
|
4587 } |
|
4588 |
|
4589 RootedObject typeObj(cx, CData::GetCType(obj)); |
|
4590 if (CType::GetTypeCode(typeObj) != TYPE_array) { |
|
4591 JS_ReportError(cx, "not an ArrayType"); |
|
4592 return false; |
|
4593 } |
|
4594 |
|
4595 if (args.length() != 1) { |
|
4596 JS_ReportError(cx, "addressOfElement takes one argument"); |
|
4597 return false; |
|
4598 } |
|
4599 |
|
4600 RootedObject baseType(cx, GetBaseType(typeObj)); |
|
4601 RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType)); |
|
4602 if (!pointerType) |
|
4603 return false; |
|
4604 |
|
4605 // Create a PointerType CData object containing null. |
|
4606 RootedObject result(cx, CData::Create(cx, pointerType, NullPtr(), nullptr, true)); |
|
4607 if (!result) |
|
4608 return false; |
|
4609 |
|
4610 args.rval().setObject(*result); |
|
4611 |
|
4612 // Convert the index to a size_t and bounds-check it. |
|
4613 size_t index; |
|
4614 size_t length = GetLength(typeObj); |
|
4615 if (!jsvalToSize(cx, args[0], false, &index) || |
|
4616 index >= length) { |
|
4617 JS_ReportError(cx, "invalid index"); |
|
4618 return false; |
|
4619 } |
|
4620 |
|
4621 // Manually set the pointer inside the object, so we skip the conversion step. |
|
4622 void** data = static_cast<void**>(CData::GetData(result)); |
|
4623 size_t elementSize = CType::GetSize(baseType); |
|
4624 *data = static_cast<char*>(CData::GetData(obj)) + elementSize * index; |
|
4625 return true; |
|
4626 } |
|
4627 |
|
4628 /******************************************************************************* |
|
4629 ** StructType implementation |
|
4630 *******************************************************************************/ |
|
4631 |
|
4632 // For a struct field descriptor 'val' of the form { name : type }, extract |
|
4633 // 'name' and 'type'. |
|
4634 static JSFlatString* |
|
4635 ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) |
|
4636 { |
|
4637 if (JSVAL_IS_PRIMITIVE(val)) { |
|
4638 JS_ReportError(cx, "struct field descriptors require a valid name and type"); |
|
4639 return nullptr; |
|
4640 } |
|
4641 |
|
4642 RootedObject obj(cx, JSVAL_TO_OBJECT(val)); |
|
4643 RootedObject iter(cx, JS_NewPropertyIterator(cx, obj)); |
|
4644 if (!iter) |
|
4645 return nullptr; |
|
4646 |
|
4647 RootedId nameid(cx); |
|
4648 if (!JS_NextProperty(cx, iter, nameid.address())) |
|
4649 return nullptr; |
|
4650 if (JSID_IS_VOID(nameid)) { |
|
4651 JS_ReportError(cx, "struct field descriptors require a valid name and type"); |
|
4652 return nullptr; |
|
4653 } |
|
4654 |
|
4655 if (!JSID_IS_STRING(nameid)) { |
|
4656 JS_ReportError(cx, "struct field descriptors require a valid name and type"); |
|
4657 return nullptr; |
|
4658 } |
|
4659 |
|
4660 // make sure we have one, and only one, property |
|
4661 jsid id; |
|
4662 if (!JS_NextProperty(cx, iter, &id)) |
|
4663 return nullptr; |
|
4664 if (!JSID_IS_VOID(id)) { |
|
4665 JS_ReportError(cx, "struct field descriptors must contain one property"); |
|
4666 return nullptr; |
|
4667 } |
|
4668 |
|
4669 RootedValue propVal(cx); |
|
4670 if (!JS_GetPropertyById(cx, obj, nameid, &propVal)) |
|
4671 return nullptr; |
|
4672 |
|
4673 if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) { |
|
4674 JS_ReportError(cx, "struct field descriptors require a valid name and type"); |
|
4675 return nullptr; |
|
4676 } |
|
4677 |
|
4678 // Undefined size or zero size struct members are illegal. |
|
4679 // (Zero-size arrays are legal as struct members in C++, but libffi will |
|
4680 // choke on a zero-size struct, so we disallow them.) |
|
4681 *typeObj = &propVal.toObject(); |
|
4682 size_t size; |
|
4683 if (!CType::GetSafeSize(*typeObj, &size) || size == 0) { |
|
4684 JS_ReportError(cx, "struct field types must have defined and nonzero size"); |
|
4685 return nullptr; |
|
4686 } |
|
4687 |
|
4688 return JSID_TO_FLAT_STRING(nameid); |
|
4689 } |
|
4690 |
|
4691 // For a struct field with 'name' and 'type', add an element of the form |
|
4692 // { name : type }. |
|
4693 static bool |
|
4694 AddFieldToArray(JSContext* cx, |
|
4695 jsval* element, |
|
4696 JSFlatString* name_, |
|
4697 JSObject* typeObj_) |
|
4698 { |
|
4699 RootedObject typeObj(cx, typeObj_); |
|
4700 Rooted<JSFlatString*> name(cx, name_); |
|
4701 RootedObject fieldObj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr())); |
|
4702 if (!fieldObj) |
|
4703 return false; |
|
4704 |
|
4705 *element = OBJECT_TO_JSVAL(fieldObj); |
|
4706 |
|
4707 if (!JS_DefineUCProperty(cx, fieldObj, |
|
4708 name->chars(), name->length(), |
|
4709 OBJECT_TO_JSVAL(typeObj), nullptr, nullptr, |
|
4710 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
|
4711 return false; |
|
4712 |
|
4713 return JS_FreezeObject(cx, fieldObj); |
|
4714 } |
|
4715 |
|
4716 bool |
|
4717 StructType::Create(JSContext* cx, unsigned argc, jsval* vp) |
|
4718 { |
|
4719 CallArgs args = CallArgsFromVp(argc, vp); |
|
4720 |
|
4721 // Construct and return a new StructType object. |
|
4722 if (args.length() < 1 || args.length() > 2) { |
|
4723 JS_ReportError(cx, "StructType takes one or two arguments"); |
|
4724 return false; |
|
4725 } |
|
4726 |
|
4727 jsval name = args[0]; |
|
4728 if (!name.isString()) { |
|
4729 JS_ReportError(cx, "first argument must be a string"); |
|
4730 return false; |
|
4731 } |
|
4732 |
|
4733 // Get ctypes.StructType.prototype from the ctypes.StructType constructor. |
|
4734 RootedObject typeProto(cx, CType::GetProtoFromCtor(&args.callee(), SLOT_STRUCTPROTO)); |
|
4735 |
|
4736 // Create a simple StructType with no defined fields. The result will be |
|
4737 // non-instantiable as CData, will have no 'prototype' property, and will |
|
4738 // have undefined size and alignment and no ffi_type. |
|
4739 RootedObject result(cx, CType::Create(cx, typeProto, NullPtr(), TYPE_struct, |
|
4740 JSVAL_TO_STRING(name), JSVAL_VOID, JSVAL_VOID, nullptr)); |
|
4741 if (!result) |
|
4742 return false; |
|
4743 |
|
4744 if (args.length() == 2) { |
|
4745 RootedObject arr(cx, JSVAL_IS_PRIMITIVE(args[1]) ? nullptr : &args[1].toObject()); |
|
4746 if (!arr || !JS_IsArrayObject(cx, arr)) { |
|
4747 JS_ReportError(cx, "second argument must be an array"); |
|
4748 return false; |
|
4749 } |
|
4750 |
|
4751 // Define the struct fields. |
|
4752 if (!DefineInternal(cx, result, arr)) |
|
4753 return false; |
|
4754 } |
|
4755 |
|
4756 args.rval().setObject(*result); |
|
4757 return true; |
|
4758 } |
|
4759 |
|
4760 static void |
|
4761 PostBarrierCallback(JSTracer *trc, JSString *key, void *data) |
|
4762 { |
|
4763 typedef HashMap<JSFlatString*, |
|
4764 UnbarrieredFieldInfo, |
|
4765 FieldHashPolicy, |
|
4766 SystemAllocPolicy> UnbarrieredFieldInfoHash; |
|
4767 |
|
4768 UnbarrieredFieldInfoHash *table = reinterpret_cast<UnbarrieredFieldInfoHash*>(data); |
|
4769 JSString *prior = key; |
|
4770 JS_CallStringTracer(trc, &key, "CType fieldName"); |
|
4771 table->rekeyIfMoved(JS_ASSERT_STRING_IS_FLAT(prior), JS_ASSERT_STRING_IS_FLAT(key)); |
|
4772 } |
|
4773 |
|
4774 bool |
|
4775 StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsObj_) |
|
4776 { |
|
4777 RootedObject typeObj(cx, typeObj_); |
|
4778 RootedObject fieldsObj(cx, fieldsObj_); |
|
4779 |
|
4780 uint32_t len; |
|
4781 ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len)); |
|
4782 |
|
4783 // Get the common prototype for CData objects of this type from |
|
4784 // ctypes.CType.prototype. |
|
4785 RootedObject dataProto(cx, CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO)); |
|
4786 if (!dataProto) |
|
4787 return false; |
|
4788 |
|
4789 // Set up the 'prototype' and 'prototype.constructor' properties. |
|
4790 // The prototype will reflect the struct fields as properties on CData objects |
|
4791 // created from this type. |
|
4792 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, NullPtr())); |
|
4793 if (!prototype) |
|
4794 return false; |
|
4795 |
|
4796 if (!JS_DefineProperty(cx, prototype, "constructor", typeObj, |
|
4797 JSPROP_READONLY | JSPROP_PERMANENT)) |
|
4798 return false; |
|
4799 |
|
4800 // Create a FieldInfoHash to stash on the type object, and an array to root |
|
4801 // its constituents. (We cannot simply stash the hash in a reserved slot now |
|
4802 // to get GC safety for free, since if anything in this function fails we |
|
4803 // do not want to mutate 'typeObj'.) |
|
4804 AutoPtr<FieldInfoHash> fields(cx->new_<FieldInfoHash>()); |
|
4805 if (!fields || !fields->init(len)) { |
|
4806 JS_ReportOutOfMemory(cx); |
|
4807 return false; |
|
4808 } |
|
4809 JS::AutoValueVector fieldRoots(cx); |
|
4810 if (!fieldRoots.resize(len)) { |
|
4811 JS_ReportOutOfMemory(cx); |
|
4812 return false; |
|
4813 } |
|
4814 |
|
4815 // Process the field types. |
|
4816 size_t structSize, structAlign; |
|
4817 if (len != 0) { |
|
4818 structSize = 0; |
|
4819 structAlign = 0; |
|
4820 |
|
4821 for (uint32_t i = 0; i < len; ++i) { |
|
4822 RootedValue item(cx); |
|
4823 if (!JS_GetElement(cx, fieldsObj, i, &item)) |
|
4824 return false; |
|
4825 |
|
4826 RootedObject fieldType(cx, nullptr); |
|
4827 Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, fieldType.address())); |
|
4828 if (!name) |
|
4829 return false; |
|
4830 fieldRoots[i] = JS::ObjectValue(*fieldType); |
|
4831 |
|
4832 // Make sure each field name is unique |
|
4833 FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name); |
|
4834 if (entryPtr) { |
|
4835 JS_ReportError(cx, "struct fields must have unique names"); |
|
4836 return false; |
|
4837 } |
|
4838 |
|
4839 // Add the field to the StructType's 'prototype' property. |
|
4840 if (!JS_DefineUCProperty(cx, prototype, |
|
4841 name->chars(), name->length(), JSVAL_VOID, |
|
4842 StructType::FieldGetter, StructType::FieldSetter, |
|
4843 JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT)) |
|
4844 return false; |
|
4845 |
|
4846 size_t fieldSize = CType::GetSize(fieldType); |
|
4847 size_t fieldAlign = CType::GetAlignment(fieldType); |
|
4848 size_t fieldOffset = Align(structSize, fieldAlign); |
|
4849 // Check for overflow. Since we hold invariant that fieldSize % fieldAlign |
|
4850 // be zero, we can safely check fieldOffset + fieldSize without first |
|
4851 // checking fieldOffset for overflow. |
|
4852 if (fieldOffset + fieldSize < structSize) { |
|
4853 JS_ReportError(cx, "size overflow"); |
|
4854 return false; |
|
4855 } |
|
4856 |
|
4857 // Add field name to the hash |
|
4858 FieldInfo info; |
|
4859 info.mType = fieldType; |
|
4860 info.mIndex = i; |
|
4861 info.mOffset = fieldOffset; |
|
4862 ASSERT_OK(fields->add(entryPtr, name, info)); |
|
4863 JS_StoreStringPostBarrierCallback(cx, PostBarrierCallback, name, fields.get()); |
|
4864 |
|
4865 structSize = fieldOffset + fieldSize; |
|
4866 |
|
4867 if (fieldAlign > structAlign) |
|
4868 structAlign = fieldAlign; |
|
4869 } |
|
4870 |
|
4871 // Pad the struct tail according to struct alignment. |
|
4872 size_t structTail = Align(structSize, structAlign); |
|
4873 if (structTail < structSize) { |
|
4874 JS_ReportError(cx, "size overflow"); |
|
4875 return false; |
|
4876 } |
|
4877 structSize = structTail; |
|
4878 |
|
4879 } else { |
|
4880 // Empty structs are illegal in C, but are legal and have a size of |
|
4881 // 1 byte in C++. We're going to allow them, and trick libffi into |
|
4882 // believing this by adding a char member. The resulting struct will have |
|
4883 // no getters or setters, and will be initialized to zero. |
|
4884 structSize = 1; |
|
4885 structAlign = 1; |
|
4886 } |
|
4887 |
|
4888 RootedValue sizeVal(cx); |
|
4889 if (!SizeTojsval(cx, structSize, sizeVal.address())) |
|
4890 return false; |
|
4891 |
|
4892 JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.forget())); |
|
4893 |
|
4894 JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal); |
|
4895 JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)); |
|
4896 //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212! |
|
4897 // return false; |
|
4898 JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); |
|
4899 return true; |
|
4900 } |
|
4901 |
|
4902 ffi_type* |
|
4903 StructType::BuildFFIType(JSContext* cx, JSObject* obj) |
|
4904 { |
|
4905 JS_ASSERT(CType::IsCType(obj)); |
|
4906 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); |
|
4907 JS_ASSERT(CType::IsSizeDefined(obj)); |
|
4908 |
|
4909 const FieldInfoHash* fields = GetFieldInfo(obj); |
|
4910 size_t len = fields->count(); |
|
4911 |
|
4912 size_t structSize = CType::GetSize(obj); |
|
4913 size_t structAlign = CType::GetAlignment(obj); |
|
4914 |
|
4915 AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>()); |
|
4916 if (!ffiType) { |
|
4917 JS_ReportOutOfMemory(cx); |
|
4918 return nullptr; |
|
4919 } |
|
4920 ffiType->type = FFI_TYPE_STRUCT; |
|
4921 |
|
4922 AutoPtr<ffi_type*> elements; |
|
4923 if (len != 0) { |
|
4924 elements = cx->pod_malloc<ffi_type*>(len + 1); |
|
4925 if (!elements) { |
|
4926 JS_ReportOutOfMemory(cx); |
|
4927 return nullptr; |
|
4928 } |
|
4929 elements[len] = nullptr; |
|
4930 |
|
4931 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { |
|
4932 const FieldInfoHash::Entry& entry = r.front(); |
|
4933 ffi_type* fieldType = CType::GetFFIType(cx, entry.value().mType); |
|
4934 if (!fieldType) |
|
4935 return nullptr; |
|
4936 elements[entry.value().mIndex] = fieldType; |
|
4937 } |
|
4938 |
|
4939 } else { |
|
4940 // Represent an empty struct as having a size of 1 byte, just like C++. |
|
4941 JS_ASSERT(structSize == 1); |
|
4942 JS_ASSERT(structAlign == 1); |
|
4943 elements = cx->pod_malloc<ffi_type*>(2); |
|
4944 if (!elements) { |
|
4945 JS_ReportOutOfMemory(cx); |
|
4946 return nullptr; |
|
4947 } |
|
4948 elements[0] = &ffi_type_uint8; |
|
4949 elements[1] = nullptr; |
|
4950 } |
|
4951 |
|
4952 ffiType->elements = elements.get(); |
|
4953 |
|
4954 #ifdef DEBUG |
|
4955 // Perform a sanity check: the result of our struct size and alignment |
|
4956 // calculations should match libffi's. We force it to do this calculation |
|
4957 // by calling ffi_prep_cif. |
|
4958 ffi_cif cif; |
|
4959 ffiType->size = 0; |
|
4960 ffiType->alignment = 0; |
|
4961 ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), nullptr); |
|
4962 JS_ASSERT(status == FFI_OK); |
|
4963 JS_ASSERT(structSize == ffiType->size); |
|
4964 JS_ASSERT(structAlign == ffiType->alignment); |
|
4965 #else |
|
4966 // Fill in the ffi_type's size and align fields. This makes libffi treat the |
|
4967 // type as initialized; it will not recompute the values. (We assume |
|
4968 // everything agrees; if it doesn't, we really want to know about it, which |
|
4969 // is the purpose of the above debug-only check.) |
|
4970 ffiType->size = structSize; |
|
4971 ffiType->alignment = structAlign; |
|
4972 #endif |
|
4973 |
|
4974 elements.forget(); |
|
4975 return ffiType.forget(); |
|
4976 } |
|
4977 |
|
4978 bool |
|
4979 StructType::Define(JSContext* cx, unsigned argc, jsval* vp) |
|
4980 { |
|
4981 CallArgs args = CallArgsFromVp(argc, vp); |
|
4982 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); |
|
4983 if (!obj) |
|
4984 return false; |
|
4985 if (!CType::IsCType(obj) || |
|
4986 CType::GetTypeCode(obj) != TYPE_struct) { |
|
4987 JS_ReportError(cx, "not a StructType"); |
|
4988 return false; |
|
4989 } |
|
4990 |
|
4991 if (CType::IsSizeDefined(obj)) { |
|
4992 JS_ReportError(cx, "StructType has already been defined"); |
|
4993 return false; |
|
4994 } |
|
4995 |
|
4996 if (args.length() != 1) { |
|
4997 JS_ReportError(cx, "define takes one argument"); |
|
4998 return false; |
|
4999 } |
|
5000 |
|
5001 jsval arg = args[0]; |
|
5002 if (JSVAL_IS_PRIMITIVE(arg)) { |
|
5003 JS_ReportError(cx, "argument must be an array"); |
|
5004 return false; |
|
5005 } |
|
5006 RootedObject arr(cx, JSVAL_TO_OBJECT(arg)); |
|
5007 if (!JS_IsArrayObject(cx, arr)) { |
|
5008 JS_ReportError(cx, "argument must be an array"); |
|
5009 return false; |
|
5010 } |
|
5011 |
|
5012 return DefineInternal(cx, obj, arr); |
|
5013 } |
|
5014 |
|
5015 bool |
|
5016 StructType::ConstructData(JSContext* cx, |
|
5017 HandleObject obj, |
|
5018 const CallArgs& args) |
|
5019 { |
|
5020 if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) { |
|
5021 JS_ReportError(cx, "not a StructType"); |
|
5022 return false; |
|
5023 } |
|
5024 |
|
5025 if (!CType::IsSizeDefined(obj)) { |
|
5026 JS_ReportError(cx, "cannot construct an opaque StructType"); |
|
5027 return false; |
|
5028 } |
|
5029 |
|
5030 JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true); |
|
5031 if (!result) |
|
5032 return false; |
|
5033 |
|
5034 args.rval().setObject(*result); |
|
5035 |
|
5036 if (args.length() == 0) |
|
5037 return true; |
|
5038 |
|
5039 char* buffer = static_cast<char*>(CData::GetData(result)); |
|
5040 const FieldInfoHash* fields = GetFieldInfo(obj); |
|
5041 |
|
5042 if (args.length() == 1) { |
|
5043 // There are two possible interpretations of the argument: |
|
5044 // 1) It may be an object '{ ... }' with properties representing the |
|
5045 // struct fields intended to ExplicitConvert wholesale to our StructType. |
|
5046 // 2) If the struct contains one field, the arg may be intended to |
|
5047 // ImplicitConvert directly to that arg's CType. |
|
5048 // Thankfully, the conditions for these two possibilities to succeed |
|
5049 // are mutually exclusive, so we can pick the right one. |
|
5050 |
|
5051 // Try option 1) first. |
|
5052 if (ExplicitConvert(cx, args[0], obj, buffer)) |
|
5053 return true; |
|
5054 |
|
5055 if (fields->count() != 1) |
|
5056 return false; |
|
5057 |
|
5058 // If ExplicitConvert failed, and there is no pending exception, then assume |
|
5059 // hard failure (out of memory, or some other similarly serious condition). |
|
5060 if (!JS_IsExceptionPending(cx)) |
|
5061 return false; |
|
5062 |
|
5063 // Otherwise, assume soft failure, and clear the pending exception so that we |
|
5064 // can throw a different one as required. |
|
5065 JS_ClearPendingException(cx); |
|
5066 |
|
5067 // Fall through to try option 2). |
|
5068 } |
|
5069 |
|
5070 // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'. |
|
5071 // ImplicitConvert each field. |
|
5072 if (args.length() == fields->count()) { |
|
5073 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { |
|
5074 const FieldInfo& field = r.front().value(); |
|
5075 STATIC_ASSUME(field.mIndex < fields->count()); /* Quantified invariant */ |
|
5076 if (!ImplicitConvert(cx, args[field.mIndex], field.mType, |
|
5077 buffer + field.mOffset, |
|
5078 false, nullptr)) |
|
5079 return false; |
|
5080 } |
|
5081 |
|
5082 return true; |
|
5083 } |
|
5084 |
|
5085 JS_ReportError(cx, "constructor takes 0, 1, or %u arguments", |
|
5086 fields->count()); |
|
5087 return false; |
|
5088 } |
|
5089 |
|
5090 const FieldInfoHash* |
|
5091 StructType::GetFieldInfo(JSObject* obj) |
|
5092 { |
|
5093 JS_ASSERT(CType::IsCType(obj)); |
|
5094 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); |
|
5095 |
|
5096 jsval slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO); |
|
5097 JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)); |
|
5098 |
|
5099 return static_cast<const FieldInfoHash*>(JSVAL_TO_PRIVATE(slot)); |
|
5100 } |
|
5101 |
|
5102 const FieldInfo* |
|
5103 StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name) |
|
5104 { |
|
5105 JS_ASSERT(CType::IsCType(obj)); |
|
5106 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); |
|
5107 |
|
5108 FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name); |
|
5109 if (ptr) |
|
5110 return &ptr->value(); |
|
5111 |
|
5112 JSAutoByteString bytes(cx, name); |
|
5113 if (!bytes) |
|
5114 return nullptr; |
|
5115 |
|
5116 JS_ReportError(cx, "%s does not name a field", bytes.ptr()); |
|
5117 return nullptr; |
|
5118 } |
|
5119 |
|
5120 JSObject* |
|
5121 StructType::BuildFieldsArray(JSContext* cx, JSObject* obj) |
|
5122 { |
|
5123 JS_ASSERT(CType::IsCType(obj)); |
|
5124 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); |
|
5125 JS_ASSERT(CType::IsSizeDefined(obj)); |
|
5126 |
|
5127 const FieldInfoHash* fields = GetFieldInfo(obj); |
|
5128 size_t len = fields->count(); |
|
5129 |
|
5130 // Prepare a new array for the 'fields' property of the StructType. |
|
5131 JS::AutoValueVector fieldsVec(cx); |
|
5132 if (!fieldsVec.resize(len)) |
|
5133 return nullptr; |
|
5134 |
|
5135 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { |
|
5136 const FieldInfoHash::Entry& entry = r.front(); |
|
5137 // Add the field descriptor to the array. |
|
5138 if (!AddFieldToArray(cx, &fieldsVec[entry.value().mIndex], |
|
5139 entry.key(), entry.value().mType)) |
|
5140 return nullptr; |
|
5141 } |
|
5142 |
|
5143 RootedObject fieldsProp(cx, JS_NewArrayObject(cx, fieldsVec)); |
|
5144 if (!fieldsProp) |
|
5145 return nullptr; |
|
5146 |
|
5147 // Seal the fields array. |
|
5148 if (!JS_FreezeObject(cx, fieldsProp)) |
|
5149 return nullptr; |
|
5150 |
|
5151 return fieldsProp; |
|
5152 } |
|
5153 |
|
5154 /* static */ bool |
|
5155 StructType::IsStruct(HandleValue v) |
|
5156 { |
|
5157 if (!v.isObject()) |
|
5158 return false; |
|
5159 JSObject* obj = &v.toObject(); |
|
5160 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_struct; |
|
5161 } |
|
5162 |
|
5163 bool |
|
5164 StructType::FieldsArrayGetter(JSContext* cx, JS::CallArgs args) |
|
5165 { |
|
5166 RootedObject obj(cx, &args.thisv().toObject()); |
|
5167 |
|
5168 args.rval().set(JS_GetReservedSlot(obj, SLOT_FIELDS)); |
|
5169 |
|
5170 if (!CType::IsSizeDefined(obj)) { |
|
5171 MOZ_ASSERT(args.rval().isUndefined()); |
|
5172 return true; |
|
5173 } |
|
5174 |
|
5175 if (args.rval().isUndefined()) { |
|
5176 // Build the 'fields' array lazily. |
|
5177 JSObject* fields = BuildFieldsArray(cx, obj); |
|
5178 if (!fields) |
|
5179 return false; |
|
5180 JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields)); |
|
5181 |
|
5182 args.rval().setObject(*fields); |
|
5183 } |
|
5184 |
|
5185 MOZ_ASSERT(args.rval().isObject()); |
|
5186 MOZ_ASSERT(JS_IsArrayObject(cx, args.rval())); |
|
5187 return true; |
|
5188 } |
|
5189 |
|
5190 bool |
|
5191 StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp) |
|
5192 { |
|
5193 if (!CData::IsCData(obj)) { |
|
5194 JS_ReportError(cx, "not a CData"); |
|
5195 return false; |
|
5196 } |
|
5197 |
|
5198 JSObject* typeObj = CData::GetCType(obj); |
|
5199 if (CType::GetTypeCode(typeObj) != TYPE_struct) { |
|
5200 JS_ReportError(cx, "not a StructType"); |
|
5201 return false; |
|
5202 } |
|
5203 |
|
5204 const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); |
|
5205 if (!field) |
|
5206 return false; |
|
5207 |
|
5208 char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset; |
|
5209 RootedObject fieldType(cx, field->mType); |
|
5210 return ConvertToJS(cx, fieldType, obj, data, false, false, vp.address()); |
|
5211 } |
|
5212 |
|
5213 bool |
|
5214 StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp) |
|
5215 { |
|
5216 if (!CData::IsCData(obj)) { |
|
5217 JS_ReportError(cx, "not a CData"); |
|
5218 return false; |
|
5219 } |
|
5220 |
|
5221 JSObject* typeObj = CData::GetCType(obj); |
|
5222 if (CType::GetTypeCode(typeObj) != TYPE_struct) { |
|
5223 JS_ReportError(cx, "not a StructType"); |
|
5224 return false; |
|
5225 } |
|
5226 |
|
5227 const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); |
|
5228 if (!field) |
|
5229 return false; |
|
5230 |
|
5231 char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset; |
|
5232 return ImplicitConvert(cx, vp, field->mType, data, false, nullptr); |
|
5233 } |
|
5234 |
|
5235 bool |
|
5236 StructType::AddressOfField(JSContext* cx, unsigned argc, jsval* vp) |
|
5237 { |
|
5238 CallArgs args = CallArgsFromVp(argc, vp); |
|
5239 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); |
|
5240 if (!obj) |
|
5241 return false; |
|
5242 if (!CData::IsCData(obj)) { |
|
5243 JS_ReportError(cx, "not a CData"); |
|
5244 return false; |
|
5245 } |
|
5246 |
|
5247 JSObject* typeObj = CData::GetCType(obj); |
|
5248 if (CType::GetTypeCode(typeObj) != TYPE_struct) { |
|
5249 JS_ReportError(cx, "not a StructType"); |
|
5250 return false; |
|
5251 } |
|
5252 |
|
5253 if (args.length() != 1) { |
|
5254 JS_ReportError(cx, "addressOfField takes one argument"); |
|
5255 return false; |
|
5256 } |
|
5257 |
|
5258 JSFlatString *str = JS_FlattenString(cx, args[0].toString()); |
|
5259 if (!str) |
|
5260 return false; |
|
5261 |
|
5262 const FieldInfo* field = LookupField(cx, typeObj, str); |
|
5263 if (!field) |
|
5264 return false; |
|
5265 |
|
5266 RootedObject baseType(cx, field->mType); |
|
5267 RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType)); |
|
5268 if (!pointerType) |
|
5269 return false; |
|
5270 |
|
5271 // Create a PointerType CData object containing null. |
|
5272 JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true); |
|
5273 if (!result) |
|
5274 return false; |
|
5275 |
|
5276 args.rval().setObject(*result); |
|
5277 |
|
5278 // Manually set the pointer inside the object, so we skip the conversion step. |
|
5279 void** data = static_cast<void**>(CData::GetData(result)); |
|
5280 *data = static_cast<char*>(CData::GetData(obj)) + field->mOffset; |
|
5281 return true; |
|
5282 } |
|
5283 |
|
5284 /******************************************************************************* |
|
5285 ** FunctionType implementation |
|
5286 *******************************************************************************/ |
|
5287 |
|
5288 // Helper class for handling allocation of function arguments. |
|
5289 struct AutoValue |
|
5290 { |
|
5291 AutoValue() : mData(nullptr) { } |
|
5292 |
|
5293 ~AutoValue() |
|
5294 { |
|
5295 js_free(mData); |
|
5296 } |
|
5297 |
|
5298 bool SizeToType(JSContext* cx, JSObject* type) |
|
5299 { |
|
5300 // Allocate a minimum of sizeof(ffi_arg) to handle small integers. |
|
5301 size_t size = Align(CType::GetSize(type), sizeof(ffi_arg)); |
|
5302 mData = js_malloc(size); |
|
5303 if (mData) |
|
5304 memset(mData, 0, size); |
|
5305 return mData != nullptr; |
|
5306 } |
|
5307 |
|
5308 void* mData; |
|
5309 }; |
|
5310 |
|
5311 static bool |
|
5312 GetABI(JSContext* cx, jsval abiType, ffi_abi* result) |
|
5313 { |
|
5314 if (JSVAL_IS_PRIMITIVE(abiType)) |
|
5315 return false; |
|
5316 |
|
5317 ABICode abi = GetABICode(JSVAL_TO_OBJECT(abiType)); |
|
5318 |
|
5319 // determine the ABI from the subset of those available on the |
|
5320 // given platform. ABI_DEFAULT specifies the default |
|
5321 // C calling convention (cdecl) on each platform. |
|
5322 switch (abi) { |
|
5323 case ABI_DEFAULT: |
|
5324 *result = FFI_DEFAULT_ABI; |
|
5325 return true; |
|
5326 case ABI_STDCALL: |
|
5327 case ABI_WINAPI: |
|
5328 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2) |
|
5329 *result = FFI_STDCALL; |
|
5330 return true; |
|
5331 #elif (defined(_WIN64)) |
|
5332 // We'd like the same code to work across Win32 and Win64, so stdcall_api |
|
5333 // and winapi_abi become aliases to the lone Win64 ABI. |
|
5334 *result = FFI_WIN64; |
|
5335 return true; |
|
5336 #endif |
|
5337 case INVALID_ABI: |
|
5338 break; |
|
5339 } |
|
5340 return false; |
|
5341 } |
|
5342 |
|
5343 static JSObject* |
|
5344 PrepareType(JSContext* cx, jsval type) |
|
5345 { |
|
5346 if (JSVAL_IS_PRIMITIVE(type) || |
|
5347 !CType::IsCType(JSVAL_TO_OBJECT(type))) { |
|
5348 JS_ReportError(cx, "not a ctypes type"); |
|
5349 return nullptr; |
|
5350 } |
|
5351 |
|
5352 JSObject* result = JSVAL_TO_OBJECT(type); |
|
5353 TypeCode typeCode = CType::GetTypeCode(result); |
|
5354 |
|
5355 if (typeCode == TYPE_array) { |
|
5356 // convert array argument types to pointers, just like C. |
|
5357 // ImplicitConvert will do the same, when passing an array as data. |
|
5358 RootedObject baseType(cx, ArrayType::GetBaseType(result)); |
|
5359 result = PointerType::CreateInternal(cx, baseType); |
|
5360 if (!result) |
|
5361 return nullptr; |
|
5362 |
|
5363 } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) { |
|
5364 // disallow void or function argument types |
|
5365 JS_ReportError(cx, "Cannot have void or function argument type"); |
|
5366 return nullptr; |
|
5367 } |
|
5368 |
|
5369 if (!CType::IsSizeDefined(result)) { |
|
5370 JS_ReportError(cx, "Argument type must have defined size"); |
|
5371 return nullptr; |
|
5372 } |
|
5373 |
|
5374 // libffi cannot pass types of zero size by value. |
|
5375 JS_ASSERT(CType::GetSize(result) != 0); |
|
5376 |
|
5377 return result; |
|
5378 } |
|
5379 |
|
5380 static JSObject* |
|
5381 PrepareReturnType(JSContext* cx, jsval type) |
|
5382 { |
|
5383 if (JSVAL_IS_PRIMITIVE(type) || |
|
5384 !CType::IsCType(JSVAL_TO_OBJECT(type))) { |
|
5385 JS_ReportError(cx, "not a ctypes type"); |
|
5386 return nullptr; |
|
5387 } |
|
5388 |
|
5389 JSObject* result = JSVAL_TO_OBJECT(type); |
|
5390 TypeCode typeCode = CType::GetTypeCode(result); |
|
5391 |
|
5392 // Arrays and functions can never be return types. |
|
5393 if (typeCode == TYPE_array || typeCode == TYPE_function) { |
|
5394 JS_ReportError(cx, "Return type cannot be an array or function"); |
|
5395 return nullptr; |
|
5396 } |
|
5397 |
|
5398 if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) { |
|
5399 JS_ReportError(cx, "Return type must have defined size"); |
|
5400 return nullptr; |
|
5401 } |
|
5402 |
|
5403 // libffi cannot pass types of zero size by value. |
|
5404 JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0); |
|
5405 |
|
5406 return result; |
|
5407 } |
|
5408 |
|
5409 static MOZ_ALWAYS_INLINE bool |
|
5410 IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis) |
|
5411 { |
|
5412 *isEllipsis = false; |
|
5413 if (!JSVAL_IS_STRING(v)) |
|
5414 return true; |
|
5415 JSString* str = JSVAL_TO_STRING(v); |
|
5416 if (str->length() != 3) |
|
5417 return true; |
|
5418 const jschar* chars = str->getChars(cx); |
|
5419 if (!chars) |
|
5420 return false; |
|
5421 jschar dot = '.'; |
|
5422 *isEllipsis = (chars[0] == dot && |
|
5423 chars[1] == dot && |
|
5424 chars[2] == dot); |
|
5425 return true; |
|
5426 } |
|
5427 |
|
5428 static bool |
|
5429 PrepareCIF(JSContext* cx, |
|
5430 FunctionInfo* fninfo) |
|
5431 { |
|
5432 ffi_abi abi; |
|
5433 if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) { |
|
5434 JS_ReportError(cx, "Invalid ABI specification"); |
|
5435 return false; |
|
5436 } |
|
5437 |
|
5438 ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType); |
|
5439 if (!rtype) |
|
5440 return false; |
|
5441 |
|
5442 ffi_status status = |
|
5443 ffi_prep_cif(&fninfo->mCIF, |
|
5444 abi, |
|
5445 fninfo->mFFITypes.length(), |
|
5446 rtype, |
|
5447 fninfo->mFFITypes.begin()); |
|
5448 |
|
5449 switch (status) { |
|
5450 case FFI_OK: |
|
5451 return true; |
|
5452 case FFI_BAD_ABI: |
|
5453 JS_ReportError(cx, "Invalid ABI specification"); |
|
5454 return false; |
|
5455 case FFI_BAD_TYPEDEF: |
|
5456 JS_ReportError(cx, "Invalid type specification"); |
|
5457 return false; |
|
5458 default: |
|
5459 JS_ReportError(cx, "Unknown libffi error"); |
|
5460 return false; |
|
5461 } |
|
5462 } |
|
5463 |
|
5464 void |
|
5465 FunctionType::BuildSymbolName(JSString* name, |
|
5466 JSObject* typeObj, |
|
5467 AutoCString& result) |
|
5468 { |
|
5469 FunctionInfo* fninfo = GetFunctionInfo(typeObj); |
|
5470 |
|
5471 switch (GetABICode(fninfo->mABI)) { |
|
5472 case ABI_DEFAULT: |
|
5473 case ABI_WINAPI: |
|
5474 // For cdecl or WINAPI functions, no mangling is necessary. |
|
5475 AppendString(result, name); |
|
5476 break; |
|
5477 |
|
5478 case ABI_STDCALL: { |
|
5479 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2) |
|
5480 // On WIN32, stdcall functions look like: |
|
5481 // _foo@40 |
|
5482 // where 'foo' is the function name, and '40' is the aligned size of the |
|
5483 // arguments. |
|
5484 AppendString(result, "_"); |
|
5485 AppendString(result, name); |
|
5486 AppendString(result, "@"); |
|
5487 |
|
5488 // Compute the suffix by aligning each argument to sizeof(ffi_arg). |
|
5489 size_t size = 0; |
|
5490 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { |
|
5491 JSObject* argType = fninfo->mArgTypes[i]; |
|
5492 size += Align(CType::GetSize(argType), sizeof(ffi_arg)); |
|
5493 } |
|
5494 |
|
5495 IntegerToString(size, 10, result); |
|
5496 #elif defined(_WIN64) |
|
5497 // On Win64, stdcall is an alias to the default ABI for compatibility, so no |
|
5498 // mangling is done. |
|
5499 AppendString(result, name); |
|
5500 #endif |
|
5501 break; |
|
5502 } |
|
5503 |
|
5504 case INVALID_ABI: |
|
5505 MOZ_ASSUME_UNREACHABLE("invalid abi"); |
|
5506 } |
|
5507 } |
|
5508 |
|
5509 static FunctionInfo* |
|
5510 NewFunctionInfo(JSContext* cx, |
|
5511 jsval abiType, |
|
5512 jsval returnType, |
|
5513 jsval* argTypes, |
|
5514 unsigned argLength) |
|
5515 { |
|
5516 AutoPtr<FunctionInfo> fninfo(cx->new_<FunctionInfo>()); |
|
5517 if (!fninfo) { |
|
5518 JS_ReportOutOfMemory(cx); |
|
5519 return nullptr; |
|
5520 } |
|
5521 |
|
5522 ffi_abi abi; |
|
5523 if (!GetABI(cx, abiType, &abi)) { |
|
5524 JS_ReportError(cx, "Invalid ABI specification"); |
|
5525 return nullptr; |
|
5526 } |
|
5527 fninfo->mABI = JSVAL_TO_OBJECT(abiType); |
|
5528 |
|
5529 // prepare the result type |
|
5530 fninfo->mReturnType = PrepareReturnType(cx, returnType); |
|
5531 if (!fninfo->mReturnType) |
|
5532 return nullptr; |
|
5533 |
|
5534 // prepare the argument types |
|
5535 if (!fninfo->mArgTypes.reserve(argLength) || |
|
5536 !fninfo->mFFITypes.reserve(argLength)) { |
|
5537 JS_ReportOutOfMemory(cx); |
|
5538 return nullptr; |
|
5539 } |
|
5540 |
|
5541 fninfo->mIsVariadic = false; |
|
5542 |
|
5543 for (uint32_t i = 0; i < argLength; ++i) { |
|
5544 bool isEllipsis; |
|
5545 if (!IsEllipsis(cx, argTypes[i], &isEllipsis)) |
|
5546 return nullptr; |
|
5547 if (isEllipsis) { |
|
5548 fninfo->mIsVariadic = true; |
|
5549 if (i < 1) { |
|
5550 JS_ReportError(cx, "\"...\" may not be the first and only parameter " |
|
5551 "type of a variadic function declaration"); |
|
5552 return nullptr; |
|
5553 } |
|
5554 if (i < argLength - 1) { |
|
5555 JS_ReportError(cx, "\"...\" must be the last parameter type of a " |
|
5556 "variadic function declaration"); |
|
5557 return nullptr; |
|
5558 } |
|
5559 if (GetABICode(fninfo->mABI) != ABI_DEFAULT) { |
|
5560 JS_ReportError(cx, "Variadic functions must use the __cdecl calling " |
|
5561 "convention"); |
|
5562 return nullptr; |
|
5563 } |
|
5564 break; |
|
5565 } |
|
5566 |
|
5567 JSObject* argType = PrepareType(cx, argTypes[i]); |
|
5568 if (!argType) |
|
5569 return nullptr; |
|
5570 |
|
5571 ffi_type* ffiType = CType::GetFFIType(cx, argType); |
|
5572 if (!ffiType) |
|
5573 return nullptr; |
|
5574 |
|
5575 fninfo->mArgTypes.infallibleAppend(argType); |
|
5576 fninfo->mFFITypes.infallibleAppend(ffiType); |
|
5577 } |
|
5578 |
|
5579 if (fninfo->mIsVariadic) |
|
5580 // wait to PrepareCIF until function is called |
|
5581 return fninfo.forget(); |
|
5582 |
|
5583 if (!PrepareCIF(cx, fninfo.get())) |
|
5584 return nullptr; |
|
5585 |
|
5586 return fninfo.forget(); |
|
5587 } |
|
5588 |
|
5589 bool |
|
5590 FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp) |
|
5591 { |
|
5592 // Construct and return a new FunctionType object. |
|
5593 CallArgs args = CallArgsFromVp(argc, vp); |
|
5594 if (args.length() < 2 || args.length() > 3) { |
|
5595 JS_ReportError(cx, "FunctionType takes two or three arguments"); |
|
5596 return false; |
|
5597 } |
|
5598 |
|
5599 AutoValueVector argTypes(cx); |
|
5600 RootedObject arrayObj(cx, nullptr); |
|
5601 |
|
5602 if (args.length() == 3) { |
|
5603 // Prepare an array of jsvals for the arguments. |
|
5604 if (!JSVAL_IS_PRIMITIVE(args[2])) |
|
5605 arrayObj = &args[2].toObject(); |
|
5606 if (!arrayObj || !JS_IsArrayObject(cx, arrayObj)) { |
|
5607 JS_ReportError(cx, "third argument must be an array"); |
|
5608 return false; |
|
5609 } |
|
5610 |
|
5611 uint32_t len; |
|
5612 ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len)); |
|
5613 |
|
5614 if (!argTypes.resize(len)) { |
|
5615 JS_ReportOutOfMemory(cx); |
|
5616 return false; |
|
5617 } |
|
5618 } |
|
5619 |
|
5620 // Pull out the argument types from the array, if any. |
|
5621 JS_ASSERT_IF(argTypes.length(), arrayObj); |
|
5622 for (uint32_t i = 0; i < argTypes.length(); ++i) { |
|
5623 if (!JS_GetElement(cx, arrayObj, i, argTypes.handleAt(i))) |
|
5624 return false; |
|
5625 } |
|
5626 |
|
5627 JSObject* result = CreateInternal(cx, args[0], args[1], |
|
5628 argTypes.begin(), argTypes.length()); |
|
5629 if (!result) |
|
5630 return false; |
|
5631 |
|
5632 args.rval().setObject(*result); |
|
5633 return true; |
|
5634 } |
|
5635 |
|
5636 JSObject* |
|
5637 FunctionType::CreateInternal(JSContext* cx, |
|
5638 jsval abi, |
|
5639 jsval rtype, |
|
5640 jsval* argtypes, |
|
5641 unsigned arglen) |
|
5642 { |
|
5643 // Determine and check the types, and prepare the function CIF. |
|
5644 AutoPtr<FunctionInfo> fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen)); |
|
5645 if (!fninfo) |
|
5646 return nullptr; |
|
5647 |
|
5648 // Get ctypes.FunctionType.prototype and the common prototype for CData objects |
|
5649 // of this type, from ctypes.CType.prototype. |
|
5650 RootedObject typeProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType, |
|
5651 SLOT_FUNCTIONPROTO)); |
|
5652 if (!typeProto) |
|
5653 return nullptr; |
|
5654 RootedObject dataProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType, |
|
5655 SLOT_FUNCTIONDATAPROTO)); |
|
5656 if (!dataProto) |
|
5657 return nullptr; |
|
5658 |
|
5659 // Create a new CType object with the common properties and slots. |
|
5660 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function, |
|
5661 nullptr, JSVAL_VOID, JSVAL_VOID, nullptr); |
|
5662 if (!typeObj) |
|
5663 return nullptr; |
|
5664 |
|
5665 // Stash the FunctionInfo in a reserved slot. |
|
5666 JS_SetReservedSlot(typeObj, SLOT_FNINFO, PRIVATE_TO_JSVAL(fninfo.forget())); |
|
5667 |
|
5668 return typeObj; |
|
5669 } |
|
5670 |
|
5671 // Construct a function pointer to a JS function (see CClosure::Create()). |
|
5672 // Regular function pointers are constructed directly in |
|
5673 // PointerType::ConstructData(). |
|
5674 bool |
|
5675 FunctionType::ConstructData(JSContext* cx, |
|
5676 HandleObject typeObj, |
|
5677 HandleObject dataObj, |
|
5678 HandleObject fnObj, |
|
5679 HandleObject thisObj, |
|
5680 jsval errVal) |
|
5681 { |
|
5682 JS_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function); |
|
5683 |
|
5684 PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj)); |
|
5685 |
|
5686 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); |
|
5687 if (fninfo->mIsVariadic) { |
|
5688 JS_ReportError(cx, "Can't declare a variadic callback function"); |
|
5689 return false; |
|
5690 } |
|
5691 if (GetABICode(fninfo->mABI) == ABI_WINAPI) { |
|
5692 JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, " |
|
5693 "use ctypes.stdcall_abi instead"); |
|
5694 return false; |
|
5695 } |
|
5696 |
|
5697 RootedObject closureObj(cx, CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data)); |
|
5698 if (!closureObj) |
|
5699 return false; |
|
5700 |
|
5701 // Set the closure object as the referent of the new CData object. |
|
5702 JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(closureObj)); |
|
5703 |
|
5704 // Seal the CData object, to prevent modification of the function pointer. |
|
5705 // This permanently associates this object with the closure, and avoids |
|
5706 // having to do things like reset SLOT_REFERENT when someone tries to |
|
5707 // change the pointer value. |
|
5708 // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter |
|
5709 // could be called on a frozen object. |
|
5710 return JS_FreezeObject(cx, dataObj); |
|
5711 } |
|
5712 |
|
5713 typedef Array<AutoValue, 16> AutoValueAutoArray; |
|
5714 |
|
5715 static bool |
|
5716 ConvertArgument(JSContext* cx, |
|
5717 HandleValue arg, |
|
5718 JSObject* type, |
|
5719 AutoValue* value, |
|
5720 AutoValueAutoArray* strings) |
|
5721 { |
|
5722 if (!value->SizeToType(cx, type)) { |
|
5723 JS_ReportAllocationOverflow(cx); |
|
5724 return false; |
|
5725 } |
|
5726 |
|
5727 bool freePointer = false; |
|
5728 if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer)) |
|
5729 return false; |
|
5730 |
|
5731 if (freePointer) { |
|
5732 // ImplicitConvert converted a string for us, which we have to free. |
|
5733 // Keep track of it. |
|
5734 if (!strings->growBy(1)) { |
|
5735 JS_ReportOutOfMemory(cx); |
|
5736 return false; |
|
5737 } |
|
5738 strings->back().mData = *static_cast<char**>(value->mData); |
|
5739 } |
|
5740 |
|
5741 return true; |
|
5742 } |
|
5743 |
|
5744 bool |
|
5745 FunctionType::Call(JSContext* cx, |
|
5746 unsigned argc, |
|
5747 jsval* vp) |
|
5748 { |
|
5749 CallArgs args = CallArgsFromVp(argc, vp); |
|
5750 // get the callee object... |
|
5751 RootedObject obj(cx, &args.callee()); |
|
5752 if (!CData::IsCData(obj)) { |
|
5753 JS_ReportError(cx, "not a CData"); |
|
5754 return false; |
|
5755 } |
|
5756 |
|
5757 RootedObject typeObj(cx, CData::GetCType(obj)); |
|
5758 if (CType::GetTypeCode(typeObj) != TYPE_pointer) { |
|
5759 JS_ReportError(cx, "not a FunctionType.ptr"); |
|
5760 return false; |
|
5761 } |
|
5762 |
|
5763 typeObj = PointerType::GetBaseType(typeObj); |
|
5764 if (CType::GetTypeCode(typeObj) != TYPE_function) { |
|
5765 JS_ReportError(cx, "not a FunctionType.ptr"); |
|
5766 return false; |
|
5767 } |
|
5768 |
|
5769 FunctionInfo* fninfo = GetFunctionInfo(typeObj); |
|
5770 uint32_t argcFixed = fninfo->mArgTypes.length(); |
|
5771 |
|
5772 if ((!fninfo->mIsVariadic && args.length() != argcFixed) || |
|
5773 (fninfo->mIsVariadic && args.length() < argcFixed)) { |
|
5774 JS_ReportError(cx, "Number of arguments does not match declaration"); |
|
5775 return false; |
|
5776 } |
|
5777 |
|
5778 // Check if we have a Library object. If we do, make sure it's open. |
|
5779 jsval slot = JS_GetReservedSlot(obj, SLOT_REFERENT); |
|
5780 if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) { |
|
5781 PRLibrary* library = Library::GetLibrary(&slot.toObject()); |
|
5782 if (!library) { |
|
5783 JS_ReportError(cx, "library is not open"); |
|
5784 return false; |
|
5785 } |
|
5786 } |
|
5787 |
|
5788 // prepare the values for each argument |
|
5789 AutoValueAutoArray values; |
|
5790 AutoValueAutoArray strings; |
|
5791 if (!values.resize(args.length())) { |
|
5792 JS_ReportOutOfMemory(cx); |
|
5793 return false; |
|
5794 } |
|
5795 |
|
5796 for (unsigned i = 0; i < argcFixed; ++i) |
|
5797 if (!ConvertArgument(cx, args[i], fninfo->mArgTypes[i], &values[i], &strings)) |
|
5798 return false; |
|
5799 |
|
5800 if (fninfo->mIsVariadic) { |
|
5801 if (!fninfo->mFFITypes.resize(args.length())) { |
|
5802 JS_ReportOutOfMemory(cx); |
|
5803 return false; |
|
5804 } |
|
5805 |
|
5806 RootedObject obj(cx); // Could reuse obj instead of declaring a second |
|
5807 RootedObject type(cx); // RootedObject, but readability would suffer. |
|
5808 |
|
5809 for (uint32_t i = argcFixed; i < args.length(); ++i) { |
|
5810 if (JSVAL_IS_PRIMITIVE(args[i]) || |
|
5811 !CData::IsCData(obj = &args[i].toObject())) { |
|
5812 // Since we know nothing about the CTypes of the ... arguments, |
|
5813 // they absolutely must be CData objects already. |
|
5814 JS_ReportError(cx, "argument %d of type %s is not a CData object", |
|
5815 i, JS_GetTypeName(cx, JS_TypeOfValue(cx, args[i]))); |
|
5816 return false; |
|
5817 } |
|
5818 if (!(type = CData::GetCType(obj)) || |
|
5819 !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) || |
|
5820 // Relying on ImplicitConvert only for the limited purpose of |
|
5821 // converting one CType to another (e.g., T[] to T*). |
|
5822 !ConvertArgument(cx, args[i], type, &values[i], &strings) || |
|
5823 !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) { |
|
5824 // These functions report their own errors. |
|
5825 return false; |
|
5826 } |
|
5827 } |
|
5828 if (!PrepareCIF(cx, fninfo)) |
|
5829 return false; |
|
5830 } |
|
5831 |
|
5832 // initialize a pointer to an appropriate location, for storing the result |
|
5833 AutoValue returnValue; |
|
5834 TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType); |
|
5835 if (typeCode != TYPE_void_t && |
|
5836 !returnValue.SizeToType(cx, fninfo->mReturnType)) { |
|
5837 JS_ReportAllocationOverflow(cx); |
|
5838 return false; |
|
5839 } |
|
5840 |
|
5841 // Let the runtime callback know that we are about to call into C. |
|
5842 js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALL_BEGIN, js::CTYPES_CALL_END); |
|
5843 |
|
5844 uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj)); |
|
5845 |
|
5846 #if defined(XP_WIN) |
|
5847 int32_t lastErrorStatus; // The status as defined by |GetLastError| |
|
5848 int32_t savedLastError = GetLastError(); |
|
5849 SetLastError(0); |
|
5850 #endif //defined(XP_WIN) |
|
5851 int errnoStatus; // The status as defined by |errno| |
|
5852 int savedErrno = errno; |
|
5853 errno = 0; |
|
5854 |
|
5855 ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData, |
|
5856 reinterpret_cast<void**>(values.begin())); |
|
5857 |
|
5858 // Save error value. |
|
5859 // We need to save it before leaving the scope of |suspend| as destructing |
|
5860 // |suspend| has the side-effect of clearing |GetLastError| |
|
5861 // (see bug 684017). |
|
5862 |
|
5863 errnoStatus = errno; |
|
5864 #if defined(XP_WIN) |
|
5865 lastErrorStatus = GetLastError(); |
|
5866 SetLastError(savedLastError); |
|
5867 #endif // defined(XP_WIN) |
|
5868 |
|
5869 errno = savedErrno; |
|
5870 |
|
5871 // We're no longer calling into C. |
|
5872 autoCallback.DoEndCallback(); |
|
5873 |
|
5874 // Store the error value for later consultation with |ctypes.getStatus| |
|
5875 JSObject *objCTypes = CType::GetGlobalCTypes(cx, typeObj); |
|
5876 if (!objCTypes) |
|
5877 return false; |
|
5878 |
|
5879 JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus)); |
|
5880 #if defined(XP_WIN) |
|
5881 JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus)); |
|
5882 #endif // defined(XP_WIN) |
|
5883 |
|
5884 // Small integer types get returned as a word-sized ffi_arg. Coerce it back |
|
5885 // into the correct size for ConvertToJS. |
|
5886 switch (typeCode) { |
|
5887 #define DEFINE_INT_TYPE(name, type, ffiType) \ |
|
5888 case TYPE_##name: \ |
|
5889 if (sizeof(type) < sizeof(ffi_arg)) { \ |
|
5890 ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData); \ |
|
5891 *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \ |
|
5892 } \ |
|
5893 break; |
|
5894 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
5895 #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
5896 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
5897 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
5898 #include "ctypes/typedefs.h" |
|
5899 default: |
|
5900 break; |
|
5901 } |
|
5902 |
|
5903 // prepare a JS object from the result |
|
5904 RootedObject returnType(cx, fninfo->mReturnType); |
|
5905 return ConvertToJS(cx, returnType, NullPtr(), returnValue.mData, false, true, vp); |
|
5906 } |
|
5907 |
|
5908 FunctionInfo* |
|
5909 FunctionType::GetFunctionInfo(JSObject* obj) |
|
5910 { |
|
5911 JS_ASSERT(CType::IsCType(obj)); |
|
5912 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_function); |
|
5913 |
|
5914 jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO); |
|
5915 JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)); |
|
5916 |
|
5917 return static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)); |
|
5918 } |
|
5919 |
|
5920 bool |
|
5921 FunctionType::IsFunctionType(HandleValue v) |
|
5922 { |
|
5923 if (!v.isObject()) |
|
5924 return false; |
|
5925 JSObject* obj = &v.toObject(); |
|
5926 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_function; |
|
5927 } |
|
5928 |
|
5929 bool |
|
5930 FunctionType::ArgTypesGetter(JSContext* cx, JS::CallArgs args) |
|
5931 { |
|
5932 JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject()); |
|
5933 |
|
5934 args.rval().set(JS_GetReservedSlot(obj, SLOT_ARGS_T)); |
|
5935 if (!args.rval().isUndefined()) |
|
5936 return true; |
|
5937 |
|
5938 FunctionInfo* fninfo = GetFunctionInfo(obj); |
|
5939 size_t len = fninfo->mArgTypes.length(); |
|
5940 |
|
5941 // Prepare a new array. |
|
5942 JS::Rooted<JSObject*> argTypes(cx); |
|
5943 { |
|
5944 JS::AutoValueVector vec(cx); |
|
5945 if (!vec.resize(len)) |
|
5946 return false; |
|
5947 |
|
5948 for (size_t i = 0; i < len; ++i) |
|
5949 vec[i] = JS::ObjectValue(*fninfo->mArgTypes[i]); |
|
5950 |
|
5951 argTypes = JS_NewArrayObject(cx, vec); |
|
5952 if (!argTypes) |
|
5953 return false; |
|
5954 } |
|
5955 |
|
5956 // Seal and cache it. |
|
5957 if (!JS_FreezeObject(cx, argTypes)) |
|
5958 return false; |
|
5959 JS_SetReservedSlot(obj, SLOT_ARGS_T, JS::ObjectValue(*argTypes)); |
|
5960 |
|
5961 args.rval().setObject(*argTypes); |
|
5962 return true; |
|
5963 } |
|
5964 |
|
5965 bool |
|
5966 FunctionType::ReturnTypeGetter(JSContext* cx, JS::CallArgs args) |
|
5967 { |
|
5968 // Get the returnType object from the FunctionInfo. |
|
5969 args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mReturnType); |
|
5970 return true; |
|
5971 } |
|
5972 |
|
5973 bool |
|
5974 FunctionType::ABIGetter(JSContext* cx, JS::CallArgs args) |
|
5975 { |
|
5976 // Get the abi object from the FunctionInfo. |
|
5977 args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mABI); |
|
5978 return true; |
|
5979 } |
|
5980 |
|
5981 bool |
|
5982 FunctionType::IsVariadicGetter(JSContext* cx, JS::CallArgs args) |
|
5983 { |
|
5984 args.rval().setBoolean(GetFunctionInfo(&args.thisv().toObject())->mIsVariadic); |
|
5985 return true; |
|
5986 } |
|
5987 |
|
5988 /******************************************************************************* |
|
5989 ** CClosure implementation |
|
5990 *******************************************************************************/ |
|
5991 |
|
5992 JSObject* |
|
5993 CClosure::Create(JSContext* cx, |
|
5994 HandleObject typeObj, |
|
5995 HandleObject fnObj, |
|
5996 HandleObject thisObj, |
|
5997 jsval errVal_, |
|
5998 PRFuncPtr* fnptr) |
|
5999 { |
|
6000 RootedValue errVal(cx, errVal_); |
|
6001 JS_ASSERT(fnObj); |
|
6002 |
|
6003 RootedObject result(cx, JS_NewObject(cx, &sCClosureClass, NullPtr(), NullPtr())); |
|
6004 if (!result) |
|
6005 return nullptr; |
|
6006 |
|
6007 // Get the FunctionInfo from the FunctionType. |
|
6008 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); |
|
6009 JS_ASSERT(!fninfo->mIsVariadic); |
|
6010 JS_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI); |
|
6011 |
|
6012 AutoPtr<ClosureInfo> cinfo(cx->new_<ClosureInfo>(JS_GetRuntime(cx))); |
|
6013 if (!cinfo) { |
|
6014 JS_ReportOutOfMemory(cx); |
|
6015 return nullptr; |
|
6016 } |
|
6017 |
|
6018 // Get the prototype of the FunctionType object, of class CTypeProto, |
|
6019 // which stores our JSContext for use with the closure. |
|
6020 RootedObject proto(cx); |
|
6021 if (!JS_GetPrototype(cx, typeObj, &proto)) |
|
6022 return nullptr; |
|
6023 JS_ASSERT(proto); |
|
6024 JS_ASSERT(CType::IsCTypeProto(proto)); |
|
6025 |
|
6026 // Get a JSContext for use with the closure. |
|
6027 cinfo->cx = js::DefaultJSContext(JS_GetRuntime(cx)); |
|
6028 |
|
6029 // Prepare the error sentinel value. It's important to do this now, because |
|
6030 // we might be unable to convert the value to the proper type. If so, we want |
|
6031 // the caller to know about it _now_, rather than some uncertain time in the |
|
6032 // future when the error sentinel is actually needed. |
|
6033 if (!JSVAL_IS_VOID(errVal)) { |
|
6034 |
|
6035 // Make sure the callback returns something. |
|
6036 if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) { |
|
6037 JS_ReportError(cx, "A void callback can't pass an error sentinel"); |
|
6038 return nullptr; |
|
6039 } |
|
6040 |
|
6041 // With the exception of void, the FunctionType constructor ensures that |
|
6042 // the return type has a defined size. |
|
6043 JS_ASSERT(CType::IsSizeDefined(fninfo->mReturnType)); |
|
6044 |
|
6045 // Allocate a buffer for the return value. |
|
6046 size_t rvSize = CType::GetSize(fninfo->mReturnType); |
|
6047 cinfo->errResult = cx->malloc_(rvSize); |
|
6048 if (!cinfo->errResult) |
|
6049 return nullptr; |
|
6050 |
|
6051 // Do the value conversion. This might fail, in which case we throw. |
|
6052 if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult, |
|
6053 false, nullptr)) |
|
6054 return nullptr; |
|
6055 } else { |
|
6056 cinfo->errResult = nullptr; |
|
6057 } |
|
6058 |
|
6059 // Copy the important bits of context into cinfo. |
|
6060 cinfo->closureObj = result; |
|
6061 cinfo->typeObj = typeObj; |
|
6062 cinfo->thisObj = thisObj; |
|
6063 cinfo->jsfnObj = fnObj; |
|
6064 |
|
6065 // Create an ffi_closure object and initialize it. |
|
6066 void* code; |
|
6067 cinfo->closure = |
|
6068 static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &code)); |
|
6069 if (!cinfo->closure || !code) { |
|
6070 JS_ReportError(cx, "couldn't create closure - libffi error"); |
|
6071 return nullptr; |
|
6072 } |
|
6073 |
|
6074 ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF, |
|
6075 CClosure::ClosureStub, cinfo.get(), code); |
|
6076 if (status != FFI_OK) { |
|
6077 JS_ReportError(cx, "couldn't create closure - libffi error"); |
|
6078 return nullptr; |
|
6079 } |
|
6080 |
|
6081 // Stash the ClosureInfo struct on our new object. |
|
6082 JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo.forget())); |
|
6083 |
|
6084 // Casting between void* and a function pointer is forbidden in C and C++. |
|
6085 // Do it via an integral type. |
|
6086 *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code)); |
|
6087 return result; |
|
6088 } |
|
6089 |
|
6090 void |
|
6091 CClosure::Trace(JSTracer* trc, JSObject* obj) |
|
6092 { |
|
6093 // Make sure our ClosureInfo slot is legit. If it's not, bail. |
|
6094 jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO); |
|
6095 if (JSVAL_IS_VOID(slot)) |
|
6096 return; |
|
6097 |
|
6098 ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot)); |
|
6099 |
|
6100 // Identify our objects to the tracer. (There's no need to identify |
|
6101 // 'closureObj', since that's us.) |
|
6102 JS_CallHeapObjectTracer(trc, &cinfo->typeObj, "typeObj"); |
|
6103 JS_CallHeapObjectTracer(trc, &cinfo->jsfnObj, "jsfnObj"); |
|
6104 if (cinfo->thisObj) |
|
6105 JS_CallHeapObjectTracer(trc, &cinfo->thisObj, "thisObj"); |
|
6106 } |
|
6107 |
|
6108 void |
|
6109 CClosure::Finalize(JSFreeOp *fop, JSObject* obj) |
|
6110 { |
|
6111 // Make sure our ClosureInfo slot is legit. If it's not, bail. |
|
6112 jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO); |
|
6113 if (JSVAL_IS_VOID(slot)) |
|
6114 return; |
|
6115 |
|
6116 ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot)); |
|
6117 FreeOp::get(fop)->delete_(cinfo); |
|
6118 } |
|
6119 |
|
6120 void |
|
6121 CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) |
|
6122 { |
|
6123 JS_ASSERT(cif); |
|
6124 JS_ASSERT(result); |
|
6125 JS_ASSERT(args); |
|
6126 JS_ASSERT(userData); |
|
6127 |
|
6128 // Retrieve the essentials from our closure object. |
|
6129 ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData); |
|
6130 JSContext* cx = cinfo->cx; |
|
6131 |
|
6132 // Let the runtime callback know that we are about to call into JS again. The end callback will |
|
6133 // fire automatically when we exit this function. |
|
6134 js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALLBACK_BEGIN, |
|
6135 js::CTYPES_CALLBACK_END); |
|
6136 |
|
6137 RootedObject typeObj(cx, cinfo->typeObj); |
|
6138 RootedObject thisObj(cx, cinfo->thisObj); |
|
6139 RootedValue jsfnVal(cx, ObjectValue(*cinfo->jsfnObj)); |
|
6140 |
|
6141 JS_AbortIfWrongThread(JS_GetRuntime(cx)); |
|
6142 |
|
6143 JSAutoRequest ar(cx); |
|
6144 JSAutoCompartment ac(cx, cinfo->jsfnObj); |
|
6145 |
|
6146 // Assert that our CIFs agree. |
|
6147 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); |
|
6148 JS_ASSERT(cif == &fninfo->mCIF); |
|
6149 |
|
6150 TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType); |
|
6151 |
|
6152 // Initialize the result to zero, in case something fails. Small integer types |
|
6153 // are promoted to a word-sized ffi_arg, so we must be careful to zero the |
|
6154 // whole word. |
|
6155 size_t rvSize = 0; |
|
6156 if (cif->rtype != &ffi_type_void) { |
|
6157 rvSize = cif->rtype->size; |
|
6158 switch (typeCode) { |
|
6159 #define DEFINE_INT_TYPE(name, type, ffiType) \ |
|
6160 case TYPE_##name: |
|
6161 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
6162 #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
6163 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
6164 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
6165 #include "ctypes/typedefs.h" |
|
6166 rvSize = Align(rvSize, sizeof(ffi_arg)); |
|
6167 break; |
|
6168 default: |
|
6169 break; |
|
6170 } |
|
6171 memset(result, 0, rvSize); |
|
6172 } |
|
6173 |
|
6174 // Set up an array for converted arguments. |
|
6175 JS::AutoValueVector argv(cx); |
|
6176 if (!argv.resize(cif->nargs)) { |
|
6177 JS_ReportOutOfMemory(cx); |
|
6178 return; |
|
6179 } |
|
6180 |
|
6181 for (uint32_t i = 0; i < cif->nargs; ++i) { |
|
6182 // Convert each argument, and have any CData objects created depend on |
|
6183 // the existing buffers. |
|
6184 RootedObject argType(cx, fninfo->mArgTypes[i]); |
|
6185 if (!ConvertToJS(cx, argType, NullPtr(), args[i], false, false, &argv[i])) |
|
6186 return; |
|
6187 } |
|
6188 |
|
6189 // Call the JS function. 'thisObj' may be nullptr, in which case the JS |
|
6190 // engine will find an appropriate object to use. |
|
6191 RootedValue rval(cx); |
|
6192 bool success = JS_CallFunctionValue(cx, thisObj, jsfnVal, argv, &rval); |
|
6193 |
|
6194 // Convert the result. Note that we pass 'isArgument = false', such that |
|
6195 // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char |
|
6196 // type, which would require an allocation that we can't track. The JS |
|
6197 // function must perform this conversion itself and return a PointerType |
|
6198 // CData; thusly, the burden of freeing the data is left to the user. |
|
6199 if (success && cif->rtype != &ffi_type_void) |
|
6200 success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false, |
|
6201 nullptr); |
|
6202 |
|
6203 if (!success) { |
|
6204 // Something failed. The callee may have thrown, or it may not have |
|
6205 // returned a value that ImplicitConvert() was happy with. Depending on how |
|
6206 // prudent the consumer has been, we may or may not have a recovery plan. |
|
6207 |
|
6208 // In any case, a JS exception cannot be passed to C code, so report the |
|
6209 // exception if any and clear it from the cx. |
|
6210 if (JS_IsExceptionPending(cx)) |
|
6211 JS_ReportPendingException(cx); |
|
6212 |
|
6213 if (cinfo->errResult) { |
|
6214 // Good case: we have a sentinel that we can return. Copy it in place of |
|
6215 // the actual return value, and then proceed. |
|
6216 |
|
6217 // The buffer we're returning might be larger than the size of the return |
|
6218 // type, due to libffi alignment issues (see above). But it should never |
|
6219 // be smaller. |
|
6220 size_t copySize = CType::GetSize(fninfo->mReturnType); |
|
6221 JS_ASSERT(copySize <= rvSize); |
|
6222 memcpy(result, cinfo->errResult, copySize); |
|
6223 } else { |
|
6224 // Bad case: not much we can do here. The rv is already zeroed out, so we |
|
6225 // just report (another) error and hope for the best. JS_ReportError will |
|
6226 // actually throw an exception here, so then we have to report it. Again. |
|
6227 // Ugh. |
|
6228 JS_ReportError(cx, "JavaScript callback failed, and an error sentinel " |
|
6229 "was not specified."); |
|
6230 if (JS_IsExceptionPending(cx)) |
|
6231 JS_ReportPendingException(cx); |
|
6232 |
|
6233 return; |
|
6234 } |
|
6235 } |
|
6236 |
|
6237 // Small integer types must be returned as a word-sized ffi_arg. Coerce it |
|
6238 // back into the size libffi expects. |
|
6239 switch (typeCode) { |
|
6240 #define DEFINE_INT_TYPE(name, type, ffiType) \ |
|
6241 case TYPE_##name: \ |
|
6242 if (sizeof(type) < sizeof(ffi_arg)) { \ |
|
6243 ffi_arg data = *static_cast<type*>(result); \ |
|
6244 *static_cast<ffi_arg*>(result) = data; \ |
|
6245 } \ |
|
6246 break; |
|
6247 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
6248 #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
6249 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
6250 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
|
6251 #include "ctypes/typedefs.h" |
|
6252 default: |
|
6253 break; |
|
6254 } |
|
6255 } |
|
6256 |
|
6257 /******************************************************************************* |
|
6258 ** CData implementation |
|
6259 *******************************************************************************/ |
|
6260 |
|
6261 // Create a new CData object of type 'typeObj' containing binary data supplied |
|
6262 // in 'source', optionally with a referent object 'refObj'. |
|
6263 // |
|
6264 // * 'typeObj' must be a CType of defined (but possibly zero) size. |
|
6265 // |
|
6266 // * If an object 'refObj' is supplied, the new CData object stores the |
|
6267 // referent object in a reserved slot for GC safety, such that 'refObj' will |
|
6268 // be held alive by the resulting CData object. 'refObj' may or may not be |
|
6269 // a CData object; merely an object we want to keep alive. |
|
6270 // * If 'refObj' is a CData object, 'ownResult' must be false. |
|
6271 // * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult' |
|
6272 // may be true or false. |
|
6273 // * Otherwise 'refObj' is nullptr. In this case, 'ownResult' may be true or |
|
6274 // false. |
|
6275 // |
|
6276 // * If 'ownResult' is true, the CData object will allocate an appropriately |
|
6277 // sized buffer, and free it upon finalization. If 'source' data is |
|
6278 // supplied, the data will be copied from 'source' into the buffer; |
|
6279 // otherwise, the entirety of the new buffer will be initialized to zero. |
|
6280 // * If 'ownResult' is false, the new CData's buffer refers to a slice of |
|
6281 // another buffer kept alive by 'refObj'. 'source' data must be provided, |
|
6282 // and the new CData's buffer will refer to 'source'. |
|
6283 JSObject* |
|
6284 CData::Create(JSContext* cx, |
|
6285 HandleObject typeObj, |
|
6286 HandleObject refObj, |
|
6287 void* source, |
|
6288 bool ownResult) |
|
6289 { |
|
6290 JS_ASSERT(typeObj); |
|
6291 JS_ASSERT(CType::IsCType(typeObj)); |
|
6292 JS_ASSERT(CType::IsSizeDefined(typeObj)); |
|
6293 JS_ASSERT(ownResult || source); |
|
6294 JS_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult); |
|
6295 |
|
6296 // Get the 'prototype' property from the type. |
|
6297 jsval slot = JS_GetReservedSlot(typeObj, SLOT_PROTO); |
|
6298 JS_ASSERT(!JSVAL_IS_PRIMITIVE(slot)); |
|
6299 |
|
6300 RootedObject proto(cx, JSVAL_TO_OBJECT(slot)); |
|
6301 RootedObject parent(cx, JS_GetParent(typeObj)); |
|
6302 JS_ASSERT(parent); |
|
6303 |
|
6304 RootedObject dataObj(cx, JS_NewObject(cx, &sCDataClass, proto, parent)); |
|
6305 if (!dataObj) |
|
6306 return nullptr; |
|
6307 |
|
6308 // set the CData's associated type |
|
6309 JS_SetReservedSlot(dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj)); |
|
6310 |
|
6311 // Stash the referent object, if any, for GC safety. |
|
6312 if (refObj) |
|
6313 JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj)); |
|
6314 |
|
6315 // Set our ownership flag. |
|
6316 JS_SetReservedSlot(dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult)); |
|
6317 |
|
6318 // attach the buffer. since it might not be 2-byte aligned, we need to |
|
6319 // allocate an aligned space for it and store it there. :( |
|
6320 char** buffer = cx->new_<char*>(); |
|
6321 if (!buffer) { |
|
6322 JS_ReportOutOfMemory(cx); |
|
6323 return nullptr; |
|
6324 } |
|
6325 |
|
6326 char* data; |
|
6327 if (!ownResult) { |
|
6328 data = static_cast<char*>(source); |
|
6329 } else { |
|
6330 // Initialize our own buffer. |
|
6331 size_t size = CType::GetSize(typeObj); |
|
6332 data = (char*)cx->malloc_(size); |
|
6333 if (!data) { |
|
6334 // Report a catchable allocation error. |
|
6335 JS_ReportAllocationOverflow(cx); |
|
6336 js_free(buffer); |
|
6337 return nullptr; |
|
6338 } |
|
6339 |
|
6340 if (!source) |
|
6341 memset(data, 0, size); |
|
6342 else |
|
6343 memcpy(data, source, size); |
|
6344 } |
|
6345 |
|
6346 *buffer = data; |
|
6347 JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer)); |
|
6348 |
|
6349 return dataObj; |
|
6350 } |
|
6351 |
|
6352 void |
|
6353 CData::Finalize(JSFreeOp *fop, JSObject* obj) |
|
6354 { |
|
6355 // Delete our buffer, and the data it contains if we own it. |
|
6356 jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS); |
|
6357 if (JSVAL_IS_VOID(slot)) |
|
6358 return; |
|
6359 |
|
6360 bool owns = JSVAL_TO_BOOLEAN(slot); |
|
6361 |
|
6362 slot = JS_GetReservedSlot(obj, SLOT_DATA); |
|
6363 if (JSVAL_IS_VOID(slot)) |
|
6364 return; |
|
6365 char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot)); |
|
6366 |
|
6367 if (owns) |
|
6368 FreeOp::get(fop)->free_(*buffer); |
|
6369 FreeOp::get(fop)->delete_(buffer); |
|
6370 } |
|
6371 |
|
6372 JSObject* |
|
6373 CData::GetCType(JSObject* dataObj) |
|
6374 { |
|
6375 JS_ASSERT(CData::IsCData(dataObj)); |
|
6376 |
|
6377 jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE); |
|
6378 JSObject* typeObj = JSVAL_TO_OBJECT(slot); |
|
6379 JS_ASSERT(CType::IsCType(typeObj)); |
|
6380 return typeObj; |
|
6381 } |
|
6382 |
|
6383 void* |
|
6384 CData::GetData(JSObject* dataObj) |
|
6385 { |
|
6386 JS_ASSERT(CData::IsCData(dataObj)); |
|
6387 |
|
6388 jsval slot = JS_GetReservedSlot(dataObj, SLOT_DATA); |
|
6389 |
|
6390 void** buffer = static_cast<void**>(JSVAL_TO_PRIVATE(slot)); |
|
6391 JS_ASSERT(buffer); |
|
6392 JS_ASSERT(*buffer); |
|
6393 return *buffer; |
|
6394 } |
|
6395 |
|
6396 bool |
|
6397 CData::IsCData(JSObject* obj) |
|
6398 { |
|
6399 return JS_GetClass(obj) == &sCDataClass; |
|
6400 } |
|
6401 |
|
6402 bool |
|
6403 CData::IsCData(HandleValue v) |
|
6404 { |
|
6405 return v.isObject() && CData::IsCData(&v.toObject()); |
|
6406 } |
|
6407 |
|
6408 bool |
|
6409 CData::IsCDataProto(JSObject* obj) |
|
6410 { |
|
6411 return JS_GetClass(obj) == &sCDataProtoClass; |
|
6412 } |
|
6413 |
|
6414 bool |
|
6415 CData::ValueGetter(JSContext* cx, JS::CallArgs args) |
|
6416 { |
|
6417 RootedObject obj(cx, &args.thisv().toObject()); |
|
6418 |
|
6419 // Convert the value to a primitive; do not create a new CData object. |
|
6420 RootedObject ctype(cx, GetCType(obj)); |
|
6421 return ConvertToJS(cx, ctype, NullPtr(), GetData(obj), true, false, args.rval().address()); |
|
6422 } |
|
6423 |
|
6424 bool |
|
6425 CData::ValueSetter(JSContext* cx, JS::CallArgs args) |
|
6426 { |
|
6427 RootedObject obj(cx, &args.thisv().toObject()); |
|
6428 args.rval().setUndefined(); |
|
6429 return ImplicitConvert(cx, args.get(0), GetCType(obj), GetData(obj), false, nullptr); |
|
6430 } |
|
6431 |
|
6432 bool |
|
6433 CData::Address(JSContext* cx, unsigned argc, jsval* vp) |
|
6434 { |
|
6435 CallArgs args = CallArgsFromVp(argc, vp); |
|
6436 if (args.length() != 0) { |
|
6437 JS_ReportError(cx, "address takes zero arguments"); |
|
6438 return false; |
|
6439 } |
|
6440 |
|
6441 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); |
|
6442 if (!obj) |
|
6443 return false; |
|
6444 if (!IsCData(obj)) { |
|
6445 JS_ReportError(cx, "not a CData"); |
|
6446 return false; |
|
6447 } |
|
6448 |
|
6449 RootedObject typeObj(cx, CData::GetCType(obj)); |
|
6450 RootedObject pointerType(cx, PointerType::CreateInternal(cx, typeObj)); |
|
6451 if (!pointerType) |
|
6452 return false; |
|
6453 |
|
6454 // Create a PointerType CData object containing null. |
|
6455 JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true); |
|
6456 if (!result) |
|
6457 return false; |
|
6458 |
|
6459 args.rval().setObject(*result); |
|
6460 |
|
6461 // Manually set the pointer inside the object, so we skip the conversion step. |
|
6462 void** data = static_cast<void**>(GetData(result)); |
|
6463 *data = GetData(obj); |
|
6464 return true; |
|
6465 } |
|
6466 |
|
6467 bool |
|
6468 CData::Cast(JSContext* cx, unsigned argc, jsval* vp) |
|
6469 { |
|
6470 CallArgs args = CallArgsFromVp(argc, vp); |
|
6471 if (args.length() != 2) { |
|
6472 JS_ReportError(cx, "cast takes two arguments"); |
|
6473 return false; |
|
6474 } |
|
6475 |
|
6476 if (JSVAL_IS_PRIMITIVE(args[0]) || |
|
6477 !CData::IsCData(&args[0].toObject())) { |
|
6478 JS_ReportError(cx, "first argument must be a CData"); |
|
6479 return false; |
|
6480 } |
|
6481 RootedObject sourceData(cx, &args[0].toObject()); |
|
6482 JSObject* sourceType = CData::GetCType(sourceData); |
|
6483 |
|
6484 if (JSVAL_IS_PRIMITIVE(args[1]) || |
|
6485 !CType::IsCType(&args[1].toObject())) { |
|
6486 JS_ReportError(cx, "second argument must be a CType"); |
|
6487 return false; |
|
6488 } |
|
6489 |
|
6490 RootedObject targetType(cx, &args[1].toObject()); |
|
6491 size_t targetSize; |
|
6492 if (!CType::GetSafeSize(targetType, &targetSize) || |
|
6493 targetSize > CType::GetSize(sourceType)) { |
|
6494 JS_ReportError(cx, |
|
6495 "target CType has undefined or larger size than source CType"); |
|
6496 return false; |
|
6497 } |
|
6498 |
|
6499 // Construct a new CData object with a type of 'targetType' and a referent |
|
6500 // of 'sourceData'. |
|
6501 void* data = CData::GetData(sourceData); |
|
6502 JSObject* result = CData::Create(cx, targetType, sourceData, data, false); |
|
6503 if (!result) |
|
6504 return false; |
|
6505 |
|
6506 args.rval().setObject(*result); |
|
6507 return true; |
|
6508 } |
|
6509 |
|
6510 bool |
|
6511 CData::GetRuntime(JSContext* cx, unsigned argc, jsval* vp) |
|
6512 { |
|
6513 CallArgs args = CallArgsFromVp(argc, vp); |
|
6514 if (args.length() != 1) { |
|
6515 JS_ReportError(cx, "getRuntime takes one argument"); |
|
6516 return false; |
|
6517 } |
|
6518 |
|
6519 if (JSVAL_IS_PRIMITIVE(args[0]) || |
|
6520 !CType::IsCType(&args[0].toObject())) { |
|
6521 JS_ReportError(cx, "first argument must be a CType"); |
|
6522 return false; |
|
6523 } |
|
6524 |
|
6525 RootedObject targetType(cx, &args[0].toObject()); |
|
6526 size_t targetSize; |
|
6527 if (!CType::GetSafeSize(targetType, &targetSize) || |
|
6528 targetSize != sizeof(void*)) { |
|
6529 JS_ReportError(cx, "target CType has non-pointer size"); |
|
6530 return false; |
|
6531 } |
|
6532 |
|
6533 void* data = static_cast<void*>(cx->runtime()); |
|
6534 JSObject* result = CData::Create(cx, targetType, NullPtr(), &data, true); |
|
6535 if (!result) |
|
6536 return false; |
|
6537 |
|
6538 args.rval().setObject(*result); |
|
6539 return true; |
|
6540 } |
|
6541 |
|
6542 typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext *, const JS::UTF8Chars, size_t *); |
|
6543 |
|
6544 static bool |
|
6545 ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc, jsval *vp) |
|
6546 { |
|
6547 CallArgs args = CallArgsFromVp(argc, vp); |
|
6548 if (args.length() != 0) { |
|
6549 JS_ReportError(cx, "readString takes zero arguments"); |
|
6550 return false; |
|
6551 } |
|
6552 |
|
6553 JSObject* obj = CDataFinalizer::GetCData(cx, JS_THIS_OBJECT(cx, vp)); |
|
6554 if (!obj || !CData::IsCData(obj)) { |
|
6555 JS_ReportError(cx, "not a CData"); |
|
6556 return false; |
|
6557 } |
|
6558 |
|
6559 // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit |
|
6560 // character or integer type. |
|
6561 JSObject* baseType; |
|
6562 JSObject* typeObj = CData::GetCType(obj); |
|
6563 TypeCode typeCode = CType::GetTypeCode(typeObj); |
|
6564 void* data; |
|
6565 size_t maxLength = -1; |
|
6566 switch (typeCode) { |
|
6567 case TYPE_pointer: |
|
6568 baseType = PointerType::GetBaseType(typeObj); |
|
6569 data = *static_cast<void**>(CData::GetData(obj)); |
|
6570 if (data == nullptr) { |
|
6571 JS_ReportError(cx, "cannot read contents of null pointer"); |
|
6572 return false; |
|
6573 } |
|
6574 break; |
|
6575 case TYPE_array: |
|
6576 baseType = ArrayType::GetBaseType(typeObj); |
|
6577 data = CData::GetData(obj); |
|
6578 maxLength = ArrayType::GetLength(typeObj); |
|
6579 break; |
|
6580 default: |
|
6581 JS_ReportError(cx, "not a PointerType or ArrayType"); |
|
6582 return false; |
|
6583 } |
|
6584 |
|
6585 // Convert the string buffer, taking care to determine the correct string |
|
6586 // length in the case of arrays (which may contain embedded nulls). |
|
6587 JSString* result; |
|
6588 switch (CType::GetTypeCode(baseType)) { |
|
6589 case TYPE_int8_t: |
|
6590 case TYPE_uint8_t: |
|
6591 case TYPE_char: |
|
6592 case TYPE_signed_char: |
|
6593 case TYPE_unsigned_char: { |
|
6594 char* bytes = static_cast<char*>(data); |
|
6595 size_t length = strnlen(bytes, maxLength); |
|
6596 |
|
6597 // Determine the length. |
|
6598 jschar *dst = inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length).get(); |
|
6599 if (!dst) |
|
6600 return false; |
|
6601 |
|
6602 result = JS_NewUCString(cx, dst, length); |
|
6603 break; |
|
6604 } |
|
6605 case TYPE_int16_t: |
|
6606 case TYPE_uint16_t: |
|
6607 case TYPE_short: |
|
6608 case TYPE_unsigned_short: |
|
6609 case TYPE_jschar: { |
|
6610 jschar* chars = static_cast<jschar*>(data); |
|
6611 size_t length = strnlen(chars, maxLength); |
|
6612 result = JS_NewUCStringCopyN(cx, chars, length); |
|
6613 break; |
|
6614 } |
|
6615 default: |
|
6616 JS_ReportError(cx, |
|
6617 "base type is not an 8-bit or 16-bit integer or character type"); |
|
6618 return false; |
|
6619 } |
|
6620 |
|
6621 if (!result) |
|
6622 return false; |
|
6623 |
|
6624 args.rval().setString(result); |
|
6625 return true; |
|
6626 } |
|
6627 |
|
6628 bool |
|
6629 CData::ReadString(JSContext* cx, unsigned argc, jsval* vp) |
|
6630 { |
|
6631 return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp); |
|
6632 } |
|
6633 |
|
6634 bool |
|
6635 CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp) |
|
6636 { |
|
6637 return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp); |
|
6638 } |
|
6639 |
|
6640 JSString * |
|
6641 CData::GetSourceString(JSContext *cx, HandleObject typeObj, void *data) |
|
6642 { |
|
6643 // Walk the types, building up the toSource() string. |
|
6644 // First, we build up the type expression: |
|
6645 // 't.ptr' for pointers; |
|
6646 // 't.array([n])' for arrays; |
|
6647 // 'n' for structs, where n = t.name, the struct's name. (We assume this is |
|
6648 // bound to a variable in the current scope.) |
|
6649 AutoString source; |
|
6650 BuildTypeSource(cx, typeObj, true, source); |
|
6651 AppendString(source, "("); |
|
6652 if (!BuildDataSource(cx, typeObj, data, false, source)) |
|
6653 return nullptr; |
|
6654 |
|
6655 AppendString(source, ")"); |
|
6656 |
|
6657 return NewUCString(cx, source); |
|
6658 } |
|
6659 |
|
6660 bool |
|
6661 CData::ToSource(JSContext* cx, unsigned argc, jsval* vp) |
|
6662 { |
|
6663 CallArgs args = CallArgsFromVp(argc, vp); |
|
6664 if (args.length() != 0) { |
|
6665 JS_ReportError(cx, "toSource takes zero arguments"); |
|
6666 return false; |
|
6667 } |
|
6668 |
|
6669 JSObject* obj = JS_THIS_OBJECT(cx, vp); |
|
6670 if (!obj) |
|
6671 return false; |
|
6672 if (!CData::IsCData(obj) && !CData::IsCDataProto(obj)) { |
|
6673 JS_ReportError(cx, "not a CData"); |
|
6674 return false; |
|
6675 } |
|
6676 |
|
6677 JSString* result; |
|
6678 if (CData::IsCData(obj)) { |
|
6679 RootedObject typeObj(cx, CData::GetCType(obj)); |
|
6680 void* data = CData::GetData(obj); |
|
6681 |
|
6682 result = CData::GetSourceString(cx, typeObj, data); |
|
6683 } else { |
|
6684 result = JS_NewStringCopyZ(cx, "[CData proto object]"); |
|
6685 } |
|
6686 |
|
6687 if (!result) |
|
6688 return false; |
|
6689 |
|
6690 args.rval().setString(result); |
|
6691 return true; |
|
6692 } |
|
6693 |
|
6694 bool |
|
6695 CData::ErrnoGetter(JSContext* cx, JS::CallArgs args) |
|
6696 { |
|
6697 args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_ERRNO)); |
|
6698 return true; |
|
6699 } |
|
6700 |
|
6701 #if defined(XP_WIN) |
|
6702 bool |
|
6703 CData::LastErrorGetter(JSContext* cx, JS::CallArgs args) |
|
6704 { |
|
6705 args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR)); |
|
6706 return true; |
|
6707 } |
|
6708 #endif // defined(XP_WIN) |
|
6709 |
|
6710 bool |
|
6711 CDataFinalizer::Methods::ToSource(JSContext *cx, unsigned argc, jsval *vp) |
|
6712 { |
|
6713 CallArgs args = CallArgsFromVp(argc, vp); |
|
6714 RootedObject objThis(cx, JS_THIS_OBJECT(cx, vp)); |
|
6715 if (!objThis) |
|
6716 return false; |
|
6717 if (!CDataFinalizer::IsCDataFinalizer(objThis)) { |
|
6718 JS_ReportError(cx, "not a CDataFinalizer"); |
|
6719 return false; |
|
6720 } |
|
6721 |
|
6722 CDataFinalizer::Private *p = (CDataFinalizer::Private *) |
|
6723 JS_GetPrivate(objThis); |
|
6724 |
|
6725 JSString *strMessage; |
|
6726 if (!p) { |
|
6727 strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()"); |
|
6728 } else { |
|
6729 RootedObject objType(cx, CDataFinalizer::GetCType(cx, objThis)); |
|
6730 if (!objType) { |
|
6731 JS_ReportError(cx, "CDataFinalizer has no type"); |
|
6732 return false; |
|
6733 } |
|
6734 |
|
6735 AutoString source; |
|
6736 AppendString(source, "ctypes.CDataFinalizer("); |
|
6737 JSString *srcValue = CData::GetSourceString(cx, objType, p->cargs); |
|
6738 if (!srcValue) { |
|
6739 return false; |
|
6740 } |
|
6741 AppendString(source, srcValue); |
|
6742 AppendString(source, ", "); |
|
6743 jsval valCodePtrType = JS_GetReservedSlot(objThis, |
|
6744 SLOT_DATAFINALIZER_CODETYPE); |
|
6745 if (JSVAL_IS_PRIMITIVE(valCodePtrType)) { |
|
6746 return false; |
|
6747 } |
|
6748 |
|
6749 RootedObject typeObj(cx, JSVAL_TO_OBJECT(valCodePtrType)); |
|
6750 JSString *srcDispose = CData::GetSourceString(cx, typeObj, &(p->code)); |
|
6751 if (!srcDispose) { |
|
6752 return false; |
|
6753 } |
|
6754 |
|
6755 AppendString(source, srcDispose); |
|
6756 AppendString(source, ")"); |
|
6757 strMessage = NewUCString(cx, source); |
|
6758 } |
|
6759 |
|
6760 if (!strMessage) { |
|
6761 // This is a memory issue, no error message |
|
6762 return false; |
|
6763 } |
|
6764 |
|
6765 args.rval().setString(strMessage); |
|
6766 return true; |
|
6767 } |
|
6768 |
|
6769 bool |
|
6770 CDataFinalizer::Methods::ToString(JSContext *cx, unsigned argc, jsval *vp) |
|
6771 { |
|
6772 CallArgs args = CallArgsFromVp(argc, vp); |
|
6773 JSObject* objThis = JS_THIS_OBJECT(cx, vp); |
|
6774 if (!objThis) |
|
6775 return false; |
|
6776 if (!CDataFinalizer::IsCDataFinalizer(objThis)) { |
|
6777 JS_ReportError(cx, "not a CDataFinalizer"); |
|
6778 return false; |
|
6779 } |
|
6780 |
|
6781 JSString *strMessage; |
|
6782 RootedValue value(cx); |
|
6783 if (!JS_GetPrivate(objThis)) { |
|
6784 // Pre-check whether CDataFinalizer::GetValue can fail |
|
6785 // to avoid reporting an error when not appropriate. |
|
6786 strMessage = JS_NewStringCopyZ(cx, "[CDataFinalizer - empty]"); |
|
6787 if (!strMessage) { |
|
6788 return false; |
|
6789 } |
|
6790 } else if (!CDataFinalizer::GetValue(cx, objThis, value.address())) { |
|
6791 MOZ_ASSUME_UNREACHABLE("Could not convert an empty CDataFinalizer"); |
|
6792 } else { |
|
6793 strMessage = ToString(cx, value); |
|
6794 if (!strMessage) { |
|
6795 return false; |
|
6796 } |
|
6797 } |
|
6798 args.rval().setString(strMessage); |
|
6799 return true; |
|
6800 } |
|
6801 |
|
6802 bool |
|
6803 CDataFinalizer::IsCDataFinalizer(JSObject *obj) |
|
6804 { |
|
6805 return JS_GetClass(obj) == &sCDataFinalizerClass; |
|
6806 } |
|
6807 |
|
6808 |
|
6809 JSObject * |
|
6810 CDataFinalizer::GetCType(JSContext *cx, JSObject *obj) |
|
6811 { |
|
6812 MOZ_ASSERT(IsCDataFinalizer(obj)); |
|
6813 |
|
6814 jsval valData = JS_GetReservedSlot(obj, |
|
6815 SLOT_DATAFINALIZER_VALTYPE); |
|
6816 if (JSVAL_IS_VOID(valData)) { |
|
6817 return nullptr; |
|
6818 } |
|
6819 |
|
6820 return JSVAL_TO_OBJECT(valData); |
|
6821 } |
|
6822 |
|
6823 JSObject* |
|
6824 CDataFinalizer::GetCData(JSContext *cx, JSObject *obj) |
|
6825 { |
|
6826 if (!obj) { |
|
6827 JS_ReportError(cx, "No C data"); |
|
6828 return nullptr; |
|
6829 } |
|
6830 if (CData::IsCData(obj)) { |
|
6831 return obj; |
|
6832 } |
|
6833 if (!CDataFinalizer::IsCDataFinalizer(obj)) { |
|
6834 JS_ReportError(cx, "Not C data"); |
|
6835 return nullptr; |
|
6836 } |
|
6837 RootedValue val(cx); |
|
6838 if (!CDataFinalizer::GetValue(cx, obj, val.address()) || JSVAL_IS_PRIMITIVE(val)) { |
|
6839 JS_ReportError(cx, "Empty CDataFinalizer"); |
|
6840 return nullptr; |
|
6841 } |
|
6842 return JSVAL_TO_OBJECT(val); |
|
6843 } |
|
6844 |
|
6845 bool |
|
6846 CDataFinalizer::GetValue(JSContext *cx, JSObject *obj, jsval *aResult) |
|
6847 { |
|
6848 MOZ_ASSERT(IsCDataFinalizer(obj)); |
|
6849 |
|
6850 CDataFinalizer::Private *p = (CDataFinalizer::Private *) |
|
6851 JS_GetPrivate(obj); |
|
6852 |
|
6853 if (!p) { |
|
6854 JS_ReportError(cx, "Attempting to get the value of an empty CDataFinalizer"); |
|
6855 return false; // We have called |dispose| or |forget| already. |
|
6856 } |
|
6857 |
|
6858 RootedObject ctype(cx, GetCType(cx, obj)); |
|
6859 return ConvertToJS(cx, ctype, /*parent*/NullPtr(), p -> cargs, false, true, aResult); |
|
6860 } |
|
6861 |
|
6862 /* |
|
6863 * Attach a C function as a finalizer to a JS object. |
|
6864 * |
|
6865 * Pseudo-JS signature: |
|
6866 * function(CData<T>, CData<T -> U>): CDataFinalizer<T> |
|
6867 * value, finalizer |
|
6868 * |
|
6869 * This function attaches strong references to the following values: |
|
6870 * - the CType of |value| |
|
6871 * |
|
6872 * Note: This function takes advantage of the fact that non-variadic |
|
6873 * CData functions are initialized during creation. |
|
6874 */ |
|
6875 bool |
|
6876 CDataFinalizer::Construct(JSContext* cx, unsigned argc, jsval *vp) |
|
6877 { |
|
6878 CallArgs args = CallArgsFromVp(argc, vp); |
|
6879 RootedObject objSelf(cx, &args.callee()); |
|
6880 RootedObject objProto(cx); |
|
6881 if (!GetObjectProperty(cx, objSelf, "prototype", &objProto)) { |
|
6882 JS_ReportError(cx, "CDataFinalizer.prototype does not exist"); |
|
6883 return false; |
|
6884 } |
|
6885 |
|
6886 // Get arguments |
|
6887 if (args.length() == 0) { // Special case: the empty (already finalized) object |
|
6888 JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr()); |
|
6889 args.rval().setObject(*objResult); |
|
6890 return true; |
|
6891 } |
|
6892 |
|
6893 if (args.length() != 2) { |
|
6894 JS_ReportError(cx, "CDataFinalizer takes 2 arguments"); |
|
6895 return false; |
|
6896 } |
|
6897 |
|
6898 JS::HandleValue valCodePtr = args[1]; |
|
6899 if (!valCodePtr.isObject()) { |
|
6900 return TypeError(cx, "_a CData object_ of a function pointer type", |
|
6901 valCodePtr); |
|
6902 } |
|
6903 JSObject *objCodePtr = &valCodePtr.toObject(); |
|
6904 |
|
6905 //Note: Using a custom argument formatter here would be awkward (requires |
|
6906 //a destructor just to uninstall the formatter). |
|
6907 |
|
6908 // 2. Extract argument type of |objCodePtr| |
|
6909 if (!CData::IsCData(objCodePtr)) { |
|
6910 return TypeError(cx, "a _CData_ object of a function pointer type", |
|
6911 valCodePtr); |
|
6912 } |
|
6913 RootedObject objCodePtrType(cx, CData::GetCType(objCodePtr)); |
|
6914 RootedValue valCodePtrType(cx, ObjectValue(*objCodePtrType)); |
|
6915 MOZ_ASSERT(objCodePtrType); |
|
6916 |
|
6917 TypeCode typCodePtr = CType::GetTypeCode(objCodePtrType); |
|
6918 if (typCodePtr != TYPE_pointer) { |
|
6919 return TypeError(cx, "a CData object of a function _pointer_ type", |
|
6920 valCodePtrType); |
|
6921 } |
|
6922 |
|
6923 JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType); |
|
6924 MOZ_ASSERT(objCodeType); |
|
6925 |
|
6926 TypeCode typCode = CType::GetTypeCode(objCodeType); |
|
6927 if (typCode != TYPE_function) { |
|
6928 return TypeError(cx, "a CData object of a _function_ pointer type", |
|
6929 valCodePtrType); |
|
6930 } |
|
6931 uintptr_t code = *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr)); |
|
6932 if (!code) { |
|
6933 return TypeError(cx, "a CData object of a _non-NULL_ function pointer type", |
|
6934 valCodePtrType); |
|
6935 } |
|
6936 |
|
6937 FunctionInfo* funInfoFinalizer = |
|
6938 FunctionType::GetFunctionInfo(objCodeType); |
|
6939 MOZ_ASSERT(funInfoFinalizer); |
|
6940 |
|
6941 if ((funInfoFinalizer->mArgTypes.length() != 1) |
|
6942 || (funInfoFinalizer->mIsVariadic)) { |
|
6943 RootedValue valCodeType(cx, ObjectValue(*objCodeType)); |
|
6944 return TypeError(cx, "a function accepting exactly one argument", |
|
6945 valCodeType); |
|
6946 } |
|
6947 RootedObject objArgType(cx, funInfoFinalizer->mArgTypes[0]); |
|
6948 RootedObject returnType(cx, funInfoFinalizer->mReturnType); |
|
6949 |
|
6950 // Invariant: At this stage, we know that funInfoFinalizer->mIsVariadic |
|
6951 // is |false|. Therefore, funInfoFinalizer->mCIF has already been initialized. |
|
6952 |
|
6953 bool freePointer = false; |
|
6954 |
|
6955 // 3. Perform dynamic cast of |args[0]| into |objType|, store it in |cargs| |
|
6956 |
|
6957 size_t sizeArg; |
|
6958 RootedValue valData(cx, args[0]); |
|
6959 if (!CType::GetSafeSize(objArgType, &sizeArg)) { |
|
6960 return TypeError(cx, "(an object with known size)", valData); |
|
6961 } |
|
6962 |
|
6963 ScopedJSFreePtr<void> cargs(malloc(sizeArg)); |
|
6964 |
|
6965 if (!ImplicitConvert(cx, valData, objArgType, cargs.get(), |
|
6966 false, &freePointer)) { |
|
6967 RootedValue valArgType(cx, ObjectValue(*objArgType)); |
|
6968 return TypeError(cx, "(an object that can be converted to the following type)", |
|
6969 valArgType); |
|
6970 } |
|
6971 if (freePointer) { |
|
6972 // Note: We could handle that case, if necessary. |
|
6973 JS_ReportError(cx, "Internal Error during CDataFinalizer. Object cannot be represented"); |
|
6974 return false; |
|
6975 } |
|
6976 |
|
6977 // 4. Prepare buffer for holding return value |
|
6978 |
|
6979 ScopedJSFreePtr<void> rvalue; |
|
6980 if (CType::GetTypeCode(returnType) != TYPE_void_t) { |
|
6981 rvalue = malloc(Align(CType::GetSize(returnType), |
|
6982 sizeof(ffi_arg))); |
|
6983 } //Otherwise, simply do not allocate |
|
6984 |
|
6985 // 5. Create |objResult| |
|
6986 |
|
6987 JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr()); |
|
6988 if (!objResult) { |
|
6989 return false; |
|
6990 } |
|
6991 |
|
6992 // If our argument is a CData, it holds a type. |
|
6993 // This is the type that we should capture, not that |
|
6994 // of the function, which may be less precise. |
|
6995 JSObject *objBestArgType = objArgType; |
|
6996 if (!JSVAL_IS_PRIMITIVE(valData)) { |
|
6997 JSObject *objData = &valData.toObject(); |
|
6998 if (CData::IsCData(objData)) { |
|
6999 objBestArgType = CData::GetCType(objData); |
|
7000 size_t sizeBestArg; |
|
7001 if (!CType::GetSafeSize(objBestArgType, &sizeBestArg)) { |
|
7002 MOZ_ASSUME_UNREACHABLE("object with unknown size"); |
|
7003 } |
|
7004 if (sizeBestArg != sizeArg) { |
|
7005 return TypeError(cx, "(an object with the same size as that expected by the C finalization function)", valData); |
|
7006 } |
|
7007 } |
|
7008 } |
|
7009 |
|
7010 // Used by GetCType |
|
7011 JS_SetReservedSlot(objResult, |
|
7012 SLOT_DATAFINALIZER_VALTYPE, |
|
7013 OBJECT_TO_JSVAL(objBestArgType)); |
|
7014 |
|
7015 // Used by ToSource |
|
7016 JS_SetReservedSlot(objResult, |
|
7017 SLOT_DATAFINALIZER_CODETYPE, |
|
7018 OBJECT_TO_JSVAL(objCodePtrType)); |
|
7019 |
|
7020 ffi_abi abi; |
|
7021 if (!GetABI(cx, OBJECT_TO_JSVAL(funInfoFinalizer->mABI), &abi)) { |
|
7022 JS_ReportError(cx, "Internal Error: " |
|
7023 "Invalid ABI specification in CDataFinalizer"); |
|
7024 return false; |
|
7025 } |
|
7026 |
|
7027 ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType); |
|
7028 if (!rtype) { |
|
7029 JS_ReportError(cx, "Internal Error: " |
|
7030 "Could not access ffi type of CDataFinalizer"); |
|
7031 return false; |
|
7032 } |
|
7033 |
|
7034 // 7. Store C information as private |
|
7035 ScopedJSFreePtr<CDataFinalizer::Private> |
|
7036 p((CDataFinalizer::Private*)malloc(sizeof(CDataFinalizer::Private))); |
|
7037 |
|
7038 memmove(&p->CIF, &funInfoFinalizer->mCIF, sizeof(ffi_cif)); |
|
7039 |
|
7040 p->cargs = cargs.forget(); |
|
7041 p->rvalue = rvalue.forget(); |
|
7042 p->cargs_size = sizeArg; |
|
7043 p->code = code; |
|
7044 |
|
7045 |
|
7046 JS_SetPrivate(objResult, p.forget()); |
|
7047 args.rval().setObject(*objResult); |
|
7048 return true; |
|
7049 } |
|
7050 |
|
7051 |
|
7052 /* |
|
7053 * Actually call the finalizer. Does not perform any cleanup on the object. |
|
7054 * |
|
7055 * Preconditions: |this| must be a |CDataFinalizer|, |p| must be non-null. |
|
7056 * The function fails if |this| has gone through |Forget|/|Dispose| |
|
7057 * or |Finalize|. |
|
7058 * |
|
7059 * This function does not alter the value of |errno|/|GetLastError|. |
|
7060 * |
|
7061 * If argument |errnoStatus| is non-nullptr, it receives the value of |errno| |
|
7062 * immediately after the call. Under Windows, if argument |lastErrorStatus| |
|
7063 * is non-nullptr, it receives the value of |GetLastError| immediately after |
|
7064 * the call. On other platforms, |lastErrorStatus| is ignored. |
|
7065 */ |
|
7066 void |
|
7067 CDataFinalizer::CallFinalizer(CDataFinalizer::Private *p, |
|
7068 int* errnoStatus, |
|
7069 int32_t* lastErrorStatus) |
|
7070 { |
|
7071 int savedErrno = errno; |
|
7072 errno = 0; |
|
7073 #if defined(XP_WIN) |
|
7074 int32_t savedLastError = GetLastError(); |
|
7075 SetLastError(0); |
|
7076 #endif // defined(XP_WIN) |
|
7077 |
|
7078 ffi_call(&p->CIF, FFI_FN(p->code), p->rvalue, &p->cargs); |
|
7079 |
|
7080 if (errnoStatus) { |
|
7081 *errnoStatus = errno; |
|
7082 } |
|
7083 errno = savedErrno; |
|
7084 #if defined(XP_WIN) |
|
7085 if (lastErrorStatus) { |
|
7086 *lastErrorStatus = GetLastError(); |
|
7087 } |
|
7088 SetLastError(savedLastError); |
|
7089 #endif // defined(XP_WIN) |
|
7090 } |
|
7091 |
|
7092 /* |
|
7093 * Forget the value. |
|
7094 * |
|
7095 * Preconditions: |this| must be a |CDataFinalizer|. |
|
7096 * The function fails if |this| has gone through |Forget|/|Dispose| |
|
7097 * or |Finalize|. |
|
7098 * |
|
7099 * Does not call the finalizer. Cleans up the Private memory and releases all |
|
7100 * strong references. |
|
7101 */ |
|
7102 bool |
|
7103 CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, jsval *vp) |
|
7104 { |
|
7105 CallArgs args = CallArgsFromVp(argc, vp); |
|
7106 if (args.length() != 0) { |
|
7107 JS_ReportError(cx, "CDataFinalizer.prototype.forget takes no arguments"); |
|
7108 return false; |
|
7109 } |
|
7110 |
|
7111 JS::Rooted<JSObject*> obj(cx, args.thisv().toObjectOrNull()); |
|
7112 if (!obj) |
|
7113 return false; |
|
7114 if (!CDataFinalizer::IsCDataFinalizer(obj)) { |
|
7115 RootedValue val(cx, ObjectValue(*obj)); |
|
7116 return TypeError(cx, "a CDataFinalizer", val); |
|
7117 } |
|
7118 |
|
7119 CDataFinalizer::Private *p = (CDataFinalizer::Private *) |
|
7120 JS_GetPrivate(obj); |
|
7121 |
|
7122 if (!p) { |
|
7123 JS_ReportError(cx, "forget called on an empty CDataFinalizer"); |
|
7124 return false; |
|
7125 } |
|
7126 |
|
7127 RootedValue valJSData(cx); |
|
7128 RootedObject ctype(cx, GetCType(cx, obj)); |
|
7129 if (!ConvertToJS(cx, ctype, NullPtr(), p->cargs, false, true, valJSData.address())) { |
|
7130 JS_ReportError(cx, "CDataFinalizer value cannot be represented"); |
|
7131 return false; |
|
7132 } |
|
7133 |
|
7134 CDataFinalizer::Cleanup(p, obj); |
|
7135 |
|
7136 args.rval().set(valJSData); |
|
7137 return true; |
|
7138 } |
|
7139 |
|
7140 /* |
|
7141 * Clean up the value. |
|
7142 * |
|
7143 * Preconditions: |this| must be a |CDataFinalizer|. |
|
7144 * The function fails if |this| has gone through |Forget|/|Dispose| |
|
7145 * or |Finalize|. |
|
7146 * |
|
7147 * Calls the finalizer, cleans up the Private memory and releases all |
|
7148 * strong references. |
|
7149 */ |
|
7150 bool |
|
7151 CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, jsval *vp) |
|
7152 { |
|
7153 CallArgs args = CallArgsFromVp(argc, vp); |
|
7154 if (args.length() != 0) { |
|
7155 JS_ReportError(cx, "CDataFinalizer.prototype.dispose takes no arguments"); |
|
7156 return false; |
|
7157 } |
|
7158 |
|
7159 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); |
|
7160 if (!obj) |
|
7161 return false; |
|
7162 if (!CDataFinalizer::IsCDataFinalizer(obj)) { |
|
7163 RootedValue val(cx, ObjectValue(*obj)); |
|
7164 return TypeError(cx, "a CDataFinalizer", val); |
|
7165 } |
|
7166 |
|
7167 CDataFinalizer::Private *p = (CDataFinalizer::Private *) |
|
7168 JS_GetPrivate(obj); |
|
7169 |
|
7170 if (!p) { |
|
7171 JS_ReportError(cx, "dispose called on an empty CDataFinalizer."); |
|
7172 return false; |
|
7173 } |
|
7174 |
|
7175 jsval valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE); |
|
7176 JS_ASSERT(!JSVAL_IS_PRIMITIVE(valType)); |
|
7177 |
|
7178 JSObject *objCTypes = CType::GetGlobalCTypes(cx, &valType.toObject()); |
|
7179 if (!objCTypes) |
|
7180 return false; |
|
7181 |
|
7182 jsval valCodePtrType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE); |
|
7183 JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCodePtrType)); |
|
7184 JSObject *objCodePtrType = &valCodePtrType.toObject(); |
|
7185 |
|
7186 JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType); |
|
7187 JS_ASSERT(objCodeType); |
|
7188 JS_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function); |
|
7189 |
|
7190 RootedObject resultType(cx, FunctionType::GetFunctionInfo(objCodeType)->mReturnType); |
|
7191 RootedValue result(cx, JSVAL_VOID); |
|
7192 |
|
7193 int errnoStatus; |
|
7194 #if defined(XP_WIN) |
|
7195 int32_t lastErrorStatus; |
|
7196 CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus); |
|
7197 #else |
|
7198 CDataFinalizer::CallFinalizer(p, &errnoStatus, nullptr); |
|
7199 #endif // defined(XP_WIN) |
|
7200 |
|
7201 JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus)); |
|
7202 #if defined(XP_WIN) |
|
7203 JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus)); |
|
7204 #endif // defined(XP_WIN) |
|
7205 |
|
7206 if (ConvertToJS(cx, resultType, NullPtr(), p->rvalue, false, true, result.address())) { |
|
7207 CDataFinalizer::Cleanup(p, obj); |
|
7208 args.rval().set(result); |
|
7209 return true; |
|
7210 } |
|
7211 CDataFinalizer::Cleanup(p, obj); |
|
7212 return false; |
|
7213 } |
|
7214 |
|
7215 /* |
|
7216 * Perform finalization. |
|
7217 * |
|
7218 * Preconditions: |this| must be the result of |CDataFinalizer|. |
|
7219 * It may have gone through |Forget|/|Dispose|. |
|
7220 * |
|
7221 * If |this| has not gone through |Forget|/|Dispose|, calls the |
|
7222 * finalizer, cleans up the Private memory and releases all |
|
7223 * strong references. |
|
7224 */ |
|
7225 void |
|
7226 CDataFinalizer::Finalize(JSFreeOp* fop, JSObject* obj) |
|
7227 { |
|
7228 CDataFinalizer::Private *p = (CDataFinalizer::Private *) |
|
7229 JS_GetPrivate(obj); |
|
7230 |
|
7231 if (!p) { |
|
7232 return; |
|
7233 } |
|
7234 |
|
7235 CDataFinalizer::CallFinalizer(p, nullptr, nullptr); |
|
7236 CDataFinalizer::Cleanup(p, nullptr); |
|
7237 } |
|
7238 |
|
7239 /* |
|
7240 * Perform cleanup of a CDataFinalizer |
|
7241 * |
|
7242 * Release strong references, cleanup |Private|. |
|
7243 * |
|
7244 * Argument |p| contains the private information of the CDataFinalizer. If |
|
7245 * nullptr, this function does nothing. |
|
7246 * Argument |obj| should contain |nullptr| during finalization (or in any |
|
7247 * context in which the object itself should not be cleaned up), or a |
|
7248 * CDataFinalizer object otherwise. |
|
7249 */ |
|
7250 void |
|
7251 CDataFinalizer::Cleanup(CDataFinalizer::Private *p, JSObject *obj) |
|
7252 { |
|
7253 if (!p) { |
|
7254 return; // We have already cleaned up |
|
7255 } |
|
7256 |
|
7257 free(p->cargs); |
|
7258 free(p->rvalue); |
|
7259 free(p); |
|
7260 |
|
7261 if (!obj) { |
|
7262 return; // No slots to clean up |
|
7263 } |
|
7264 |
|
7265 JS_ASSERT(CDataFinalizer::IsCDataFinalizer(obj)); |
|
7266 |
|
7267 JS_SetPrivate(obj, nullptr); |
|
7268 for (int i = 0; i < CDATAFINALIZER_SLOTS; ++i) { |
|
7269 JS_SetReservedSlot(obj, i, JSVAL_NULL); |
|
7270 } |
|
7271 } |
|
7272 |
|
7273 |
|
7274 /******************************************************************************* |
|
7275 ** Int64 and UInt64 implementation |
|
7276 *******************************************************************************/ |
|
7277 |
|
7278 JSObject* |
|
7279 Int64Base::Construct(JSContext* cx, |
|
7280 HandleObject proto, |
|
7281 uint64_t data, |
|
7282 bool isUnsigned) |
|
7283 { |
|
7284 const JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class; |
|
7285 RootedObject parent(cx, JS_GetParent(proto)); |
|
7286 RootedObject result(cx, JS_NewObject(cx, clasp, proto, parent)); |
|
7287 if (!result) |
|
7288 return nullptr; |
|
7289 |
|
7290 // attach the Int64's data |
|
7291 uint64_t* buffer = cx->new_<uint64_t>(data); |
|
7292 if (!buffer) { |
|
7293 JS_ReportOutOfMemory(cx); |
|
7294 return nullptr; |
|
7295 } |
|
7296 |
|
7297 JS_SetReservedSlot(result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer)); |
|
7298 |
|
7299 if (!JS_FreezeObject(cx, result)) |
|
7300 return nullptr; |
|
7301 |
|
7302 return result; |
|
7303 } |
|
7304 |
|
7305 void |
|
7306 Int64Base::Finalize(JSFreeOp *fop, JSObject* obj) |
|
7307 { |
|
7308 jsval slot = JS_GetReservedSlot(obj, SLOT_INT64); |
|
7309 if (JSVAL_IS_VOID(slot)) |
|
7310 return; |
|
7311 |
|
7312 FreeOp::get(fop)->delete_(static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot))); |
|
7313 } |
|
7314 |
|
7315 uint64_t |
|
7316 Int64Base::GetInt(JSObject* obj) { |
|
7317 JS_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj)); |
|
7318 |
|
7319 jsval slot = JS_GetReservedSlot(obj, SLOT_INT64); |
|
7320 return *static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot)); |
|
7321 } |
|
7322 |
|
7323 bool |
|
7324 Int64Base::ToString(JSContext* cx, |
|
7325 JSObject* obj, |
|
7326 const CallArgs& args, |
|
7327 bool isUnsigned) |
|
7328 { |
|
7329 if (args.length() > 1) { |
|
7330 JS_ReportError(cx, "toString takes zero or one argument"); |
|
7331 return false; |
|
7332 } |
|
7333 |
|
7334 int radix = 10; |
|
7335 if (args.length() == 1) { |
|
7336 jsval arg = args[0]; |
|
7337 if (arg.isInt32()) |
|
7338 radix = arg.toInt32(); |
|
7339 if (!arg.isInt32() || radix < 2 || radix > 36) { |
|
7340 JS_ReportError(cx, "radix argument must be an integer between 2 and 36"); |
|
7341 return false; |
|
7342 } |
|
7343 } |
|
7344 |
|
7345 AutoString intString; |
|
7346 if (isUnsigned) { |
|
7347 IntegerToString(GetInt(obj), radix, intString); |
|
7348 } else { |
|
7349 IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString); |
|
7350 } |
|
7351 |
|
7352 JSString *result = NewUCString(cx, intString); |
|
7353 if (!result) |
|
7354 return false; |
|
7355 |
|
7356 args.rval().setString(result); |
|
7357 return true; |
|
7358 } |
|
7359 |
|
7360 bool |
|
7361 Int64Base::ToSource(JSContext* cx, |
|
7362 JSObject* obj, |
|
7363 const CallArgs& args, |
|
7364 bool isUnsigned) |
|
7365 { |
|
7366 if (args.length() != 0) { |
|
7367 JS_ReportError(cx, "toSource takes zero arguments"); |
|
7368 return false; |
|
7369 } |
|
7370 |
|
7371 // Return a decimal string suitable for constructing the number. |
|
7372 AutoString source; |
|
7373 if (isUnsigned) { |
|
7374 AppendString(source, "ctypes.UInt64(\""); |
|
7375 IntegerToString(GetInt(obj), 10, source); |
|
7376 } else { |
|
7377 AppendString(source, "ctypes.Int64(\""); |
|
7378 IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source); |
|
7379 } |
|
7380 AppendString(source, "\")"); |
|
7381 |
|
7382 JSString *result = NewUCString(cx, source); |
|
7383 if (!result) |
|
7384 return false; |
|
7385 |
|
7386 args.rval().setString(result); |
|
7387 return true; |
|
7388 } |
|
7389 |
|
7390 bool |
|
7391 Int64::Construct(JSContext* cx, |
|
7392 unsigned argc, |
|
7393 jsval* vp) |
|
7394 { |
|
7395 CallArgs args = CallArgsFromVp(argc, vp); |
|
7396 |
|
7397 // Construct and return a new Int64 object. |
|
7398 if (args.length() != 1) { |
|
7399 JS_ReportError(cx, "Int64 takes one argument"); |
|
7400 return false; |
|
7401 } |
|
7402 |
|
7403 int64_t i = 0; |
|
7404 if (!jsvalToBigInteger(cx, args[0], true, &i)) |
|
7405 return TypeError(cx, "int64", args[0]); |
|
7406 |
|
7407 // Get ctypes.Int64.prototype from the 'prototype' property of the ctor. |
|
7408 RootedValue slot(cx); |
|
7409 RootedObject callee(cx, &args.callee()); |
|
7410 ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot)); |
|
7411 RootedObject proto(cx, JSVAL_TO_OBJECT(slot)); |
|
7412 JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass); |
|
7413 |
|
7414 JSObject* result = Int64Base::Construct(cx, proto, i, false); |
|
7415 if (!result) |
|
7416 return false; |
|
7417 |
|
7418 args.rval().setObject(*result); |
|
7419 return true; |
|
7420 } |
|
7421 |
|
7422 bool |
|
7423 Int64::IsInt64(JSObject* obj) |
|
7424 { |
|
7425 return JS_GetClass(obj) == &sInt64Class; |
|
7426 } |
|
7427 |
|
7428 bool |
|
7429 Int64::ToString(JSContext* cx, unsigned argc, jsval* vp) |
|
7430 { |
|
7431 CallArgs args = CallArgsFromVp(argc, vp); |
|
7432 JSObject* obj = JS_THIS_OBJECT(cx, vp); |
|
7433 if (!obj) |
|
7434 return false; |
|
7435 if (!Int64::IsInt64(obj)) { |
|
7436 JS_ReportError(cx, "not an Int64"); |
|
7437 return false; |
|
7438 } |
|
7439 |
|
7440 return Int64Base::ToString(cx, obj, args, false); |
|
7441 } |
|
7442 |
|
7443 bool |
|
7444 Int64::ToSource(JSContext* cx, unsigned argc, jsval* vp) |
|
7445 { |
|
7446 CallArgs args = CallArgsFromVp(argc, vp); |
|
7447 JSObject* obj = JS_THIS_OBJECT(cx, vp); |
|
7448 if (!obj) |
|
7449 return false; |
|
7450 if (!Int64::IsInt64(obj)) { |
|
7451 JS_ReportError(cx, "not an Int64"); |
|
7452 return false; |
|
7453 } |
|
7454 |
|
7455 return Int64Base::ToSource(cx, obj, args, false); |
|
7456 } |
|
7457 |
|
7458 bool |
|
7459 Int64::Compare(JSContext* cx, unsigned argc, jsval* vp) |
|
7460 { |
|
7461 CallArgs args = CallArgsFromVp(argc, vp); |
|
7462 if (args.length() != 2 || |
|
7463 JSVAL_IS_PRIMITIVE(args[0]) || |
|
7464 JSVAL_IS_PRIMITIVE(args[1]) || |
|
7465 !Int64::IsInt64(&args[0].toObject()) || |
|
7466 !Int64::IsInt64(&args[1].toObject())) { |
|
7467 JS_ReportError(cx, "compare takes two Int64 arguments"); |
|
7468 return false; |
|
7469 } |
|
7470 |
|
7471 JSObject* obj1 = &args[0].toObject(); |
|
7472 JSObject* obj2 = &args[1].toObject(); |
|
7473 |
|
7474 int64_t i1 = Int64Base::GetInt(obj1); |
|
7475 int64_t i2 = Int64Base::GetInt(obj2); |
|
7476 |
|
7477 if (i1 == i2) |
|
7478 args.rval().setInt32(0); |
|
7479 else if (i1 < i2) |
|
7480 args.rval().setInt32(-1); |
|
7481 else |
|
7482 args.rval().setInt32(1); |
|
7483 |
|
7484 return true; |
|
7485 } |
|
7486 |
|
7487 #define LO_MASK ((uint64_t(1) << 32) - 1) |
|
7488 #define INT64_LO(i) ((i) & LO_MASK) |
|
7489 #define INT64_HI(i) ((i) >> 32) |
|
7490 |
|
7491 bool |
|
7492 Int64::Lo(JSContext* cx, unsigned argc, jsval* vp) |
|
7493 { |
|
7494 CallArgs args = CallArgsFromVp(argc, vp); |
|
7495 if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || |
|
7496 !Int64::IsInt64(&args[0].toObject())) { |
|
7497 JS_ReportError(cx, "lo takes one Int64 argument"); |
|
7498 return false; |
|
7499 } |
|
7500 |
|
7501 JSObject* obj = &args[0].toObject(); |
|
7502 int64_t u = Int64Base::GetInt(obj); |
|
7503 double d = uint32_t(INT64_LO(u)); |
|
7504 |
|
7505 args.rval().setNumber(d); |
|
7506 return true; |
|
7507 } |
|
7508 |
|
7509 bool |
|
7510 Int64::Hi(JSContext* cx, unsigned argc, jsval* vp) |
|
7511 { |
|
7512 CallArgs args = CallArgsFromVp(argc, vp); |
|
7513 if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || |
|
7514 !Int64::IsInt64(&args[0].toObject())) { |
|
7515 JS_ReportError(cx, "hi takes one Int64 argument"); |
|
7516 return false; |
|
7517 } |
|
7518 |
|
7519 JSObject* obj = &args[0].toObject(); |
|
7520 int64_t u = Int64Base::GetInt(obj); |
|
7521 double d = int32_t(INT64_HI(u)); |
|
7522 |
|
7523 args.rval().setDouble(d); |
|
7524 return true; |
|
7525 } |
|
7526 |
|
7527 bool |
|
7528 Int64::Join(JSContext* cx, unsigned argc, jsval* vp) |
|
7529 { |
|
7530 CallArgs args = CallArgsFromVp(argc, vp); |
|
7531 if (args.length() != 2) { |
|
7532 JS_ReportError(cx, "join takes two arguments"); |
|
7533 return false; |
|
7534 } |
|
7535 |
|
7536 int32_t hi; |
|
7537 uint32_t lo; |
|
7538 if (!jsvalToInteger(cx, args[0], &hi)) |
|
7539 return TypeError(cx, "int32", args[0]); |
|
7540 if (!jsvalToInteger(cx, args[1], &lo)) |
|
7541 return TypeError(cx, "uint32", args[1]); |
|
7542 |
|
7543 int64_t i = (int64_t(hi) << 32) + int64_t(lo); |
|
7544 |
|
7545 // Get Int64.prototype from the function's reserved slot. |
|
7546 JSObject* callee = &args.callee(); |
|
7547 |
|
7548 jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO); |
|
7549 RootedObject proto(cx, &slot.toObject()); |
|
7550 JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass); |
|
7551 |
|
7552 JSObject* result = Int64Base::Construct(cx, proto, i, false); |
|
7553 if (!result) |
|
7554 return false; |
|
7555 |
|
7556 args.rval().setObject(*result); |
|
7557 return true; |
|
7558 } |
|
7559 |
|
7560 bool |
|
7561 UInt64::Construct(JSContext* cx, |
|
7562 unsigned argc, |
|
7563 jsval* vp) |
|
7564 { |
|
7565 CallArgs args = CallArgsFromVp(argc, vp); |
|
7566 |
|
7567 // Construct and return a new UInt64 object. |
|
7568 if (args.length() != 1) { |
|
7569 JS_ReportError(cx, "UInt64 takes one argument"); |
|
7570 return false; |
|
7571 } |
|
7572 |
|
7573 uint64_t u = 0; |
|
7574 if (!jsvalToBigInteger(cx, args[0], true, &u)) |
|
7575 return TypeError(cx, "uint64", args[0]); |
|
7576 |
|
7577 // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor. |
|
7578 RootedValue slot(cx); |
|
7579 RootedObject callee(cx, &args.callee()); |
|
7580 ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot)); |
|
7581 RootedObject proto(cx, &slot.toObject()); |
|
7582 JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass); |
|
7583 |
|
7584 JSObject* result = Int64Base::Construct(cx, proto, u, true); |
|
7585 if (!result) |
|
7586 return false; |
|
7587 |
|
7588 args.rval().setObject(*result); |
|
7589 return true; |
|
7590 } |
|
7591 |
|
7592 bool |
|
7593 UInt64::IsUInt64(JSObject* obj) |
|
7594 { |
|
7595 return JS_GetClass(obj) == &sUInt64Class; |
|
7596 } |
|
7597 |
|
7598 bool |
|
7599 UInt64::ToString(JSContext* cx, unsigned argc, jsval* vp) |
|
7600 { |
|
7601 CallArgs args = CallArgsFromVp(argc, vp); |
|
7602 JSObject* obj = JS_THIS_OBJECT(cx, vp); |
|
7603 if (!obj) |
|
7604 return false; |
|
7605 if (!UInt64::IsUInt64(obj)) { |
|
7606 JS_ReportError(cx, "not a UInt64"); |
|
7607 return false; |
|
7608 } |
|
7609 |
|
7610 return Int64Base::ToString(cx, obj, args, true); |
|
7611 } |
|
7612 |
|
7613 bool |
|
7614 UInt64::ToSource(JSContext* cx, unsigned argc, jsval* vp) |
|
7615 { |
|
7616 CallArgs args = CallArgsFromVp(argc, vp); |
|
7617 JSObject* obj = JS_THIS_OBJECT(cx, vp); |
|
7618 if (!obj) |
|
7619 return false; |
|
7620 if (!UInt64::IsUInt64(obj)) { |
|
7621 JS_ReportError(cx, "not a UInt64"); |
|
7622 return false; |
|
7623 } |
|
7624 |
|
7625 return Int64Base::ToSource(cx, obj, args, true); |
|
7626 } |
|
7627 |
|
7628 bool |
|
7629 UInt64::Compare(JSContext* cx, unsigned argc, jsval* vp) |
|
7630 { |
|
7631 CallArgs args = CallArgsFromVp(argc, vp); |
|
7632 if (args.length() != 2 || |
|
7633 JSVAL_IS_PRIMITIVE(args[0]) || |
|
7634 JSVAL_IS_PRIMITIVE(args[1]) || |
|
7635 !UInt64::IsUInt64(&args[0].toObject()) || |
|
7636 !UInt64::IsUInt64(&args[1].toObject())) { |
|
7637 JS_ReportError(cx, "compare takes two UInt64 arguments"); |
|
7638 return false; |
|
7639 } |
|
7640 |
|
7641 JSObject* obj1 = &args[0].toObject(); |
|
7642 JSObject* obj2 = &args[1].toObject(); |
|
7643 |
|
7644 uint64_t u1 = Int64Base::GetInt(obj1); |
|
7645 uint64_t u2 = Int64Base::GetInt(obj2); |
|
7646 |
|
7647 if (u1 == u2) |
|
7648 args.rval().setInt32(0); |
|
7649 else if (u1 < u2) |
|
7650 args.rval().setInt32(-1); |
|
7651 else |
|
7652 args.rval().setInt32(1); |
|
7653 |
|
7654 return true; |
|
7655 } |
|
7656 |
|
7657 bool |
|
7658 UInt64::Lo(JSContext* cx, unsigned argc, jsval* vp) |
|
7659 { |
|
7660 CallArgs args = CallArgsFromVp(argc, vp); |
|
7661 if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || |
|
7662 !UInt64::IsUInt64(&args[0].toObject())) { |
|
7663 JS_ReportError(cx, "lo takes one UInt64 argument"); |
|
7664 return false; |
|
7665 } |
|
7666 |
|
7667 JSObject* obj = &args[0].toObject(); |
|
7668 uint64_t u = Int64Base::GetInt(obj); |
|
7669 double d = uint32_t(INT64_LO(u)); |
|
7670 |
|
7671 args.rval().setDouble(d); |
|
7672 return true; |
|
7673 } |
|
7674 |
|
7675 bool |
|
7676 UInt64::Hi(JSContext* cx, unsigned argc, jsval* vp) |
|
7677 { |
|
7678 CallArgs args = CallArgsFromVp(argc, vp); |
|
7679 if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || |
|
7680 !UInt64::IsUInt64(&args[0].toObject())) { |
|
7681 JS_ReportError(cx, "hi takes one UInt64 argument"); |
|
7682 return false; |
|
7683 } |
|
7684 |
|
7685 JSObject* obj = &args[0].toObject(); |
|
7686 uint64_t u = Int64Base::GetInt(obj); |
|
7687 double d = uint32_t(INT64_HI(u)); |
|
7688 |
|
7689 args.rval().setDouble(d); |
|
7690 return true; |
|
7691 } |
|
7692 |
|
7693 bool |
|
7694 UInt64::Join(JSContext* cx, unsigned argc, jsval* vp) |
|
7695 { |
|
7696 CallArgs args = CallArgsFromVp(argc, vp); |
|
7697 if (args.length() != 2) { |
|
7698 JS_ReportError(cx, "join takes two arguments"); |
|
7699 return false; |
|
7700 } |
|
7701 |
|
7702 uint32_t hi; |
|
7703 uint32_t lo; |
|
7704 if (!jsvalToInteger(cx, args[0], &hi)) |
|
7705 return TypeError(cx, "uint32_t", args[0]); |
|
7706 if (!jsvalToInteger(cx, args[1], &lo)) |
|
7707 return TypeError(cx, "uint32_t", args[1]); |
|
7708 |
|
7709 uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo); |
|
7710 |
|
7711 // Get UInt64.prototype from the function's reserved slot. |
|
7712 JSObject* callee = &args.callee(); |
|
7713 |
|
7714 jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO); |
|
7715 RootedObject proto(cx, &slot.toObject()); |
|
7716 JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass); |
|
7717 |
|
7718 JSObject* result = Int64Base::Construct(cx, proto, u, true); |
|
7719 if (!result) |
|
7720 return false; |
|
7721 |
|
7722 args.rval().setObject(*result); |
|
7723 return true; |
|
7724 } |
|
7725 |
|
7726 } |
|
7727 } |