|
1 # This Source Code Form is subject to the terms of the Mozilla Public |
|
2 # License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 # You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 |
|
5 # Common codegen classes. |
|
6 |
|
7 import os |
|
8 import re |
|
9 import string |
|
10 import math |
|
11 import textwrap |
|
12 |
|
13 from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLUndefinedValue |
|
14 from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor |
|
15 |
|
16 AUTOGENERATED_WARNING_COMMENT = \ |
|
17 "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" |
|
18 ADDPROPERTY_HOOK_NAME = '_addProperty' |
|
19 FINALIZE_HOOK_NAME = '_finalize' |
|
20 CONSTRUCT_HOOK_NAME = '_constructor' |
|
21 LEGACYCALLER_HOOK_NAME = '_legacycaller' |
|
22 HASINSTANCE_HOOK_NAME = '_hasInstance' |
|
23 NEWRESOLVE_HOOK_NAME = '_newResolve' |
|
24 ENUMERATE_HOOK_NAME = '_enumerate' |
|
25 ENUM_ENTRY_VARIABLE_NAME = 'strings' |
|
26 INSTANCE_RESERVED_SLOTS = 3 |
|
27 |
|
28 |
|
29 def memberReservedSlot(member): |
|
30 return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex |
|
31 |
|
32 |
|
33 def toStringBool(arg): |
|
34 return str(not not arg).lower() |
|
35 |
|
36 |
|
37 def toBindingNamespace(arg): |
|
38 return re.sub("((_workers)?$)", "Binding\\1", arg) |
|
39 |
|
40 |
|
41 def isTypeCopyConstructible(type): |
|
42 # Nullable and sequence stuff doesn't affect copy-constructibility |
|
43 type = type.unroll() |
|
44 return (type.isPrimitive() or type.isString() or type.isEnum() or |
|
45 (type.isUnion() and |
|
46 CGUnionStruct.isUnionCopyConstructible(type)) or |
|
47 (type.isDictionary() and |
|
48 CGDictionary.isDictionaryCopyConstructible(type.inner)) or |
|
49 # Interface types are only copy-constructible if they're Gecko |
|
50 # interfaces. SpiderMonkey interfaces are not copy-constructible |
|
51 # because of rooting issues. |
|
52 (type.isInterface() and type.isGeckoInterface())) |
|
53 |
|
54 |
|
55 def wantsAddProperty(desc): |
|
56 return (desc.concrete and |
|
57 desc.wrapperCache and |
|
58 not (desc.workers and |
|
59 desc.interface.getExtendedAttribute("Global"))) |
|
60 |
|
61 |
|
62 # We'll want to insert the indent at the beginnings of lines, but we |
|
63 # don't want to indent empty lines. So only indent lines that have a |
|
64 # non-newline character on them. |
|
65 lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE) |
|
66 |
|
67 |
|
68 def indent(s, indentLevel=2): |
|
69 """ |
|
70 Indent C++ code. |
|
71 |
|
72 Weird secret feature: this doesn't indent lines that start with # (such as |
|
73 #include lines). |
|
74 """ |
|
75 if s == "": |
|
76 return s |
|
77 return re.sub(lineStartDetector, indentLevel * " ", s) |
|
78 |
|
79 |
|
80 def dedent(s): |
|
81 """ |
|
82 Remove all leading whitespace from s, and remove a blank line |
|
83 at the beginning. |
|
84 """ |
|
85 if s.startswith('\n'): |
|
86 s = s[1:] |
|
87 return textwrap.dedent(s) |
|
88 |
|
89 def fill(template, **args): |
|
90 """ |
|
91 Convenience function for filling in a multiline template. |
|
92 |
|
93 `fill(template, name1=v1, name2=v2)` is a lot like |
|
94 `string.Template(template).substitute({"name1": v1, "name2": v2})`. |
|
95 |
|
96 However, it's shorter, and has a few nice features: |
|
97 |
|
98 * If `template` is indented, fill() automatically dedents it! |
|
99 This makes code using fill() with Python's multiline strings |
|
100 much nicer to look at. |
|
101 |
|
102 * If `template` starts with a blank line, fill() strips it off. |
|
103 (Again, convenient with multiline strings.) |
|
104 |
|
105 * fill() recognizes a special kind of substitution |
|
106 of the form `$*{name}`. |
|
107 |
|
108 Use this to paste in, and automatically indent, multiple lines. |
|
109 (Mnemonic: The `*` is for "multiple lines"). |
|
110 |
|
111 A `$*` substitution must appear by itself on a line, with optional |
|
112 preceding indentation (spaces only). The whole line is replaced by the |
|
113 corresponding keyword argument, indented appropriately. If the |
|
114 argument is an empty string, no output is generated, not even a blank |
|
115 line. |
|
116 """ |
|
117 |
|
118 # This works by transforming the fill()-template to an equivalent |
|
119 # string.Template. |
|
120 multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?") |
|
121 |
|
122 def replace(match): |
|
123 """ |
|
124 Replaces a line like ' $*{xyz}\n' with '${xyz_n}', |
|
125 where n is the indent depth, and add a corresponding entry to args. |
|
126 """ |
|
127 indentation, name, nl = match.groups() |
|
128 depth = len(indentation) |
|
129 |
|
130 # Check that $*{xyz} appears by itself on a line. |
|
131 prev = match.string[:match.start()] |
|
132 if (prev and not prev.endswith("\n")) or nl is None: |
|
133 raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name) |
|
134 |
|
135 # Multiline text without a newline at the end is probably a mistake. |
|
136 if not (args[name] == "" or args[name].endswith("\n")): |
|
137 raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name])) |
|
138 |
|
139 # Now replace this whole line of template with the indented equivalent. |
|
140 modified_name = name + "_" + str(depth) |
|
141 indented_value = indent(args[name], depth) |
|
142 if modified_name in args: |
|
143 assert args[modified_name] == indented_value |
|
144 else: |
|
145 args[modified_name] = indented_value |
|
146 return "${" + modified_name + "}" |
|
147 |
|
148 t = dedent(template) |
|
149 assert t.endswith("\n") or "\n" not in t |
|
150 t = re.sub(multiline_substitution_re, replace, t) |
|
151 t = string.Template(t) |
|
152 return t.substitute(args) |
|
153 |
|
154 |
|
155 class CGThing(): |
|
156 """ |
|
157 Abstract base class for things that spit out code. |
|
158 """ |
|
159 def __init__(self): |
|
160 pass # Nothing for now |
|
161 |
|
162 def declare(self): |
|
163 """Produce code for a header file.""" |
|
164 assert False # Override me! |
|
165 |
|
166 def define(self): |
|
167 """Produce code for a cpp file.""" |
|
168 assert False # Override me! |
|
169 |
|
170 def deps(self): |
|
171 """Produce the deps for a pp file""" |
|
172 assert False # Override me! |
|
173 |
|
174 |
|
175 class CGStringTable(CGThing): |
|
176 """ |
|
177 Generate a string table for the given strings with a function accessor: |
|
178 |
|
179 const char *accessorName(unsigned int index) { |
|
180 static const char table[] = "..."; |
|
181 static const uint16_t indices = { ... }; |
|
182 return &table[indices[index]]; |
|
183 } |
|
184 |
|
185 This is more efficient than the more natural: |
|
186 |
|
187 const char *table[] = { |
|
188 ... |
|
189 }; |
|
190 |
|
191 The uint16_t indices are smaller than the pointer equivalents, and the |
|
192 string table requires no runtime relocations. |
|
193 """ |
|
194 def __init__(self, accessorName, strings): |
|
195 CGThing.__init__(self) |
|
196 self.accessorName = accessorName |
|
197 self.strings = strings |
|
198 |
|
199 def declare(self): |
|
200 return "extern const char *%s(unsigned int aIndex);\n" % self.accessorName |
|
201 |
|
202 def define(self): |
|
203 table = ' "\\0" '.join('"%s"' % s for s in self.strings) |
|
204 indices = [] |
|
205 currentIndex = 0 |
|
206 for s in self.strings: |
|
207 indices.append(currentIndex) |
|
208 currentIndex += len(s) + 1 # for the null terminator |
|
209 return fill( |
|
210 """ |
|
211 const char *${name}(unsigned int aIndex) |
|
212 { |
|
213 static const char table[] = ${table}; |
|
214 static const uint16_t indices[] = { ${indices} }; |
|
215 static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!"); |
|
216 return &table[indices[aIndex]]; |
|
217 } |
|
218 """, |
|
219 name=self.accessorName, |
|
220 table=table, |
|
221 indices=", ".join("%d" % index for index in indices), |
|
222 currentIndex=currentIndex) |
|
223 |
|
224 |
|
225 class CGNativePropertyHooks(CGThing): |
|
226 """ |
|
227 Generate a NativePropertyHooks for a given descriptor |
|
228 """ |
|
229 def __init__(self, descriptor, properties): |
|
230 CGThing.__init__(self) |
|
231 self.descriptor = descriptor |
|
232 self.properties = properties |
|
233 |
|
234 def declare(self): |
|
235 if self.descriptor.workers: |
|
236 return "" |
|
237 return dedent(""" |
|
238 // We declare this as an array so that retrieving a pointer to this |
|
239 // binding's property hooks only requires compile/link-time resolvable |
|
240 // address arithmetic. Declaring it as a pointer instead would require |
|
241 // doing a run-time load to fetch a pointer to this binding's property |
|
242 // hooks. And then structures which embedded a pointer to this structure |
|
243 // would require a run-time load for proper initialization, which would |
|
244 // then induce static constructors. Lots of static constructors. |
|
245 extern const NativePropertyHooks sNativePropertyHooks[]; |
|
246 """).rstrip() # BOGUS strip newline from the last line here (!) |
|
247 |
|
248 def define(self): |
|
249 if self.descriptor.workers: |
|
250 return "" |
|
251 if self.descriptor.concrete and self.descriptor.proxy: |
|
252 resolveOwnProperty = "ResolveOwnProperty" |
|
253 enumerateOwnProperties = "EnumerateOwnProperties" |
|
254 elif self.descriptor.needsXrayResolveHooks(): |
|
255 resolveOwnProperty = "ResolveOwnPropertyViaNewresolve" |
|
256 enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames" |
|
257 else: |
|
258 resolveOwnProperty = "nullptr" |
|
259 enumerateOwnProperties = "nullptr" |
|
260 if self.properties.hasNonChromeOnly(): |
|
261 regular = "&sNativeProperties" |
|
262 else: |
|
263 regular = "nullptr" |
|
264 if self.properties.hasChromeOnly(): |
|
265 chrome = "&sChromeOnlyNativeProperties" |
|
266 else: |
|
267 chrome = "nullptr" |
|
268 constructorID = "constructors::id::" |
|
269 if self.descriptor.interface.hasInterfaceObject(): |
|
270 constructorID += self.descriptor.name |
|
271 else: |
|
272 constructorID += "_ID_Count" |
|
273 prototypeID = "prototypes::id::" |
|
274 if self.descriptor.interface.hasInterfacePrototypeObject(): |
|
275 prototypeID += self.descriptor.name |
|
276 else: |
|
277 prototypeID += "_ID_Count" |
|
278 parent = self.descriptor.interface.parent |
|
279 parentHooks = (toBindingNamespace(parent.identifier.name) + "::sNativePropertyHooks" |
|
280 if parent else 'nullptr') |
|
281 |
|
282 return fill( |
|
283 """ |
|
284 const NativePropertyHooks sNativePropertyHooks[] = { { |
|
285 ${resolveOwnProperty}, |
|
286 ${enumerateOwnProperties}, |
|
287 { ${regular}, ${chrome} }, |
|
288 ${prototypeID}, |
|
289 ${constructorID}, |
|
290 ${parentHooks} |
|
291 } }; |
|
292 """, |
|
293 resolveOwnProperty=resolveOwnProperty, |
|
294 enumerateOwnProperties=enumerateOwnProperties, |
|
295 regular=regular, |
|
296 chrome=chrome, |
|
297 prototypeID=prototypeID, |
|
298 constructorID=constructorID, |
|
299 parentHooks=parentHooks) |
|
300 |
|
301 |
|
302 def NativePropertyHooks(descriptor): |
|
303 return "&sWorkerNativePropertyHooks" if descriptor.workers else "sNativePropertyHooks" |
|
304 |
|
305 |
|
306 def DOMClass(descriptor): |
|
307 def make_name(d): |
|
308 return "%s%s" % (d.interface.identifier.name, '_workers' if d.workers else '') |
|
309 |
|
310 protoList = ['prototypes::id::' + make_name(descriptor.getDescriptor(proto)) for proto in descriptor.prototypeChain] |
|
311 # Pad out the list to the right length with _ID_Count so we |
|
312 # guarantee that all the lists are the same length. _ID_Count |
|
313 # is never the ID of any prototype, so it's safe to use as |
|
314 # padding. |
|
315 protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList))) |
|
316 |
|
317 return fill( |
|
318 """ |
|
319 { |
|
320 { ${protoChain} }, |
|
321 IsBaseOf<nsISupports, ${nativeType} >::value, |
|
322 ${hooks}, |
|
323 GetParentObject<${nativeType}>::Get, |
|
324 GetProtoObject, |
|
325 GetCCParticipant<${nativeType}>::Get() |
|
326 } |
|
327 """, |
|
328 protoChain=', '.join(protoList), |
|
329 nativeType=descriptor.nativeType, |
|
330 hooks=NativePropertyHooks(descriptor)) |
|
331 |
|
332 |
|
333 class CGDOMJSClass(CGThing): |
|
334 """ |
|
335 Generate a DOMJSClass for a given descriptor |
|
336 """ |
|
337 def __init__(self, descriptor): |
|
338 CGThing.__init__(self) |
|
339 self.descriptor = descriptor |
|
340 |
|
341 def declare(self): |
|
342 return "" |
|
343 |
|
344 def define(self): |
|
345 traceHook = 'nullptr' |
|
346 callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr' |
|
347 slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots |
|
348 classFlags = "JSCLASS_IS_DOMJSCLASS | " |
|
349 classExtensionAndObjectOps = """\ |
|
350 JS_NULL_CLASS_EXT, |
|
351 JS_NULL_OBJECT_OPS |
|
352 """ |
|
353 if self.descriptor.interface.getExtendedAttribute("Global"): |
|
354 classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS" |
|
355 traceHook = "JS_GlobalObjectTraceHook" |
|
356 if not self.descriptor.workers: |
|
357 classExtensionAndObjectOps = """\ |
|
358 { |
|
359 nsGlobalWindow::OuterObject, /* outerObject */ |
|
360 nullptr, /* innerObject */ |
|
361 nullptr, /* iteratorObject */ |
|
362 false, /* isWrappedNative */ |
|
363 nullptr /* weakmapKeyDelegateOp */ |
|
364 }, |
|
365 { |
|
366 nullptr, /* lookupGeneric */ |
|
367 nullptr, /* lookupProperty */ |
|
368 nullptr, /* lookupElement */ |
|
369 nullptr, /* defineGeneric */ |
|
370 nullptr, /* defineProperty */ |
|
371 nullptr, /* defineElement */ |
|
372 nullptr, /* getGeneric */ |
|
373 nullptr, /* getProperty */ |
|
374 nullptr, /* getElement */ |
|
375 nullptr, /* setGeneric */ |
|
376 nullptr, /* setProperty */ |
|
377 nullptr, /* setElement */ |
|
378 nullptr, /* getGenericAttributes */ |
|
379 nullptr, /* setGenericAttributes */ |
|
380 nullptr, /* deleteProperty */ |
|
381 nullptr, /* deleteElement */ |
|
382 nullptr, /* watch */ |
|
383 nullptr, /* unwatch */ |
|
384 nullptr, /* slice */ |
|
385 nullptr, /* enumerate */ |
|
386 JS_ObjectToOuterObject /* thisObject */ |
|
387 } |
|
388 """ |
|
389 else: |
|
390 classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount |
|
391 if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"): |
|
392 newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME |
|
393 classFlags += " | JSCLASS_NEW_RESOLVE" |
|
394 enumerateHook = ENUMERATE_HOOK_NAME |
|
395 elif self.descriptor.interface.getExtendedAttribute("Global"): |
|
396 newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal" |
|
397 classFlags += " | JSCLASS_NEW_RESOLVE" |
|
398 enumerateHook = "mozilla::dom::EnumerateGlobal" |
|
399 else: |
|
400 newResolveHook = "JS_ResolveStub" |
|
401 enumerateHook = "JS_EnumerateStub" |
|
402 |
|
403 return fill( # BOGUS extra blank line at the top |
|
404 """ |
|
405 |
|
406 static const DOMJSClass Class = { |
|
407 { "${name}", |
|
408 ${flags}, |
|
409 ${addProperty}, /* addProperty */ |
|
410 JS_DeletePropertyStub, /* delProperty */ |
|
411 JS_PropertyStub, /* getProperty */ |
|
412 JS_StrictPropertyStub, /* setProperty */ |
|
413 ${enumerate}, /* enumerate */ |
|
414 ${resolve}, /* resolve */ |
|
415 JS_ConvertStub, |
|
416 ${finalize}, /* finalize */ |
|
417 ${call}, /* call */ |
|
418 nullptr, /* hasInstance */ |
|
419 nullptr, /* construct */ |
|
420 ${trace}, /* trace */ |
|
421 JS_NULL_CLASS_SPEC, |
|
422 $*{classExtensionAndObjectOps} |
|
423 }, |
|
424 $*{descriptor} |
|
425 }; |
|
426 """, |
|
427 name=self.descriptor.interface.identifier.name, |
|
428 flags=classFlags, |
|
429 addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub', |
|
430 enumerate=enumerateHook, |
|
431 resolve=newResolveHook, |
|
432 finalize=FINALIZE_HOOK_NAME, |
|
433 call=callHook, |
|
434 trace=traceHook, |
|
435 classExtensionAndObjectOps=classExtensionAndObjectOps, |
|
436 descriptor=DOMClass(self.descriptor)) |
|
437 |
|
438 |
|
439 class CGDOMProxyJSClass(CGThing): |
|
440 """ |
|
441 Generate a DOMJSClass for a given proxy descriptor |
|
442 """ |
|
443 def __init__(self, descriptor): |
|
444 CGThing.__init__(self) |
|
445 self.descriptor = descriptor |
|
446 |
|
447 def declare(self): |
|
448 return "" |
|
449 |
|
450 def define(self): |
|
451 flags = ["JSCLASS_IS_DOMJSCLASS"] |
|
452 # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because |
|
453 # we don't want people ever adding that to any interface other than |
|
454 # HTMLAllCollection. So just hardcode it here. |
|
455 if self.descriptor.interface.identifier.name == "HTMLAllCollection": |
|
456 flags.append("JSCLASS_EMULATES_UNDEFINED") |
|
457 callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr' |
|
458 return fill( # BOGUS extra blank line at the top |
|
459 """ |
|
460 |
|
461 static const DOMJSClass Class = { |
|
462 PROXY_CLASS_DEF("${name}", |
|
463 0, /* extra slots */ |
|
464 ${flags}, |
|
465 ${call}, /* call */ |
|
466 nullptr /* construct */), |
|
467 $*{descriptor} |
|
468 }; |
|
469 """, |
|
470 name=self.descriptor.interface.identifier.name, |
|
471 flags=" | ".join(flags), |
|
472 call=callHook, |
|
473 descriptor=DOMClass(self.descriptor)) |
|
474 |
|
475 |
|
476 def PrototypeIDAndDepth(descriptor): |
|
477 prototypeID = "prototypes::id::" |
|
478 if descriptor.interface.hasInterfacePrototypeObject(): |
|
479 prototypeID += descriptor.interface.identifier.name |
|
480 if descriptor.workers: |
|
481 prototypeID += "_workers" |
|
482 depth = "PrototypeTraits<%s>::Depth" % prototypeID |
|
483 else: |
|
484 prototypeID += "_ID_Count" |
|
485 depth = "0" |
|
486 return (prototypeID, depth) |
|
487 |
|
488 |
|
489 def UseHolderForUnforgeable(descriptor): |
|
490 return (descriptor.concrete and |
|
491 descriptor.proxy and |
|
492 any(m for m in descriptor.interface.members if m.isAttr() and m.isUnforgeable())) |
|
493 |
|
494 |
|
495 def CallOnUnforgeableHolder(descriptor, code, isXrayCheck=None, |
|
496 useSharedRoot=False): |
|
497 """ |
|
498 Generate the code to execute the code in "code" on an unforgeable holder if |
|
499 needed. code should be a string containing the code to execute. If it |
|
500 contains a ${holder} string parameter it will be replaced with the |
|
501 unforgeable holder object. |
|
502 |
|
503 If isXrayCheck is not None it should be a string that contains a statement |
|
504 returning whether proxy is an Xray. If isXrayCheck is None the generated |
|
505 code won't try to unwrap Xrays. |
|
506 |
|
507 If useSharedRoot is true, we will use an existing |
|
508 JS::Rooted<JSObject*> sharedRoot for storing our unforgeable holder instead |
|
509 of declaring a new Rooted. |
|
510 """ |
|
511 if isXrayCheck is not None: |
|
512 pre = fill( |
|
513 """ |
|
514 // Scope for 'global', 'ac' and 'unforgeableHolder' |
|
515 { |
|
516 JS::Rooted<JSObject*> global(cx); |
|
517 Maybe<JSAutoCompartment> ac; |
|
518 if (${isXrayCheck}) { |
|
519 global = js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(proxy)); |
|
520 ac.construct(cx, global); |
|
521 } else { |
|
522 global = js::GetGlobalForObjectCrossCompartment(proxy); |
|
523 } |
|
524 """, |
|
525 isXrayCheck=isXrayCheck) |
|
526 else: |
|
527 pre = dedent(""" |
|
528 // Scope for 'global' and 'unforgeableHolder' |
|
529 { |
|
530 JSObject* global = js::GetGlobalForObjectCrossCompartment(proxy); |
|
531 """) |
|
532 |
|
533 if useSharedRoot: |
|
534 holderDecl = "JS::Rooted<JSObject*>& unforgeableHolder(sharedRoot);\n" |
|
535 else: |
|
536 holderDecl = "JS::Rooted<JSObject*> unforgeableHolder(cx);\n" |
|
537 |
|
538 code = string.Template(code).substitute({"holder": "unforgeableHolder"}) |
|
539 return fill( |
|
540 """ |
|
541 $*{pre} |
|
542 $*{holderDecl} |
|
543 unforgeableHolder = GetUnforgeableHolder(global, prototypes::id::${name}); |
|
544 $*{code} |
|
545 } |
|
546 """, |
|
547 pre=pre, |
|
548 holderDecl=holderDecl, |
|
549 name=descriptor.name, |
|
550 code=code) |
|
551 |
|
552 |
|
553 class CGPrototypeJSClass(CGThing): |
|
554 def __init__(self, descriptor, properties): |
|
555 CGThing.__init__(self) |
|
556 self.descriptor = descriptor |
|
557 self.properties = properties |
|
558 |
|
559 def declare(self): |
|
560 # We're purely for internal consumption |
|
561 return "" |
|
562 |
|
563 def define(self): |
|
564 prototypeID, depth = PrototypeIDAndDepth(self.descriptor) |
|
565 slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE" |
|
566 if UseHolderForUnforgeable(self.descriptor): |
|
567 slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */" |
|
568 return fill( |
|
569 """ |
|
570 static const DOMIfaceAndProtoJSClass PrototypeClass = { |
|
571 { |
|
572 "${name}Prototype", |
|
573 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}), |
|
574 JS_PropertyStub, /* addProperty */ |
|
575 JS_DeletePropertyStub, /* delProperty */ |
|
576 JS_PropertyStub, /* getProperty */ |
|
577 JS_StrictPropertyStub, /* setProperty */ |
|
578 JS_EnumerateStub, |
|
579 JS_ResolveStub, |
|
580 JS_ConvertStub, |
|
581 nullptr, /* finalize */ |
|
582 nullptr, /* call */ |
|
583 nullptr, /* hasInstance */ |
|
584 nullptr, /* construct */ |
|
585 nullptr, /* trace */ |
|
586 JSCLASS_NO_INTERNAL_MEMBERS |
|
587 }, |
|
588 eInterfacePrototype, |
|
589 ${hooks}, |
|
590 "[object ${name}Prototype]", |
|
591 ${prototypeID}, |
|
592 ${depth} |
|
593 }; |
|
594 """, |
|
595 name=self.descriptor.interface.identifier.name, |
|
596 slotCount=slotCount, |
|
597 hooks=NativePropertyHooks(self.descriptor), |
|
598 prototypeID=prototypeID, |
|
599 depth=depth) |
|
600 |
|
601 |
|
602 def NeedsGeneratedHasInstance(descriptor): |
|
603 return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential() |
|
604 |
|
605 |
|
606 class CGInterfaceObjectJSClass(CGThing): |
|
607 def __init__(self, descriptor, properties): |
|
608 CGThing.__init__(self) |
|
609 self.descriptor = descriptor |
|
610 self.properties = properties |
|
611 |
|
612 def declare(self): |
|
613 # We're purely for internal consumption |
|
614 return "" |
|
615 |
|
616 def define(self): |
|
617 if self.descriptor.interface.ctor(): |
|
618 ctorname = CONSTRUCT_HOOK_NAME |
|
619 else: |
|
620 ctorname = "ThrowingConstructor" |
|
621 if NeedsGeneratedHasInstance(self.descriptor): |
|
622 hasinstance = HASINSTANCE_HOOK_NAME |
|
623 elif self.descriptor.interface.hasInterfacePrototypeObject(): |
|
624 hasinstance = "InterfaceHasInstance" |
|
625 else: |
|
626 hasinstance = "nullptr" |
|
627 prototypeID, depth = PrototypeIDAndDepth(self.descriptor) |
|
628 slotCount = "DOM_INTERFACE_SLOTS_BASE" |
|
629 if len(self.descriptor.interface.namedConstructors) > 0: |
|
630 slotCount += (" + %i /* slots for the named constructors */" % |
|
631 len(self.descriptor.interface.namedConstructors)) |
|
632 return fill( # BOGUS extra newline at the top |
|
633 """ |
|
634 |
|
635 static const DOMIfaceAndProtoJSClass InterfaceObjectClass = { |
|
636 { |
|
637 "Function", |
|
638 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}), |
|
639 JS_PropertyStub, /* addProperty */ |
|
640 JS_DeletePropertyStub, /* delProperty */ |
|
641 JS_PropertyStub, /* getProperty */ |
|
642 JS_StrictPropertyStub, /* setProperty */ |
|
643 JS_EnumerateStub, |
|
644 JS_ResolveStub, |
|
645 JS_ConvertStub, |
|
646 nullptr, /* finalize */ |
|
647 ${ctorname}, /* call */ |
|
648 ${hasInstance}, /* hasInstance */ |
|
649 ${ctorname}, /* construct */ |
|
650 nullptr, /* trace */ |
|
651 JSCLASS_NO_INTERNAL_MEMBERS |
|
652 }, |
|
653 eInterface, |
|
654 ${hooks}, |
|
655 "function ${name}() {\\n [native code]\\n}", |
|
656 ${prototypeID}, |
|
657 ${depth} |
|
658 }; |
|
659 """, |
|
660 slotCount=slotCount, |
|
661 ctorname=ctorname, |
|
662 hasInstance=hasinstance, |
|
663 hooks=NativePropertyHooks(self.descriptor), |
|
664 name=self.descriptor.interface.identifier.name, |
|
665 prototypeID=prototypeID, |
|
666 depth=depth) |
|
667 |
|
668 |
|
669 class CGList(CGThing): |
|
670 """ |
|
671 Generate code for a list of GCThings. Just concatenates them together, with |
|
672 an optional joiner string. "\n" is a common joiner. |
|
673 """ |
|
674 def __init__(self, children, joiner=""): |
|
675 CGThing.__init__(self) |
|
676 # Make a copy of the kids into a list, because if someone passes in a |
|
677 # generator we won't be able to both declare and define ourselves, or |
|
678 # define ourselves more than once! |
|
679 self.children = list(children) |
|
680 self.joiner = joiner |
|
681 |
|
682 def append(self, child): |
|
683 self.children.append(child) |
|
684 |
|
685 def prepend(self, child): |
|
686 self.children.insert(0, child) |
|
687 |
|
688 def extend(self, kids): |
|
689 self.children.extend(kids) |
|
690 |
|
691 def join(self, iterable): |
|
692 return self.joiner.join(s for s in iterable if len(s) > 0) |
|
693 |
|
694 def declare(self): |
|
695 return self.join(child.declare() for child in self.children if child is not None) |
|
696 |
|
697 def define(self): |
|
698 return self.join(child.define() for child in self.children if child is not None) |
|
699 |
|
700 def deps(self): |
|
701 deps = set() |
|
702 for child in self.children: |
|
703 if child is None: |
|
704 continue |
|
705 deps = deps.union(child.deps()) |
|
706 return deps |
|
707 |
|
708 |
|
709 class CGGeneric(CGThing): |
|
710 """ |
|
711 A class that spits out a fixed string into the codegen. Can spit out a |
|
712 separate string for the declaration too. |
|
713 """ |
|
714 def __init__(self, define="", declare=""): |
|
715 self.declareText = declare |
|
716 self.defineText = define |
|
717 |
|
718 def declare(self): |
|
719 return self.declareText |
|
720 |
|
721 def define(self): |
|
722 return self.defineText |
|
723 |
|
724 def deps(self): |
|
725 return set() |
|
726 |
|
727 |
|
728 class CGIndenter(CGThing): |
|
729 """ |
|
730 A class that takes another CGThing and generates code that indents that |
|
731 CGThing by some number of spaces. The default indent is two spaces. |
|
732 """ |
|
733 def __init__(self, child, indentLevel=2, declareOnly=False): |
|
734 assert isinstance(child, CGThing) |
|
735 CGThing.__init__(self) |
|
736 self.child = child |
|
737 self.indentLevel = indentLevel |
|
738 self.declareOnly = declareOnly |
|
739 |
|
740 def declare(self): |
|
741 return indent(self.child.declare(), self.indentLevel) |
|
742 |
|
743 def define(self): |
|
744 defn = self.child.define() |
|
745 if self.declareOnly: |
|
746 return defn |
|
747 else: |
|
748 return indent(defn, self.indentLevel) |
|
749 |
|
750 |
|
751 class CGWrapper(CGThing): |
|
752 """ |
|
753 Generic CGThing that wraps other CGThings with pre and post text. |
|
754 """ |
|
755 def __init__(self, child, pre="", post="", declarePre=None, |
|
756 declarePost=None, definePre=None, definePost=None, |
|
757 declareOnly=False, defineOnly=False, reindent=False): |
|
758 CGThing.__init__(self) |
|
759 self.child = child |
|
760 self.declarePre = declarePre or pre |
|
761 self.declarePost = declarePost or post |
|
762 self.definePre = definePre or pre |
|
763 self.definePost = definePost or post |
|
764 self.declareOnly = declareOnly |
|
765 self.defineOnly = defineOnly |
|
766 self.reindent = reindent |
|
767 |
|
768 def declare(self): |
|
769 if self.defineOnly: |
|
770 return '' |
|
771 decl = self.child.declare() |
|
772 if self.reindent: |
|
773 decl = self.reindentString(decl, self.declarePre) |
|
774 return self.declarePre + decl + self.declarePost |
|
775 |
|
776 def define(self): |
|
777 if self.declareOnly: |
|
778 return '' |
|
779 defn = self.child.define() |
|
780 if self.reindent: |
|
781 defn = self.reindentString(defn, self.definePre) |
|
782 return self.definePre + defn + self.definePost |
|
783 |
|
784 @staticmethod |
|
785 def reindentString(stringToIndent, widthString): |
|
786 # We don't use lineStartDetector because we don't want to |
|
787 # insert whitespace at the beginning of our _first_ line. |
|
788 # Use the length of the last line of width string, in case |
|
789 # it is a multiline string. |
|
790 lastLineWidth = len(widthString.splitlines()[-1]) |
|
791 return stripTrailingWhitespace( |
|
792 stringToIndent.replace("\n", "\n" + (" " * lastLineWidth))) |
|
793 |
|
794 def deps(self): |
|
795 return self.child.deps() |
|
796 |
|
797 |
|
798 class CGIfWrapper(CGList): |
|
799 def __init__(self, child, condition): |
|
800 CGList.__init__(self, [ |
|
801 CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True), |
|
802 CGIndenter(child), |
|
803 CGGeneric("}\n") |
|
804 ]) |
|
805 |
|
806 |
|
807 class CGIfElseWrapper(CGList): |
|
808 def __init__(self, condition, ifTrue, ifFalse): |
|
809 CGList.__init__(self, [ |
|
810 CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True), |
|
811 CGIndenter(ifTrue), |
|
812 CGGeneric("} else {\n"), |
|
813 CGIndenter(ifFalse), |
|
814 CGGeneric("}\n") |
|
815 ]) |
|
816 |
|
817 |
|
818 class CGElseChain(CGThing): |
|
819 """ |
|
820 Concatenate if statements in an if-else-if-else chain. |
|
821 """ |
|
822 def __init__(self, children): |
|
823 self.children = [c for c in children if c is not None] |
|
824 |
|
825 def declare(self): |
|
826 assert False |
|
827 |
|
828 def define(self): |
|
829 if not self.children: |
|
830 return "" |
|
831 s = self.children[0].define() |
|
832 assert s.endswith("\n") |
|
833 for child in self.children[1:]: |
|
834 code = child.define() |
|
835 assert code.startswith("if") or code.startswith("{") |
|
836 assert code.endswith("\n") |
|
837 s = s.rstrip() + " else " + code |
|
838 return s |
|
839 |
|
840 |
|
841 class CGTemplatedType(CGWrapper): |
|
842 def __init__(self, templateName, child, isConst=False, isReference=False): |
|
843 const = "const " if isConst else "" |
|
844 pre = "%s%s<" % (const, templateName) |
|
845 ref = "&" if isReference else "" |
|
846 post = ">%s" % ref |
|
847 CGWrapper.__init__(self, child, pre=pre, post=post) |
|
848 |
|
849 |
|
850 class CGNamespace(CGWrapper): |
|
851 def __init__(self, namespace, child, declareOnly=False): |
|
852 pre = "namespace %s {\n" % namespace |
|
853 post = "} // namespace %s\n" % namespace |
|
854 CGWrapper.__init__(self, child, pre=pre, post=post, |
|
855 declareOnly=declareOnly) |
|
856 |
|
857 @staticmethod |
|
858 def build(namespaces, child, declareOnly=False): |
|
859 """ |
|
860 Static helper method to build multiple wrapped namespaces. |
|
861 """ |
|
862 if not namespaces: |
|
863 return CGWrapper(child, declareOnly=declareOnly) |
|
864 inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly) |
|
865 return CGNamespace(namespaces[0], inner, declareOnly=declareOnly) |
|
866 |
|
867 |
|
868 class CGIncludeGuard(CGWrapper): |
|
869 """ |
|
870 Generates include guards for a header. |
|
871 """ |
|
872 def __init__(self, prefix, child): |
|
873 """|prefix| is the filename without the extension.""" |
|
874 define = 'mozilla_dom_%s_h' % prefix |
|
875 CGWrapper.__init__(self, child, |
|
876 declarePre='#ifndef %s\n#define %s\n\n' % (define, define), |
|
877 declarePost='\n#endif // %s\n' % define) |
|
878 |
|
879 |
|
880 def getRelevantProviders(descriptor, config): |
|
881 if descriptor is not None: |
|
882 return [descriptor] |
|
883 # Do both the non-worker and worker versions |
|
884 return [ |
|
885 config.getDescriptorProvider(False), |
|
886 config.getDescriptorProvider(True) |
|
887 ] |
|
888 |
|
889 |
|
890 def getAllTypes(descriptors, dictionaries, callbacks): |
|
891 """ |
|
892 Generate all the types we're dealing with. For each type, a tuple |
|
893 containing type, descriptor, dictionary is yielded. The |
|
894 descriptor and dictionary can be None if the type does not come |
|
895 from a descriptor or dictionary; they will never both be non-None. |
|
896 """ |
|
897 for d in descriptors: |
|
898 if d.interface.isExternal(): |
|
899 continue |
|
900 for t in getTypesFromDescriptor(d): |
|
901 yield (t, d, None) |
|
902 for dictionary in dictionaries: |
|
903 for t in getTypesFromDictionary(dictionary): |
|
904 yield (t, None, dictionary) |
|
905 for callback in callbacks: |
|
906 for t in getTypesFromCallback(callback): |
|
907 yield (t, None, None) |
|
908 |
|
909 |
|
910 class CGHeaders(CGWrapper): |
|
911 """ |
|
912 Generates the appropriate include statements. |
|
913 """ |
|
914 def __init__(self, descriptors, dictionaries, callbacks, |
|
915 callbackDescriptors, |
|
916 declareIncludes, defineIncludes, prefix, child, |
|
917 config=None, jsImplementedDescriptors=[]): |
|
918 """ |
|
919 Builds a set of includes to cover |descriptors|. |
|
920 |
|
921 Also includes the files in |declareIncludes| in the header |
|
922 file and the files in |defineIncludes| in the .cpp. |
|
923 |
|
924 |prefix| contains the basename of the file that we generate include |
|
925 statements for. |
|
926 """ |
|
927 |
|
928 # Determine the filenames for which we need headers. |
|
929 interfaceDeps = [d.interface for d in descriptors] |
|
930 ancestors = [] |
|
931 for iface in interfaceDeps: |
|
932 if iface.parent: |
|
933 # We're going to need our parent's prototype, to use as the |
|
934 # prototype of our prototype object. |
|
935 ancestors.append(iface.parent) |
|
936 # And if we have an interface object, we'll need the nearest |
|
937 # ancestor with an interface object too, so we can use its |
|
938 # interface object as the proto of our interface object. |
|
939 if iface.hasInterfaceObject(): |
|
940 parent = iface.parent |
|
941 while parent and not parent.hasInterfaceObject(): |
|
942 parent = parent.parent |
|
943 if parent: |
|
944 ancestors.append(parent) |
|
945 interfaceDeps.extend(ancestors) |
|
946 bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps) |
|
947 |
|
948 # Grab all the implementation declaration files we need. |
|
949 implementationIncludes = set(d.headerFile for d in descriptors if d.needsHeaderInclude()) |
|
950 |
|
951 # Grab the includes for checking hasInstance |
|
952 interfacesImplementingSelf = set() |
|
953 for d in descriptors: |
|
954 interfacesImplementingSelf |= d.interface.interfacesImplementingSelf |
|
955 implementationIncludes |= set(self.getDeclarationFilename(i) for i in |
|
956 interfacesImplementingSelf) |
|
957 |
|
958 # Grab the includes for the things that involve XPCOM interfaces |
|
959 hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d |
|
960 in descriptors if |
|
961 NeedsGeneratedHasInstance(d) and |
|
962 d.interface.hasInterfacePrototypeObject()) |
|
963 |
|
964 # Now find all the things we'll need as arguments because we |
|
965 # need to wrap or unwrap them. |
|
966 bindingHeaders = set() |
|
967 declareIncludes = set(declareIncludes) |
|
968 |
|
969 def addHeadersForType((t, descriptor, dictionary)): |
|
970 """ |
|
971 Add the relevant headers for this type. We use descriptor and |
|
972 dictionary, if passed, to decide what to do with interface types. |
|
973 """ |
|
974 assert not descriptor or not dictionary |
|
975 # Dictionaries have members that need to be actually |
|
976 # declared, not just forward-declared. |
|
977 if dictionary: |
|
978 headerSet = declareIncludes |
|
979 else: |
|
980 headerSet = bindingHeaders |
|
981 if t.nullable(): |
|
982 # Need to make sure that Nullable as a dictionary |
|
983 # member works. |
|
984 headerSet.add("mozilla/dom/Nullable.h") |
|
985 unrolled = t.unroll() |
|
986 if unrolled.isUnion(): |
|
987 # UnionConversions.h includes UnionTypes.h |
|
988 bindingHeaders.add("mozilla/dom/UnionConversions.h") |
|
989 if dictionary: |
|
990 # Our dictionary definition is in the header and |
|
991 # needs the union type. |
|
992 declareIncludes.add("mozilla/dom/UnionTypes.h") |
|
993 elif unrolled.isDate(): |
|
994 if dictionary or jsImplementedDescriptors: |
|
995 declareIncludes.add("mozilla/dom/Date.h") |
|
996 else: |
|
997 bindingHeaders.add("mozilla/dom/Date.h") |
|
998 elif unrolled.isInterface(): |
|
999 if unrolled.isSpiderMonkeyInterface(): |
|
1000 bindingHeaders.add("jsfriendapi.h") |
|
1001 headerSet.add("mozilla/dom/TypedArray.h") |
|
1002 else: |
|
1003 providers = getRelevantProviders(descriptor, config) |
|
1004 for p in providers: |
|
1005 try: |
|
1006 typeDesc = p.getDescriptor(unrolled.inner.identifier.name) |
|
1007 except NoSuchDescriptorError: |
|
1008 continue |
|
1009 # Dictionaries with interface members rely on the |
|
1010 # actual class definition of that interface member |
|
1011 # being visible in the binding header, because they |
|
1012 # store them in nsRefPtr and have inline |
|
1013 # constructors/destructors. |
|
1014 # |
|
1015 # XXXbz maybe dictionaries with interface members |
|
1016 # should just have out-of-line constructors and |
|
1017 # destructors? |
|
1018 headerSet.add(typeDesc.headerFile) |
|
1019 elif unrolled.isDictionary(): |
|
1020 headerSet.add(self.getDeclarationFilename(unrolled.inner)) |
|
1021 elif unrolled.isCallback(): |
|
1022 # Callbacks are both a type and an object |
|
1023 headerSet.add(self.getDeclarationFilename(unrolled)) |
|
1024 elif unrolled.isFloat() and not unrolled.isUnrestricted(): |
|
1025 # Restricted floats are tested for finiteness |
|
1026 bindingHeaders.add("mozilla/FloatingPoint.h") |
|
1027 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") |
|
1028 elif unrolled.isEnum(): |
|
1029 filename = self.getDeclarationFilename(unrolled.inner) |
|
1030 declareIncludes.add(filename) |
|
1031 elif unrolled.isPrimitive(): |
|
1032 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") |
|
1033 elif unrolled.isMozMap(): |
|
1034 if dictionary or jsImplementedDescriptors: |
|
1035 declareIncludes.add("mozilla/dom/MozMap.h") |
|
1036 else: |
|
1037 bindingHeaders.add("mozilla/dom/MozMap.h") |
|
1038 # Also add headers for the type the MozMap is |
|
1039 # parametrized over, if needed. |
|
1040 addHeadersForType((t.inner, descriptor, dictionary)) |
|
1041 |
|
1042 map(addHeadersForType, |
|
1043 getAllTypes(descriptors + callbackDescriptors, dictionaries, |
|
1044 callbacks)) |
|
1045 |
|
1046 # Now make sure we're not trying to include the header from inside itself |
|
1047 declareIncludes.discard(prefix + ".h") |
|
1048 |
|
1049 # Now for non-callback descriptors make sure we include any |
|
1050 # headers needed by Func declarations. |
|
1051 for desc in descriptors: |
|
1052 if desc.interface.isExternal(): |
|
1053 continue |
|
1054 |
|
1055 def addHeaderForFunc(func): |
|
1056 if func is None: |
|
1057 return |
|
1058 # Include the right class header, which we can only do |
|
1059 # if this is a class member function. |
|
1060 if not desc.headerIsDefault: |
|
1061 # An explicit header file was provided, assume that we know |
|
1062 # what we're doing. |
|
1063 return |
|
1064 |
|
1065 if "::" in func: |
|
1066 # Strip out the function name and convert "::" to "/" |
|
1067 bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h") |
|
1068 |
|
1069 for m in desc.interface.members: |
|
1070 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func")) |
|
1071 # getExtendedAttribute() returns a list, extract the entry. |
|
1072 funcList = desc.interface.getExtendedAttribute("Func") |
|
1073 if funcList is not None: |
|
1074 addHeaderForFunc(funcList[0]) |
|
1075 |
|
1076 for d in dictionaries: |
|
1077 if d.parent: |
|
1078 declareIncludes.add(self.getDeclarationFilename(d.parent)) |
|
1079 bindingHeaders.add(self.getDeclarationFilename(d)) |
|
1080 |
|
1081 for c in callbacks: |
|
1082 bindingHeaders.add(self.getDeclarationFilename(c)) |
|
1083 |
|
1084 for c in callbackDescriptors: |
|
1085 bindingHeaders.add(self.getDeclarationFilename(c.interface)) |
|
1086 |
|
1087 if len(callbacks) != 0: |
|
1088 # We need CallbackFunction to serve as our parent class |
|
1089 declareIncludes.add("mozilla/dom/CallbackFunction.h") |
|
1090 # And we need BindingUtils.h so we can wrap "this" objects |
|
1091 declareIncludes.add("mozilla/dom/BindingUtils.h") |
|
1092 |
|
1093 if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0: |
|
1094 # We need CallbackInterface to serve as our parent class |
|
1095 declareIncludes.add("mozilla/dom/CallbackInterface.h") |
|
1096 # And we need BindingUtils.h so we can wrap "this" objects |
|
1097 declareIncludes.add("mozilla/dom/BindingUtils.h") |
|
1098 |
|
1099 # Also need to include the headers for ancestors of |
|
1100 # JS-implemented interfaces. |
|
1101 for jsImplemented in jsImplementedDescriptors: |
|
1102 jsParent = jsImplemented.interface.parent |
|
1103 if jsParent: |
|
1104 parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name) |
|
1105 declareIncludes.add(parentDesc.jsImplParentHeader) |
|
1106 |
|
1107 # Let the machinery do its thing. |
|
1108 def _includeString(includes): |
|
1109 return ''.join(['#include "%s"\n' % i for i in includes]) + '\n' |
|
1110 CGWrapper.__init__(self, child, |
|
1111 declarePre=_includeString(sorted(declareIncludes)), |
|
1112 definePre=_includeString(sorted(set(defineIncludes) | |
|
1113 bindingIncludes | |
|
1114 bindingHeaders | |
|
1115 hasInstanceIncludes | |
|
1116 implementationIncludes))) |
|
1117 |
|
1118 @staticmethod |
|
1119 def getDeclarationFilename(decl): |
|
1120 # Use our local version of the header, not the exported one, so that |
|
1121 # test bindings, which don't export, will work correctly. |
|
1122 basename = os.path.basename(decl.filename()) |
|
1123 return basename.replace('.webidl', 'Binding.h') |
|
1124 |
|
1125 |
|
1126 def SortedDictValues(d): |
|
1127 """ |
|
1128 Returns a list of values from the dict sorted by key. |
|
1129 """ |
|
1130 return [v for k, v in sorted(d.items())] |
|
1131 |
|
1132 |
|
1133 def UnionTypes(descriptors, dictionaries, callbacks, config): |
|
1134 """ |
|
1135 Returns a tuple containing a set of header filenames to include in |
|
1136 UnionTypes.h, a set of header filenames to include in UnionTypes.cpp, a set |
|
1137 of tuples containing a type declaration and a boolean if the type is a |
|
1138 struct for member types of the unions and a CGList containing CGUnionStructs |
|
1139 for every union. |
|
1140 """ |
|
1141 |
|
1142 # Now find all the things we'll need as arguments and return values because |
|
1143 # we need to wrap or unwrap them. |
|
1144 headers = set() |
|
1145 implheaders = set(["UnionTypes.h"]) |
|
1146 declarations = set() |
|
1147 unionStructs = dict() |
|
1148 owningUnionStructs = dict() |
|
1149 |
|
1150 for t, descriptor, dictionary in getAllTypes(descriptors, dictionaries, callbacks): |
|
1151 # Add info for the given type. descriptor and dictionary, if present, are |
|
1152 # used to figure out what to do with interface types. |
|
1153 assert not descriptor or not dictionary |
|
1154 |
|
1155 t = t.unroll() |
|
1156 while t.isMozMap(): |
|
1157 t = t.inner.unroll() |
|
1158 if not t.isUnion(): |
|
1159 continue |
|
1160 name = str(t) |
|
1161 if name not in unionStructs: |
|
1162 providers = getRelevantProviders(descriptor, config) |
|
1163 # FIXME: Unions are broken in workers. See bug 809899. |
|
1164 unionStructs[name] = CGUnionStruct(t, providers[0]) |
|
1165 owningUnionStructs[name] = CGUnionStruct(t, providers[0], |
|
1166 ownsMembers=True) |
|
1167 |
|
1168 def addHeadersForType(f): |
|
1169 if f.nullable(): |
|
1170 headers.add("mozilla/dom/Nullable.h") |
|
1171 f = f.unroll() |
|
1172 if f.isInterface(): |
|
1173 if f.isSpiderMonkeyInterface(): |
|
1174 headers.add("jsfriendapi.h") |
|
1175 headers.add("mozilla/dom/TypedArray.h") |
|
1176 else: |
|
1177 for p in providers: |
|
1178 try: |
|
1179 typeDesc = p.getDescriptor(f.inner.identifier.name) |
|
1180 except NoSuchDescriptorError: |
|
1181 continue |
|
1182 if typeDesc.interface.isCallback(): |
|
1183 # Callback interfaces always use strong refs, so |
|
1184 # we need to include the right header to be able |
|
1185 # to Release() in our inlined code. |
|
1186 headers.add(typeDesc.headerFile) |
|
1187 else: |
|
1188 declarations.add((typeDesc.nativeType, False)) |
|
1189 implheaders.add(typeDesc.headerFile) |
|
1190 elif f.isDictionary(): |
|
1191 # For a dictionary, we need to see its declaration in |
|
1192 # UnionTypes.h so we have its sizeof and know how big to |
|
1193 # make our union. |
|
1194 headers.add(CGHeaders.getDeclarationFilename(f.inner)) |
|
1195 # And if it needs rooting, we need RootedDictionary too |
|
1196 if typeNeedsRooting(f): |
|
1197 headers.add("mozilla/dom/RootedDictionary.h") |
|
1198 elif f.isEnum(): |
|
1199 # Need to see the actual definition of the enum, |
|
1200 # unfortunately. |
|
1201 headers.add(CGHeaders.getDeclarationFilename(f.inner)) |
|
1202 elif f.isCallback(): |
|
1203 # Callbacks always use strong refs, so we need to include |
|
1204 # the right header to be able to Release() in our inlined |
|
1205 # code. |
|
1206 headers.add(CGHeaders.getDeclarationFilename(f)) |
|
1207 elif f.isMozMap(): |
|
1208 headers.add("mozilla/dom/MozMap.h") |
|
1209 # And add headers for the type we're parametrized over |
|
1210 addHeadersForType(f.inner) |
|
1211 |
|
1212 for f in t.flatMemberTypes: |
|
1213 assert not f.nullable() |
|
1214 addHeadersForType(f) |
|
1215 |
|
1216 return (headers, implheaders, declarations, |
|
1217 CGList(SortedDictValues(unionStructs) + |
|
1218 SortedDictValues(owningUnionStructs), |
|
1219 "\n")) |
|
1220 |
|
1221 |
|
1222 def UnionConversions(descriptors, dictionaries, callbacks, config): |
|
1223 """ |
|
1224 Returns a CGThing to declare all union argument conversion helper structs. |
|
1225 """ |
|
1226 # Now find all the things we'll need as arguments because we |
|
1227 # need to unwrap them. |
|
1228 headers = set() |
|
1229 unionConversions = dict() |
|
1230 |
|
1231 for t, descriptor, dictionary in getAllTypes(descriptors, dictionaries, callbacks): |
|
1232 # Add info for the given type. descriptor and dictionary, if passed, are |
|
1233 # used to figure out what to do with interface types. |
|
1234 assert not descriptor or not dictionary |
|
1235 |
|
1236 t = t.unroll() |
|
1237 if not t.isUnion(): |
|
1238 continue |
|
1239 name = str(t) |
|
1240 if name not in unionConversions: |
|
1241 providers = getRelevantProviders(descriptor, config) |
|
1242 unionConversions[name] = CGUnionConversionStruct(t, providers[0]) |
|
1243 def addHeadersForType(f, providers): |
|
1244 f = f.unroll() |
|
1245 if f.isInterface(): |
|
1246 if f.isSpiderMonkeyInterface(): |
|
1247 headers.add("jsfriendapi.h") |
|
1248 headers.add("mozilla/dom/TypedArray.h") |
|
1249 elif f.inner.isExternal(): |
|
1250 providers = getRelevantProviders(descriptor, config) |
|
1251 for p in providers: |
|
1252 try: |
|
1253 typeDesc = p.getDescriptor(f.inner.identifier.name) |
|
1254 except NoSuchDescriptorError: |
|
1255 continue |
|
1256 headers.add(typeDesc.headerFile) |
|
1257 else: |
|
1258 headers.add(CGHeaders.getDeclarationFilename(f.inner)) |
|
1259 # Check for whether we have a possibly-XPConnect-implemented |
|
1260 # interface. If we do, the right descriptor will come from |
|
1261 # providers[0], because that would be the non-worker |
|
1262 # descriptor provider, if we have one at all. |
|
1263 if (f.isGeckoInterface() and |
|
1264 providers[0].getDescriptor(f.inner.identifier.name).hasXPConnectImpls): |
|
1265 headers.add("nsDOMQS.h") |
|
1266 elif f.isDictionary(): |
|
1267 headers.add(CGHeaders.getDeclarationFilename(f.inner)) |
|
1268 elif f.isPrimitive(): |
|
1269 headers.add("mozilla/dom/PrimitiveConversions.h") |
|
1270 elif f.isMozMap(): |
|
1271 headers.add("mozilla/dom/MozMap.h") |
|
1272 # And the internal type of the MozMap |
|
1273 addHeadersForType(f.inner, providers) |
|
1274 |
|
1275 for f in t.flatMemberTypes: |
|
1276 addHeadersForType(f, providers) |
|
1277 |
|
1278 return (headers, |
|
1279 CGWrapper(CGList(SortedDictValues(unionConversions), "\n"), |
|
1280 post="\n\n")) |
|
1281 |
|
1282 |
|
1283 class Argument(): |
|
1284 """ |
|
1285 A class for outputting the type and name of an argument |
|
1286 """ |
|
1287 def __init__(self, argType, name, default=None): |
|
1288 self.argType = argType |
|
1289 self.name = name |
|
1290 self.default = default |
|
1291 |
|
1292 def declare(self): |
|
1293 string = self.argType + ' ' + self.name |
|
1294 if self.default is not None: |
|
1295 string += " = " + self.default |
|
1296 return string |
|
1297 |
|
1298 def define(self): |
|
1299 return self.argType + ' ' + self.name |
|
1300 |
|
1301 |
|
1302 class CGAbstractMethod(CGThing): |
|
1303 """ |
|
1304 An abstract class for generating code for a method. Subclasses |
|
1305 should override definition_body to create the actual code. |
|
1306 |
|
1307 descriptor is the descriptor for the interface the method is associated with |
|
1308 |
|
1309 name is the name of the method as a string |
|
1310 |
|
1311 returnType is the IDLType of the return value |
|
1312 |
|
1313 args is a list of Argument objects |
|
1314 |
|
1315 inline should be True to generate an inline method, whose body is |
|
1316 part of the declaration. |
|
1317 |
|
1318 alwaysInline should be True to generate an inline method annotated with |
|
1319 MOZ_ALWAYS_INLINE. |
|
1320 |
|
1321 static should be True to generate a static method, which only has |
|
1322 a definition. |
|
1323 |
|
1324 If templateArgs is not None it should be a list of strings containing |
|
1325 template arguments, and the function will be templatized using those |
|
1326 arguments. |
|
1327 """ |
|
1328 def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None): |
|
1329 CGThing.__init__(self) |
|
1330 self.descriptor = descriptor |
|
1331 self.name = name |
|
1332 self.returnType = returnType |
|
1333 self.args = args |
|
1334 self.inline = inline |
|
1335 self.alwaysInline = alwaysInline |
|
1336 self.static = static |
|
1337 self.templateArgs = templateArgs |
|
1338 |
|
1339 def _argstring(self, declare): |
|
1340 return ', '.join([a.declare() if declare else a.define() for a in self.args]) |
|
1341 |
|
1342 def _template(self): |
|
1343 if self.templateArgs is None: |
|
1344 return '' |
|
1345 return 'template <%s>\n' % ', '.join(self.templateArgs) |
|
1346 |
|
1347 def _decorators(self): |
|
1348 decorators = [] |
|
1349 if self.alwaysInline: |
|
1350 decorators.append('MOZ_ALWAYS_INLINE') |
|
1351 elif self.inline: |
|
1352 decorators.append('inline') |
|
1353 if self.static: |
|
1354 decorators.append('static') |
|
1355 decorators.append(self.returnType) |
|
1356 maybeNewline = " " if self.inline else "\n" |
|
1357 return ' '.join(decorators) + maybeNewline |
|
1358 |
|
1359 def declare(self): |
|
1360 if self.inline: |
|
1361 return self._define(True) |
|
1362 return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True)) |
|
1363 |
|
1364 def _define(self, fromDeclare=False): |
|
1365 return self.definition_prologue(fromDeclare) + self.definition_body() + self.definition_epilogue() |
|
1366 |
|
1367 def define(self): |
|
1368 return "" if self.inline else self._define() |
|
1369 |
|
1370 def definition_prologue(self, fromDeclare): |
|
1371 return "%s%s%s(%s)\n{\n" % (self._template(), self._decorators(), |
|
1372 self.name, self._argstring(fromDeclare)) |
|
1373 |
|
1374 def definition_epilogue(self): |
|
1375 return "}\n" |
|
1376 |
|
1377 def definition_body(self): |
|
1378 assert False # Override me! |
|
1379 |
|
1380 |
|
1381 class CGAbstractStaticMethod(CGAbstractMethod): |
|
1382 """ |
|
1383 Abstract base class for codegen of implementation-only (no |
|
1384 declaration) static methods. |
|
1385 """ |
|
1386 def __init__(self, descriptor, name, returnType, args): |
|
1387 CGAbstractMethod.__init__(self, descriptor, name, returnType, args, |
|
1388 inline=False, static=True) |
|
1389 |
|
1390 def declare(self): |
|
1391 # We only have implementation |
|
1392 return "" |
|
1393 |
|
1394 |
|
1395 class CGAbstractClassHook(CGAbstractStaticMethod): |
|
1396 """ |
|
1397 Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw |
|
1398 'this' unwrapping as it assumes that the unwrapped type is always known. |
|
1399 """ |
|
1400 def __init__(self, descriptor, name, returnType, args): |
|
1401 CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, |
|
1402 args) |
|
1403 |
|
1404 def definition_body_prologue(self): |
|
1405 return ("\n" # BOGUS extra blank line at start of function |
|
1406 " %s* self = UnwrapDOMObject<%s>(obj);\n" % |
|
1407 (self.descriptor.nativeType, self.descriptor.nativeType)) |
|
1408 |
|
1409 def definition_body(self): |
|
1410 return self.definition_body_prologue() + self.generate_code() |
|
1411 |
|
1412 def generate_code(self): |
|
1413 assert False # Override me! |
|
1414 |
|
1415 |
|
1416 class CGGetJSClassMethod(CGAbstractMethod): |
|
1417 def __init__(self, descriptor): |
|
1418 CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'const JSClass*', |
|
1419 []) |
|
1420 |
|
1421 def definition_body(self): |
|
1422 return " return Class.ToJSClass();\n" |
|
1423 |
|
1424 |
|
1425 class CGAddPropertyHook(CGAbstractClassHook): |
|
1426 """ |
|
1427 A hook for addProperty, used to preserve our wrapper from GC. |
|
1428 """ |
|
1429 def __init__(self, descriptor): |
|
1430 args = [Argument('JSContext*', 'cx'), |
|
1431 Argument('JS::Handle<JSObject*>', 'obj'), |
|
1432 Argument('JS::Handle<jsid>', 'id'), |
|
1433 Argument('JS::MutableHandle<JS::Value>', 'vp')] |
|
1434 CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME, |
|
1435 'bool', args) |
|
1436 |
|
1437 def generate_code(self): |
|
1438 assert self.descriptor.wrapperCache |
|
1439 return indent(dedent(""" |
|
1440 // We don't want to preserve if we don't have a wrapper. |
|
1441 if (self->GetWrapperPreserveColor()) { |
|
1442 PreserveWrapper(self); |
|
1443 } |
|
1444 return true; |
|
1445 """)) |
|
1446 |
|
1447 |
|
1448 def DeferredFinalizeSmartPtr(descriptor): |
|
1449 if descriptor.nativeOwnership == 'owned': |
|
1450 smartPtr = 'nsAutoPtr' |
|
1451 else: |
|
1452 smartPtr = 'nsRefPtr' |
|
1453 return smartPtr |
|
1454 |
|
1455 |
|
1456 def finalizeHook(descriptor, hookName, freeOp): |
|
1457 finalize = "JSBindingFinalized<%s>::Finalized(self);\n" % descriptor.nativeType |
|
1458 if descriptor.wrapperCache: |
|
1459 finalize += "ClearWrapper(self, self);\n" |
|
1460 if descriptor.interface.getExtendedAttribute('OverrideBuiltins'): |
|
1461 finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n" |
|
1462 if descriptor.interface.getExtendedAttribute("Global"): |
|
1463 finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp |
|
1464 finalize += ("AddForDeferredFinalization<%s, %s >(self);\n" % |
|
1465 (descriptor.nativeType, DeferredFinalizeSmartPtr(descriptor))) |
|
1466 return CGIfWrapper(CGGeneric(finalize), "self") |
|
1467 |
|
1468 |
|
1469 class CGClassFinalizeHook(CGAbstractClassHook): |
|
1470 """ |
|
1471 A hook for finalize, used to release our native object. |
|
1472 """ |
|
1473 def __init__(self, descriptor): |
|
1474 args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')] |
|
1475 CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, |
|
1476 'void', args) |
|
1477 |
|
1478 def generate_code(self): |
|
1479 return indent(finalizeHook(self.descriptor, self.name, self.args[0].name).define()) |
|
1480 |
|
1481 |
|
1482 class CGClassConstructor(CGAbstractStaticMethod): |
|
1483 """ |
|
1484 JS-visible constructor for our objects |
|
1485 """ |
|
1486 def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME): |
|
1487 args = [Argument('JSContext*', 'cx'), |
|
1488 Argument('unsigned', 'argc'), |
|
1489 Argument('JS::Value*', 'vp')] |
|
1490 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args) |
|
1491 self._ctor = ctor |
|
1492 |
|
1493 def define(self): |
|
1494 if not self._ctor: |
|
1495 return "" |
|
1496 return CGAbstractStaticMethod.define(self) |
|
1497 |
|
1498 def definition_body(self): |
|
1499 return self.generate_code() |
|
1500 |
|
1501 def generate_code(self): |
|
1502 # [ChromeOnly] interfaces may only be constructed by chrome. |
|
1503 chromeOnlyCheck = "" |
|
1504 if isChromeOnly(self._ctor): |
|
1505 chromeOnlyCheck = dedent(""" |
|
1506 if (!nsContentUtils::ThreadsafeIsCallerChrome()) { |
|
1507 return ThrowingConstructor(cx, argc, vp); |
|
1508 } |
|
1509 |
|
1510 """) |
|
1511 |
|
1512 # Additionally, we want to throw if a caller does a bareword invocation |
|
1513 # of a constructor without |new|. We don't enforce this for chrome in |
|
1514 # realease builds to avoid the addon compat fallout of making that |
|
1515 # change. See bug 916644. |
|
1516 # |
|
1517 # Figure out the name of our constructor for error reporting purposes. |
|
1518 # For unnamed webidl constructors, identifier.name is "constructor" but |
|
1519 # the name JS sees is the interface name; for named constructors |
|
1520 # identifier.name is the actual name. |
|
1521 name = self._ctor.identifier.name |
|
1522 if name != "constructor": |
|
1523 ctorName = name |
|
1524 else: |
|
1525 ctorName = self.descriptor.interface.identifier.name |
|
1526 |
|
1527 preamble = fill( # BOGUS extra blank line at beginning of function body |
|
1528 """ |
|
1529 |
|
1530 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
|
1531 JS::Rooted<JSObject*> obj(cx, &args.callee()); |
|
1532 $*{chromeOnlyCheck} |
|
1533 bool mayInvoke = args.isConstructing(); |
|
1534 #ifdef RELEASE_BUILD |
|
1535 mayInvoke = mayInvoke || nsContentUtils::ThreadsafeIsCallerChrome(); |
|
1536 #endif // RELEASE_BUILD |
|
1537 if (!mayInvoke) { |
|
1538 // XXXbz wish I could get the name from the callee instead of |
|
1539 // Adding more relocations |
|
1540 return ThrowConstructorWithoutNew(cx, "${ctorName}"); |
|
1541 } |
|
1542 """, |
|
1543 chromeOnlyCheck=chromeOnlyCheck, |
|
1544 ctorName=ctorName) |
|
1545 |
|
1546 name = self._ctor.identifier.name |
|
1547 nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name)) |
|
1548 callGenerator = CGMethodCall(nativeName, True, self.descriptor, |
|
1549 self._ctor, isConstructor=True, |
|
1550 constructorName=ctorName) |
|
1551 return indent(preamble) + callGenerator.define() |
|
1552 |
|
1553 |
|
1554 # Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod. |
|
1555 class CGConstructNavigatorObjectHelper(CGAbstractStaticMethod): |
|
1556 """ |
|
1557 Construct a new JS-implemented WebIDL DOM object, for use on navigator. |
|
1558 """ |
|
1559 def __init__(self, descriptor): |
|
1560 name = "ConstructNavigatorObjectHelper" |
|
1561 args = [Argument('JSContext*', 'cx'), |
|
1562 Argument('GlobalObject&', 'global'), |
|
1563 Argument('ErrorResult&', 'aRv')] |
|
1564 rtype = 'already_AddRefed<%s>' % descriptor.name |
|
1565 CGAbstractStaticMethod.__init__(self, descriptor, name, rtype, args) |
|
1566 |
|
1567 def definition_body(self): |
|
1568 return indent(genConstructorBody(self.descriptor)) |
|
1569 |
|
1570 |
|
1571 class CGConstructNavigatorObject(CGAbstractMethod): |
|
1572 """ |
|
1573 Wrap a JS-implemented WebIDL object into a JS value, for use on navigator. |
|
1574 """ |
|
1575 def __init__(self, descriptor): |
|
1576 name = 'ConstructNavigatorObject' |
|
1577 args = [Argument('JSContext*', 'aCx'), Argument('JS::Handle<JSObject*>', 'aObj')] |
|
1578 CGAbstractMethod.__init__(self, descriptor, name, 'JSObject*', args) |
|
1579 |
|
1580 def definition_body(self): |
|
1581 if not self.descriptor.interface.isJSImplemented(): |
|
1582 raise TypeError("Only JS-implemented classes are currently supported " |
|
1583 "on navigator. See bug 856820.") |
|
1584 return indent(fill( |
|
1585 """ |
|
1586 GlobalObject global(aCx, aObj); |
|
1587 if (global.Failed()) { |
|
1588 return nullptr; |
|
1589 } |
|
1590 ErrorResult rv; |
|
1591 JS::Rooted<JS::Value> v(aCx); |
|
1592 { // Scope to make sure |result| goes out of scope while |v| is rooted |
|
1593 nsRefPtr<mozilla::dom::${descriptorName}> result = ConstructNavigatorObjectHelper(aCx, global, rv); |
|
1594 rv.WouldReportJSException(); |
|
1595 if (rv.Failed()) { |
|
1596 ThrowMethodFailedWithDetails(aCx, rv, "${descriptorName}", "navigatorConstructor"); |
|
1597 return nullptr; |
|
1598 } |
|
1599 if (!WrapNewBindingObject(aCx, result, &v)) { |
|
1600 //XXX Assertion disabled for now, see bug 991271. |
|
1601 MOZ_ASSERT(true || JS_IsExceptionPending(aCx)); |
|
1602 return nullptr; |
|
1603 } |
|
1604 } |
|
1605 return &v.toObject(); |
|
1606 """, |
|
1607 descriptorName=self.descriptor.name)) |
|
1608 |
|
1609 |
|
1610 class CGClassConstructHookHolder(CGGeneric): |
|
1611 def __init__(self, descriptor): |
|
1612 if descriptor.interface.ctor(): |
|
1613 constructHook = CONSTRUCT_HOOK_NAME |
|
1614 else: |
|
1615 constructHook = "ThrowingConstructor" |
|
1616 CGGeneric.__init__(self, fill( |
|
1617 """ |
|
1618 static const JSNativeHolder ${CONSTRUCT_HOOK_NAME}_holder = { |
|
1619 ${constructHook}, |
|
1620 ${hooks} |
|
1621 }; |
|
1622 """, |
|
1623 CONSTRUCT_HOOK_NAME=CONSTRUCT_HOOK_NAME, |
|
1624 constructHook=constructHook, |
|
1625 hooks=NativePropertyHooks(descriptor))) |
|
1626 |
|
1627 |
|
1628 def NamedConstructorName(m): |
|
1629 return '_' + m.identifier.name |
|
1630 |
|
1631 |
|
1632 class CGNamedConstructors(CGThing): |
|
1633 def __init__(self, descriptor): |
|
1634 self.descriptor = descriptor |
|
1635 CGThing.__init__(self) |
|
1636 |
|
1637 def declare(self): |
|
1638 return "" |
|
1639 |
|
1640 def define(self): |
|
1641 if len(self.descriptor.interface.namedConstructors) == 0: |
|
1642 return "" |
|
1643 |
|
1644 constructorID = "constructors::id::" |
|
1645 if self.descriptor.interface.hasInterfaceObject(): |
|
1646 constructorID += self.descriptor.name |
|
1647 else: |
|
1648 constructorID += "_ID_Count" |
|
1649 |
|
1650 namedConstructors = "" |
|
1651 for n in self.descriptor.interface.namedConstructors: |
|
1652 namedConstructors += ( |
|
1653 "{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i },\n" % |
|
1654 (n.identifier.name, NamedConstructorName(n), methodLength(n))) |
|
1655 |
|
1656 return fill( |
|
1657 """ |
|
1658 const NativePropertyHooks sNamedConstructorNativePropertyHooks = { |
|
1659 nullptr, |
|
1660 nullptr, |
|
1661 { nullptr, nullptr }, |
|
1662 prototypes::id::${name}, |
|
1663 ${constructorID}, |
|
1664 nullptr |
|
1665 }; |
|
1666 |
|
1667 static const NamedConstructor namedConstructors[] = { |
|
1668 $*{namedConstructors} |
|
1669 { nullptr, { nullptr, nullptr }, 0 } |
|
1670 }; |
|
1671 """, |
|
1672 name=self.descriptor.name, |
|
1673 constructorID=constructorID, |
|
1674 namedConstructors=namedConstructors) |
|
1675 |
|
1676 |
|
1677 class CGClassHasInstanceHook(CGAbstractStaticMethod): |
|
1678 def __init__(self, descriptor): |
|
1679 args = [Argument('JSContext*', 'cx'), |
|
1680 Argument('JS::Handle<JSObject*>', 'obj'), |
|
1681 Argument('JS::MutableHandle<JS::Value>', 'vp'), |
|
1682 Argument('bool*', 'bp')] |
|
1683 CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME, |
|
1684 'bool', args) |
|
1685 |
|
1686 def define(self): |
|
1687 if not NeedsGeneratedHasInstance(self.descriptor): |
|
1688 return "" |
|
1689 return CGAbstractStaticMethod.define(self) |
|
1690 |
|
1691 def definition_body(self): |
|
1692 return self.generate_code() |
|
1693 |
|
1694 def generate_code(self): |
|
1695 # BOGUS extra blank line at start of function |
|
1696 header = dedent(""" |
|
1697 |
|
1698 if (!vp.isObject()) { |
|
1699 *bp = false; |
|
1700 return true; |
|
1701 } |
|
1702 |
|
1703 JS::Rooted<JSObject*> instance(cx, &vp.toObject()); |
|
1704 """) |
|
1705 if self.descriptor.interface.hasInterfacePrototypeObject(): |
|
1706 return indent( |
|
1707 header + |
|
1708 fill( |
|
1709 """ |
|
1710 |
|
1711 static_assert(IsBaseOf<nsISupports, ${nativeType}>::value, |
|
1712 "HasInstance only works for nsISupports-based classes."); |
|
1713 |
|
1714 bool ok = InterfaceHasInstance(cx, obj, instance, bp); |
|
1715 if (!ok || *bp) { |
|
1716 return ok; |
|
1717 } |
|
1718 |
|
1719 // FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj). |
|
1720 nsISupports* native = |
|
1721 nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, |
|
1722 js::UncheckedUnwrap(instance)); |
|
1723 nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native); |
|
1724 *bp = !!qiResult; |
|
1725 return true; |
|
1726 |
|
1727 """, # BOGUS extra blank line at end of function |
|
1728 nativeType=self.descriptor.nativeType, |
|
1729 name=self.descriptor.interface.identifier.name)) |
|
1730 |
|
1731 hasInstanceCode = dedent(""" |
|
1732 |
|
1733 const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance)); |
|
1734 *bp = false; |
|
1735 if (!domClass) { |
|
1736 // Not a DOM object, so certainly not an instance of this interface |
|
1737 return true; |
|
1738 } |
|
1739 """) |
|
1740 if self.descriptor.interface.identifier.name == "ChromeWindow": |
|
1741 setBp = "*bp = UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance))->IsChromeWindow()" |
|
1742 else: |
|
1743 setBp = "*bp = true" |
|
1744 # Sort interaces implementing self by name so we get stable output. |
|
1745 for iface in sorted(self.descriptor.interface.interfacesImplementingSelf, |
|
1746 key=lambda iface: iface.identifier.name): |
|
1747 hasInstanceCode += fill( |
|
1748 """ |
|
1749 |
|
1750 if (domClass->mInterfaceChain[PrototypeTraits<prototypes::id::${name}>::Depth] == prototypes::id::${name}) { |
|
1751 ${setBp}; |
|
1752 return true; |
|
1753 } |
|
1754 """, |
|
1755 name=iface.identifier.name, |
|
1756 setBp=setBp) |
|
1757 hasInstanceCode += "return true;\n" |
|
1758 return indent(header + hasInstanceCode) |
|
1759 |
|
1760 |
|
1761 def isChromeOnly(m): |
|
1762 return m.getExtendedAttribute("ChromeOnly") |
|
1763 |
|
1764 |
|
1765 def getAvailableInTestFunc(obj): |
|
1766 availableIn = obj.getExtendedAttribute("AvailableIn") |
|
1767 if availableIn is None: |
|
1768 return None |
|
1769 assert isinstance(availableIn, list) and len(availableIn) == 1 |
|
1770 if availableIn[0] == "PrivilegedApps": |
|
1771 return "IsInPrivilegedApp" |
|
1772 if availableIn[0] == "CertifiedApps": |
|
1773 return "IsInCertifiedApp" |
|
1774 raise TypeError("Unknown AvailableIn value '%s'" % availableIn[0]) |
|
1775 |
|
1776 |
|
1777 class MemberCondition: |
|
1778 """ |
|
1779 An object representing the condition for a member to actually be |
|
1780 exposed. Any of pref, func, and available can be None. If not |
|
1781 None, they should be strings that have the pref name (for "pref") |
|
1782 or function name (for "func" and "available"). |
|
1783 """ |
|
1784 def __init__(self, pref, func, available=None): |
|
1785 assert pref is None or isinstance(pref, str) |
|
1786 assert func is None or isinstance(func, str) |
|
1787 assert available is None or isinstance(available, str) |
|
1788 self.pref = pref |
|
1789 |
|
1790 def toFuncPtr(val): |
|
1791 if val is None: |
|
1792 return "nullptr" |
|
1793 return "&" + val |
|
1794 self.func = toFuncPtr(func) |
|
1795 self.available = toFuncPtr(available) |
|
1796 |
|
1797 def __eq__(self, other): |
|
1798 return (self.pref == other.pref and self.func == other.func and |
|
1799 self.available == other.available) |
|
1800 |
|
1801 def __ne__(self, other): |
|
1802 return not self.__eq__(other) |
|
1803 |
|
1804 |
|
1805 class PropertyDefiner: |
|
1806 """ |
|
1807 A common superclass for defining things on prototype objects. |
|
1808 |
|
1809 Subclasses should implement generateArray to generate the actual arrays of |
|
1810 things we're defining. They should also set self.chrome to the list of |
|
1811 things only exposed to chrome and self.regular to the list of things exposed |
|
1812 to both chrome and web pages. |
|
1813 """ |
|
1814 def __init__(self, descriptor, name): |
|
1815 self.descriptor = descriptor |
|
1816 self.name = name |
|
1817 # self.prefCacheData will store an array of (prefname, bool*) |
|
1818 # pairs for our bool var caches. generateArray will fill it |
|
1819 # in as needed. |
|
1820 self.prefCacheData = [] |
|
1821 |
|
1822 def hasChromeOnly(self): |
|
1823 return len(self.chrome) > 0 |
|
1824 |
|
1825 def hasNonChromeOnly(self): |
|
1826 return len(self.regular) > 0 |
|
1827 |
|
1828 def variableName(self, chrome): |
|
1829 if chrome: |
|
1830 if self.hasChromeOnly(): |
|
1831 return "sChrome" + self.name |
|
1832 else: |
|
1833 if self.hasNonChromeOnly(): |
|
1834 return "s" + self.name |
|
1835 return "nullptr" |
|
1836 |
|
1837 def usedForXrays(self): |
|
1838 # No Xrays in workers. |
|
1839 return not self.descriptor.workers |
|
1840 |
|
1841 def __str__(self): |
|
1842 # We only need to generate id arrays for things that will end |
|
1843 # up used via ResolveProperty or EnumerateProperties. |
|
1844 str = self.generateArray(self.regular, self.variableName(False), |
|
1845 self.usedForXrays()) |
|
1846 if self.hasChromeOnly(): |
|
1847 str += self.generateArray(self.chrome, self.variableName(True), |
|
1848 self.usedForXrays()) |
|
1849 return str |
|
1850 |
|
1851 @staticmethod |
|
1852 def getStringAttr(member, name): |
|
1853 attr = member.getExtendedAttribute(name) |
|
1854 if attr is None: |
|
1855 return None |
|
1856 # It's a list of strings |
|
1857 assert len(attr) == 1 |
|
1858 assert attr[0] is not None |
|
1859 return attr[0] |
|
1860 |
|
1861 @staticmethod |
|
1862 def getControllingCondition(interfaceMember): |
|
1863 return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember, |
|
1864 "Pref"), |
|
1865 PropertyDefiner.getStringAttr(interfaceMember, |
|
1866 "Func"), |
|
1867 getAvailableInTestFunc(interfaceMember)) |
|
1868 |
|
1869 def generatePrefableArray(self, array, name, specTemplate, specTerminator, |
|
1870 specType, getCondition, getDataTuple, doIdArrays): |
|
1871 """ |
|
1872 This method generates our various arrays. |
|
1873 |
|
1874 array is an array of interface members as passed to generateArray |
|
1875 |
|
1876 name is the name as passed to generateArray |
|
1877 |
|
1878 specTemplate is a template for each entry of the spec array |
|
1879 |
|
1880 specTerminator is a terminator for the spec array (inserted every time |
|
1881 our controlling pref changes and at the end of the array) |
|
1882 |
|
1883 specType is the actual typename of our spec |
|
1884 |
|
1885 getCondition is a callback function that takes an array entry and |
|
1886 returns the corresponding MemberCondition. |
|
1887 |
|
1888 getDataTuple is a callback function that takes an array entry and |
|
1889 returns a tuple suitable for substitution into specTemplate. |
|
1890 """ |
|
1891 |
|
1892 # We want to generate a single list of specs, but with specTerminator |
|
1893 # inserted at every point where the pref name controlling the member |
|
1894 # changes. That will make sure the order of the properties as exposed |
|
1895 # on the interface and interface prototype objects does not change when |
|
1896 # pref control is added to members while still allowing us to define all |
|
1897 # the members in the smallest number of JSAPI calls. |
|
1898 assert len(array) != 0 |
|
1899 lastCondition = getCondition(array[0]) # So we won't put a specTerminator |
|
1900 # at the very front of the list. |
|
1901 specs = [] |
|
1902 prefableSpecs = [] |
|
1903 |
|
1904 prefableTemplate = ' { true, %s, %s, &%s[%d] }' |
|
1905 prefCacheTemplate = '&%s[%d].enabled' |
|
1906 |
|
1907 def switchToCondition(props, condition): |
|
1908 # Remember the info about where our pref-controlled |
|
1909 # booleans live. |
|
1910 if condition.pref is not None: |
|
1911 props.prefCacheData.append( |
|
1912 (condition.pref, |
|
1913 prefCacheTemplate % (name, len(prefableSpecs)))) |
|
1914 # Set up pointers to the new sets of specs inside prefableSpecs |
|
1915 prefableSpecs.append(prefableTemplate % |
|
1916 (condition.func, |
|
1917 condition.available, |
|
1918 name + "_specs", len(specs))) |
|
1919 |
|
1920 switchToCondition(self, lastCondition) |
|
1921 |
|
1922 for member in array: |
|
1923 curCondition = getCondition(member) |
|
1924 if lastCondition != curCondition: |
|
1925 # Terminate previous list |
|
1926 specs.append(specTerminator) |
|
1927 # And switch to our new pref |
|
1928 switchToCondition(self, curCondition) |
|
1929 lastCondition = curCondition |
|
1930 # And the actual spec |
|
1931 specs.append(specTemplate % getDataTuple(member)) |
|
1932 specs.append(specTerminator) |
|
1933 prefableSpecs.append(" { false, nullptr }") |
|
1934 |
|
1935 specType = "const " + specType |
|
1936 arrays = fill( |
|
1937 """ |
|
1938 static ${specType} ${name}_specs[] = { |
|
1939 ${specs} |
|
1940 }; |
|
1941 |
|
1942 // Can't be const because the pref-enabled boolean needs to be writable |
|
1943 static Prefable<${specType}> ${name}[] = { |
|
1944 ${prefableSpecs} |
|
1945 }; |
|
1946 |
|
1947 """, |
|
1948 specType=specType, |
|
1949 name=name, |
|
1950 specs=',\n'.join(specs), |
|
1951 prefableSpecs=',\n'.join(prefableSpecs)) |
|
1952 if doIdArrays: |
|
1953 arrays += "static jsid %s_ids[%i];\n\n" % (name, len(specs)) |
|
1954 return arrays |
|
1955 |
|
1956 |
|
1957 # The length of a method is the minimum of the lengths of the |
|
1958 # argument lists of all its overloads. |
|
1959 def overloadLength(arguments): |
|
1960 i = len(arguments) |
|
1961 while i > 0 and arguments[i - 1].optional: |
|
1962 i -= 1 |
|
1963 return i |
|
1964 |
|
1965 |
|
1966 def methodLength(method): |
|
1967 signatures = method.signatures() |
|
1968 return min(overloadLength(arguments) for retType, arguments in signatures) |
|
1969 |
|
1970 |
|
1971 class MethodDefiner(PropertyDefiner): |
|
1972 """ |
|
1973 A class for defining methods on a prototype object. |
|
1974 """ |
|
1975 def __init__(self, descriptor, name, static): |
|
1976 PropertyDefiner.__init__(self, descriptor, name) |
|
1977 |
|
1978 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822 |
|
1979 # We should be able to check for special operations without an |
|
1980 # identifier. For now we check if the name starts with __ |
|
1981 |
|
1982 # Ignore non-static methods for interfaces without a proto object |
|
1983 if descriptor.interface.hasInterfacePrototypeObject() or static: |
|
1984 methods = [m for m in descriptor.interface.members if |
|
1985 m.isMethod() and m.isStatic() == static and |
|
1986 not m.isIdentifierLess()] |
|
1987 else: |
|
1988 methods = [] |
|
1989 self.chrome = [] |
|
1990 self.regular = [] |
|
1991 for m in methods: |
|
1992 if m.identifier.name == 'queryInterface': |
|
1993 if self.descriptor.workers: |
|
1994 continue |
|
1995 if m.isStatic(): |
|
1996 raise TypeError("Legacy queryInterface member shouldn't be static") |
|
1997 signatures = m.signatures() |
|
1998 |
|
1999 def argTypeIsIID(arg): |
|
2000 return arg.type.inner.isExternal() and arg.type.inner.identifier.name == 'IID' |
|
2001 if len(signatures) > 1 or len(signatures[0][1]) > 1 or not argTypeIsIID(signatures[0][1][0]): |
|
2002 raise TypeError("There should be only one queryInterface method with 1 argument of type IID") |
|
2003 |
|
2004 # Make sure to not stick QueryInterface on abstract interfaces that |
|
2005 # have hasXPConnectImpls (like EventTarget). So only put it on |
|
2006 # interfaces that are concrete and all of whose ancestors are abstract. |
|
2007 def allAncestorsAbstract(iface): |
|
2008 if not iface.parent: |
|
2009 return True |
|
2010 desc = self.descriptor.getDescriptor(iface.parent.identifier.name) |
|
2011 if desc.concrete: |
|
2012 return False |
|
2013 return allAncestorsAbstract(iface.parent) |
|
2014 if (not self.descriptor.interface.hasInterfacePrototypeObject() or |
|
2015 not self.descriptor.concrete or |
|
2016 not allAncestorsAbstract(self.descriptor.interface)): |
|
2017 raise TypeError("QueryInterface is only supported on " |
|
2018 "interfaces that are concrete and all " |
|
2019 "of whose ancestors are abstract: " + |
|
2020 self.descriptor.name) |
|
2021 condition = "WantsQueryInterface<%s>::Enabled" % descriptor.nativeType |
|
2022 self.regular.append({ |
|
2023 "name": 'QueryInterface', |
|
2024 "methodInfo": False, |
|
2025 "length": 1, |
|
2026 "flags": "0", |
|
2027 "condition": MemberCondition(None, condition) |
|
2028 }) |
|
2029 continue |
|
2030 |
|
2031 method = { |
|
2032 "name": m.identifier.name, |
|
2033 "methodInfo": not m.isStatic(), |
|
2034 "length": methodLength(m), |
|
2035 "flags": "JSPROP_ENUMERATE", |
|
2036 "condition": PropertyDefiner.getControllingCondition(m), |
|
2037 "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"), |
|
2038 "returnsPromise": m.returnsPromise() |
|
2039 } |
|
2040 if isChromeOnly(m): |
|
2041 self.chrome.append(method) |
|
2042 else: |
|
2043 self.regular.append(method) |
|
2044 |
|
2045 # FIXME Check for an existing iterator on the interface first. |
|
2046 if any(m.isGetter() and m.isIndexed() for m in methods): |
|
2047 self.regular.append({ |
|
2048 "name": "@@iterator", |
|
2049 "methodInfo": False, |
|
2050 "selfHostedName": "ArrayValues", |
|
2051 "length": 0, |
|
2052 "flags": "JSPROP_ENUMERATE", |
|
2053 "condition": MemberCondition(None, None) |
|
2054 }) |
|
2055 |
|
2056 if not static: |
|
2057 stringifier = descriptor.operations['Stringifier'] |
|
2058 if stringifier: |
|
2059 toStringDesc = { |
|
2060 "name": "toString", |
|
2061 "nativeName": stringifier.identifier.name, |
|
2062 "length": 0, |
|
2063 "flags": "JSPROP_ENUMERATE", |
|
2064 "condition": PropertyDefiner.getControllingCondition(stringifier) |
|
2065 } |
|
2066 if isChromeOnly(stringifier): |
|
2067 self.chrome.append(toStringDesc) |
|
2068 else: |
|
2069 self.regular.append(toStringDesc) |
|
2070 jsonifier = descriptor.operations['Jsonifier'] |
|
2071 if jsonifier: |
|
2072 toJSONDesc = { |
|
2073 "name": "toJSON", |
|
2074 "nativeName": jsonifier.identifier.name, |
|
2075 "length": 0, |
|
2076 "flags": "JSPROP_ENUMERATE", |
|
2077 "condition": PropertyDefiner.getControllingCondition(jsonifier) |
|
2078 } |
|
2079 if isChromeOnly(jsonifier): |
|
2080 self.chrome.append(toJSONDesc) |
|
2081 else: |
|
2082 self.regular.append(toJSONDesc) |
|
2083 elif (descriptor.interface.isJSImplemented() and |
|
2084 descriptor.interface.hasInterfaceObject()): |
|
2085 self.chrome.append({ |
|
2086 "name": '_create', |
|
2087 "nativeName": ("%s::_Create" % descriptor.name), |
|
2088 "methodInfo": False, |
|
2089 "length": 2, |
|
2090 "flags": "0", |
|
2091 "condition": MemberCondition(None, None) |
|
2092 }) |
|
2093 |
|
2094 if static: |
|
2095 if not descriptor.interface.hasInterfaceObject(): |
|
2096 # static methods go on the interface object |
|
2097 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() |
|
2098 else: |
|
2099 if not descriptor.interface.hasInterfacePrototypeObject(): |
|
2100 # non-static methods go on the interface prototype object |
|
2101 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() |
|
2102 |
|
2103 def generateArray(self, array, name, doIdArrays): |
|
2104 if len(array) == 0: |
|
2105 return "" |
|
2106 |
|
2107 def condition(m): |
|
2108 return m["condition"] |
|
2109 |
|
2110 def specData(m): |
|
2111 if "selfHostedName" in m: |
|
2112 selfHostedName = '"%s"' % m["selfHostedName"] |
|
2113 assert not m.get("methodInfo", True) |
|
2114 accessor = "nullptr" |
|
2115 jitinfo = "nullptr" |
|
2116 else: |
|
2117 selfHostedName = "nullptr" |
|
2118 accessor = m.get("nativeName", m["name"]) |
|
2119 if m.get("methodInfo", True): |
|
2120 # Cast this in case the methodInfo is a |
|
2121 # JSTypedMethodJitInfo. |
|
2122 jitinfo = ("reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor) |
|
2123 if m.get("allowCrossOriginThis", False): |
|
2124 if m.get("returnsPromise", False): |
|
2125 raise TypeError("%s returns a Promise but should " |
|
2126 "be allowed cross-origin?" % |
|
2127 accessor) |
|
2128 accessor = "genericCrossOriginMethod" |
|
2129 elif self.descriptor.needsSpecialGenericOps(): |
|
2130 if m.get("returnsPromise", False): |
|
2131 raise TypeError("%s returns a Promise but needs " |
|
2132 "special generic ops?" % |
|
2133 accessor) |
|
2134 accessor = "genericMethod" |
|
2135 elif m.get("returnsPromise", False): |
|
2136 accessor = "GenericPromiseReturningBindingMethod" |
|
2137 else: |
|
2138 accessor = "GenericBindingMethod" |
|
2139 else: |
|
2140 if m.get("returnsPromise", False): |
|
2141 jitinfo = "&%s_methodinfo" % accessor |
|
2142 accessor = "StaticMethodPromiseWrapper" |
|
2143 else: |
|
2144 jitinfo = "nullptr" |
|
2145 |
|
2146 return (m["name"], accessor, jitinfo, m["length"], m["flags"], selfHostedName) |
|
2147 |
|
2148 return self.generatePrefableArray( |
|
2149 array, name, |
|
2150 ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)', |
|
2151 ' JS_FS_END', |
|
2152 'JSFunctionSpec', |
|
2153 condition, specData, doIdArrays) |
|
2154 |
|
2155 |
|
2156 class AttrDefiner(PropertyDefiner): |
|
2157 def __init__(self, descriptor, name, static, unforgeable=False): |
|
2158 assert not (static and unforgeable) |
|
2159 PropertyDefiner.__init__(self, descriptor, name) |
|
2160 self.name = name |
|
2161 # Ignore non-static attributes for interfaces without a proto object |
|
2162 if descriptor.interface.hasInterfacePrototypeObject() or static: |
|
2163 attributes = [m for m in descriptor.interface.members if |
|
2164 m.isAttr() and m.isStatic() == static and |
|
2165 m.isUnforgeable() == unforgeable] |
|
2166 else: |
|
2167 attributes = [] |
|
2168 self.chrome = [m for m in attributes if isChromeOnly(m)] |
|
2169 self.regular = [m for m in attributes if not isChromeOnly(m)] |
|
2170 self.static = static |
|
2171 self.unforgeable = unforgeable |
|
2172 |
|
2173 if static: |
|
2174 if not descriptor.interface.hasInterfaceObject(): |
|
2175 # static attributes go on the interface object |
|
2176 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() |
|
2177 else: |
|
2178 if not descriptor.interface.hasInterfacePrototypeObject(): |
|
2179 # non-static attributes go on the interface prototype object |
|
2180 assert not self.hasChromeOnly() and not self.hasNonChromeOnly() |
|
2181 |
|
2182 def generateArray(self, array, name, doIdArrays): |
|
2183 if len(array) == 0: |
|
2184 return "" |
|
2185 |
|
2186 def flags(attr): |
|
2187 unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else "" |
|
2188 return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" + |
|
2189 unforgeable) |
|
2190 |
|
2191 def getter(attr): |
|
2192 if self.static: |
|
2193 accessor = 'get_' + attr.identifier.name |
|
2194 jitinfo = "nullptr" |
|
2195 else: |
|
2196 if attr.hasLenientThis(): |
|
2197 accessor = "genericLenientGetter" |
|
2198 elif attr.getExtendedAttribute("CrossOriginReadable"): |
|
2199 accessor = "genericCrossOriginGetter" |
|
2200 elif self.descriptor.needsSpecialGenericOps(): |
|
2201 accessor = "genericGetter" |
|
2202 else: |
|
2203 accessor = "GenericBindingGetter" |
|
2204 jitinfo = "&%s_getterinfo" % attr.identifier.name |
|
2205 return "{ { JS_CAST_NATIVE_TO(%s, JSPropertyOp), %s } }" % \ |
|
2206 (accessor, jitinfo) |
|
2207 |
|
2208 def setter(attr): |
|
2209 if (attr.readonly and |
|
2210 attr.getExtendedAttribute("PutForwards") is None and |
|
2211 attr.getExtendedAttribute("Replaceable") is None): |
|
2212 return "JSOP_NULLWRAPPER" |
|
2213 if self.static: |
|
2214 accessor = 'set_' + attr.identifier.name |
|
2215 jitinfo = "nullptr" |
|
2216 else: |
|
2217 if attr.hasLenientThis(): |
|
2218 accessor = "genericLenientSetter" |
|
2219 elif attr.getExtendedAttribute("CrossOriginWritable"): |
|
2220 accessor = "genericCrossOriginSetter" |
|
2221 elif self.descriptor.needsSpecialGenericOps(): |
|
2222 accessor = "genericSetter" |
|
2223 else: |
|
2224 accessor = "GenericBindingSetter" |
|
2225 jitinfo = "&%s_setterinfo" % attr.identifier.name |
|
2226 return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \ |
|
2227 (accessor, jitinfo) |
|
2228 |
|
2229 def specData(attr): |
|
2230 return (attr.identifier.name, flags(attr), getter(attr), |
|
2231 setter(attr)) |
|
2232 |
|
2233 return self.generatePrefableArray( |
|
2234 array, name, |
|
2235 ' { "%s", %s, %s, %s}', |
|
2236 ' JS_PS_END', |
|
2237 'JSPropertySpec', |
|
2238 PropertyDefiner.getControllingCondition, specData, doIdArrays) |
|
2239 |
|
2240 |
|
2241 class ConstDefiner(PropertyDefiner): |
|
2242 """ |
|
2243 A class for definining constants on the interface object |
|
2244 """ |
|
2245 def __init__(self, descriptor, name): |
|
2246 PropertyDefiner.__init__(self, descriptor, name) |
|
2247 self.name = name |
|
2248 constants = [m for m in descriptor.interface.members if m.isConst()] |
|
2249 self.chrome = [m for m in constants if isChromeOnly(m)] |
|
2250 self.regular = [m for m in constants if not isChromeOnly(m)] |
|
2251 |
|
2252 def generateArray(self, array, name, doIdArrays): |
|
2253 if len(array) == 0: |
|
2254 return "" |
|
2255 |
|
2256 def specData(const): |
|
2257 return (const.identifier.name, |
|
2258 convertConstIDLValueToJSVal(const.value)) |
|
2259 |
|
2260 return self.generatePrefableArray( |
|
2261 array, name, |
|
2262 ' { "%s", %s }', |
|
2263 ' { 0, JS::UndefinedValue() }', |
|
2264 'ConstantSpec', |
|
2265 PropertyDefiner.getControllingCondition, specData, doIdArrays) |
|
2266 |
|
2267 |
|
2268 class PropertyArrays(): |
|
2269 def __init__(self, descriptor): |
|
2270 self.staticMethods = MethodDefiner(descriptor, "StaticMethods", |
|
2271 static=True) |
|
2272 self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes", |
|
2273 static=True) |
|
2274 self.methods = MethodDefiner(descriptor, "Methods", static=False) |
|
2275 self.attrs = AttrDefiner(descriptor, "Attributes", static=False) |
|
2276 self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes", |
|
2277 static=False, unforgeable=True) |
|
2278 self.consts = ConstDefiner(descriptor, "Constants") |
|
2279 |
|
2280 @staticmethod |
|
2281 def arrayNames(): |
|
2282 return ["staticMethods", "staticAttrs", "methods", "attrs", |
|
2283 "unforgeableAttrs", "consts"] |
|
2284 |
|
2285 def hasChromeOnly(self): |
|
2286 return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames()) |
|
2287 |
|
2288 def hasNonChromeOnly(self): |
|
2289 return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames()) |
|
2290 |
|
2291 def __str__(self): |
|
2292 define = "" |
|
2293 for array in self.arrayNames(): |
|
2294 define += str(getattr(self, array)) |
|
2295 return define |
|
2296 |
|
2297 |
|
2298 class CGNativeProperties(CGList): |
|
2299 def __init__(self, descriptor, properties): |
|
2300 def generateNativeProperties(name, chrome): |
|
2301 def check(p): |
|
2302 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly() |
|
2303 |
|
2304 nativeProps = [] |
|
2305 for array in properties.arrayNames(): |
|
2306 propertyArray = getattr(properties, array) |
|
2307 if check(propertyArray): |
|
2308 if propertyArray.usedForXrays(): |
|
2309 ids = "%(name)s_ids" |
|
2310 else: |
|
2311 ids = "nullptr" |
|
2312 props = "%(name)s, " + ids + ", %(name)s_specs" |
|
2313 props = (props % {'name': propertyArray.variableName(chrome)}) |
|
2314 else: |
|
2315 props = "nullptr, nullptr, nullptr" |
|
2316 nativeProps.append(CGGeneric(props)) |
|
2317 return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")), |
|
2318 pre="static const NativeProperties %s = {\n" % name, |
|
2319 post="\n};\n") |
|
2320 |
|
2321 nativeProperties = [] |
|
2322 if properties.hasNonChromeOnly(): |
|
2323 nativeProperties.append( |
|
2324 generateNativeProperties("sNativeProperties", False)) |
|
2325 if properties.hasChromeOnly(): |
|
2326 nativeProperties.append( |
|
2327 generateNativeProperties("sChromeOnlyNativeProperties", True)) |
|
2328 |
|
2329 CGList.__init__(self, nativeProperties, "\n") |
|
2330 |
|
2331 def declare(self): |
|
2332 return "" |
|
2333 |
|
2334 def define(self): |
|
2335 # BOGUSly strip off a newline |
|
2336 return CGList.define(self).rstrip() |
|
2337 |
|
2338 |
|
2339 class CGCreateInterfaceObjectsMethod(CGAbstractMethod): |
|
2340 """ |
|
2341 Generate the CreateInterfaceObjects method for an interface descriptor. |
|
2342 |
|
2343 properties should be a PropertyArrays instance. |
|
2344 """ |
|
2345 def __init__(self, descriptor, properties): |
|
2346 args = [Argument('JSContext*', 'aCx'), |
|
2347 Argument('JS::Handle<JSObject*>', 'aGlobal'), |
|
2348 Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'), |
|
2349 Argument('bool', 'aDefineOnGlobal')] |
|
2350 CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args) |
|
2351 self.properties = properties |
|
2352 |
|
2353 def definition_body(self): |
|
2354 if len(self.descriptor.prototypeChain) == 1: |
|
2355 parentProtoType = "Rooted" |
|
2356 if self.descriptor.interface.getExtendedAttribute("ArrayClass"): |
|
2357 getParentProto = "aCx, JS_GetArrayPrototype(aCx, aGlobal)" |
|
2358 else: |
|
2359 getParentProto = "aCx, JS_GetObjectPrototype(aCx, aGlobal)" |
|
2360 else: |
|
2361 parentProtoName = self.descriptor.prototypeChain[-2] |
|
2362 parentDesc = self.descriptor.getDescriptor(parentProtoName) |
|
2363 if parentDesc.workers: |
|
2364 parentProtoName += '_workers' |
|
2365 getParentProto = ("%s::GetProtoObject(aCx, aGlobal)" % |
|
2366 toBindingNamespace(parentProtoName)) |
|
2367 parentProtoType = "Handle" |
|
2368 |
|
2369 parentWithInterfaceObject = self.descriptor.interface.parent |
|
2370 while (parentWithInterfaceObject and |
|
2371 not parentWithInterfaceObject.hasInterfaceObject()): |
|
2372 parentWithInterfaceObject = parentWithInterfaceObject.parent |
|
2373 if parentWithInterfaceObject: |
|
2374 parentIfaceName = parentWithInterfaceObject.identifier.name |
|
2375 parentDesc = self.descriptor.getDescriptor(parentIfaceName) |
|
2376 if parentDesc.workers: |
|
2377 parentIfaceName += "_workers" |
|
2378 getConstructorProto = ("%s::GetConstructorObject(aCx, aGlobal)" % |
|
2379 toBindingNamespace(parentIfaceName)) |
|
2380 constructorProtoType = "Handle" |
|
2381 else: |
|
2382 getConstructorProto = "aCx, JS_GetFunctionPrototype(aCx, aGlobal)" |
|
2383 constructorProtoType = "Rooted" |
|
2384 |
|
2385 needInterfaceObject = self.descriptor.interface.hasInterfaceObject() |
|
2386 needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject() |
|
2387 |
|
2388 # if we don't need to create anything, why are we generating this? |
|
2389 assert needInterfaceObject or needInterfacePrototypeObject |
|
2390 |
|
2391 idsToInit = [] |
|
2392 # There is no need to init any IDs in workers, because worker bindings |
|
2393 # don't have Xrays. |
|
2394 if not self.descriptor.workers: |
|
2395 for var in self.properties.arrayNames(): |
|
2396 props = getattr(self.properties, var) |
|
2397 # We only have non-chrome ids to init if we have no chrome ids. |
|
2398 if props.hasChromeOnly(): |
|
2399 idsToInit.append(props.variableName(True)) |
|
2400 if props.hasNonChromeOnly(): |
|
2401 idsToInit.append(props.variableName(False)) |
|
2402 if len(idsToInit) > 0: |
|
2403 initIdCalls = ["!InitIds(aCx, %s, %s_ids)" % (varname, varname) |
|
2404 for varname in idsToInit] |
|
2405 idsInitedFlag = CGGeneric("static bool sIdsInited = false;\n") |
|
2406 setFlag = CGGeneric("sIdsInited = true;\n") |
|
2407 initIdConditionals = [CGIfWrapper(CGGeneric("return;\n"), call) |
|
2408 for call in initIdCalls] |
|
2409 initIds = CGList([idsInitedFlag, |
|
2410 CGIfWrapper(CGList(initIdConditionals + [setFlag]), |
|
2411 "!sIdsInited && NS_IsMainThread()")]) |
|
2412 else: |
|
2413 initIds = None |
|
2414 |
|
2415 prefCacheData = [] |
|
2416 for var in self.properties.arrayNames(): |
|
2417 props = getattr(self.properties, var) |
|
2418 prefCacheData.extend(props.prefCacheData) |
|
2419 if len(prefCacheData) != 0: |
|
2420 prefCacheData = [ |
|
2421 CGGeneric('Preferences::AddBoolVarCache(%s, "%s");\n' % (ptr, pref)) |
|
2422 for pref, ptr in prefCacheData] |
|
2423 prefCache = CGWrapper(CGIndenter(CGList(prefCacheData)), |
|
2424 pre=("static bool sPrefCachesInited = false;\n" |
|
2425 "if (!sPrefCachesInited) {\n" |
|
2426 " sPrefCachesInited = true;\n"), |
|
2427 post="}\n") |
|
2428 else: |
|
2429 prefCache = None |
|
2430 |
|
2431 if UseHolderForUnforgeable(self.descriptor): |
|
2432 createUnforgeableHolder = CGGeneric(dedent(""" |
|
2433 JS::Rooted<JSObject*> unforgeableHolder(aCx, |
|
2434 JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); |
|
2435 if (!unforgeableHolder) { |
|
2436 return; |
|
2437 } |
|
2438 """)) |
|
2439 defineUnforgeables = InitUnforgeablePropertiesOnObject(self.descriptor, |
|
2440 "unforgeableHolder", |
|
2441 self.properties) |
|
2442 createUnforgeableHolder = CGList( |
|
2443 [createUnforgeableHolder, defineUnforgeables]) |
|
2444 else: |
|
2445 createUnforgeableHolder = None |
|
2446 |
|
2447 getParentProto = fill( |
|
2448 """ |
|
2449 JS::${type}<JSObject*> parentProto(${getParentProto}); |
|
2450 if (!parentProto) { |
|
2451 return; |
|
2452 } |
|
2453 """, |
|
2454 type=parentProtoType, |
|
2455 getParentProto=getParentProto) |
|
2456 |
|
2457 getConstructorProto = fill( |
|
2458 """ |
|
2459 JS::${type}<JSObject*> constructorProto(${getConstructorProto}); |
|
2460 if (!constructorProto) { |
|
2461 return; |
|
2462 } |
|
2463 """, |
|
2464 type=constructorProtoType, |
|
2465 getConstructorProto=getConstructorProto) |
|
2466 |
|
2467 if (needInterfaceObject and |
|
2468 self.descriptor.needsConstructHookHolder()): |
|
2469 constructHookHolder = "&" + CONSTRUCT_HOOK_NAME + "_holder" |
|
2470 else: |
|
2471 constructHookHolder = "nullptr" |
|
2472 if self.descriptor.interface.ctor(): |
|
2473 constructArgs = methodLength(self.descriptor.interface.ctor()) |
|
2474 else: |
|
2475 constructArgs = 0 |
|
2476 if len(self.descriptor.interface.namedConstructors) > 0: |
|
2477 namedConstructors = "namedConstructors" |
|
2478 else: |
|
2479 namedConstructors = "nullptr" |
|
2480 |
|
2481 if needInterfacePrototypeObject: |
|
2482 protoClass = "&PrototypeClass.mBase" |
|
2483 protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name |
|
2484 else: |
|
2485 protoClass = "nullptr" |
|
2486 protoCache = "nullptr" |
|
2487 if needInterfaceObject: |
|
2488 interfaceClass = "&InterfaceObjectClass.mBase" |
|
2489 interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name |
|
2490 else: |
|
2491 # We don't have slots to store the named constructors. |
|
2492 assert len(self.descriptor.interface.namedConstructors) == 0 |
|
2493 interfaceClass = "nullptr" |
|
2494 interfaceCache = "nullptr" |
|
2495 |
|
2496 if self.descriptor.concrete: |
|
2497 domClass = "&Class.mClass" |
|
2498 else: |
|
2499 domClass = "nullptr" |
|
2500 |
|
2501 if self.properties.hasNonChromeOnly(): |
|
2502 properties = "&sNativeProperties" |
|
2503 else: |
|
2504 properties = "nullptr" |
|
2505 if self.properties.hasChromeOnly(): |
|
2506 accessCheck = "nsContentUtils::ThreadsafeIsCallerChrome()" |
|
2507 chromeProperties = accessCheck + " ? &sChromeOnlyNativeProperties : nullptr" |
|
2508 else: |
|
2509 chromeProperties = "nullptr" |
|
2510 |
|
2511 call = fill( |
|
2512 """ |
|
2513 dom::CreateInterfaceObjects(aCx, aGlobal, parentProto, |
|
2514 ${protoClass}, ${protoCache}, |
|
2515 constructorProto, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors}, |
|
2516 ${interfaceCache}, |
|
2517 ${domClass}, |
|
2518 ${properties}, |
|
2519 ${chromeProperties}, |
|
2520 ${name}, aDefineOnGlobal); |
|
2521 """, |
|
2522 protoClass=protoClass, |
|
2523 protoCache=protoCache, |
|
2524 interfaceClass=interfaceClass, |
|
2525 constructHookHolder=constructHookHolder, |
|
2526 constructArgs=constructArgs, |
|
2527 namedConstructors=namedConstructors, |
|
2528 interfaceCache=interfaceCache, |
|
2529 domClass=domClass, |
|
2530 properties=properties, |
|
2531 chromeProperties=chromeProperties, |
|
2532 name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr") |
|
2533 |
|
2534 if UseHolderForUnforgeable(self.descriptor): |
|
2535 assert needInterfacePrototypeObject |
|
2536 setUnforgeableHolder = CGGeneric(fill( |
|
2537 """ |
|
2538 JSObject* proto = aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::${name}); |
|
2539 if (proto) { |
|
2540 js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE, |
|
2541 JS::ObjectValue(*unforgeableHolder)); |
|
2542 } |
|
2543 """, |
|
2544 name=self.descriptor.name)) |
|
2545 else: |
|
2546 setUnforgeableHolder = None |
|
2547 functionBody = CGList( |
|
2548 [CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds, |
|
2549 prefCache, createUnforgeableHolder, CGGeneric(call), setUnforgeableHolder], |
|
2550 "\n") |
|
2551 return CGIndenter(functionBody).define() |
|
2552 |
|
2553 |
|
2554 class CGGetPerInterfaceObject(CGAbstractMethod): |
|
2555 """ |
|
2556 A method for getting a per-interface object (a prototype object or interface |
|
2557 constructor object). |
|
2558 """ |
|
2559 def __init__(self, descriptor, name, idPrefix="", extraArgs=[]): |
|
2560 args = [Argument('JSContext*', 'aCx'), |
|
2561 Argument('JS::Handle<JSObject*>', 'aGlobal')] + extraArgs |
|
2562 CGAbstractMethod.__init__(self, descriptor, name, |
|
2563 'JS::Handle<JSObject*>', args) |
|
2564 self.id = idPrefix + "id::" + self.descriptor.name |
|
2565 |
|
2566 def definition_body(self): |
|
2567 # BOGUS extra blank line at the beginning of the code below |
|
2568 # BOGUS - should be a blank line between an if-block and following comment below |
|
2569 return indent(fill( |
|
2570 """ |
|
2571 |
|
2572 /* Make sure our global is sane. Hopefully we can remove this sometime */ |
|
2573 if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) { |
|
2574 return JS::NullPtr(); |
|
2575 } |
|
2576 /* Check to see whether the interface objects are already installed */ |
|
2577 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal); |
|
2578 if (!protoAndIfaceCache.EntrySlotIfExists(${id})) { |
|
2579 CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceCache, aDefineOnGlobal); |
|
2580 } |
|
2581 |
|
2582 /* |
|
2583 * The object might _still_ be null, but that's OK. |
|
2584 * |
|
2585 * Calling fromMarkedLocation() is safe because protoAndIfaceCache is |
|
2586 * traced by TraceProtoAndIfaceCache() and its contents are never |
|
2587 * changed after they have been set. |
|
2588 */ |
|
2589 return JS::Handle<JSObject*>::fromMarkedLocation(protoAndIfaceCache.EntrySlotMustExist(${id}).address()); |
|
2590 """, |
|
2591 id=self.id)) |
|
2592 |
|
2593 |
|
2594 class CGGetProtoObjectMethod(CGGetPerInterfaceObject): |
|
2595 """ |
|
2596 A method for getting the interface prototype object. |
|
2597 """ |
|
2598 def __init__(self, descriptor): |
|
2599 CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject", |
|
2600 "prototypes::") |
|
2601 |
|
2602 def definition_body(self): |
|
2603 # BOGUS extra blank line at start of method |
|
2604 return indent(dedent(""" |
|
2605 |
|
2606 /* Get the interface prototype object for this class. This will create the |
|
2607 object as needed. */ |
|
2608 bool aDefineOnGlobal = true; |
|
2609 """)) + CGGetPerInterfaceObject.definition_body(self) |
|
2610 |
|
2611 |
|
2612 class CGGetConstructorObjectMethod(CGGetPerInterfaceObject): |
|
2613 """ |
|
2614 A method for getting the interface constructor object. |
|
2615 """ |
|
2616 def __init__(self, descriptor): |
|
2617 CGGetPerInterfaceObject.__init__( |
|
2618 self, descriptor, "GetConstructorObject", |
|
2619 "constructors::", |
|
2620 extraArgs=[Argument("bool", "aDefineOnGlobal", "true")]) |
|
2621 |
|
2622 def definition_body(self): |
|
2623 # BOGUS extra blank line at start of method |
|
2624 return indent(dedent(""" |
|
2625 |
|
2626 /* Get the interface object for this class. This will create the object as |
|
2627 needed. */ |
|
2628 """)) + CGGetPerInterfaceObject.definition_body(self) |
|
2629 |
|
2630 |
|
2631 class CGDefineDOMInterfaceMethod(CGAbstractMethod): |
|
2632 """ |
|
2633 A method for resolve hooks to try to lazily define the interface object for |
|
2634 a given interface. |
|
2635 """ |
|
2636 def __init__(self, descriptor): |
|
2637 args = [Argument('JSContext*', 'aCx'), |
|
2638 Argument('JS::Handle<JSObject*>', 'aGlobal'), |
|
2639 Argument('JS::Handle<jsid>', 'id'), |
|
2640 Argument('bool', 'aDefineOnGlobal')] |
|
2641 CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args) |
|
2642 |
|
2643 def declare(self): |
|
2644 if self.descriptor.workers: |
|
2645 return '' |
|
2646 return CGAbstractMethod.declare(self) |
|
2647 |
|
2648 def define(self): |
|
2649 if self.descriptor.workers: |
|
2650 return '' |
|
2651 return CGAbstractMethod.define(self) |
|
2652 |
|
2653 def definition_body(self): |
|
2654 if len(self.descriptor.interface.namedConstructors) > 0: |
|
2655 getConstructor = indent(dedent(""" |
|
2656 JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal, aDefineOnGlobal); |
|
2657 if (!interfaceObject) { |
|
2658 return nullptr; |
|
2659 } |
|
2660 for (unsigned slot = DOM_INTERFACE_SLOTS_BASE; slot < JSCLASS_RESERVED_SLOTS(&InterfaceObjectClass.mBase); ++slot) { |
|
2661 JSObject* constructor = &js::GetReservedSlot(interfaceObject, slot).toObject(); |
|
2662 if (JS_GetFunctionId(JS_GetObjectFunction(constructor)) == JSID_TO_STRING(id)) { |
|
2663 return constructor; |
|
2664 } |
|
2665 } |
|
2666 return interfaceObject; |
|
2667 """)) |
|
2668 else: |
|
2669 getConstructor = " return GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);\n" |
|
2670 return getConstructor |
|
2671 |
|
2672 |
|
2673 class CGConstructorEnabled(CGAbstractMethod): |
|
2674 """ |
|
2675 A method for testing whether we should be exposing this interface |
|
2676 object or navigator property. This can perform various tests |
|
2677 depending on what conditions are specified on the interface. |
|
2678 """ |
|
2679 def __init__(self, descriptor): |
|
2680 CGAbstractMethod.__init__(self, descriptor, |
|
2681 'ConstructorEnabled', 'bool', |
|
2682 [Argument("JSContext*", "aCx"), |
|
2683 Argument("JS::Handle<JSObject*>", "aObj")]) |
|
2684 |
|
2685 def definition_body(self): |
|
2686 conditions = [] |
|
2687 iface = self.descriptor.interface |
|
2688 pref = iface.getExtendedAttribute("Pref") |
|
2689 if pref: |
|
2690 assert isinstance(pref, list) and len(pref) == 1 |
|
2691 conditions.append('Preferences::GetBool("%s")' % pref[0]) |
|
2692 if iface.getExtendedAttribute("ChromeOnly"): |
|
2693 conditions.append("nsContentUtils::ThreadsafeIsCallerChrome()") |
|
2694 func = iface.getExtendedAttribute("Func") |
|
2695 if func: |
|
2696 assert isinstance(func, list) and len(func) == 1 |
|
2697 conditions.append("%s(aCx, aObj)" % func[0]) |
|
2698 availableIn = getAvailableInTestFunc(iface) |
|
2699 if availableIn: |
|
2700 conditions.append("%s(aCx, aObj)" % availableIn) |
|
2701 # We should really have some conditions |
|
2702 assert len(conditions) |
|
2703 body = CGWrapper(CGList((CGGeneric(cond) for cond in conditions), |
|
2704 " &&\n"), |
|
2705 pre="return ", post=";\n", reindent=True) |
|
2706 return CGIndenter(body).define() |
|
2707 |
|
2708 |
|
2709 def CreateBindingJSObject(descriptor, properties, parent): |
|
2710 # We don't always need to root obj, but there are a variety |
|
2711 # of cases where we do, so for simplicity, just always root it. |
|
2712 objDecl = "JS::Rooted<JSObject*> obj(aCx);\n" |
|
2713 if descriptor.proxy: |
|
2714 create = fill( |
|
2715 """ |
|
2716 JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aObject)); |
|
2717 js::ProxyOptions options; |
|
2718 options.setClass(&Class.mBase); |
|
2719 obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(), |
|
2720 proxyPrivateVal, proto, ${parent}, options); |
|
2721 if (!obj) { |
|
2722 return nullptr; |
|
2723 } |
|
2724 |
|
2725 """, |
|
2726 parent=parent) |
|
2727 if descriptor.interface.getExtendedAttribute('OverrideBuiltins'): |
|
2728 create += dedent(""" |
|
2729 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, |
|
2730 JS::PrivateValue(&aObject->mExpandoAndGeneration)); |
|
2731 |
|
2732 """) |
|
2733 else: |
|
2734 create = fill( |
|
2735 """ |
|
2736 obj = JS_NewObject(aCx, Class.ToJSClass(), proto, ${parent}); |
|
2737 if (!obj) { |
|
2738 return nullptr; |
|
2739 } |
|
2740 |
|
2741 js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject)); |
|
2742 """, |
|
2743 parent=parent) |
|
2744 if "Window" in descriptor.interface.identifier.name: |
|
2745 create = dedent(""" |
|
2746 MOZ_ASSERT(false, |
|
2747 "Our current reserved slot situation is unsafe for globals. Fix " |
|
2748 "bug 760095!"); |
|
2749 """) + create |
|
2750 create = objDecl + create |
|
2751 |
|
2752 if descriptor.nativeOwnership == 'refcounted': |
|
2753 create += "NS_ADDREF(aObject);\n" |
|
2754 else: |
|
2755 create += dedent(""" |
|
2756 // Make sure the native objects inherit from NonRefcountedDOMObject so that we |
|
2757 // log their ctor and dtor. |
|
2758 MustInheritFromNonRefcountedDOMObject(aObject); |
|
2759 *aTookOwnership = true; |
|
2760 """) |
|
2761 return create |
|
2762 |
|
2763 |
|
2764 def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""): |
|
2765 """ |
|
2766 properties is a PropertyArrays instance |
|
2767 """ |
|
2768 defineUnforgeables = fill( |
|
2769 """ |
|
2770 if (!DefineUnforgeableAttributes(aCx, ${obj}, %s)) { |
|
2771 return${rv}; |
|
2772 } |
|
2773 """, |
|
2774 obj=obj, |
|
2775 rv=" " + failureReturnValue if failureReturnValue else "") |
|
2776 |
|
2777 unforgeableAttrs = properties.unforgeableAttrs |
|
2778 unforgeables = [] |
|
2779 if unforgeableAttrs.hasNonChromeOnly(): |
|
2780 unforgeables.append(CGGeneric(defineUnforgeables % |
|
2781 unforgeableAttrs.variableName(False))) |
|
2782 if unforgeableAttrs.hasChromeOnly(): |
|
2783 unforgeables.append( |
|
2784 CGIfWrapper(CGGeneric(defineUnforgeables % |
|
2785 unforgeableAttrs.variableName(True)), |
|
2786 "nsContentUtils::ThreadsafeIsCallerChrome()")) |
|
2787 return CGList(unforgeables) |
|
2788 |
|
2789 |
|
2790 def InitUnforgeableProperties(descriptor, properties): |
|
2791 """ |
|
2792 properties is a PropertyArrays instance |
|
2793 """ |
|
2794 unforgeableAttrs = properties.unforgeableAttrs |
|
2795 if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly(): |
|
2796 return "" |
|
2797 |
|
2798 if descriptor.proxy: |
|
2799 unforgeableProperties = CGGeneric( |
|
2800 "// Unforgeable properties on proxy-based bindings are stored in an object held\n" |
|
2801 "// by the interface prototype object.\n" |
|
2802 "\n") # BOGUS extra blank line |
|
2803 else: |
|
2804 unforgeableProperties = CGWrapper( |
|
2805 InitUnforgeablePropertiesOnObject(descriptor, "obj", properties, "nullptr"), |
|
2806 pre=( |
|
2807 "// Important: do unforgeable property setup after we have handed\n" |
|
2808 "// over ownership of the C++ object to obj as needed, so that if\n" |
|
2809 "// we fail and it ends up GCed it won't have problems in the\n" |
|
2810 "// finalizer trying to drop its ownership of the C++ object.\n")) |
|
2811 return CGWrapper(unforgeableProperties, pre="\n").define() |
|
2812 |
|
2813 |
|
2814 def AssertInheritanceChain(descriptor): |
|
2815 asserts = "" |
|
2816 iface = descriptor.interface |
|
2817 while iface: |
|
2818 desc = descriptor.getDescriptor(iface.identifier.name) |
|
2819 asserts += ( |
|
2820 " MOZ_ASSERT(static_cast<%s*>(aObject) == \n" |
|
2821 " reinterpret_cast<%s*>(aObject));\n" % |
|
2822 (desc.nativeType, desc.nativeType)) |
|
2823 iface = iface.parent |
|
2824 asserts += " MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n" |
|
2825 return asserts |
|
2826 |
|
2827 |
|
2828 def InitMemberSlots(descriptor, wrapperCache): |
|
2829 """ |
|
2830 Initialize member slots on our JS object if we're supposed to have some. |
|
2831 |
|
2832 Note that this is called after the SetWrapper() call in the |
|
2833 wrapperCache case, since that can affect how our getters behave |
|
2834 and we plan to invoke them here. So if we fail, we need to |
|
2835 ClearWrapper. |
|
2836 """ |
|
2837 if not descriptor.interface.hasMembersInSlots(): |
|
2838 return "\n" # BOGUS blank line only if this returns empty |
|
2839 if wrapperCache: |
|
2840 clearWrapper = " aCache->ClearWrapper();\n" |
|
2841 else: |
|
2842 clearWrapper = "" |
|
2843 return ("if (!UpdateMemberSlots(aCx, obj, aObject)) {\n" |
|
2844 "%s" |
|
2845 " return nullptr;\n" |
|
2846 "}\n" % clearWrapper) |
|
2847 |
|
2848 |
|
2849 class CGWrapWithCacheMethod(CGAbstractMethod): |
|
2850 """ |
|
2851 Create a wrapper JSObject for a given native that implements nsWrapperCache. |
|
2852 |
|
2853 properties should be a PropertyArrays instance. |
|
2854 """ |
|
2855 def __init__(self, descriptor, properties): |
|
2856 assert descriptor.interface.hasInterfacePrototypeObject() |
|
2857 args = [Argument('JSContext*', 'aCx'), |
|
2858 Argument(descriptor.nativeType + '*', 'aObject'), |
|
2859 Argument('nsWrapperCache*', 'aCache')] |
|
2860 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args) |
|
2861 self.properties = properties |
|
2862 |
|
2863 def definition_body(self): |
|
2864 return fill( |
|
2865 """ |
|
2866 ${assertion} |
|
2867 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), |
|
2868 "nsISupports must be on our primary inheritance chain"); |
|
2869 |
|
2870 JS::Rooted<JSObject*> parent(aCx, |
|
2871 GetRealParentObject(aObject, |
|
2872 WrapNativeParent(aCx, aObject->GetParentObject()))); |
|
2873 if (!parent) { |
|
2874 return nullptr; |
|
2875 } |
|
2876 |
|
2877 // That might have ended up wrapping us already, due to the wonders |
|
2878 // of XBL. Check for that, and bail out as needed. Scope so we don't |
|
2879 // collide with the "obj" we declare in CreateBindingJSObject. |
|
2880 { |
|
2881 JSObject* obj = aCache->GetWrapper(); |
|
2882 if (obj) { |
|
2883 return obj; |
|
2884 } |
|
2885 } |
|
2886 |
|
2887 JSAutoCompartment ac(aCx, parent); |
|
2888 JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, parent)); |
|
2889 JS::Handle<JSObject*> proto = GetProtoObject(aCx, global); |
|
2890 if (!proto) { |
|
2891 return nullptr; |
|
2892 } |
|
2893 |
|
2894 $*{parent} |
|
2895 |
|
2896 $*{unforgeable} |
|
2897 |
|
2898 aCache->SetWrapper(obj); |
|
2899 $*{slots} |
|
2900 return obj; |
|
2901 """, |
|
2902 assertion=AssertInheritanceChain(self.descriptor), |
|
2903 parent=CreateBindingJSObject(self.descriptor, self.properties, |
|
2904 "parent"), |
|
2905 unforgeable=InitUnforgeableProperties(self.descriptor, self.properties), |
|
2906 slots=InitMemberSlots(self.descriptor, True)) |
|
2907 |
|
2908 |
|
2909 class CGWrapMethod(CGAbstractMethod): |
|
2910 def __init__(self, descriptor): |
|
2911 # XXX can we wrap if we don't have an interface prototype object? |
|
2912 assert descriptor.interface.hasInterfacePrototypeObject() |
|
2913 args = [Argument('JSContext*', 'aCx'), |
|
2914 Argument('T*', 'aObject')] |
|
2915 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args, |
|
2916 inline=True, templateArgs=["class T"]) |
|
2917 |
|
2918 def definition_body(self): |
|
2919 return " return Wrap(aCx, aObject, aObject);\n" |
|
2920 |
|
2921 |
|
2922 class CGWrapNonWrapperCacheMethod(CGAbstractMethod): |
|
2923 """ |
|
2924 Create a wrapper JSObject for a given native that does not implement |
|
2925 nsWrapperCache. |
|
2926 |
|
2927 properties should be a PropertyArrays instance. |
|
2928 """ |
|
2929 def __init__(self, descriptor, properties): |
|
2930 # XXX can we wrap if we don't have an interface prototype object? |
|
2931 assert descriptor.interface.hasInterfacePrototypeObject() |
|
2932 args = [Argument('JSContext*', 'aCx'), |
|
2933 Argument(descriptor.nativeType + '*', 'aObject')] |
|
2934 if descriptor.nativeOwnership == 'owned': |
|
2935 args.append(Argument('bool*', 'aTookOwnership')) |
|
2936 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args) |
|
2937 self.properties = properties |
|
2938 |
|
2939 def definition_body(self): |
|
2940 return fill( |
|
2941 """ |
|
2942 ${assertions} |
|
2943 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx)); |
|
2944 JS::Handle<JSObject*> proto = GetProtoObject(aCx, global); |
|
2945 if (!proto) { |
|
2946 return nullptr; |
|
2947 } |
|
2948 |
|
2949 $*{global_} |
|
2950 |
|
2951 $*{unforgeable} |
|
2952 |
|
2953 $*{slots} |
|
2954 return obj; |
|
2955 """, |
|
2956 assertions=AssertInheritanceChain(self.descriptor), |
|
2957 global_=CreateBindingJSObject(self.descriptor, self.properties, |
|
2958 "global"), |
|
2959 unforgeable=InitUnforgeableProperties(self.descriptor, self.properties), |
|
2960 slots=InitMemberSlots(self.descriptor, False)) |
|
2961 |
|
2962 |
|
2963 class CGWrapGlobalMethod(CGAbstractMethod): |
|
2964 """ |
|
2965 Create a wrapper JSObject for a global. The global must implement |
|
2966 nsWrapperCache. |
|
2967 |
|
2968 properties should be a PropertyArrays instance. |
|
2969 """ |
|
2970 def __init__(self, descriptor, properties): |
|
2971 assert descriptor.interface.hasInterfacePrototypeObject() |
|
2972 args = [Argument('JSContext*', 'aCx'), |
|
2973 Argument(descriptor.nativeType + '*', 'aObject'), |
|
2974 Argument('nsWrapperCache*', 'aCache'), |
|
2975 Argument('JS::CompartmentOptions&', 'aOptions'), |
|
2976 Argument('JSPrincipals*', 'aPrincipal')] |
|
2977 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args) |
|
2978 self.descriptor = descriptor |
|
2979 self.properties = properties |
|
2980 |
|
2981 def definition_body(self): |
|
2982 return fill( |
|
2983 """ |
|
2984 ${assertions} |
|
2985 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), |
|
2986 "nsISupports must be on our primary inheritance chain"); |
|
2987 |
|
2988 JS::Rooted<JSObject*> obj(aCx); |
|
2989 obj = CreateGlobal<${nativeType}, GetProtoObject>(aCx, |
|
2990 aObject, |
|
2991 aCache, |
|
2992 Class.ToJSClass(), |
|
2993 aOptions, |
|
2994 aPrincipal); |
|
2995 |
|
2996 $*{unforgeable} |
|
2997 |
|
2998 $*{slots} |
|
2999 |
|
3000 // XXXkhuey can't do this yet until workers can lazy resolve. |
|
3001 // JS_FireOnNewGlobalObject(aCx, obj); |
|
3002 |
|
3003 return obj; |
|
3004 """, |
|
3005 assertions=AssertInheritanceChain(self.descriptor), |
|
3006 nativeType=self.descriptor.nativeType, |
|
3007 unforgeable=InitUnforgeableProperties(self.descriptor, self.properties), |
|
3008 slots=InitMemberSlots(self.descriptor, True)) |
|
3009 |
|
3010 |
|
3011 class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod): |
|
3012 def __init__(self, descriptor): |
|
3013 args = [Argument('JSContext*', 'aCx'), |
|
3014 Argument('JS::Handle<JSObject*>', 'aWrapper'), |
|
3015 Argument(descriptor.nativeType + '*', 'aObject')] |
|
3016 CGAbstractStaticMethod.__init__(self, descriptor, 'UpdateMemberSlots', 'bool', args) |
|
3017 |
|
3018 def definition_body(self): |
|
3019 body = ("JS::Rooted<JS::Value> temp(aCx);\n" |
|
3020 "JSJitGetterCallArgs args(&temp);\n") |
|
3021 for m in self.descriptor.interface.members: |
|
3022 if m.isAttr() and m.getExtendedAttribute("StoreInSlot"): |
|
3023 body += fill( |
|
3024 """ |
|
3025 |
|
3026 static_assert(${slot} < js::shadow::Object::MAX_FIXED_SLOTS, |
|
3027 "Not enough fixed slots to fit '${interface}.${member}'"); |
|
3028 if (!get_${member}(aCx, aWrapper, aObject, args)) { |
|
3029 return false; |
|
3030 } |
|
3031 // Getter handled setting our reserved slots |
|
3032 """, |
|
3033 slot=memberReservedSlot(m), |
|
3034 interface=self.descriptor.interface.identifier.name, |
|
3035 member=m.identifier.name) |
|
3036 |
|
3037 body += "\nreturn true;\n" |
|
3038 return indent(body) |
|
3039 |
|
3040 |
|
3041 class CGClearCachedValueMethod(CGAbstractMethod): |
|
3042 def __init__(self, descriptor, member): |
|
3043 self.member = member |
|
3044 # If we're StoreInSlot, we'll need to call the getter |
|
3045 if member.getExtendedAttribute("StoreInSlot"): |
|
3046 args = [Argument('JSContext*', 'aCx')] |
|
3047 returnType = 'bool' |
|
3048 else: |
|
3049 args = [] |
|
3050 returnType = 'void' |
|
3051 args.append(Argument(descriptor.nativeType + '*', 'aObject')) |
|
3052 name = ("ClearCached%sValue" % MakeNativeName(member.identifier.name)) |
|
3053 CGAbstractMethod.__init__(self, descriptor, name, returnType, args) |
|
3054 |
|
3055 def definition_body(self): |
|
3056 slotIndex = memberReservedSlot(self.member) |
|
3057 if self.member.getExtendedAttribute("StoreInSlot"): |
|
3058 # We have to root things and save the old value in case |
|
3059 # regetting fails, so we can restore it. |
|
3060 declObj = "JS::Rooted<JSObject*> obj(aCx);\n" |
|
3061 noopRetval = " true" |
|
3062 saveMember = ( |
|
3063 "JS::Rooted<JS::Value> oldValue(aCx, js::GetReservedSlot(obj, %s));\n" % |
|
3064 slotIndex) |
|
3065 regetMember = fill( |
|
3066 """ |
|
3067 JS::Rooted<JS::Value> temp(aCx); |
|
3068 JSJitGetterCallArgs args(&temp); |
|
3069 JSAutoCompartment ac(aCx, obj); |
|
3070 if (!get_${name}(aCx, obj, aObject, args)) { |
|
3071 js::SetReservedSlot(obj, ${slotIndex}, oldValue); |
|
3072 nsJSUtils::ReportPendingException(aCx); |
|
3073 return false; |
|
3074 } |
|
3075 return true; |
|
3076 """, |
|
3077 name=self.member.identifier.name, |
|
3078 slotIndex=slotIndex) |
|
3079 else: |
|
3080 declObj = "JSObject* obj;\n" |
|
3081 noopRetval = "" |
|
3082 saveMember = "" |
|
3083 regetMember = "" |
|
3084 |
|
3085 return indent(fill( |
|
3086 """ |
|
3087 $*{declObj} |
|
3088 obj = aObject->GetWrapper(); |
|
3089 if (!obj) { |
|
3090 return${noopRetval}; |
|
3091 } |
|
3092 $*{saveMember} |
|
3093 js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue()); |
|
3094 $*{regetMember} |
|
3095 """, |
|
3096 declObj=declObj, |
|
3097 noopRetval=noopRetval, |
|
3098 saveMember=saveMember, |
|
3099 slotIndex=slotIndex, |
|
3100 regetMember=regetMember)) |
|
3101 |
|
3102 |
|
3103 class CGIsPermittedMethod(CGAbstractMethod): |
|
3104 """ |
|
3105 crossOriginGetters/Setters/Methods are sets of names of the relevant members. |
|
3106 """ |
|
3107 def __init__(self, descriptor, crossOriginGetters, crossOriginSetters, |
|
3108 crossOriginMethods): |
|
3109 self.crossOriginGetters = crossOriginGetters |
|
3110 self.crossOriginSetters = crossOriginSetters |
|
3111 self.crossOriginMethods = crossOriginMethods |
|
3112 args = [Argument("JSFlatString*", "prop"), |
|
3113 Argument("jschar", "propFirstChar"), |
|
3114 Argument("bool", "set")] |
|
3115 CGAbstractMethod.__init__(self, descriptor, "IsPermitted", "bool", args, |
|
3116 inline=True) |
|
3117 |
|
3118 def definition_body(self): |
|
3119 allNames = self.crossOriginGetters | self.crossOriginSetters | self.crossOriginMethods |
|
3120 readwrite = self.crossOriginGetters & self.crossOriginSetters |
|
3121 readonly = (self.crossOriginGetters - self.crossOriginSetters) | self.crossOriginMethods |
|
3122 writeonly = self.crossOriginSetters - self.crossOriginGetters |
|
3123 cases = {} |
|
3124 for name in sorted(allNames): |
|
3125 cond = 'JS_FlatStringEqualsAscii(prop, "%s")' % name |
|
3126 if name in readonly: |
|
3127 cond = "!set && %s" % cond |
|
3128 elif name in writeonly: |
|
3129 cond = "set && %s" % cond |
|
3130 else: |
|
3131 assert name in readwrite |
|
3132 firstLetter = name[0] |
|
3133 case = cases.get(firstLetter, CGList([])) |
|
3134 case.append(CGGeneric("if (%s) {\n" |
|
3135 " return true;\n" |
|
3136 "}\n" % cond)) |
|
3137 cases[firstLetter] = case |
|
3138 caseList = [] |
|
3139 for firstLetter in sorted(cases.keys()): |
|
3140 caseList.append(CGCase("'%s'" % firstLetter, cases[firstLetter])) |
|
3141 switch = CGSwitch("propFirstChar", caseList) |
|
3142 return indent(switch.define() + "\nreturn false;\n") |
|
3143 |
|
3144 builtinNames = { |
|
3145 IDLType.Tags.bool: 'bool', |
|
3146 IDLType.Tags.int8: 'int8_t', |
|
3147 IDLType.Tags.int16: 'int16_t', |
|
3148 IDLType.Tags.int32: 'int32_t', |
|
3149 IDLType.Tags.int64: 'int64_t', |
|
3150 IDLType.Tags.uint8: 'uint8_t', |
|
3151 IDLType.Tags.uint16: 'uint16_t', |
|
3152 IDLType.Tags.uint32: 'uint32_t', |
|
3153 IDLType.Tags.uint64: 'uint64_t', |
|
3154 IDLType.Tags.unrestricted_float: 'float', |
|
3155 IDLType.Tags.float: 'float', |
|
3156 IDLType.Tags.unrestricted_double: 'double', |
|
3157 IDLType.Tags.double: 'double' |
|
3158 } |
|
3159 |
|
3160 numericSuffixes = { |
|
3161 IDLType.Tags.int8: '', |
|
3162 IDLType.Tags.uint8: '', |
|
3163 IDLType.Tags.int16: '', |
|
3164 IDLType.Tags.uint16: '', |
|
3165 IDLType.Tags.int32: '', |
|
3166 IDLType.Tags.uint32: 'U', |
|
3167 IDLType.Tags.int64: 'LL', |
|
3168 IDLType.Tags.uint64: 'ULL', |
|
3169 IDLType.Tags.unrestricted_float: 'F', |
|
3170 IDLType.Tags.float: 'F', |
|
3171 IDLType.Tags.unrestricted_double: '', |
|
3172 IDLType.Tags.double: '' |
|
3173 } |
|
3174 |
|
3175 |
|
3176 def numericValue(t, v): |
|
3177 if (t == IDLType.Tags.unrestricted_double or |
|
3178 t == IDLType.Tags.unrestricted_float): |
|
3179 typeName = builtinNames[t] |
|
3180 if v == float("inf"): |
|
3181 return "mozilla::PositiveInfinity<%s>()" % typeName |
|
3182 if v == float("-inf"): |
|
3183 return "mozilla::NegativeInfinity<%s>()" % typeName |
|
3184 if math.isnan(v): |
|
3185 return "mozilla::UnspecifiedNaN<%s>()" % typeName |
|
3186 return "%s%s" % (v, numericSuffixes[t]) |
|
3187 |
|
3188 |
|
3189 class CastableObjectUnwrapper(): |
|
3190 """ |
|
3191 A class for unwrapping an object named by the "source" argument |
|
3192 based on the passed-in descriptor and storing it in a variable |
|
3193 called by the name in the "target" argument. |
|
3194 |
|
3195 codeOnFailure is the code to run if unwrapping fails. |
|
3196 |
|
3197 If isCallbackReturnValue is "JSImpl" and our descriptor is also |
|
3198 JS-implemented, fall back to just creating the right object if what we |
|
3199 have isn't one already. |
|
3200 |
|
3201 If allowCrossOriginObj is True, then we'll first do an |
|
3202 UncheckedUnwrap and then operate on the result. |
|
3203 """ |
|
3204 def __init__(self, descriptor, source, target, codeOnFailure, |
|
3205 exceptionCode=None, isCallbackReturnValue=False, |
|
3206 allowCrossOriginObj=False): |
|
3207 self.substitution = { |
|
3208 "type": descriptor.nativeType, |
|
3209 "protoID": "prototypes::id::" + descriptor.name, |
|
3210 "target": target, |
|
3211 "codeOnFailure": codeOnFailure, |
|
3212 } |
|
3213 if allowCrossOriginObj: |
|
3214 self.substitution["uncheckedObjDecl"] = ( |
|
3215 "JS::Rooted<JSObject*> uncheckedObj(cx, js::UncheckedUnwrap(%s));\n" % source) |
|
3216 self.substitution["source"] = "uncheckedObj" |
|
3217 xpconnectUnwrap = dedent(""" |
|
3218 nsresult rv; |
|
3219 { // Scope for the JSAutoCompartment, because we only |
|
3220 // want to be in that compartment for the UnwrapArg call. |
|
3221 JSAutoCompartment ac(cx, ${source}); |
|
3222 rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val); |
|
3223 } |
|
3224 """) |
|
3225 else: |
|
3226 self.substitution["uncheckedObjDecl"] = "" |
|
3227 self.substitution["source"] = source |
|
3228 xpconnectUnwrap = "nsresult rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n" |
|
3229 |
|
3230 if descriptor.hasXPConnectImpls: |
|
3231 # We don't use xpc_qsUnwrapThis because it will always throw on |
|
3232 # unwrap failure, whereas we want to control whether we throw or |
|
3233 # not. |
|
3234 self.substitution["codeOnFailure"] = string.Template( |
|
3235 "${type} *objPtr;\n" |
|
3236 "SelfRef objRef;\n" |
|
3237 "JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*${source}));\n" + |
|
3238 xpconnectUnwrap + |
|
3239 "if (NS_FAILED(rv)) {\n" |
|
3240 "${indentedCodeOnFailure}" |
|
3241 "}\n" |
|
3242 "// We should be castable!\n" |
|
3243 "MOZ_ASSERT(!objRef.ptr);\n" |
|
3244 "// We should have an object, too!\n" |
|
3245 "MOZ_ASSERT(objPtr);\n" |
|
3246 "${target} = objPtr;\n" |
|
3247 ).substitute(self.substitution, |
|
3248 indentedCodeOnFailure=indent(codeOnFailure)) |
|
3249 elif (isCallbackReturnValue == "JSImpl" and |
|
3250 descriptor.interface.isJSImplemented()): |
|
3251 exceptionCode = exceptionCode or codeOnFailure |
|
3252 self.substitution["codeOnFailure"] = fill( |
|
3253 """ |
|
3254 // Be careful to not wrap random DOM objects here, even if |
|
3255 // they're wrapped in opaque security wrappers for some reason. |
|
3256 // XXXbz Wish we could check for a JS-implemented object |
|
3257 // that already has a content reflection... |
|
3258 if (!IsDOMObject(js::UncheckedUnwrap(${source}))) { |
|
3259 nsCOMPtr<nsPIDOMWindow> ourWindow; |
|
3260 if (!GetWindowForJSImplementedObject(cx, Callback(), getter_AddRefs(ourWindow))) { |
|
3261 $*{exceptionCode} |
|
3262 } |
|
3263 JS::Rooted<JSObject*> jsImplSourceObj(cx, ${source}); |
|
3264 ${target} = new ${type}(jsImplSourceObj, ourWindow); |
|
3265 } else { |
|
3266 $*{codeOnFailure} |
|
3267 } |
|
3268 """, |
|
3269 exceptionCode=exceptionCode, |
|
3270 **self.substitution) |
|
3271 else: |
|
3272 self.substitution["codeOnFailure"] = codeOnFailure |
|
3273 |
|
3274 def __str__(self): |
|
3275 substitution = self.substitution.copy() |
|
3276 substitution["codeOnFailure"] %= { |
|
3277 'securityError': 'rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO' |
|
3278 } |
|
3279 return fill( |
|
3280 """ |
|
3281 { |
|
3282 $*{uncheckedObjDecl} |
|
3283 nsresult rv = UnwrapObject<${protoID}, ${type}>(${source}, ${target}); |
|
3284 if (NS_FAILED(rv)) { |
|
3285 $*{codeOnFailure} |
|
3286 } |
|
3287 } |
|
3288 """, |
|
3289 **substitution) |
|
3290 |
|
3291 |
|
3292 class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper): |
|
3293 """ |
|
3294 As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails |
|
3295 """ |
|
3296 def __init__(self, descriptor, source, target, exceptionCode, |
|
3297 isCallbackReturnValue, sourceDescription): |
|
3298 CastableObjectUnwrapper.__init__( |
|
3299 self, descriptor, source, target, |
|
3300 'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n' |
|
3301 '%s' % (sourceDescription, descriptor.interface.identifier.name, |
|
3302 exceptionCode), |
|
3303 exceptionCode, |
|
3304 isCallbackReturnValue) |
|
3305 |
|
3306 |
|
3307 class CGCallbackTempRoot(CGGeneric): |
|
3308 def __init__(self, name): |
|
3309 define = dedent(""" |
|
3310 { // Scope for tempRoot |
|
3311 JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject()); |
|
3312 ${declName} = new %s(tempRoot, mozilla::dom::GetIncumbentGlobal()); |
|
3313 } |
|
3314 """) % name |
|
3315 CGGeneric.__init__(self, define=define) |
|
3316 |
|
3317 |
|
3318 class JSToNativeConversionInfo(): |
|
3319 """ |
|
3320 An object representing information about a JS-to-native conversion. |
|
3321 """ |
|
3322 def __init__(self, template, declType=None, holderType=None, |
|
3323 dealWithOptional=False, declArgs=None, |
|
3324 holderArgs=None): |
|
3325 """ |
|
3326 template: A string representing the conversion code. This will have |
|
3327 template substitution performed on it as follows: |
|
3328 |
|
3329 ${val} is a handle to the JS::Value in question |
|
3330 ${mutableVal} is a mutable handle to the JS::Value in question |
|
3331 ${holderName} replaced by the holder's name, if any |
|
3332 ${declName} replaced by the declaration's name |
|
3333 ${haveValue} replaced by an expression that evaluates to a boolean |
|
3334 for whether we have a JS::Value. Only used when |
|
3335 defaultValue is not None or when True is passed for |
|
3336 checkForValue to instantiateJSToNativeConversion. |
|
3337 |
|
3338 declType: A CGThing representing the native C++ type we're converting |
|
3339 to. This is allowed to be None if the conversion code is |
|
3340 supposed to be used as-is. |
|
3341 |
|
3342 holderType: A CGThing representing the type of a "holder" which will |
|
3343 hold a possible reference to the C++ thing whose type we |
|
3344 returned in declType, or None if no such holder is needed. |
|
3345 |
|
3346 dealWithOptional: A boolean indicating whether the caller has to do |
|
3347 optional-argument handling. This should only be set |
|
3348 to true if the JS-to-native conversion is being done |
|
3349 for an optional argument or dictionary member with no |
|
3350 default value and if the returned template expects |
|
3351 both declType and holderType to be wrapped in |
|
3352 Optional<>, with ${declName} and ${holderName} |
|
3353 adjusted to point to the Value() of the Optional, and |
|
3354 Construct() calls to be made on the Optional<>s as |
|
3355 needed. |
|
3356 |
|
3357 declArgs: If not None, the arguments to pass to the ${declName} |
|
3358 constructor. These will have template substitution performed |
|
3359 on them so you can use things like ${val}. This is a |
|
3360 single string, not a list of strings. |
|
3361 |
|
3362 holderArgs: If not None, the arguments to pass to the ${holderName} |
|
3363 constructor. These will have template substitution |
|
3364 performed on them so you can use things like ${val}. |
|
3365 This is a single string, not a list of strings. |
|
3366 |
|
3367 ${declName} must be in scope before the code from 'template' is entered. |
|
3368 |
|
3369 If holderType is not None then ${holderName} must be in scope before |
|
3370 the code from 'template' is entered. |
|
3371 """ |
|
3372 assert isinstance(template, str) |
|
3373 assert declType is None or isinstance(declType, CGThing) |
|
3374 assert holderType is None or isinstance(holderType, CGThing) |
|
3375 self.template = template |
|
3376 self.declType = declType |
|
3377 self.holderType = holderType |
|
3378 self.dealWithOptional = dealWithOptional |
|
3379 self.declArgs = declArgs |
|
3380 self.holderArgs = holderArgs |
|
3381 |
|
3382 |
|
3383 def getHandleDefault(defaultValue): |
|
3384 tag = defaultValue.type.tag() |
|
3385 if tag in numericSuffixes: |
|
3386 # Some numeric literals require a suffix to compile without warnings |
|
3387 return numericValue(tag, defaultValue.value) |
|
3388 assert tag == IDLType.Tags.bool |
|
3389 return toStringBool(defaultValue.value) |
|
3390 |
|
3391 |
|
3392 def handleDefaultStringValue(defaultValue, method): |
|
3393 """ |
|
3394 Returns a string which ends up calling 'method' with a (char16_t*, length) |
|
3395 pair that sets this string default value. This string is suitable for |
|
3396 passing as the second argument of handleDefault; in particular it does not |
|
3397 end with a ';' |
|
3398 """ |
|
3399 assert defaultValue.type.isDOMString() |
|
3400 return ("static const char16_t data[] = { %s };\n" |
|
3401 "%s(data, ArrayLength(data) - 1)" % |
|
3402 (", ".join(["'" + char + "'" for char in |
|
3403 defaultValue.value] + ["0"]), |
|
3404 method)) |
|
3405 |
|
3406 |
|
3407 # If this function is modified, modify CGNativeMember.getArg and |
|
3408 # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype |
|
3409 # and holdertype we end up using, because it needs to be able to return the code |
|
3410 # that will convert those to the actual return value of the callback function. |
|
3411 def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, |
|
3412 isDefinitelyObject=False, |
|
3413 isMember=False, |
|
3414 isOptional=False, |
|
3415 invalidEnumValueFatal=True, |
|
3416 defaultValue=None, |
|
3417 treatNullAs="Default", |
|
3418 isEnforceRange=False, |
|
3419 isClamp=False, |
|
3420 isNullOrUndefined=False, |
|
3421 exceptionCode=None, |
|
3422 lenientFloatCode=None, |
|
3423 allowTreatNonCallableAsNull=False, |
|
3424 isCallbackReturnValue=False, |
|
3425 sourceDescription="value"): |
|
3426 """ |
|
3427 Get a template for converting a JS value to a native object based on the |
|
3428 given type and descriptor. If failureCode is given, then we're actually |
|
3429 testing whether we can convert the argument to the desired type. That |
|
3430 means that failures to convert due to the JS value being the wrong type of |
|
3431 value need to use failureCode instead of throwing exceptions. Failures to |
|
3432 convert that are due to JS exceptions (from toString or valueOf methods) or |
|
3433 out of memory conditions need to throw exceptions no matter what |
|
3434 failureCode is. However what actually happens when throwing an exception |
|
3435 can be controlled by exceptionCode. The only requirement on that is that |
|
3436 exceptionCode must end up doing a return, and every return from this |
|
3437 function must happen via exceptionCode if exceptionCode is not None. |
|
3438 |
|
3439 If isDefinitelyObject is True, that means we know the value |
|
3440 isObject() and we have no need to recheck that. |
|
3441 |
|
3442 if isMember is not False, we're being converted from a property of some JS |
|
3443 object, not from an actual method argument, so we can't rely on our jsval |
|
3444 being rooted or outliving us in any way. Callers can pass "Dictionary", |
|
3445 "Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is |
|
3446 for something that is a dictionary member, a variadic argument, a sequence, |
|
3447 or an owning union respectively. |
|
3448 |
|
3449 If isOptional is true, then we are doing conversion of an optional |
|
3450 argument with no default value. |
|
3451 |
|
3452 invalidEnumValueFatal controls whether an invalid enum value conversion |
|
3453 attempt will throw (if true) or simply return without doing anything (if |
|
3454 false). |
|
3455 |
|
3456 If defaultValue is not None, it's the IDL default value for this conversion |
|
3457 |
|
3458 If isEnforceRange is true, we're converting an integer and throwing if the |
|
3459 value is out of range. |
|
3460 |
|
3461 If isClamp is true, we're converting an integer and clamping if the |
|
3462 value is out of range. |
|
3463 |
|
3464 If lenientFloatCode is not None, it should be used in cases when |
|
3465 we're a non-finite float that's not unrestricted. |
|
3466 |
|
3467 If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and |
|
3468 [TreatNonObjectAsNull] extended attributes on nullable callback functions |
|
3469 will be honored. |
|
3470 |
|
3471 If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be |
|
3472 adjusted to make it easier to return from a callback. Since that type is |
|
3473 never directly observable by any consumers of the callback code, this is OK. |
|
3474 Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior |
|
3475 of the FailureFatalCastableObjectUnwrapper conversion; this is used for |
|
3476 implementing auto-wrapping of JS-implemented return values from a |
|
3477 JS-implemented interface. |
|
3478 |
|
3479 sourceDescription is a description of what this JS value represents, to be |
|
3480 used in error reporting. Callers should assume that it might get placed in |
|
3481 the middle of a sentence. If it ends up at the beginning of a sentence, its |
|
3482 first character will be automatically uppercased. |
|
3483 |
|
3484 The return value from this function is a JSToNativeConversionInfo. |
|
3485 """ |
|
3486 # If we have a defaultValue then we're not actually optional for |
|
3487 # purposes of what we need to be declared as. |
|
3488 assert defaultValue is None or not isOptional |
|
3489 |
|
3490 # Also, we should not have a defaultValue if we know we're an object |
|
3491 assert not isDefinitelyObject or defaultValue is None |
|
3492 |
|
3493 # And we can't both be an object and be null or undefined |
|
3494 assert not isDefinitelyObject or not isNullOrUndefined |
|
3495 |
|
3496 # If exceptionCode is not set, we'll just rethrow the exception we got. |
|
3497 # Note that we can't just set failureCode to exceptionCode, because setting |
|
3498 # failureCode will prevent pending exceptions from being set in cases when |
|
3499 # they really should be! |
|
3500 if exceptionCode is None: |
|
3501 exceptionCode = "return false;\n" |
|
3502 # We often want exceptionCode to be indented, since it often appears in an |
|
3503 # if body. |
|
3504 exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode)) |
|
3505 |
|
3506 # Unfortunately, .capitalize() on a string will lowercase things inside the |
|
3507 # string, which we do not want. |
|
3508 def firstCap(string): |
|
3509 return string[0].upper() + string[1:] |
|
3510 |
|
3511 # Helper functions for dealing with failures due to the JS value being the |
|
3512 # wrong type of value |
|
3513 def onFailureNotAnObject(failureCode): |
|
3514 return CGGeneric( |
|
3515 failureCode or |
|
3516 ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n' |
|
3517 '%s' % (firstCap(sourceDescription), exceptionCode))) |
|
3518 |
|
3519 def onFailureBadType(failureCode, typeName): |
|
3520 return CGGeneric( |
|
3521 failureCode or |
|
3522 ('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n' |
|
3523 '%s' % (firstCap(sourceDescription), typeName, exceptionCode))) |
|
3524 |
|
3525 def onFailureNotCallable(failureCode): |
|
3526 return CGGeneric( |
|
3527 failureCode or |
|
3528 ('ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "%s");\n' |
|
3529 '%s' % (firstCap(sourceDescription), exceptionCode))) |
|
3530 |
|
3531 # A helper function for handling default values. Takes a template |
|
3532 # body and the C++ code to set the default value and wraps the |
|
3533 # given template body in handling for the default value. |
|
3534 def handleDefault(template, setDefault): |
|
3535 if defaultValue is None: |
|
3536 return template |
|
3537 return ( |
|
3538 "if (${haveValue}) {\n" + |
|
3539 indent(template) + |
|
3540 "} else {\n" + |
|
3541 indent(setDefault) + |
|
3542 "}\n") |
|
3543 |
|
3544 # A helper function for handling null default values. Much like |
|
3545 # handleDefault, but checks that the default value, if it exists, is null. |
|
3546 def handleDefaultNull(template, codeToSetNull): |
|
3547 if (defaultValue is not None and |
|
3548 not isinstance(defaultValue, IDLNullValue)): |
|
3549 raise TypeError("Can't handle non-null default value here") |
|
3550 return handleDefault(template, codeToSetNull) |
|
3551 |
|
3552 # A helper function for wrapping up the template body for |
|
3553 # possibly-nullable objecty stuff |
|
3554 def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None): |
|
3555 if isNullOrUndefined and type.nullable(): |
|
3556 # Just ignore templateBody and set ourselves to null. |
|
3557 # Note that we don't have to worry about default values |
|
3558 # here either, since we already examined this value. |
|
3559 return codeToSetNull |
|
3560 |
|
3561 if not isDefinitelyObject: |
|
3562 # Handle the non-object cases by wrapping up the whole |
|
3563 # thing in an if cascade. |
|
3564 if type.nullable(): |
|
3565 elifLine = "} else if (${val}.isNullOrUndefined()) {\n" |
|
3566 elifBody = codeToSetNull |
|
3567 else: |
|
3568 elifLine = "" |
|
3569 elifBody = "" |
|
3570 |
|
3571 # Note that $${val} below expands to ${val}. This string is |
|
3572 # used as a template later, and val will be filled in then. |
|
3573 templateBody = fill( |
|
3574 """ |
|
3575 if ($${val}.isObject()) { |
|
3576 $*{templateBody} |
|
3577 $*{elifLine} |
|
3578 $*{elifBody} |
|
3579 } else { |
|
3580 $*{failureBody} |
|
3581 } |
|
3582 """, |
|
3583 templateBody=templateBody, |
|
3584 elifLine=elifLine, |
|
3585 elifBody=elifBody, |
|
3586 failureBody=onFailureNotAnObject(failureCode).define()) |
|
3587 |
|
3588 if type.nullable(): |
|
3589 templateBody = handleDefaultNull(templateBody, codeToSetNull) |
|
3590 else: |
|
3591 assert defaultValue is None |
|
3592 |
|
3593 return templateBody |
|
3594 |
|
3595 # A helper function for converting things that look like a JSObject*. |
|
3596 def handleJSObjectType(type, isMember, failureCode): |
|
3597 if not isMember: |
|
3598 if isOptional: |
|
3599 # We have a specialization of Optional that will use a |
|
3600 # Rooted for the storage here. |
|
3601 declType = CGGeneric("JS::Handle<JSObject*>") |
|
3602 else: |
|
3603 declType = CGGeneric("JS::Rooted<JSObject*>") |
|
3604 declArgs = "cx" |
|
3605 else: |
|
3606 assert (isMember in |
|
3607 ("Sequence", "Variadic", "Dictionary", "OwningUnion", "MozMap")) |
|
3608 # We'll get traced by the sequence or dictionary or union tracer |
|
3609 declType = CGGeneric("JSObject*") |
|
3610 declArgs = None |
|
3611 templateBody = "${declName} = &${val}.toObject();\n" |
|
3612 setToNullCode = "${declName} = nullptr;\n" |
|
3613 template = wrapObjectTemplate(templateBody, type, setToNullCode, |
|
3614 failureCode) |
|
3615 return JSToNativeConversionInfo(template, declType=declType, |
|
3616 dealWithOptional=isOptional, |
|
3617 declArgs=declArgs) |
|
3618 |
|
3619 assert not (isEnforceRange and isClamp) # These are mutually exclusive |
|
3620 |
|
3621 if type.isArray(): |
|
3622 raise TypeError("Can't handle array arguments yet") |
|
3623 |
|
3624 if type.isSequence(): |
|
3625 assert not isEnforceRange and not isClamp |
|
3626 |
|
3627 if failureCode is None: |
|
3628 notSequence = ('ThrowErrorMessage(cx, MSG_NOT_SEQUENCE, "%s");\n' |
|
3629 "%s" % (firstCap(sourceDescription), exceptionCode)) |
|
3630 else: |
|
3631 notSequence = failureCode |
|
3632 |
|
3633 nullable = type.nullable() |
|
3634 # Be very careful not to change "type": we need it later |
|
3635 if nullable: |
|
3636 elementType = type.inner.inner |
|
3637 else: |
|
3638 elementType = type.inner |
|
3639 |
|
3640 # We want to use auto arrays if we can, but we have to be careful with |
|
3641 # reallocation behavior for arrays. In particular, if we use auto |
|
3642 # arrays for sequences and have a sequence of elements which are |
|
3643 # themselves sequences or have sequences as members, we have a problem. |
|
3644 # In that case, resizing the outermost nsAutoTarray to the right size |
|
3645 # will memmove its elements, but nsAutoTArrays are not memmovable and |
|
3646 # hence will end up with pointers to bogus memory, which is bad. To |
|
3647 # deal with this, we typically map WebIDL sequences to our Sequence |
|
3648 # type, which is in fact memmovable. The one exception is when we're |
|
3649 # passing in a sequence directly as an argument without any sort of |
|
3650 # optional or nullable complexity going on. In that situation, we can |
|
3651 # use an AutoSequence instead. We have to keep using Sequence in the |
|
3652 # nullable and optional cases because we don't want to leak the |
|
3653 # AutoSequence type to consumers, which would be unavoidable with |
|
3654 # Nullable<AutoSequence> or Optional<AutoSequence>. |
|
3655 if isMember or isOptional or nullable or isCallbackReturnValue: |
|
3656 sequenceClass = "Sequence" |
|
3657 else: |
|
3658 sequenceClass = "binding_detail::AutoSequence" |
|
3659 |
|
3660 # XXXbz we can't include the index in the sourceDescription, because |
|
3661 # we don't really have a way to pass one in dynamically at runtime... |
|
3662 elementInfo = getJSToNativeConversionInfo( |
|
3663 elementType, descriptorProvider, isMember="Sequence", |
|
3664 exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode, |
|
3665 isCallbackReturnValue=isCallbackReturnValue, |
|
3666 sourceDescription="element of %s" % sourceDescription) |
|
3667 if elementInfo.dealWithOptional: |
|
3668 raise TypeError("Shouldn't have optional things in sequences") |
|
3669 if elementInfo.holderType is not None: |
|
3670 raise TypeError("Shouldn't need holders for sequences") |
|
3671 |
|
3672 typeName = CGTemplatedType(sequenceClass, elementInfo.declType) |
|
3673 sequenceType = typeName.define() |
|
3674 if nullable: |
|
3675 typeName = CGTemplatedType("Nullable", typeName) |
|
3676 arrayRef = "${declName}.SetValue()" |
|
3677 else: |
|
3678 arrayRef = "${declName}" |
|
3679 |
|
3680 elementConversion = string.Template(elementInfo.template).substitute({ |
|
3681 "val": "temp", |
|
3682 "mutableVal": "&temp", |
|
3683 "declName": "slot", |
|
3684 # We only need holderName here to handle isExternal() |
|
3685 # interfaces, which use an internal holder for the |
|
3686 # conversion even when forceOwningType ends up true. |
|
3687 "holderName": "tempHolder" |
|
3688 }) |
|
3689 |
|
3690 # NOTE: Keep this in sync with variadic conversions as needed |
|
3691 templateBody = fill( |
|
3692 """ |
|
3693 JS::ForOfIterator iter(cx); |
|
3694 if (!iter.init($${val}, JS::ForOfIterator::AllowNonIterable)) { |
|
3695 $*{exceptionCode} |
|
3696 } |
|
3697 if (!iter.valueIsIterable()) { |
|
3698 $*{notSequence} |
|
3699 } |
|
3700 ${sequenceType} &arr = ${arrayRef}; |
|
3701 JS::Rooted<JS::Value> temp(cx); |
|
3702 while (true) { |
|
3703 bool done; |
|
3704 if (!iter.next(&temp, &done)) { |
|
3705 $*{exceptionCode} |
|
3706 } |
|
3707 if (done) { |
|
3708 break; |
|
3709 } |
|
3710 ${elementType}* slotPtr = arr.AppendElement(); |
|
3711 if (!slotPtr) { |
|
3712 JS_ReportOutOfMemory(cx); |
|
3713 $*{exceptionCode} |
|
3714 } |
|
3715 ${elementType}& slot = *slotPtr; |
|
3716 $*{elementConversion} |
|
3717 } |
|
3718 """, |
|
3719 exceptionCode=exceptionCode, |
|
3720 notSequence=notSequence, |
|
3721 sequenceType=sequenceType, |
|
3722 arrayRef=arrayRef, |
|
3723 elementType=elementInfo.declType.define(), |
|
3724 elementConversion=elementConversion) |
|
3725 |
|
3726 templateBody = wrapObjectTemplate(templateBody, type, |
|
3727 "${declName}.SetNull();\n", notSequence) |
|
3728 # Sequence arguments that might contain traceable things need |
|
3729 # to get traced |
|
3730 if not isMember and typeNeedsRooting(elementType): |
|
3731 holderType = CGTemplatedType("SequenceRooter", elementInfo.declType) |
|
3732 # If our sequence is nullable, this will set the Nullable to be |
|
3733 # not-null, but that's ok because we make an explicit SetNull() call |
|
3734 # on it as needed if our JS value is actually null. |
|
3735 holderArgs = "cx, &%s" % arrayRef |
|
3736 else: |
|
3737 holderType = None |
|
3738 holderArgs = None |
|
3739 |
|
3740 return JSToNativeConversionInfo(templateBody, declType=typeName, |
|
3741 holderType=holderType, |
|
3742 dealWithOptional=isOptional, |
|
3743 holderArgs=holderArgs) |
|
3744 |
|
3745 if type.isMozMap(): |
|
3746 assert not isEnforceRange and not isClamp |
|
3747 if failureCode is None: |
|
3748 notMozMap = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n' |
|
3749 "%s" % (firstCap(sourceDescription), exceptionCode)) |
|
3750 else: |
|
3751 notMozMap = failureCode |
|
3752 |
|
3753 nullable = type.nullable() |
|
3754 # Be very careful not to change "type": we need it later |
|
3755 if nullable: |
|
3756 valueType = type.inner.inner |
|
3757 else: |
|
3758 valueType = type.inner |
|
3759 |
|
3760 valueInfo = getJSToNativeConversionInfo( |
|
3761 valueType, descriptorProvider, isMember="MozMap", |
|
3762 exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode, |
|
3763 isCallbackReturnValue=isCallbackReturnValue, |
|
3764 sourceDescription="value in %s" % sourceDescription) |
|
3765 if valueInfo.dealWithOptional: |
|
3766 raise TypeError("Shouldn't have optional things in MozMap") |
|
3767 if valueInfo.holderType is not None: |
|
3768 raise TypeError("Shouldn't need holders for MozMap") |
|
3769 |
|
3770 typeName = CGTemplatedType("MozMap", valueInfo.declType) |
|
3771 mozMapType = typeName.define() |
|
3772 if nullable: |
|
3773 typeName = CGTemplatedType("Nullable", typeName) |
|
3774 mozMapRef = "${declName}.SetValue()" |
|
3775 else: |
|
3776 mozMapRef = "${declName}" |
|
3777 |
|
3778 valueConversion = string.Template(valueInfo.template).substitute({ |
|
3779 "val": "temp", |
|
3780 "mutableVal": "&temp", |
|
3781 "declName": "slot", |
|
3782 # We only need holderName here to handle isExternal() |
|
3783 # interfaces, which use an internal holder for the |
|
3784 # conversion even when forceOwningType ends up true. |
|
3785 "holderName": "tempHolder" |
|
3786 }) |
|
3787 |
|
3788 templateBody = fill( |
|
3789 """ |
|
3790 ${mozMapType} &mozMap = ${mozMapRef}; |
|
3791 |
|
3792 JS::Rooted<JSObject*> mozMapObj(cx, &$${val}.toObject()); |
|
3793 JS::AutoIdArray ids(cx, JS_Enumerate(cx, mozMapObj)); |
|
3794 if (!ids) { |
|
3795 $*{exceptionCode} |
|
3796 } |
|
3797 JS::Rooted<JS::Value> propNameValue(cx); |
|
3798 JS::Rooted<JS::Value> temp(cx); |
|
3799 JS::Rooted<jsid> curId(cx); |
|
3800 for (size_t i = 0; i < ids.length(); ++i) { |
|
3801 // Make sure we get the value before converting the name, since |
|
3802 // getting the value can trigger GC but our name is a dependent |
|
3803 // string. |
|
3804 curId = ids[i]; |
|
3805 binding_detail::FakeDependentString propName; |
|
3806 if (!JS_GetPropertyById(cx, mozMapObj, curId, &temp) || |
|
3807 !JS_IdToValue(cx, curId, &propNameValue) || |
|
3808 !ConvertJSValueToString(cx, propNameValue, &propNameValue, |
|
3809 eStringify, eStringify, propName)) { |
|
3810 $*{exceptionCode} |
|
3811 } |
|
3812 |
|
3813 ${valueType}* slotPtr = mozMap.AddEntry(propName); |
|
3814 if (!slotPtr) { |
|
3815 JS_ReportOutOfMemory(cx); |
|
3816 $*{exceptionCode} |
|
3817 } |
|
3818 ${valueType}& slot = *slotPtr; |
|
3819 $*{valueConversion} |
|
3820 } |
|
3821 """, |
|
3822 exceptionCode=exceptionCode, |
|
3823 mozMapType=mozMapType, |
|
3824 mozMapRef=mozMapRef, |
|
3825 valueType=valueInfo.declType.define(), |
|
3826 valueConversion=valueConversion) |
|
3827 |
|
3828 templateBody = wrapObjectTemplate(templateBody, type, |
|
3829 "${declName}.SetNull();\n", |
|
3830 notMozMap) |
|
3831 |
|
3832 declType = typeName |
|
3833 declArgs = None |
|
3834 holderType = None |
|
3835 holderArgs = None |
|
3836 # MozMap arguments that might contain traceable things need |
|
3837 # to get traced |
|
3838 if not isMember and isCallbackReturnValue: |
|
3839 # Go ahead and just convert directly into our actual return value |
|
3840 declType = CGWrapper(declType, post="&") |
|
3841 declArgs = "aRetVal" |
|
3842 elif not isMember and typeNeedsRooting(valueType): |
|
3843 holderType = CGTemplatedType("MozMapRooter", valueInfo.declType) |
|
3844 # If our MozMap is nullable, this will set the Nullable to be |
|
3845 # not-null, but that's ok because we make an explicit SetNull() call |
|
3846 # on it as needed if our JS value is actually null. |
|
3847 holderArgs = "cx, &%s" % mozMapRef |
|
3848 |
|
3849 return JSToNativeConversionInfo(templateBody, declType=declType, |
|
3850 declArgs=declArgs, |
|
3851 holderType=holderType, |
|
3852 dealWithOptional=isOptional, |
|
3853 holderArgs=holderArgs) |
|
3854 |
|
3855 if type.isUnion(): |
|
3856 nullable = type.nullable() |
|
3857 if nullable: |
|
3858 type = type.inner |
|
3859 |
|
3860 unionArgumentObj = "${declName}" if isMember else "${holderName}" |
|
3861 if nullable: |
|
3862 # If we're a member, we're a Nullable, which hasn't been told it has |
|
3863 # a value. Otherwise we're an already-constructed Maybe. |
|
3864 unionArgumentObj += ".SetValue()" if isMember else ".ref()" |
|
3865 |
|
3866 memberTypes = type.flatMemberTypes |
|
3867 names = [] |
|
3868 |
|
3869 interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes) |
|
3870 if len(interfaceMemberTypes) > 0: |
|
3871 interfaceObject = [] |
|
3872 for memberType in interfaceMemberTypes: |
|
3873 if type.isGeckoInterface(): |
|
3874 name = memberType.inner.identifier.name |
|
3875 else: |
|
3876 name = memberType.name |
|
3877 interfaceObject.append( |
|
3878 CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext" % |
|
3879 (unionArgumentObj, name))) |
|
3880 names.append(name) |
|
3881 interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), |
|
3882 pre="done = ", post=";\n\n", reindent=True) |
|
3883 else: |
|
3884 interfaceObject = None |
|
3885 |
|
3886 arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes) |
|
3887 if len(arrayObjectMemberTypes) > 0: |
|
3888 raise TypeError("Bug 767924: We don't support sequences in unions yet") |
|
3889 else: |
|
3890 arrayObject = None |
|
3891 |
|
3892 dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes) |
|
3893 if len(dateObjectMemberTypes) > 0: |
|
3894 assert len(dateObjectMemberTypes) == 1 |
|
3895 memberType = dateObjectMemberTypes[0] |
|
3896 name = memberType.name |
|
3897 dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${mutableVal});\n" |
|
3898 "done = true;\n" % (unionArgumentObj, name)) |
|
3899 dateObject = CGIfWrapper(dateObject, "JS_ObjectIsDate(cx, argObj)") |
|
3900 names.append(name) |
|
3901 else: |
|
3902 dateObject = None |
|
3903 |
|
3904 callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes) |
|
3905 if len(callbackMemberTypes) > 0: |
|
3906 assert len(callbackMemberTypes) == 1 |
|
3907 memberType = callbackMemberTypes[0] |
|
3908 name = memberType.name |
|
3909 callbackObject = CGGeneric( |
|
3910 "done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" % |
|
3911 (unionArgumentObj, name)) |
|
3912 names.append(name) |
|
3913 else: |
|
3914 callbackObject = None |
|
3915 |
|
3916 dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes) |
|
3917 if len(dictionaryMemberTypes) > 0: |
|
3918 assert len(dictionaryMemberTypes) == 1 |
|
3919 name = dictionaryMemberTypes[0].inner.identifier.name |
|
3920 setDictionary = CGGeneric( |
|
3921 "done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" % |
|
3922 (unionArgumentObj, name)) |
|
3923 names.append(name) |
|
3924 else: |
|
3925 setDictionary = None |
|
3926 |
|
3927 mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes) |
|
3928 if len(mozMapMemberTypes) > 0: |
|
3929 raise TypeError("We don't support MozMap in unions yet") |
|
3930 |
|
3931 objectMemberTypes = filter(lambda t: t.isObject(), memberTypes) |
|
3932 if len(objectMemberTypes) > 0: |
|
3933 assert len(objectMemberTypes) == 1 |
|
3934 # Very important to NOT construct a temporary Rooted here, since the |
|
3935 # SetToObject call can call a Rooted constructor and we need to keep |
|
3936 # stack discipline for Rooted. |
|
3937 object = CGGeneric("%s.SetToObject(cx, &${val}.toObject());\n" |
|
3938 "done = true;\n" % unionArgumentObj) |
|
3939 names.append(objectMemberTypes[0].name) |
|
3940 else: |
|
3941 object = None |
|
3942 |
|
3943 hasObjectTypes = interfaceObject or arrayObject or dateObject or callbackObject or object |
|
3944 if hasObjectTypes: |
|
3945 # "object" is not distinguishable from other types |
|
3946 assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject) |
|
3947 if arrayObject or dateObject or callbackObject: |
|
3948 # An object can be both an array object and a callback or |
|
3949 # dictionary, but we shouldn't have both in the union's members |
|
3950 # because they are not distinguishable. |
|
3951 assert not (arrayObject and callbackObject) |
|
3952 templateBody = CGElseChain([arrayObject, dateObject, callbackObject]) |
|
3953 else: |
|
3954 templateBody = None |
|
3955 if interfaceObject: |
|
3956 assert not object |
|
3957 if templateBody: |
|
3958 templateBody = CGIfWrapper(templateBody, "!done") |
|
3959 templateBody = CGList([interfaceObject, templateBody]) |
|
3960 else: |
|
3961 templateBody = CGList([templateBody, object]) |
|
3962 |
|
3963 if dateObject: |
|
3964 templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());\n")) |
|
3965 templateBody = CGIfWrapper(templateBody, "${val}.isObject()") |
|
3966 else: |
|
3967 templateBody = CGGeneric() |
|
3968 |
|
3969 if setDictionary: |
|
3970 assert not object |
|
3971 templateBody = CGList([templateBody, |
|
3972 CGIfWrapper(setDictionary, "!done")]) |
|
3973 |
|
3974 stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()] |
|
3975 numericTypes = [t for t in memberTypes if t.isNumeric()] |
|
3976 booleanTypes = [t for t in memberTypes if t.isBoolean()] |
|
3977 if stringTypes or numericTypes or booleanTypes: |
|
3978 assert len(stringTypes) <= 1 |
|
3979 assert len(numericTypes) <= 1 |
|
3980 assert len(booleanTypes) <= 1 |
|
3981 |
|
3982 # We will wrap all this stuff in a do { } while (0); so we |
|
3983 # can use "break" for flow control. |
|
3984 def getStringOrPrimitiveConversion(memberType): |
|
3985 if memberType.isEnum(): |
|
3986 name = memberType.inner.identifier.name |
|
3987 else: |
|
3988 name = memberType.name |
|
3989 return CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" |
|
3990 "break;\n" % (unionArgumentObj, name)) |
|
3991 other = CGList([]) |
|
3992 stringConversion = map(getStringOrPrimitiveConversion, stringTypes) |
|
3993 numericConversion = map(getStringOrPrimitiveConversion, numericTypes) |
|
3994 booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes) |
|
3995 if stringConversion: |
|
3996 if booleanConversion: |
|
3997 other.append(CGIfWrapper(booleanConversion[0], |
|
3998 "${val}.isBoolean()")) |
|
3999 if numericConversion: |
|
4000 other.append(CGIfWrapper(numericConversion[0], |
|
4001 "${val}.isNumber()")) |
|
4002 other.append(stringConversion[0]) |
|
4003 elif numericConversion: |
|
4004 if booleanConversion: |
|
4005 other.append(CGIfWrapper(booleanConversion[0], |
|
4006 "${val}.isBoolean()")) |
|
4007 other.append(numericConversion[0]) |
|
4008 else: |
|
4009 assert booleanConversion |
|
4010 other.append(booleanConversion[0]) |
|
4011 |
|
4012 other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (0);\n") |
|
4013 if hasObjectTypes or setDictionary: |
|
4014 other = CGWrapper(CGIndenter(other), "{\n", post="}\n") |
|
4015 if object: |
|
4016 templateBody = CGElseChain([templateBody, other]) |
|
4017 else: |
|
4018 other = CGWrapper(other, pre="if (!done) ") |
|
4019 templateBody = CGList([templateBody, other]) |
|
4020 else: |
|
4021 assert templateBody.define() == "" |
|
4022 templateBody = other |
|
4023 else: |
|
4024 other = None |
|
4025 |
|
4026 templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n") |
|
4027 throw = CGGeneric(fill( |
|
4028 """ |
|
4029 if (failed) { |
|
4030 $*{exceptionCode} |
|
4031 } |
|
4032 if (!done) { |
|
4033 ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "${desc}", "${names}"); |
|
4034 $*{exceptionCode} |
|
4035 } |
|
4036 """, |
|
4037 exceptionCode=exceptionCode, |
|
4038 desc=firstCap(sourceDescription), |
|
4039 names=", ".join(names))) |
|
4040 |
|
4041 templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw])), pre="{\n", post="}\n") |
|
4042 |
|
4043 typeName = CGUnionStruct.unionTypeDecl(type, isMember) |
|
4044 argumentTypeName = typeName + "Argument" |
|
4045 if nullable: |
|
4046 typeName = "Nullable<" + typeName + " >" |
|
4047 |
|
4048 def handleNull(templateBody, setToNullVar, extraConditionForNull=""): |
|
4049 nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull |
|
4050 return CGIfElseWrapper(nullTest, |
|
4051 CGGeneric("%s.SetNull();\n" % setToNullVar), |
|
4052 templateBody) |
|
4053 |
|
4054 if type.hasNullableType: |
|
4055 assert not nullable |
|
4056 # Make sure to handle a null default value here |
|
4057 if defaultValue and isinstance(defaultValue, IDLNullValue): |
|
4058 assert defaultValue.type == type |
|
4059 extraConditionForNull = "!(${haveValue}) || " |
|
4060 else: |
|
4061 extraConditionForNull = "" |
|
4062 templateBody = handleNull(templateBody, unionArgumentObj, |
|
4063 extraConditionForNull=extraConditionForNull) |
|
4064 |
|
4065 declType = CGGeneric(typeName) |
|
4066 if isMember: |
|
4067 holderType = None |
|
4068 else: |
|
4069 holderType = CGGeneric(argumentTypeName) |
|
4070 if nullable: |
|
4071 holderType = CGTemplatedType("Maybe", holderType) |
|
4072 |
|
4073 # If we're isOptional and not nullable the normal optional handling will |
|
4074 # handle lazy construction of our holder. If we're nullable and not |
|
4075 # isMember we do it all by hand because we do not want our holder |
|
4076 # constructed if we're null. But if we're isMember we don't have a |
|
4077 # holder anyway, so we can do the normal Optional codepath. |
|
4078 declLoc = "${declName}" |
|
4079 constructDecl = None |
|
4080 if nullable: |
|
4081 if isOptional and not isMember: |
|
4082 holderArgs = "${declName}.Value().SetValue()" |
|
4083 declType = CGTemplatedType("Optional", declType) |
|
4084 constructDecl = CGGeneric("${declName}.Construct();\n") |
|
4085 declLoc = "${declName}.Value()" |
|
4086 else: |
|
4087 holderArgs = "${declName}.SetValue()" |
|
4088 if holderType is not None: |
|
4089 constructHolder = CGGeneric("${holderName}.construct(%s);\n" % holderArgs) |
|
4090 else: |
|
4091 constructHolder = None |
|
4092 # Don't need to pass those args when the holder is being constructed |
|
4093 holderArgs = None |
|
4094 else: |
|
4095 holderArgs = "${declName}" |
|
4096 constructHolder = None |
|
4097 |
|
4098 if defaultValue and not isinstance(defaultValue, IDLNullValue): |
|
4099 tag = defaultValue.type.tag() |
|
4100 |
|
4101 if tag in numericSuffixes or tag is IDLType.Tags.bool: |
|
4102 defaultStr = getHandleDefault(defaultValue) |
|
4103 value = declLoc + (".Value()" if nullable else "") |
|
4104 default = CGGeneric("%s.RawSetAs%s() = %s;\n" % |
|
4105 (value, defaultValue.type, defaultStr)) |
|
4106 else: |
|
4107 default = CGGeneric( |
|
4108 handleDefaultStringValue( |
|
4109 defaultValue, "%s.SetStringData" % unionArgumentObj) + |
|
4110 ";\n") |
|
4111 |
|
4112 templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody) |
|
4113 |
|
4114 templateBody = CGList([constructHolder, templateBody]) |
|
4115 |
|
4116 if nullable: |
|
4117 if defaultValue: |
|
4118 if isinstance(defaultValue, IDLNullValue): |
|
4119 extraConditionForNull = "!(${haveValue}) || " |
|
4120 else: |
|
4121 extraConditionForNull = "${haveValue} && " |
|
4122 else: |
|
4123 extraConditionForNull = "" |
|
4124 templateBody = handleNull(templateBody, declLoc, |
|
4125 extraConditionForNull=extraConditionForNull) |
|
4126 elif (not type.hasNullableType and defaultValue and |
|
4127 isinstance(defaultValue, IDLNullValue)): |
|
4128 assert type.hasDictionaryType |
|
4129 assert defaultValue.type.isDictionary() |
|
4130 if not isMember and typeNeedsRooting(defaultValue.type): |
|
4131 ctorArgs = "cx" |
|
4132 else: |
|
4133 ctorArgs = "" |
|
4134 initDictionaryWithNull = CGIfWrapper( |
|
4135 CGGeneric("return false;\n"), |
|
4136 ('!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")' |
|
4137 % (declLoc, getUnionMemberName(defaultValue.type), |
|
4138 ctorArgs, type))) |
|
4139 templateBody = CGIfElseWrapper("!(${haveValue})", |
|
4140 initDictionaryWithNull, |
|
4141 templateBody) |
|
4142 |
|
4143 templateBody = CGList([constructDecl, templateBody]) |
|
4144 |
|
4145 return JSToNativeConversionInfo(templateBody.define(), |
|
4146 declType=declType, |
|
4147 holderType=holderType, |
|
4148 holderArgs=holderArgs, |
|
4149 dealWithOptional=isOptional and (not nullable or isMember)) |
|
4150 |
|
4151 if type.isGeckoInterface(): |
|
4152 assert not isEnforceRange and not isClamp |
|
4153 |
|
4154 descriptor = descriptorProvider.getDescriptor( |
|
4155 type.unroll().inner.identifier.name) |
|
4156 |
|
4157 if descriptor.nativeType == 'JSObject': |
|
4158 # XXXbz Workers code does this sometimes |
|
4159 assert descriptor.workers |
|
4160 return handleJSObjectType(type, isMember, failureCode) |
|
4161 |
|
4162 if descriptor.interface.isCallback(): |
|
4163 name = descriptor.interface.identifier.name |
|
4164 if type.nullable() or isCallbackReturnValue: |
|
4165 declType = CGGeneric("nsRefPtr<%s>" % name) |
|
4166 else: |
|
4167 declType = CGGeneric("OwningNonNull<%s>" % name) |
|
4168 # BOGUS extra blank line here turns out to be at the end of a block: |
|
4169 conversion = indent(CGCallbackTempRoot(name).define()) + "\n" |
|
4170 |
|
4171 template = wrapObjectTemplate(conversion, type, |
|
4172 "${declName} = nullptr;\n", |
|
4173 failureCode) |
|
4174 return JSToNativeConversionInfo(template, declType=declType, |
|
4175 dealWithOptional=isOptional) |
|
4176 |
|
4177 # This is an interface that we implement as a concrete class |
|
4178 # or an XPCOM interface. |
|
4179 |
|
4180 # Allow null pointers for nullable types and old-binding classes, and |
|
4181 # use an nsRefPtr or raw pointer for callback return values to make |
|
4182 # them easier to return. |
|
4183 argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or |
|
4184 isCallbackReturnValue) |
|
4185 |
|
4186 # Sequences and non-worker callbacks have to hold a strong ref to the |
|
4187 # thing being passed down. Union return values must hold a strong ref |
|
4188 # because they may be returning an addrefed pointer. |
|
4189 # Also, callback return values always end up |
|
4190 # addrefing anyway, so there is no point trying to avoid it here and it |
|
4191 # makes other things simpler since we can assume the return value is a |
|
4192 # strong ref. |
|
4193 forceOwningType = ((descriptor.interface.isCallback() and |
|
4194 not descriptor.workers) or |
|
4195 isMember or |
|
4196 isCallbackReturnValue) |
|
4197 |
|
4198 if forceOwningType and descriptor.nativeOwnership == 'owned': |
|
4199 raise TypeError("Interface %s has 'owned' nativeOwnership, so we " |
|
4200 "don't know how to keep it alive in %s" % |
|
4201 (descriptor.interface.identifier.name, |
|
4202 sourceDescription)) |
|
4203 |
|
4204 typeName = descriptor.nativeType |
|
4205 typePtr = typeName + "*" |
|
4206 |
|
4207 # Compute a few things: |
|
4208 # - declType is the type we want to return as the first element of our |
|
4209 # tuple. |
|
4210 # - holderType is the type we want to return as the third element |
|
4211 # of our tuple. |
|
4212 |
|
4213 # Set up some sensible defaults for these things insofar as we can. |
|
4214 holderType = None |
|
4215 if argIsPointer: |
|
4216 if forceOwningType: |
|
4217 declType = "nsRefPtr<" + typeName + ">" |
|
4218 else: |
|
4219 declType = typePtr |
|
4220 else: |
|
4221 if forceOwningType: |
|
4222 declType = "OwningNonNull<" + typeName + ">" |
|
4223 else: |
|
4224 declType = "NonNull<" + typeName + ">" |
|
4225 |
|
4226 templateBody = "" |
|
4227 if not descriptor.skipGen and not descriptor.interface.isConsequential() and not descriptor.interface.isExternal(): |
|
4228 if failureCode is not None: |
|
4229 templateBody += str(CastableObjectUnwrapper( |
|
4230 descriptor, |
|
4231 "&${val}.toObject()", |
|
4232 "${declName}", |
|
4233 failureCode)) |
|
4234 else: |
|
4235 templateBody += str(FailureFatalCastableObjectUnwrapper( |
|
4236 descriptor, |
|
4237 "&${val}.toObject()", |
|
4238 "${declName}", |
|
4239 exceptionCode, |
|
4240 isCallbackReturnValue, |
|
4241 firstCap(sourceDescription))) |
|
4242 elif descriptor.workers: |
|
4243 return handleJSObjectType(type, isMember, failureCode) |
|
4244 else: |
|
4245 # Either external, or new-binding non-castable. We always have a |
|
4246 # holder for these, because we don't actually know whether we have |
|
4247 # to addref when unwrapping or not. So we just pass an |
|
4248 # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release |
|
4249 # it'll put a non-null pointer in there. |
|
4250 if forceOwningType: |
|
4251 # Don't return a holderType in this case; our declName |
|
4252 # will just own stuff. |
|
4253 templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n" |
|
4254 else: |
|
4255 holderType = "nsRefPtr<" + typeName + ">" |
|
4256 templateBody += ( |
|
4257 "JS::Rooted<JS::Value> tmpVal(cx, ${val});\n" + |
|
4258 typePtr + " tmp;\n" |
|
4259 "if (NS_FAILED(UnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n") |
|
4260 templateBody += CGIndenter(onFailureBadType(failureCode, |
|
4261 descriptor.interface.identifier.name)).define() |
|
4262 templateBody += ("}\n" |
|
4263 "MOZ_ASSERT(tmp);\n") |
|
4264 |
|
4265 if not isDefinitelyObject and not forceOwningType: |
|
4266 # Our tmpVal will go out of scope, so we can't rely on it |
|
4267 # for rooting |
|
4268 templateBody += dedent(""" |
|
4269 if (tmpVal != ${val} && !${holderName}) { |
|
4270 // We have to have a strong ref, because we got this off |
|
4271 // some random object that might get GCed |
|
4272 ${holderName} = tmp; |
|
4273 } |
|
4274 """) |
|
4275 |
|
4276 # And store our tmp, before it goes out of scope. |
|
4277 templateBody += "${declName} = tmp;\n" |
|
4278 |
|
4279 # Just pass failureCode, not onFailureBadType, here, so we'll report the |
|
4280 # thing as not an object as opposed to not implementing whatever our |
|
4281 # interface is. |
|
4282 templateBody = wrapObjectTemplate(templateBody, type, |
|
4283 "${declName} = nullptr;\n", failureCode) |
|
4284 |
|
4285 declType = CGGeneric(declType) |
|
4286 if holderType is not None: |
|
4287 holderType = CGGeneric(holderType) |
|
4288 return JSToNativeConversionInfo(templateBody, |
|
4289 declType=declType, |
|
4290 holderType=holderType, |
|
4291 dealWithOptional=isOptional) |
|
4292 |
|
4293 if type.isSpiderMonkeyInterface(): |
|
4294 assert not isEnforceRange and not isClamp |
|
4295 name = type.name |
|
4296 arrayType = CGGeneric(name) |
|
4297 declType = arrayType |
|
4298 if type.nullable(): |
|
4299 declType = CGTemplatedType("Nullable", declType) |
|
4300 objRef = "${declName}.SetValue()" |
|
4301 else: |
|
4302 objRef = "${declName}" |
|
4303 |
|
4304 # Again, this is a bit strange since we are actually building a |
|
4305 # template string here. ${objRef} and $*{badType} below are filled in |
|
4306 # right now; $${val} expands to ${val}, to be filled in later. |
|
4307 template = fill( |
|
4308 """ |
|
4309 if (!${objRef}.Init(&$${val}.toObject())) { |
|
4310 $*{badType} |
|
4311 } |
|
4312 |
|
4313 """, # BOGUS extra blank line |
|
4314 objRef=objRef, |
|
4315 badType=onFailureBadType(failureCode, type.name).define()) |
|
4316 template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n", |
|
4317 failureCode) |
|
4318 if not isMember: |
|
4319 # This is a bit annoying. In a union we don't want to have a |
|
4320 # holder, since unions don't support that. But if we're optional we |
|
4321 # want to have a holder, so that the callee doesn't see |
|
4322 # Optional<RootedTypedArray<ArrayType> >. So do a holder if we're |
|
4323 # optional and use a RootedTypedArray otherwise. |
|
4324 if isOptional: |
|
4325 holderType = CGTemplatedType("TypedArrayRooter", arrayType) |
|
4326 # If our typed array is nullable, this will set the Nullable to |
|
4327 # be not-null, but that's ok because we make an explicit |
|
4328 # SetNull() call on it as needed if our JS value is actually |
|
4329 # null. XXXbz Because "Maybe" takes const refs for constructor |
|
4330 # arguments, we can't pass a reference here; have to pass a |
|
4331 # pointer. |
|
4332 holderArgs = "cx, &%s" % objRef |
|
4333 declArgs = None |
|
4334 else: |
|
4335 holderType = None |
|
4336 holderArgs = None |
|
4337 declType = CGTemplatedType("RootedTypedArray", declType) |
|
4338 declArgs = "cx" |
|
4339 else: |
|
4340 holderType = None |
|
4341 holderArgs = None |
|
4342 declArgs = None |
|
4343 return JSToNativeConversionInfo(template, |
|
4344 declType=declType, |
|
4345 holderType=holderType, |
|
4346 dealWithOptional=isOptional, |
|
4347 declArgs=declArgs, |
|
4348 holderArgs=holderArgs) |
|
4349 |
|
4350 if type.isDOMString(): |
|
4351 assert not isEnforceRange and not isClamp |
|
4352 |
|
4353 treatAs = { |
|
4354 "Default": "eStringify", |
|
4355 "EmptyString": "eEmpty", |
|
4356 "Null": "eNull", |
|
4357 } |
|
4358 if type.nullable(): |
|
4359 # For nullable strings null becomes a null string. |
|
4360 treatNullAs = "Null" |
|
4361 # For nullable strings undefined also becomes a null string. |
|
4362 undefinedBehavior = "eNull" |
|
4363 else: |
|
4364 undefinedBehavior = "eStringify" |
|
4365 nullBehavior = treatAs[treatNullAs] |
|
4366 |
|
4367 def getConversionCode(varName): |
|
4368 conversionCode = ( |
|
4369 "if (!ConvertJSValueToString(cx, ${val}, ${mutableVal}, %s, %s, %s)) {\n" |
|
4370 "%s" |
|
4371 "}\n" % (nullBehavior, undefinedBehavior, varName, |
|
4372 exceptionCodeIndented.define())) |
|
4373 if defaultValue is None: |
|
4374 return conversionCode |
|
4375 |
|
4376 if isinstance(defaultValue, IDLNullValue): |
|
4377 assert(type.nullable()) |
|
4378 defaultCode = "%s.SetNull()" % varName |
|
4379 else: |
|
4380 defaultCode = handleDefaultStringValue(defaultValue, |
|
4381 "%s.SetData" % varName) |
|
4382 return handleDefault(conversionCode, defaultCode + ";\n") |
|
4383 |
|
4384 if isMember: |
|
4385 # We have to make a copy, except in the variadic case, because our |
|
4386 # jsval may well not live as long as our string needs to. |
|
4387 declType = CGGeneric("nsString") |
|
4388 if isMember == "Variadic": |
|
4389 # The string is kept alive by the argument, so we can just |
|
4390 # depend on it. |
|
4391 assignString = "${declName}.Rebind(str.Data(), str.Length());\n" |
|
4392 else: |
|
4393 assignString = "${declName} = str;\n" |
|
4394 return JSToNativeConversionInfo( |
|
4395 fill( |
|
4396 """ |
|
4397 { |
|
4398 binding_detail::FakeDependentString str; |
|
4399 $*{convert} |
|
4400 $*{assign} |
|
4401 } |
|
4402 |
|
4403 """, # BOGUS extra newline |
|
4404 convert=getConversionCode("str"), |
|
4405 assign=assignString), |
|
4406 declType=declType, |
|
4407 dealWithOptional=isOptional) |
|
4408 |
|
4409 if isOptional: |
|
4410 declType = "Optional<nsAString>" |
|
4411 holderType = CGGeneric("binding_detail::FakeDependentString") |
|
4412 conversionCode = ("%s" |
|
4413 "${declName} = &${holderName};\n" % |
|
4414 getConversionCode("${holderName}")) |
|
4415 else: |
|
4416 declType = "binding_detail::FakeDependentString" |
|
4417 holderType = None |
|
4418 conversionCode = getConversionCode("${declName}") |
|
4419 |
|
4420 # No need to deal with optional here; we handled it already |
|
4421 return JSToNativeConversionInfo( |
|
4422 conversionCode, |
|
4423 declType=CGGeneric(declType), |
|
4424 holderType=holderType) |
|
4425 |
|
4426 if type.isByteString(): |
|
4427 assert not isEnforceRange and not isClamp |
|
4428 |
|
4429 nullable = toStringBool(type.nullable()) |
|
4430 |
|
4431 conversionCode = ( |
|
4432 "if (!ConvertJSValueToByteString(cx, ${val}, ${mutableVal}, %s, ${declName})) {\n" |
|
4433 "%s" |
|
4434 "}\n" % (nullable, exceptionCodeIndented.define())) |
|
4435 # ByteString arguments cannot have a default value. |
|
4436 assert defaultValue is None |
|
4437 |
|
4438 return JSToNativeConversionInfo( |
|
4439 conversionCode, |
|
4440 declType=CGGeneric("nsCString"), |
|
4441 dealWithOptional=isOptional) |
|
4442 |
|
4443 if type.isEnum(): |
|
4444 assert not isEnforceRange and not isClamp |
|
4445 |
|
4446 enumName = type.unroll().inner.identifier.name |
|
4447 declType = CGGeneric(enumName) |
|
4448 if type.nullable(): |
|
4449 declType = CGTemplatedType("Nullable", declType) |
|
4450 declType = declType.define() |
|
4451 enumLoc = "${declName}.SetValue()" |
|
4452 else: |
|
4453 enumLoc = "${declName}" |
|
4454 declType = declType.define() |
|
4455 |
|
4456 if invalidEnumValueFatal: |
|
4457 handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n" |
|
4458 else: |
|
4459 # invalidEnumValueFatal is false only for attributes. So we won't |
|
4460 # have a non-default exceptionCode here unless attribute "arg |
|
4461 # conversion" code starts passing in an exceptionCode. At which |
|
4462 # point we'll need to figure out what that even means. |
|
4463 assert exceptionCode == "return false;\n" |
|
4464 handleInvalidEnumValueCode = dedent(""" |
|
4465 if (index < 0) { |
|
4466 return true; |
|
4467 } |
|
4468 """) |
|
4469 |
|
4470 template = fill( |
|
4471 """ |
|
4472 { |
|
4473 bool ok; |
|
4474 int index = FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &ok); |
|
4475 if (!ok) { |
|
4476 $*{exceptionCode} |
|
4477 } |
|
4478 $*{handleInvalidEnumValueCode} |
|
4479 ${enumLoc} = static_cast<${enumtype}>(index); |
|
4480 } |
|
4481 """, |
|
4482 enumtype=enumName, |
|
4483 values=enumName + "Values::" + ENUM_ENTRY_VARIABLE_NAME, |
|
4484 invalidEnumValueFatal=toStringBool(invalidEnumValueFatal), |
|
4485 handleInvalidEnumValueCode=handleInvalidEnumValueCode, |
|
4486 exceptionCode=exceptionCode, |
|
4487 enumLoc=enumLoc, |
|
4488 sourceDescription=firstCap(sourceDescription)) |
|
4489 |
|
4490 setNull = "${declName}.SetNull();\n" |
|
4491 |
|
4492 if type.nullable(): |
|
4493 template = CGIfElseWrapper("${val}.isNullOrUndefined()", |
|
4494 CGGeneric(setNull), |
|
4495 CGGeneric(template)).define() |
|
4496 |
|
4497 if defaultValue is not None: |
|
4498 if isinstance(defaultValue, IDLNullValue): |
|
4499 assert type.nullable() |
|
4500 template = handleDefault(template, setNull) |
|
4501 else: |
|
4502 assert(defaultValue.type.tag() == IDLType.Tags.domstring) |
|
4503 template = handleDefault(template, |
|
4504 ("%s = %s::%s;\n" % |
|
4505 (enumLoc, enumName, |
|
4506 getEnumValueName(defaultValue.value)))) |
|
4507 return JSToNativeConversionInfo(template, declType=CGGeneric(declType), |
|
4508 dealWithOptional=isOptional) |
|
4509 |
|
4510 if type.isCallback(): |
|
4511 assert not isEnforceRange and not isClamp |
|
4512 assert not type.treatNonCallableAsNull() or type.nullable() |
|
4513 assert not type.treatNonObjectAsNull() or type.nullable() |
|
4514 assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull() |
|
4515 |
|
4516 name = type.unroll().identifier.name |
|
4517 if type.nullable(): |
|
4518 declType = CGGeneric("nsRefPtr<%s>" % name) |
|
4519 else: |
|
4520 declType = CGGeneric("OwningNonNull<%s>" % name) |
|
4521 conversion = indent(CGCallbackTempRoot(name).define()) |
|
4522 |
|
4523 if allowTreatNonCallableAsNull and type.treatNonCallableAsNull(): |
|
4524 haveCallable = "JS_ObjectIsCallable(cx, &${val}.toObject())" |
|
4525 if not isDefinitelyObject: |
|
4526 haveCallable = "${val}.isObject() && " + haveCallable |
|
4527 if defaultValue is not None: |
|
4528 assert(isinstance(defaultValue, IDLNullValue)) |
|
4529 haveCallable = "${haveValue} && " + haveCallable |
|
4530 template = ( |
|
4531 ("if (%s) {\n" % haveCallable) + |
|
4532 conversion + |
|
4533 "} else {\n" |
|
4534 " ${declName} = nullptr;\n" |
|
4535 "}\n") |
|
4536 elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull(): |
|
4537 if not isDefinitelyObject: |
|
4538 haveObject = "${val}.isObject()" |
|
4539 if defaultValue is not None: |
|
4540 assert(isinstance(defaultValue, IDLNullValue)) |
|
4541 haveObject = "${haveValue} && " + haveObject |
|
4542 template = CGIfElseWrapper(haveObject, |
|
4543 CGGeneric(conversion + "\n"), # BOGUS extra blank line |
|
4544 CGGeneric("${declName} = nullptr;\n")).define() |
|
4545 else: |
|
4546 template = conversion |
|
4547 else: |
|
4548 template = wrapObjectTemplate( |
|
4549 "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" + |
|
4550 conversion + |
|
4551 "} else {\n" + |
|
4552 indent(onFailureNotCallable(failureCode).define()) + |
|
4553 "}\n", |
|
4554 type, |
|
4555 "${declName} = nullptr;\n", |
|
4556 failureCode) |
|
4557 return JSToNativeConversionInfo(template, declType=declType, |
|
4558 dealWithOptional=isOptional) |
|
4559 |
|
4560 if type.isAny(): |
|
4561 assert not isEnforceRange and not isClamp |
|
4562 |
|
4563 declArgs = None |
|
4564 if isMember in ("Variadic", "Sequence", "Dictionary", "MozMap"): |
|
4565 # Rooting is handled by the sequence and dictionary tracers. |
|
4566 declType = "JS::Value" |
|
4567 else: |
|
4568 assert not isMember |
|
4569 declType = "JS::Rooted<JS::Value>" |
|
4570 declArgs = "cx" |
|
4571 |
|
4572 assert not isOptional |
|
4573 templateBody = "${declName} = ${val};\n" |
|
4574 # We may not have a default value if we're being converted for |
|
4575 # a setter, say. |
|
4576 if defaultValue: |
|
4577 if isinstance(defaultValue, IDLNullValue): |
|
4578 defaultHandling = "${declName} = JS::NullValue();\n" |
|
4579 else: |
|
4580 assert isinstance(defaultValue, IDLUndefinedValue) |
|
4581 defaultHandling = "${declName} = JS::UndefinedValue();\n" |
|
4582 templateBody = handleDefault(templateBody, defaultHandling) |
|
4583 return JSToNativeConversionInfo(templateBody, |
|
4584 declType=CGGeneric(declType), |
|
4585 declArgs=declArgs) |
|
4586 |
|
4587 if type.isObject(): |
|
4588 assert not isEnforceRange and not isClamp |
|
4589 return handleJSObjectType(type, isMember, failureCode) |
|
4590 |
|
4591 if type.isDictionary(): |
|
4592 # There are no nullable dictionaries |
|
4593 assert not type.nullable() or isCallbackReturnValue |
|
4594 # All optional dictionaries always have default values, so we |
|
4595 # should be able to assume not isOptional here. |
|
4596 assert not isOptional |
|
4597 # In the callback return value case we never have to worry |
|
4598 # about a default value; we always have a value. |
|
4599 assert not isCallbackReturnValue or defaultValue is None |
|
4600 |
|
4601 typeName = CGDictionary.makeDictionaryName(type.unroll().inner) |
|
4602 if not isMember and not isCallbackReturnValue: |
|
4603 # Since we're not a member and not nullable or optional, no one will |
|
4604 # see our real type, so we can do the fast version of the dictionary |
|
4605 # that doesn't pre-initialize members. |
|
4606 typeName = "binding_detail::Fast" + typeName |
|
4607 |
|
4608 declType = CGGeneric(typeName) |
|
4609 |
|
4610 # We do manual default value handling here, because we |
|
4611 # actually do want a jsval, and we only handle null anyway |
|
4612 # NOTE: if isNullOrUndefined or isDefinitelyObject are true, |
|
4613 # we know we have a value, so we don't have to worry about the |
|
4614 # default value. |
|
4615 if (not isNullOrUndefined and not isDefinitelyObject and |
|
4616 defaultValue is not None): |
|
4617 assert(isinstance(defaultValue, IDLNullValue)) |
|
4618 val = "(${haveValue}) ? ${val} : JS::NullHandleValue" |
|
4619 else: |
|
4620 val = "${val}" |
|
4621 |
|
4622 if failureCode is not None: |
|
4623 if isDefinitelyObject: |
|
4624 dictionaryTest = "IsObjectValueConvertibleToDictionary" |
|
4625 else: |
|
4626 dictionaryTest = "IsConvertibleToDictionary" |
|
4627 # Check that the value we have can in fact be converted to |
|
4628 # a dictionary, and return failureCode if not. |
|
4629 template = CGIfWrapper( |
|
4630 CGGeneric(failureCode), |
|
4631 "!%s(cx, ${val})" % dictionaryTest).define() + "\n" |
|
4632 else: |
|
4633 template = "" |
|
4634 |
|
4635 dictLoc = "${declName}" |
|
4636 if type.nullable(): |
|
4637 dictLoc += ".SetValue()" |
|
4638 |
|
4639 template += ('if (!%s.Init(cx, %s, "%s")) {\n' |
|
4640 "%s" |
|
4641 "}\n" % (dictLoc, val, firstCap(sourceDescription), |
|
4642 exceptionCodeIndented.define())) |
|
4643 |
|
4644 if type.nullable(): |
|
4645 declType = CGTemplatedType("Nullable", declType) |
|
4646 template = CGIfElseWrapper("${val}.isNullOrUndefined()", |
|
4647 CGGeneric("${declName}.SetNull();\n"), |
|
4648 CGGeneric(template)).define() |
|
4649 |
|
4650 # Dictionary arguments that might contain traceable things need to get |
|
4651 # traced |
|
4652 if not isMember and isCallbackReturnValue: |
|
4653 # Go ahead and just convert directly into our actual return value |
|
4654 declType = CGWrapper(declType, post="&") |
|
4655 declArgs = "aRetVal" |
|
4656 elif not isMember and typeNeedsRooting(type): |
|
4657 declType = CGTemplatedType("RootedDictionary", declType) |
|
4658 declArgs = "cx" |
|
4659 else: |
|
4660 declArgs = None |
|
4661 |
|
4662 return JSToNativeConversionInfo(template, declType=declType, |
|
4663 declArgs=declArgs) |
|
4664 |
|
4665 if type.isVoid(): |
|
4666 assert not isOptional |
|
4667 # This one only happens for return values, and its easy: Just |
|
4668 # ignore the jsval. |
|
4669 return JSToNativeConversionInfo("") |
|
4670 |
|
4671 if type.isDate(): |
|
4672 assert not isEnforceRange and not isClamp |
|
4673 |
|
4674 declType = CGGeneric("Date") |
|
4675 if type.nullable(): |
|
4676 declType = CGTemplatedType("Nullable", declType) |
|
4677 dateVal = "${declName}.SetValue()" |
|
4678 else: |
|
4679 dateVal = "${declName}" |
|
4680 |
|
4681 if failureCode is None: |
|
4682 notDate = ('ThrowErrorMessage(cx, MSG_NOT_DATE, "%s");\n' |
|
4683 "%s" % (firstCap(sourceDescription), exceptionCode)) |
|
4684 else: |
|
4685 notDate = failureCode |
|
4686 |
|
4687 conversion = fill( |
|
4688 """ |
|
4689 JS::Rooted<JSObject*> possibleDateObject(cx, &$${val}.toObject()); |
|
4690 if (!JS_ObjectIsDate(cx, possibleDateObject) || |
|
4691 !${dateVal}.SetTimeStamp(cx, possibleDateObject)) { |
|
4692 $*{notDate} |
|
4693 } |
|
4694 """, |
|
4695 dateVal=dateVal, |
|
4696 notDate=notDate) |
|
4697 |
|
4698 conversion = wrapObjectTemplate(conversion, type, |
|
4699 "${declName}.SetNull();\n", notDate) |
|
4700 return JSToNativeConversionInfo(conversion, |
|
4701 declType=declType, |
|
4702 dealWithOptional=isOptional) |
|
4703 |
|
4704 if not type.isPrimitive(): |
|
4705 raise TypeError("Need conversion for argument type '%s'" % str(type)) |
|
4706 |
|
4707 typeName = builtinNames[type.tag()] |
|
4708 |
|
4709 conversionBehavior = "eDefault" |
|
4710 if isEnforceRange: |
|
4711 assert type.isInteger() |
|
4712 conversionBehavior = "eEnforceRange" |
|
4713 elif isClamp: |
|
4714 assert type.isInteger() |
|
4715 conversionBehavior = "eClamp" |
|
4716 |
|
4717 if type.nullable(): |
|
4718 declType = CGGeneric("Nullable<" + typeName + ">") |
|
4719 writeLoc = "${declName}.SetValue()" |
|
4720 readLoc = "${declName}.Value()" |
|
4721 nullCondition = "${val}.isNullOrUndefined()" |
|
4722 if defaultValue is not None and isinstance(defaultValue, IDLNullValue): |
|
4723 nullCondition = "!(${haveValue}) || " + nullCondition |
|
4724 template = ( |
|
4725 "if (%s) {\n" |
|
4726 " ${declName}.SetNull();\n" |
|
4727 "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n" |
|
4728 "%s" |
|
4729 "}\n" % (nullCondition, typeName, conversionBehavior, |
|
4730 writeLoc, exceptionCodeIndented.define())) |
|
4731 else: |
|
4732 assert(defaultValue is None or |
|
4733 not isinstance(defaultValue, IDLNullValue)) |
|
4734 writeLoc = "${declName}" |
|
4735 readLoc = writeLoc |
|
4736 template = ( |
|
4737 "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n" |
|
4738 "%s" |
|
4739 "}\n" % (typeName, conversionBehavior, writeLoc, |
|
4740 exceptionCodeIndented.define())) |
|
4741 declType = CGGeneric(typeName) |
|
4742 |
|
4743 if type.isFloat() and not type.isUnrestricted(): |
|
4744 if lenientFloatCode is not None: |
|
4745 nonFiniteCode = lenientFloatCode |
|
4746 else: |
|
4747 nonFiniteCode = ('ThrowErrorMessage(cx, MSG_NOT_FINITE, "%s");\n' |
|
4748 "%s" % (firstCap(sourceDescription), exceptionCode)) |
|
4749 template = template.rstrip() |
|
4750 template += fill( |
|
4751 """ |
|
4752 else if (!mozilla::IsFinite(${readLoc})) { |
|
4753 // Note: mozilla::IsFinite will do the right thing |
|
4754 // when passed a non-finite float too. |
|
4755 $*{nonFiniteCode} |
|
4756 } |
|
4757 """, |
|
4758 readLoc=readLoc, |
|
4759 nonFiniteCode=nonFiniteCode) |
|
4760 |
|
4761 if (defaultValue is not None and |
|
4762 # We already handled IDLNullValue, so just deal with the other ones |
|
4763 not isinstance(defaultValue, IDLNullValue)): |
|
4764 tag = defaultValue.type.tag() |
|
4765 defaultStr = getHandleDefault(defaultValue) |
|
4766 template = CGIfElseWrapper( |
|
4767 "${haveValue}", |
|
4768 CGGeneric(template), |
|
4769 CGGeneric("%s = %s;\n" % (writeLoc, defaultStr))).define() |
|
4770 |
|
4771 return JSToNativeConversionInfo(template, declType=declType, |
|
4772 dealWithOptional=isOptional) |
|
4773 |
|
4774 |
|
4775 def instantiateJSToNativeConversion(info, replacements, checkForValue=False): |
|
4776 """ |
|
4777 Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo |
|
4778 and a set of replacements as required by the strings in such an object, and |
|
4779 generate code to convert into stack C++ types. |
|
4780 |
|
4781 If checkForValue is True, then the conversion will get wrapped in |
|
4782 a check for ${haveValue}. |
|
4783 """ |
|
4784 templateBody, declType, holderType, dealWithOptional = ( |
|
4785 info.template, info.declType, info.holderType, info.dealWithOptional) |
|
4786 |
|
4787 if dealWithOptional and not checkForValue: |
|
4788 raise TypeError("Have to deal with optional things, but don't know how") |
|
4789 if checkForValue and declType is None: |
|
4790 raise TypeError("Need to predeclare optional things, so they will be " |
|
4791 "outside the check for big enough arg count!") |
|
4792 |
|
4793 # We can't precompute our holder constructor arguments, since |
|
4794 # those might depend on ${declName}, which we change below. Just |
|
4795 # compute arguments at the point when we need them as we go. |
|
4796 def getArgsCGThing(args): |
|
4797 return CGGeneric(string.Template(args).substitute(replacements)) |
|
4798 |
|
4799 result = CGList([]) |
|
4800 # Make a copy of "replacements" since we may be about to start modifying it |
|
4801 replacements = dict(replacements) |
|
4802 originalDeclName = replacements["declName"] |
|
4803 if declType is not None: |
|
4804 if dealWithOptional: |
|
4805 replacements["declName"] = "%s.Value()" % originalDeclName |
|
4806 declType = CGTemplatedType("Optional", declType) |
|
4807 declCtorArgs = None |
|
4808 elif info.declArgs is not None: |
|
4809 declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs), |
|
4810 pre="(", post=")") |
|
4811 else: |
|
4812 declCtorArgs = None |
|
4813 result.append( |
|
4814 CGList([declType, CGGeneric(" "), |
|
4815 CGGeneric(originalDeclName), |
|
4816 declCtorArgs, CGGeneric(";\n")])) |
|
4817 |
|
4818 originalHolderName = replacements["holderName"] |
|
4819 if holderType is not None: |
|
4820 if dealWithOptional: |
|
4821 replacements["holderName"] = "%s.ref()" % originalHolderName |
|
4822 holderType = CGTemplatedType("Maybe", holderType) |
|
4823 holderCtorArgs = None |
|
4824 elif info.holderArgs is not None: |
|
4825 holderCtorArgs = CGWrapper(getArgsCGThing(info.holderArgs), |
|
4826 pre="(", post=")") |
|
4827 else: |
|
4828 holderCtorArgs = None |
|
4829 result.append( |
|
4830 CGList([holderType, CGGeneric(" "), |
|
4831 CGGeneric(originalHolderName), |
|
4832 holderCtorArgs, CGGeneric(";\n")])) |
|
4833 |
|
4834 conversion = CGGeneric( |
|
4835 string.Template(templateBody).substitute(replacements)) |
|
4836 |
|
4837 if checkForValue: |
|
4838 if dealWithOptional: |
|
4839 declConstruct = CGIndenter( |
|
4840 CGGeneric("%s.Construct(%s);\n" % |
|
4841 (originalDeclName, |
|
4842 getArgsCGThing(info.declArgs).define() if |
|
4843 info.declArgs else ""))) |
|
4844 if holderType is not None: |
|
4845 holderConstruct = CGIndenter( |
|
4846 CGGeneric("%s.construct(%s);\n" % |
|
4847 (originalHolderName, |
|
4848 getArgsCGThing(info.holderArgs).define() if |
|
4849 info.holderArgs else ""))) |
|
4850 else: |
|
4851 holderConstruct = None |
|
4852 else: |
|
4853 declConstruct = None |
|
4854 holderConstruct = None |
|
4855 |
|
4856 conversion = CGList([ |
|
4857 CGGeneric( |
|
4858 string.Template("if (${haveValue}) {\n").substitute(replacements)), |
|
4859 declConstruct, |
|
4860 holderConstruct, |
|
4861 CGIndenter(conversion), |
|
4862 CGGeneric("}\n") |
|
4863 ]) |
|
4864 |
|
4865 result.append(conversion) |
|
4866 return result |
|
4867 |
|
4868 |
|
4869 def convertConstIDLValueToJSVal(value): |
|
4870 if isinstance(value, IDLNullValue): |
|
4871 return "JS::NullValue()" |
|
4872 if isinstance(value, IDLUndefinedValue): |
|
4873 return "JS::UndefinedValue()" |
|
4874 tag = value.type.tag() |
|
4875 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, |
|
4876 IDLType.Tags.uint16, IDLType.Tags.int32]: |
|
4877 return "INT_TO_JSVAL(%s)" % (value.value) |
|
4878 if tag == IDLType.Tags.uint32: |
|
4879 return "UINT_TO_JSVAL(%sU)" % (value.value) |
|
4880 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]: |
|
4881 return "DOUBLE_TO_JSVAL(%s)" % numericValue(tag, value.value) |
|
4882 if tag == IDLType.Tags.bool: |
|
4883 return "JSVAL_TRUE" if value.value else "JSVAL_FALSE" |
|
4884 if tag in [IDLType.Tags.float, IDLType.Tags.double]: |
|
4885 return "DOUBLE_TO_JSVAL(%s)" % (value.value) |
|
4886 raise TypeError("Const value of unhandled type: %s" % value.type) |
|
4887 |
|
4888 |
|
4889 class CGArgumentConverter(CGThing): |
|
4890 """ |
|
4891 A class that takes an IDL argument object and its index in the |
|
4892 argument list and generates code to unwrap the argument to the |
|
4893 right native type. |
|
4894 |
|
4895 argDescription is a description of the argument for error-reporting |
|
4896 purposes. Callers should assume that it might get placed in the middle of a |
|
4897 sentence. If it ends up at the beginning of a sentence, its first character |
|
4898 will be automatically uppercased. |
|
4899 """ |
|
4900 def __init__(self, argument, index, descriptorProvider, |
|
4901 argDescription, |
|
4902 invalidEnumValueFatal=True, lenientFloatCode=None): |
|
4903 CGThing.__init__(self) |
|
4904 self.argument = argument |
|
4905 self.argDescription = argDescription |
|
4906 assert(not argument.defaultValue or argument.optional) |
|
4907 |
|
4908 replacer = { |
|
4909 "index": index, |
|
4910 "argc": "args.length()" |
|
4911 } |
|
4912 self.replacementVariables = { |
|
4913 "declName": "arg%d" % index, |
|
4914 "holderName": ("arg%d" % index) + "_holder", |
|
4915 "obj": "obj" |
|
4916 } |
|
4917 self.replacementVariables["val"] = string.Template( |
|
4918 "args[${index}]").substitute(replacer) |
|
4919 self.replacementVariables["mutableVal"] = self.replacementVariables["val"] |
|
4920 haveValueCheck = string.Template( |
|
4921 "args.hasDefined(${index})").substitute(replacer) |
|
4922 self.replacementVariables["haveValue"] = haveValueCheck |
|
4923 self.descriptorProvider = descriptorProvider |
|
4924 if self.argument.optional and not self.argument.defaultValue: |
|
4925 self.argcAndIndex = replacer |
|
4926 else: |
|
4927 self.argcAndIndex = None |
|
4928 self.invalidEnumValueFatal = invalidEnumValueFatal |
|
4929 self.lenientFloatCode = lenientFloatCode |
|
4930 |
|
4931 def define(self): |
|
4932 typeConversion = getJSToNativeConversionInfo( |
|
4933 self.argument.type, |
|
4934 self.descriptorProvider, |
|
4935 isOptional=(self.argcAndIndex is not None and |
|
4936 not self.argument.variadic), |
|
4937 invalidEnumValueFatal=self.invalidEnumValueFatal, |
|
4938 defaultValue=self.argument.defaultValue, |
|
4939 treatNullAs=self.argument.treatNullAs, |
|
4940 isEnforceRange=self.argument.enforceRange, |
|
4941 isClamp=self.argument.clamp, |
|
4942 lenientFloatCode=self.lenientFloatCode, |
|
4943 isMember="Variadic" if self.argument.variadic else False, |
|
4944 allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(), |
|
4945 sourceDescription=self.argDescription) |
|
4946 |
|
4947 if not self.argument.variadic: |
|
4948 return instantiateJSToNativeConversion( |
|
4949 typeConversion, |
|
4950 self.replacementVariables, |
|
4951 self.argcAndIndex is not None).define() |
|
4952 |
|
4953 # Variadic arguments get turned into a sequence. |
|
4954 if typeConversion.dealWithOptional: |
|
4955 raise TypeError("Shouldn't have optional things in variadics") |
|
4956 if typeConversion.holderType is not None: |
|
4957 raise TypeError("Shouldn't need holders for variadics") |
|
4958 |
|
4959 replacer = dict(self.argcAndIndex, **self.replacementVariables) |
|
4960 replacer["seqType"] = CGTemplatedType("binding_detail::AutoSequence", |
|
4961 typeConversion.declType).define() |
|
4962 if typeNeedsRooting(self.argument.type): |
|
4963 rooterDecl = ("SequenceRooter<%s> ${holderName}(cx, &${declName});\n" % |
|
4964 typeConversion.declType.define()) |
|
4965 else: |
|
4966 rooterDecl = "" |
|
4967 replacer["elemType"] = typeConversion.declType.define() |
|
4968 |
|
4969 # NOTE: Keep this in sync with sequence conversions as needed |
|
4970 variadicConversion = string.Template( |
|
4971 "${seqType} ${declName};\n" + |
|
4972 rooterDecl + |
|
4973 dedent(""" |
|
4974 if (${argc} > ${index}) { |
|
4975 if (!${declName}.SetCapacity(${argc} - ${index})) { |
|
4976 JS_ReportOutOfMemory(cx); |
|
4977 return false; |
|
4978 } |
|
4979 for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) { |
|
4980 ${elemType}& slot = *${declName}.AppendElement(); |
|
4981 """) |
|
4982 ).substitute(replacer) |
|
4983 |
|
4984 val = string.Template("args[variadicArg]").substitute(replacer) |
|
4985 variadicConversion += indent( |
|
4986 string.Template(typeConversion.template).substitute({ |
|
4987 "val": val, |
|
4988 "mutableVal": val, |
|
4989 "declName": "slot", |
|
4990 # We only need holderName here to handle isExternal() |
|
4991 # interfaces, which use an internal holder for the |
|
4992 # conversion even when forceOwningType ends up true. |
|
4993 "holderName": "tempHolder", |
|
4994 # Use the same ${obj} as for the variadic arg itself |
|
4995 "obj": replacer["obj"] |
|
4996 }), 4) |
|
4997 |
|
4998 variadicConversion += (" }\n" |
|
4999 "}\n") |
|
5000 return variadicConversion |
|
5001 |
|
5002 |
|
5003 def getMaybeWrapValueFuncForType(type): |
|
5004 # Callbacks might actually be DOM objects; nothing prevents a page from |
|
5005 # doing that. |
|
5006 if type.isCallback() or type.isCallbackInterface() or type.isObject(): |
|
5007 if type.nullable(): |
|
5008 return "MaybeWrapObjectOrNullValue" |
|
5009 return "MaybeWrapObjectValue" |
|
5010 # Spidermonkey interfaces are never DOM objects. Neither are sequences or |
|
5011 # dictionaries, since those are always plain JS objects. |
|
5012 if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence(): |
|
5013 if type.nullable(): |
|
5014 return "MaybeWrapNonDOMObjectOrNullValue" |
|
5015 return "MaybeWrapNonDOMObjectValue" |
|
5016 if type.isAny(): |
|
5017 return "MaybeWrapValue" |
|
5018 |
|
5019 # For other types, just go ahead an fall back on MaybeWrapValue for now: |
|
5020 # it's always safe to do, and shouldn't be particularly slow for any of |
|
5021 # them |
|
5022 return "MaybeWrapValue" |
|
5023 |
|
5024 |
|
5025 sequenceWrapLevel = 0 |
|
5026 mozMapWrapLevel = 0 |
|
5027 |
|
5028 |
|
5029 def getWrapTemplateForType(type, descriptorProvider, result, successCode, |
|
5030 returnsNewObject, exceptionCode, typedArraysAreStructs): |
|
5031 """ |
|
5032 Reflect a C++ value stored in "result", of IDL type "type" into JS. The |
|
5033 "successCode" is the code to run once we have successfully done the |
|
5034 conversion and must guarantee that execution of the conversion template |
|
5035 stops once the successCode has executed (e.g. by doing a 'return', or by |
|
5036 doing a 'break' if the entire conversion template is inside a block that |
|
5037 the 'break' will exit). |
|
5038 |
|
5039 If typedArraysAreStructs is true, then if the type is a typed array, |
|
5040 "result" is one of the dom::TypedArray subclasses, not a JSObject*. |
|
5041 |
|
5042 The resulting string should be used with string.Template. It |
|
5043 needs the following keys when substituting: |
|
5044 |
|
5045 jsvalHandle: something that can be passed to methods taking a |
|
5046 JS::MutableHandle<JS::Value>. This can be a |
|
5047 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*. |
|
5048 jsvalRef: something that can have .address() called on it to get a |
|
5049 JS::Value* and .set() called on it to set it to a JS::Value. |
|
5050 This can be a JS::MutableHandle<JS::Value> or a |
|
5051 JS::Rooted<JS::Value>. |
|
5052 obj: a JS::Handle<JSObject*>. |
|
5053 |
|
5054 Returns (templateString, infallibility of conversion template) |
|
5055 """ |
|
5056 if successCode is None: |
|
5057 successCode = "return true;\n" |
|
5058 |
|
5059 def setUndefined(): |
|
5060 return _setValue("", setter="setUndefined") |
|
5061 |
|
5062 def setNull(): |
|
5063 return _setValue("", setter="setNull") |
|
5064 |
|
5065 def setInt32(value): |
|
5066 return _setValue(value, setter="setInt32") |
|
5067 |
|
5068 def setString(value): |
|
5069 return _setValue(value, setter="setString") |
|
5070 |
|
5071 def setObject(value, wrapAsType=None): |
|
5072 return _setValue(value, wrapAsType=wrapAsType, setter="setObject") |
|
5073 |
|
5074 def setObjectOrNull(value, wrapAsType=None): |
|
5075 return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull") |
|
5076 |
|
5077 def setUint32(value): |
|
5078 return _setValue(value, setter="setNumber") |
|
5079 |
|
5080 def setDouble(value): |
|
5081 return _setValue("JS_NumberValue(%s)" % value) |
|
5082 |
|
5083 def setBoolean(value): |
|
5084 return _setValue(value, setter="setBoolean") |
|
5085 |
|
5086 def _setValue(value, wrapAsType=None, setter="set"): |
|
5087 """ |
|
5088 Returns the code to set the jsval to value. |
|
5089 |
|
5090 If wrapAsType is not None, then will wrap the resulting value using the |
|
5091 function that getMaybeWrapValueFuncForType(wrapAsType) returns. |
|
5092 Otherwise, no wrapping will be done. |
|
5093 """ |
|
5094 if wrapAsType is None: |
|
5095 tail = successCode |
|
5096 else: |
|
5097 tail = fill( |
|
5098 """ |
|
5099 if (!${maybeWrap}(cx, $${jsvalHandle})) { |
|
5100 $*{exceptionCode} |
|
5101 } |
|
5102 $*{successCode} |
|
5103 """, |
|
5104 maybeWrap=getMaybeWrapValueFuncForType(wrapAsType), |
|
5105 exceptionCode=exceptionCode, |
|
5106 successCode=successCode) |
|
5107 return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail |
|
5108 |
|
5109 def wrapAndSetPtr(wrapCall, failureCode=None): |
|
5110 """ |
|
5111 Returns the code to set the jsval by calling "wrapCall". "failureCode" |
|
5112 is the code to run if calling "wrapCall" fails |
|
5113 """ |
|
5114 if failureCode is None: |
|
5115 failureCode = exceptionCode |
|
5116 return fill( |
|
5117 """ |
|
5118 if (!${wrapCall}) { |
|
5119 $*{failureCode} |
|
5120 } |
|
5121 $*{successCode} |
|
5122 """, |
|
5123 wrapCall=wrapCall, |
|
5124 failureCode=failureCode, |
|
5125 successCode=successCode) |
|
5126 |
|
5127 if type is None or type.isVoid(): |
|
5128 return (setUndefined(), True) |
|
5129 |
|
5130 if type.isArray(): |
|
5131 raise TypeError("Can't handle array return values yet") |
|
5132 |
|
5133 if (type.isSequence() or type.isMozMap()) and type.nullable(): |
|
5134 # These are both wrapped in Nullable<> |
|
5135 recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider, |
|
5136 "%s.Value()" % result, successCode, |
|
5137 returnsNewObject, exceptionCode, |
|
5138 typedArraysAreStructs) |
|
5139 code = fill( |
|
5140 """ |
|
5141 |
|
5142 if (${result}.IsNull()) { |
|
5143 $*{setNull} |
|
5144 } |
|
5145 $*{recTemplate} |
|
5146 """, |
|
5147 result=result, |
|
5148 setNull=setNull(), |
|
5149 recTemplate=recTemplate) |
|
5150 return code, recInfall |
|
5151 |
|
5152 if type.isSequence(): |
|
5153 # Now do non-nullable sequences. Our success code is just to break to |
|
5154 # where we set the element in the array. Note that we bump the |
|
5155 # sequenceWrapLevel around this call so that nested sequence conversions |
|
5156 # will use different iteration variables. |
|
5157 global sequenceWrapLevel |
|
5158 index = "sequenceIdx%d" % sequenceWrapLevel |
|
5159 sequenceWrapLevel += 1 |
|
5160 innerTemplate = wrapForType( |
|
5161 type.inner, descriptorProvider, |
|
5162 { |
|
5163 'result': "%s[%s]" % (result, index), |
|
5164 'successCode': "break;\n", |
|
5165 'jsvalRef': "tmp", |
|
5166 'jsvalHandle': "&tmp", |
|
5167 'returnsNewObject': returnsNewObject, |
|
5168 'exceptionCode': exceptionCode, |
|
5169 'obj': "returnArray" |
|
5170 }) |
|
5171 sequenceWrapLevel -= 1 |
|
5172 code = fill( |
|
5173 """ |
|
5174 |
|
5175 uint32_t length = ${result}.Length(); |
|
5176 JS::Rooted<JSObject*> returnArray(cx, JS_NewArrayObject(cx, length)); |
|
5177 if (!returnArray) { |
|
5178 $*{exceptionCode} |
|
5179 } |
|
5180 // Scope for 'tmp' |
|
5181 { |
|
5182 JS::Rooted<JS::Value> tmp(cx); |
|
5183 for (uint32_t ${index} = 0; ${index} < length; ++${index}) { |
|
5184 // Control block to let us common up the JS_DefineElement calls when there |
|
5185 // are different ways to succeed at wrapping the object. |
|
5186 do { |
|
5187 $*{innerTemplate} |
|
5188 } while (0); |
|
5189 if (!JS_DefineElement(cx, returnArray, ${index}, tmp, |
|
5190 nullptr, nullptr, JSPROP_ENUMERATE)) { |
|
5191 $*{exceptionCode} |
|
5192 } |
|
5193 } |
|
5194 } |
|
5195 $*{set} |
|
5196 """, |
|
5197 result=result, |
|
5198 exceptionCode=exceptionCode, |
|
5199 index=index, |
|
5200 innerTemplate=innerTemplate, |
|
5201 set=setObject("*returnArray")) |
|
5202 |
|
5203 return (code, False) |
|
5204 |
|
5205 if type.isMozMap(): |
|
5206 # Now do non-nullable MozMap. Our success code is just to break to |
|
5207 # where we define the property on the object. Note that we bump the |
|
5208 # mozMapWrapLevel around this call so that nested MozMap conversions |
|
5209 # will use different temp value names. |
|
5210 global mozMapWrapLevel |
|
5211 valueName = "mozMapValue%d" % mozMapWrapLevel |
|
5212 mozMapWrapLevel += 1 |
|
5213 innerTemplate = wrapForType( |
|
5214 type.inner, descriptorProvider, |
|
5215 { |
|
5216 'result': valueName, |
|
5217 'successCode': "break;\n", |
|
5218 'jsvalRef': "tmp", |
|
5219 'jsvalHandle': "&tmp", |
|
5220 'returnsNewObject': returnsNewObject, |
|
5221 'exceptionCode': exceptionCode, |
|
5222 'obj': "returnObj" |
|
5223 }) |
|
5224 mozMapWrapLevel -= 1 |
|
5225 code = fill( |
|
5226 """ |
|
5227 |
|
5228 nsTArray<nsString> keys; |
|
5229 ${result}.GetKeys(keys); |
|
5230 JS::Rooted<JSObject*> returnObj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); |
|
5231 if (!returnObj) { |
|
5232 $*{exceptionCode} |
|
5233 } |
|
5234 // Scope for 'tmp' |
|
5235 { |
|
5236 JS::Rooted<JS::Value> tmp(cx); |
|
5237 for (size_t idx = 0; idx < keys.Length(); ++idx) { |
|
5238 auto& ${valueName} = ${result}.Get(keys[idx]); |
|
5239 // Control block to let us common up the JS_DefineUCProperty calls when there |
|
5240 // are different ways to succeed at wrapping the value. |
|
5241 do { |
|
5242 $*{innerTemplate} |
|
5243 } while (0); |
|
5244 if (!JS_DefineUCProperty(cx, returnObj, keys[idx].get(), |
|
5245 keys[idx].Length(), tmp, |
|
5246 JS_PropertyStub, JS_StrictPropertyStub, |
|
5247 JSPROP_ENUMERATE)) { |
|
5248 $*{exceptionCode} |
|
5249 } |
|
5250 } |
|
5251 } |
|
5252 $*{set} |
|
5253 """, |
|
5254 result=result, |
|
5255 exceptionCode=exceptionCode, |
|
5256 valueName=valueName, |
|
5257 innerTemplate=innerTemplate, |
|
5258 set=setObject("*returnObj")) |
|
5259 |
|
5260 return (code, False) |
|
5261 |
|
5262 if type.isGeckoInterface() and not type.isCallbackInterface(): |
|
5263 descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name) |
|
5264 if type.nullable(): |
|
5265 wrappingCode = ("if (!%s) {\n" % (result) + |
|
5266 indent(setNull()) + |
|
5267 "}\n") |
|
5268 else: |
|
5269 wrappingCode = "" |
|
5270 |
|
5271 if not descriptor.interface.isExternal() and not descriptor.skipGen: |
|
5272 if descriptor.wrapperCache: |
|
5273 assert descriptor.nativeOwnership != 'owned' |
|
5274 wrapMethod = "WrapNewBindingObject" |
|
5275 else: |
|
5276 if not returnsNewObject: |
|
5277 raise MethodNotNewObjectError(descriptor.interface.identifier.name) |
|
5278 if descriptor.nativeOwnership == 'owned': |
|
5279 wrapMethod = "WrapNewBindingNonWrapperCachedOwnedObject" |
|
5280 else: |
|
5281 wrapMethod = "WrapNewBindingNonWrapperCachedObject" |
|
5282 wrap = "%s(cx, ${obj}, %s, ${jsvalHandle})" % (wrapMethod, result) |
|
5283 if not descriptor.hasXPConnectImpls: |
|
5284 # Can only fail to wrap as a new-binding object |
|
5285 # if they already threw an exception. |
|
5286 #XXX Assertion disabled for now, see bug 991271. |
|
5287 failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" + |
|
5288 exceptionCode) |
|
5289 else: |
|
5290 if descriptor.notflattened: |
|
5291 raise TypeError("%s has XPConnect impls but not flattened; " |
|
5292 "fallback won't work correctly" % |
|
5293 descriptor.interface.identifier.name) |
|
5294 # Try old-style wrapping for bindings which might be XPConnect impls. |
|
5295 failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalHandle})" % result) |
|
5296 else: |
|
5297 if descriptor.notflattened: |
|
5298 getIID = "&NS_GET_IID(%s), " % descriptor.nativeType |
|
5299 else: |
|
5300 getIID = "" |
|
5301 wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID) |
|
5302 failed = None |
|
5303 |
|
5304 wrappingCode += wrapAndSetPtr(wrap, failed) |
|
5305 return (wrappingCode, False) |
|
5306 |
|
5307 if type.isDOMString(): |
|
5308 if type.nullable(): |
|
5309 return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), False) |
|
5310 else: |
|
5311 return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result), False) |
|
5312 |
|
5313 if type.isByteString(): |
|
5314 if type.nullable(): |
|
5315 return (wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False) |
|
5316 else: |
|
5317 return (wrapAndSetPtr("NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False) |
|
5318 |
|
5319 if type.isEnum(): |
|
5320 if type.nullable(): |
|
5321 resultLoc = "%s.Value()" % result |
|
5322 else: |
|
5323 resultLoc = result |
|
5324 conversion = fill( |
|
5325 """ |
|
5326 { |
|
5327 // Scope for resultStr |
|
5328 MOZ_ASSERT(uint32_t(${result}) < ArrayLength(${strings})); |
|
5329 JSString* resultStr = JS_NewStringCopyN(cx, ${strings}[uint32_t(${result})].value, ${strings}[uint32_t(${result})].length); |
|
5330 if (!resultStr) { |
|
5331 $*{exceptionCode} |
|
5332 } |
|
5333 $*{setResultStr} |
|
5334 } |
|
5335 """, |
|
5336 result=resultLoc, |
|
5337 strings=(type.unroll().inner.identifier.name + "Values::" + |
|
5338 ENUM_ENTRY_VARIABLE_NAME), |
|
5339 exceptionCode=exceptionCode, |
|
5340 setResultStr=setString("resultStr")) |
|
5341 |
|
5342 if type.nullable(): |
|
5343 conversion = CGIfElseWrapper( |
|
5344 "%s.IsNull()" % result, |
|
5345 CGGeneric(setNull()), |
|
5346 CGGeneric(conversion)).define() |
|
5347 return conversion, False |
|
5348 |
|
5349 if type.isCallback() or type.isCallbackInterface(): |
|
5350 wrapCode = setObject( |
|
5351 "*GetCallbackFromCallbackObject(%(result)s)", |
|
5352 wrapAsType=type) |
|
5353 if type.nullable(): |
|
5354 wrapCode = ( |
|
5355 "if (%(result)s) {\n" + |
|
5356 indent(wrapCode) + |
|
5357 "} else {\n" + |
|
5358 indent(setNull()) + |
|
5359 "}\n") |
|
5360 wrapCode = wrapCode % {"result": result} |
|
5361 return wrapCode, False |
|
5362 |
|
5363 if type.isAny(): |
|
5364 # See comments in WrapNewBindingObject explaining why we need |
|
5365 # to wrap here. |
|
5366 # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible |
|
5367 return (_setValue(result, wrapAsType=type), False) |
|
5368 |
|
5369 if (type.isObject() or (type.isSpiderMonkeyInterface() and |
|
5370 not typedArraysAreStructs)): |
|
5371 # See comments in WrapNewBindingObject explaining why we need |
|
5372 # to wrap here. |
|
5373 if type.nullable(): |
|
5374 toValue = "%s" |
|
5375 setter = setObjectOrNull |
|
5376 else: |
|
5377 toValue = "*%s" |
|
5378 setter = setObject |
|
5379 # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible |
|
5380 return (setter(toValue % result, wrapAsType=type), False) |
|
5381 |
|
5382 if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or |
|
5383 type.isDate() or |
|
5384 (type.isSpiderMonkeyInterface() and typedArraysAreStructs)): |
|
5385 raise TypeError("Need to learn to wrap %s" % type) |
|
5386 |
|
5387 if type.nullable(): |
|
5388 recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider, |
|
5389 "%s.Value()" % result, successCode, |
|
5390 returnsNewObject, exceptionCode, |
|
5391 typedArraysAreStructs) |
|
5392 return ("if (%s.IsNull()) {\n" % result + |
|
5393 indent(setNull()) + |
|
5394 "}\n" + |
|
5395 recTemplate, recInfal) |
|
5396 |
|
5397 if type.isSpiderMonkeyInterface(): |
|
5398 assert typedArraysAreStructs |
|
5399 # See comments in WrapNewBindingObject explaining why we need |
|
5400 # to wrap here. |
|
5401 # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible |
|
5402 return (setObject("*%s.Obj()" % result, |
|
5403 wrapAsType=type), False) |
|
5404 |
|
5405 if type.isUnion(): |
|
5406 return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result), |
|
5407 False) |
|
5408 |
|
5409 if type.isDictionary(): |
|
5410 return (wrapAndSetPtr("%s.ToObject(cx, ${jsvalHandle})" % result), |
|
5411 False) |
|
5412 |
|
5413 if type.isDate(): |
|
5414 return (wrapAndSetPtr("%s.ToDateObject(cx, ${jsvalHandle})" % result), |
|
5415 False) |
|
5416 |
|
5417 tag = type.tag() |
|
5418 |
|
5419 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, |
|
5420 IDLType.Tags.uint16, IDLType.Tags.int32]: |
|
5421 return (setInt32("int32_t(%s)" % result), True) |
|
5422 |
|
5423 elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, |
|
5424 IDLType.Tags.unrestricted_float, IDLType.Tags.float, |
|
5425 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: |
|
5426 # XXXbz will cast to double do the "even significand" thing that webidl |
|
5427 # calls for for 64-bit ints? Do we care? |
|
5428 return (setDouble("double(%s)" % result), True) |
|
5429 |
|
5430 elif tag == IDLType.Tags.uint32: |
|
5431 return (setUint32(result), True) |
|
5432 |
|
5433 elif tag == IDLType.Tags.bool: |
|
5434 return (setBoolean(result), True) |
|
5435 |
|
5436 else: |
|
5437 raise TypeError("Need to learn to wrap primitive: %s" % type) |
|
5438 |
|
5439 |
|
5440 def wrapForType(type, descriptorProvider, templateValues): |
|
5441 """ |
|
5442 Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict |
|
5443 that should contain: |
|
5444 |
|
5445 * 'jsvalRef': something that can have .address() called on it to get a |
|
5446 JS::Value* and .set() called on it to set it to a JS::Value. |
|
5447 This can be a JS::MutableHandle<JS::Value> or a |
|
5448 JS::Rooted<JS::Value>. |
|
5449 * 'jsvalHandle': something that can be passed to methods taking a |
|
5450 JS::MutableHandle<JS::Value>. This can be a |
|
5451 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*. |
|
5452 * 'obj' (optional): the name of the variable that contains the JSObject to |
|
5453 use as a scope when wrapping, if not supplied 'obj' |
|
5454 will be used as the name |
|
5455 * 'result' (optional): the name of the variable in which the C++ value is |
|
5456 stored, if not supplied 'result' will be used as |
|
5457 the name |
|
5458 * 'successCode' (optional): the code to run once we have successfully |
|
5459 done the conversion, if not supplied 'return |
|
5460 true;' will be used as the code. The |
|
5461 successCode must ensure that once it runs no |
|
5462 more of the conversion template will be |
|
5463 executed (e.g. by doing a 'return' or 'break' |
|
5464 as appropriate). |
|
5465 * 'returnsNewObject' (optional): If true, we're wrapping for the return |
|
5466 value of a [NewObject] method. Assumed |
|
5467 false if not set. |
|
5468 * 'exceptionCode' (optional): Code to run when a JS exception is thrown. |
|
5469 The default is "return false;". The code |
|
5470 passed here must return. |
|
5471 """ |
|
5472 wrap = getWrapTemplateForType(type, descriptorProvider, |
|
5473 templateValues.get('result', 'result'), |
|
5474 templateValues.get('successCode', None), |
|
5475 templateValues.get('returnsNewObject', False), |
|
5476 templateValues.get('exceptionCode', |
|
5477 "return false;\n"), |
|
5478 templateValues.get('typedArraysAreStructs', |
|
5479 False))[0] |
|
5480 |
|
5481 defaultValues = {'obj': 'obj'} |
|
5482 return string.Template(wrap).substitute(defaultValues, **templateValues) |
|
5483 |
|
5484 |
|
5485 def infallibleForMember(member, type, descriptorProvider): |
|
5486 """ |
|
5487 Determine the fallibility of changing a C++ value of IDL type "type" into |
|
5488 JS for the given attribute. Apart from returnsNewObject, all the defaults |
|
5489 are used, since the fallbility does not change based on the boolean values, |
|
5490 and the template will be discarded. |
|
5491 |
|
5492 CURRENT ASSUMPTIONS: |
|
5493 We assume that successCode for wrapping up return values cannot contain |
|
5494 failure conditions. |
|
5495 """ |
|
5496 return getWrapTemplateForType(type, descriptorProvider, 'result', None, |
|
5497 memberReturnsNewObject(member), "return false;\n", |
|
5498 False)[1] |
|
5499 |
|
5500 |
|
5501 def leafTypeNeedsCx(type, retVal): |
|
5502 return (type.isAny() or type.isObject() or |
|
5503 (retVal and type.isSpiderMonkeyInterface())) |
|
5504 |
|
5505 |
|
5506 def leafTypeNeedsScopeObject(type, retVal): |
|
5507 return retVal and type.isSpiderMonkeyInterface() |
|
5508 |
|
5509 |
|
5510 def leafTypeNeedsRooting(type): |
|
5511 return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface() |
|
5512 |
|
5513 |
|
5514 def typeNeedsRooting(type): |
|
5515 return typeMatchesLambda(type, |
|
5516 lambda t: leafTypeNeedsRooting(t)) |
|
5517 |
|
5518 |
|
5519 def typeNeedsCx(type, retVal=False): |
|
5520 return typeMatchesLambda(type, |
|
5521 lambda t: leafTypeNeedsCx(t, retVal)) |
|
5522 |
|
5523 |
|
5524 def typeNeedsScopeObject(type, retVal=False): |
|
5525 return typeMatchesLambda(type, |
|
5526 lambda t: leafTypeNeedsScopeObject(t, retVal)) |
|
5527 |
|
5528 |
|
5529 def typeMatchesLambda(type, func): |
|
5530 if type is None: |
|
5531 return False |
|
5532 if type.nullable(): |
|
5533 return typeMatchesLambda(type.inner, func) |
|
5534 if type.isSequence() or type.isMozMap() or type.isArray(): |
|
5535 return typeMatchesLambda(type.inner, func) |
|
5536 if type.isUnion(): |
|
5537 return any(typeMatchesLambda(t, func) for t in |
|
5538 type.unroll().flatMemberTypes) |
|
5539 if type.isDictionary(): |
|
5540 return dictionaryMatchesLambda(type.inner, func) |
|
5541 return func(type) |
|
5542 |
|
5543 |
|
5544 def dictionaryMatchesLambda(dictionary, func): |
|
5545 return (any(typeMatchesLambda(m.type, func) for m in dictionary.members) or |
|
5546 (dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func))) |
|
5547 |
|
5548 |
|
5549 # Whenever this is modified, please update CGNativeMember.getRetvalInfo as |
|
5550 # needed to keep the types compatible. |
|
5551 def getRetvalDeclarationForType(returnType, descriptorProvider, |
|
5552 resultAlreadyAddRefed, |
|
5553 isMember=False): |
|
5554 """ |
|
5555 Returns a tuple containing four things: |
|
5556 |
|
5557 1) A CGThing for the type of the return value, or None if there is no need |
|
5558 for a return value. |
|
5559 |
|
5560 2) A value indicating the kind of ourparam to pass the value as. Valid |
|
5561 options are None to not pass as an out param at all, "ref" (to pass a |
|
5562 reference as an out param), and "ptr" (to pass a pointer as an out |
|
5563 param). |
|
5564 |
|
5565 3) A CGThing for a tracer for the return value, or None if no tracing is |
|
5566 needed. |
|
5567 |
|
5568 4) An argument string to pass to the retval declaration |
|
5569 constructor or None if there are no arguments. |
|
5570 """ |
|
5571 if returnType is None or returnType.isVoid(): |
|
5572 # Nothing to declare |
|
5573 return None, None, None, None |
|
5574 if returnType.isPrimitive() and returnType.tag() in builtinNames: |
|
5575 result = CGGeneric(builtinNames[returnType.tag()]) |
|
5576 if returnType.nullable(): |
|
5577 result = CGTemplatedType("Nullable", result) |
|
5578 return result, None, None, None |
|
5579 if returnType.isDOMString(): |
|
5580 if isMember: |
|
5581 return CGGeneric("nsString"), "ref", None, None |
|
5582 return CGGeneric("DOMString"), "ref", None, None |
|
5583 if returnType.isByteString(): |
|
5584 return CGGeneric("nsCString"), "ref", None, None |
|
5585 if returnType.isEnum(): |
|
5586 result = CGGeneric(returnType.unroll().inner.identifier.name) |
|
5587 if returnType.nullable(): |
|
5588 result = CGTemplatedType("Nullable", result) |
|
5589 return result, None, None, None |
|
5590 if returnType.isGeckoInterface(): |
|
5591 result = CGGeneric(descriptorProvider.getDescriptor( |
|
5592 returnType.unroll().inner.identifier.name).nativeType) |
|
5593 if descriptorProvider.getDescriptor( |
|
5594 returnType.unroll().inner.identifier.name).nativeOwnership == 'owned': |
|
5595 result = CGTemplatedType("nsAutoPtr", result) |
|
5596 elif resultAlreadyAddRefed: |
|
5597 result = CGTemplatedType("nsRefPtr", result) |
|
5598 else: |
|
5599 result = CGWrapper(result, post="*") |
|
5600 return result, None, None, None |
|
5601 if returnType.isCallback(): |
|
5602 name = returnType.unroll().identifier.name |
|
5603 return CGGeneric("nsRefPtr<%s>" % name), None, None, None |
|
5604 if returnType.isAny(): |
|
5605 if isMember: |
|
5606 return CGGeneric("JS::Value"), None, None, None |
|
5607 return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx" |
|
5608 if returnType.isObject() or returnType.isSpiderMonkeyInterface(): |
|
5609 if isMember: |
|
5610 return CGGeneric("JSObject*"), None, None, None |
|
5611 return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx" |
|
5612 if returnType.isSequence(): |
|
5613 nullable = returnType.nullable() |
|
5614 if nullable: |
|
5615 returnType = returnType.inner |
|
5616 # If our result is already addrefed, use the right type in the |
|
5617 # sequence argument here. |
|
5618 result, _, _, _ = getRetvalDeclarationForType(returnType.inner, |
|
5619 descriptorProvider, |
|
5620 resultAlreadyAddRefed, |
|
5621 isMember="Sequence") |
|
5622 # While we have our inner type, set up our rooter, if needed |
|
5623 if not isMember and typeNeedsRooting(returnType): |
|
5624 rooter = CGGeneric("SequenceRooter<%s > resultRooter(cx, &result);\n" % |
|
5625 result.define()) |
|
5626 else: |
|
5627 rooter = None |
|
5628 result = CGTemplatedType("nsTArray", result) |
|
5629 if nullable: |
|
5630 result = CGTemplatedType("Nullable", result) |
|
5631 return result, "ref", rooter, None |
|
5632 if returnType.isMozMap(): |
|
5633 nullable = returnType.nullable() |
|
5634 if nullable: |
|
5635 returnType = returnType.inner |
|
5636 # If our result is already addrefed, use the right type in the |
|
5637 # MozMap argument here. |
|
5638 result, _, _, _ = getRetvalDeclarationForType(returnType.inner, |
|
5639 descriptorProvider, |
|
5640 resultAlreadyAddRefed, |
|
5641 isMember="MozMap") |
|
5642 # While we have our inner type, set up our rooter, if needed |
|
5643 if not isMember and typeNeedsRooting(returnType): |
|
5644 rooter = CGGeneric("MozMapRooter<%s> resultRooter(cx, &result);\n" % |
|
5645 result.define()) |
|
5646 else: |
|
5647 rooter = None |
|
5648 result = CGTemplatedType("MozMap", result) |
|
5649 if nullable: |
|
5650 result = CGTemplatedType("Nullable", result) |
|
5651 return result, "ref", rooter, None |
|
5652 if returnType.isDictionary(): |
|
5653 nullable = returnType.nullable() |
|
5654 dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner) |
|
5655 result = CGGeneric(dictName) |
|
5656 if not isMember and typeNeedsRooting(returnType): |
|
5657 if nullable: |
|
5658 result = CGTemplatedType("NullableRootedDictionary", result) |
|
5659 else: |
|
5660 result = CGTemplatedType("RootedDictionary", result) |
|
5661 resultArgs = "cx" |
|
5662 else: |
|
5663 if nullable: |
|
5664 result = CGTemplatedType("Nullable", result) |
|
5665 resultArgs = None |
|
5666 return result, "ref", None, resultArgs |
|
5667 if returnType.isUnion(): |
|
5668 result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True)) |
|
5669 if not isMember and typeNeedsRooting(returnType): |
|
5670 if returnType.nullable(): |
|
5671 result = CGTemplatedType("NullableRootedUnion", result) |
|
5672 else: |
|
5673 result = CGTemplatedType("RootedUnion", result) |
|
5674 resultArgs = "cx" |
|
5675 else: |
|
5676 if returnType.nullable(): |
|
5677 result = CGTemplatedType("Nullable", result) |
|
5678 resultArgs = None |
|
5679 return result, "ref", None, resultArgs |
|
5680 if returnType.isDate(): |
|
5681 result = CGGeneric("Date") |
|
5682 if returnType.nullable(): |
|
5683 result = CGTemplatedType("Nullable", result) |
|
5684 return result, None, None, None |
|
5685 raise TypeError("Don't know how to declare return value for %s" % |
|
5686 returnType) |
|
5687 |
|
5688 |
|
5689 def isResultAlreadyAddRefed(extendedAttributes): |
|
5690 return 'resultNotAddRefed' not in extendedAttributes |
|
5691 |
|
5692 |
|
5693 def needCx(returnType, arguments, extendedAttributes, considerTypes): |
|
5694 return (considerTypes and |
|
5695 (typeNeedsCx(returnType, True) or |
|
5696 any(typeNeedsCx(a.type) for a in arguments)) or |
|
5697 'implicitJSContext' in extendedAttributes) |
|
5698 |
|
5699 |
|
5700 def needScopeObject(returnType, arguments, extendedAttributes, |
|
5701 isWrapperCached, considerTypes, isMember): |
|
5702 """ |
|
5703 isMember should be true if we're dealing with an attribute |
|
5704 annotated as [StoreInSlot]. |
|
5705 """ |
|
5706 return (considerTypes and not isWrapperCached and |
|
5707 ((not isMember and typeNeedsScopeObject(returnType, True)) or |
|
5708 any(typeNeedsScopeObject(a.type) for a in arguments))) |
|
5709 |
|
5710 |
|
5711 class CGCallGenerator(CGThing): |
|
5712 """ |
|
5713 A class to generate an actual call to a C++ object. Assumes that the C++ |
|
5714 object is stored in a variable whose name is given by the |object| argument. |
|
5715 |
|
5716 errorReport should be a CGThing for an error report or None if no |
|
5717 error reporting is needed. |
|
5718 """ |
|
5719 def __init__(self, errorReport, arguments, argsPre, returnType, |
|
5720 extendedAttributes, descriptorProvider, nativeMethodName, |
|
5721 static, object="self", argsPost=[]): |
|
5722 CGThing.__init__(self) |
|
5723 |
|
5724 assert errorReport is None or isinstance(errorReport, CGThing) |
|
5725 |
|
5726 isFallible = errorReport is not None |
|
5727 |
|
5728 resultAlreadyAddRefed = isResultAlreadyAddRefed(extendedAttributes) |
|
5729 result, resultOutParam, resultRooter, resultArgs = \ |
|
5730 getRetvalDeclarationForType( |
|
5731 returnType, descriptorProvider, resultAlreadyAddRefed) |
|
5732 |
|
5733 args = CGList([CGGeneric(arg) for arg in argsPre], ", ") |
|
5734 for a, name in arguments: |
|
5735 arg = CGGeneric(name) |
|
5736 |
|
5737 # Now constify the things that need it |
|
5738 def needsConst(a): |
|
5739 if a.type.isDictionary(): |
|
5740 return True |
|
5741 if a.type.isSequence(): |
|
5742 return True |
|
5743 if a.type.isMozMap(): |
|
5744 return True |
|
5745 # isObject() types are always a JS::Rooted, whether |
|
5746 # nullable or not, and it turns out a const JS::Rooted |
|
5747 # is not very helpful at all (in particular, it won't |
|
5748 # even convert to a JS::Handle). |
|
5749 # XXX bz Well, why not??? |
|
5750 if a.type.nullable() and not a.type.isObject(): |
|
5751 return True |
|
5752 if a.type.isString(): |
|
5753 return True |
|
5754 if a.optional and not a.defaultValue: |
|
5755 # If a.defaultValue, then it's not going to use an Optional, |
|
5756 # so doesn't need to be const just due to being optional. |
|
5757 # This also covers variadic arguments. |
|
5758 return True |
|
5759 if a.type.isUnion(): |
|
5760 return True |
|
5761 if a.type.isSpiderMonkeyInterface(): |
|
5762 return True |
|
5763 return False |
|
5764 if needsConst(a): |
|
5765 arg = CGWrapper(arg, pre="Constify(", post=")") |
|
5766 # And convert NonNull<T> to T& |
|
5767 if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or |
|
5768 a.type.isDOMString()): |
|
5769 arg = CGWrapper(arg, pre="NonNullHelper(", post=")") |
|
5770 args.append(arg) |
|
5771 |
|
5772 # Return values that go in outparams go here |
|
5773 if resultOutParam is not None: |
|
5774 if resultOutParam is "ref": |
|
5775 args.append(CGGeneric("result")) |
|
5776 else: |
|
5777 assert resultOutParam is "ptr" |
|
5778 args.append(CGGeneric("&result")) |
|
5779 |
|
5780 if isFallible: |
|
5781 args.append(CGGeneric("rv")) |
|
5782 args.extend(CGGeneric(arg) for arg in argsPost) |
|
5783 |
|
5784 # Build up our actual call |
|
5785 self.cgRoot = CGList([]) |
|
5786 |
|
5787 call = CGGeneric(nativeMethodName) |
|
5788 if not static: |
|
5789 call = CGWrapper(call, pre="%s->" % object) |
|
5790 call = CGList([call, CGWrapper(args, pre="(", post=");\n")]) |
|
5791 if result is not None: |
|
5792 if resultRooter is not None: |
|
5793 self.cgRoot.prepend(resultRooter) |
|
5794 if resultArgs is not None: |
|
5795 resultArgs = "(%s)" % resultArgs |
|
5796 else: |
|
5797 resultArgs = "" |
|
5798 result = CGWrapper(result, post=(" result%s;\n" % resultArgs)) |
|
5799 self.cgRoot.prepend(result) |
|
5800 if not resultOutParam: |
|
5801 call = CGWrapper(call, pre="result = ") |
|
5802 |
|
5803 call = CGWrapper(call) |
|
5804 self.cgRoot.append(call) |
|
5805 |
|
5806 if isFallible: |
|
5807 self.cgRoot.prepend(CGGeneric("ErrorResult rv;\n")) |
|
5808 self.cgRoot.append(CGGeneric("rv.WouldReportJSException();\n")) |
|
5809 self.cgRoot.append(CGGeneric("if (rv.Failed()) {\n")) |
|
5810 self.cgRoot.append(CGIndenter(errorReport)) |
|
5811 self.cgRoot.append(CGGeneric("}\n")) |
|
5812 |
|
5813 def define(self): |
|
5814 return self.cgRoot.define() |
|
5815 |
|
5816 |
|
5817 def getUnionMemberName(type): |
|
5818 if type.isGeckoInterface(): |
|
5819 return type.inner.identifier.name |
|
5820 if type.isEnum(): |
|
5821 return type.inner.identifier.name |
|
5822 if type.isArray() or type.isSequence(): |
|
5823 return str(type) |
|
5824 return type.name |
|
5825 |
|
5826 |
|
5827 class MethodNotNewObjectError(Exception): |
|
5828 def __init__(self, typename): |
|
5829 self.typename = typename |
|
5830 |
|
5831 # A counter for making sure that when we're wrapping up things in |
|
5832 # nested sequences we don't use the same variable name to iterate over |
|
5833 # different sequences. |
|
5834 sequenceWrapLevel = 0 |
|
5835 |
|
5836 |
|
5837 def wrapTypeIntoCurrentCompartment(type, value, isMember=True): |
|
5838 """ |
|
5839 Take the thing named by "value" and if it contains "any", |
|
5840 "object", or spidermonkey-interface types inside return a CGThing |
|
5841 that will wrap them into the current compartment. |
|
5842 """ |
|
5843 if type.isAny(): |
|
5844 assert not type.nullable() |
|
5845 if isMember: |
|
5846 value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value |
|
5847 else: |
|
5848 value = "&" + value |
|
5849 return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n" |
|
5850 " return false;\n" |
|
5851 "}\n" % value) |
|
5852 |
|
5853 if type.isObject(): |
|
5854 if isMember: |
|
5855 value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value |
|
5856 else: |
|
5857 value = "&" + value |
|
5858 return CGGeneric("if (!JS_WrapObject(cx, %s)) {\n" |
|
5859 " return false;\n" |
|
5860 "}\n" % value) |
|
5861 |
|
5862 if type.isSpiderMonkeyInterface(): |
|
5863 origValue = value |
|
5864 if type.nullable(): |
|
5865 value = "%s.Value()" % value |
|
5866 wrapCode = CGGeneric("if (!%s.WrapIntoNewCompartment(cx)) {\n" |
|
5867 " return false;\n" |
|
5868 "}\n" % value) |
|
5869 if type.nullable(): |
|
5870 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) |
|
5871 return wrapCode |
|
5872 |
|
5873 if type.isSequence(): |
|
5874 origValue = value |
|
5875 origType = type |
|
5876 if type.nullable(): |
|
5877 type = type.inner |
|
5878 value = "%s.Value()" % value |
|
5879 global sequenceWrapLevel |
|
5880 index = "indexName%d" % sequenceWrapLevel |
|
5881 sequenceWrapLevel += 1 |
|
5882 wrapElement = wrapTypeIntoCurrentCompartment(type.inner, |
|
5883 "%s[%s]" % (value, index)) |
|
5884 sequenceWrapLevel -= 1 |
|
5885 if not wrapElement: |
|
5886 return None |
|
5887 wrapCode = CGWrapper(CGIndenter(wrapElement), |
|
5888 pre=("for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" % |
|
5889 (index, index, value, index)), |
|
5890 post="}\n") |
|
5891 if origType.nullable(): |
|
5892 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) |
|
5893 return wrapCode |
|
5894 |
|
5895 if type.isDictionary(): |
|
5896 assert not type.nullable() |
|
5897 myDict = type.inner |
|
5898 memberWraps = [] |
|
5899 while myDict: |
|
5900 for member in myDict.members: |
|
5901 memberWrap = wrapArgIntoCurrentCompartment( |
|
5902 member, |
|
5903 "%s.%s" % (value, CGDictionary.makeMemberName(member.identifier.name))) |
|
5904 if memberWrap: |
|
5905 memberWraps.append(memberWrap) |
|
5906 myDict = myDict.parent |
|
5907 return CGList(memberWraps) if len(memberWraps) != 0 else None |
|
5908 |
|
5909 if type.isUnion(): |
|
5910 memberWraps = [] |
|
5911 if type.nullable(): |
|
5912 type = type.inner |
|
5913 value = "%s.Value()" % value |
|
5914 for member in type.flatMemberTypes: |
|
5915 memberName = getUnionMemberName(member) |
|
5916 memberWrap = wrapTypeIntoCurrentCompartment( |
|
5917 member, "%s.GetAs%s()" % (value, memberName)) |
|
5918 if memberWrap: |
|
5919 memberWrap = CGIfWrapper( |
|
5920 memberWrap, "%s.Is%s()" % (value, memberName)) |
|
5921 memberWraps.append(memberWrap) |
|
5922 return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None |
|
5923 |
|
5924 if (type.isString() or type.isPrimitive() or type.isEnum() or |
|
5925 type.isGeckoInterface() or type.isCallback() or type.isDate()): |
|
5926 # All of these don't need wrapping |
|
5927 return None |
|
5928 |
|
5929 raise TypeError("Unknown type; we don't know how to wrap it in constructor " |
|
5930 "arguments: %s" % type) |
|
5931 |
|
5932 |
|
5933 def wrapArgIntoCurrentCompartment(arg, value, isMember=True): |
|
5934 """ |
|
5935 As wrapTypeIntoCurrentCompartment but handles things being optional |
|
5936 """ |
|
5937 origValue = value |
|
5938 isOptional = arg.optional and not arg.defaultValue |
|
5939 if isOptional: |
|
5940 value = value + ".Value()" |
|
5941 wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember) |
|
5942 if wrap and isOptional: |
|
5943 wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue) |
|
5944 return wrap |
|
5945 |
|
5946 |
|
5947 class CGPerSignatureCall(CGThing): |
|
5948 """ |
|
5949 This class handles the guts of generating code for a particular |
|
5950 call signature. A call signature consists of four things: |
|
5951 |
|
5952 1) A return type, which can be None to indicate that there is no |
|
5953 actual return value (e.g. this is an attribute setter) or an |
|
5954 IDLType if there's an IDL type involved (including |void|). |
|
5955 2) An argument list, which is allowed to be empty. |
|
5956 3) A name of a native method to call. |
|
5957 4) Whether or not this method is static. |
|
5958 |
|
5959 We also need to know whether this is a method or a getter/setter |
|
5960 to do error reporting correctly. |
|
5961 |
|
5962 The idlNode parameter can be either a method or an attr. We can query |
|
5963 |idlNode.identifier| in both cases, so we can be agnostic between the two. |
|
5964 """ |
|
5965 # XXXbz For now each entry in the argument list is either an |
|
5966 # IDLArgument or a FakeArgument, but longer-term we may want to |
|
5967 # have ways of flagging things like JSContext* or optional_argc in |
|
5968 # there. |
|
5969 |
|
5970 def __init__(self, returnType, arguments, nativeMethodName, static, |
|
5971 descriptor, idlNode, argConversionStartsAt=0, getter=False, |
|
5972 setter=False, isConstructor=False): |
|
5973 assert idlNode.isMethod() == (not getter and not setter) |
|
5974 assert idlNode.isAttr() == (getter or setter) |
|
5975 # Constructors are always static |
|
5976 assert not isConstructor or static |
|
5977 |
|
5978 CGThing.__init__(self) |
|
5979 self.returnType = returnType |
|
5980 self.descriptor = descriptor |
|
5981 self.idlNode = idlNode |
|
5982 self.extendedAttributes = descriptor.getExtendedAttributes(idlNode, |
|
5983 getter=getter, |
|
5984 setter=setter) |
|
5985 self.arguments = arguments |
|
5986 self.argCount = len(arguments) |
|
5987 cgThings = [] |
|
5988 lenientFloatCode = None |
|
5989 if idlNode.getExtendedAttribute('LenientFloat') is not None: |
|
5990 if setter: |
|
5991 lenientFloatCode = "return true;\n" |
|
5992 elif idlNode.isMethod(): |
|
5993 lenientFloatCode = ("args.rval().setUndefined();\n" |
|
5994 "return true;\n") |
|
5995 |
|
5996 argsPre = [] |
|
5997 if static: |
|
5998 nativeMethodName = "%s::%s" % (descriptor.nativeType, |
|
5999 nativeMethodName) |
|
6000 # If we're a constructor, "obj" may not be a function, so calling |
|
6001 # XrayAwareCalleeGlobal() on it is not safe. Of course in the |
|
6002 # constructor case either "obj" is an Xray or we're already in the |
|
6003 # content compartment, not the Xray compartment, so just |
|
6004 # constructing the GlobalObject from "obj" is fine. |
|
6005 if isConstructor: |
|
6006 objForGlobalObject = "obj" |
|
6007 else: |
|
6008 objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)" |
|
6009 cgThings.append(CGGeneric(fill( |
|
6010 """ |
|
6011 GlobalObject global(cx, ${obj}); |
|
6012 if (global.Failed()) { |
|
6013 return false; |
|
6014 } |
|
6015 |
|
6016 """, |
|
6017 obj=objForGlobalObject))) |
|
6018 argsPre.append("global") |
|
6019 |
|
6020 # For JS-implemented interfaces we do not want to base the |
|
6021 # needsCx decision on the types involved, just on our extended |
|
6022 # attributes. |
|
6023 needsCx = needCx(returnType, arguments, self.extendedAttributes, |
|
6024 not descriptor.interface.isJSImplemented()) |
|
6025 if needsCx and not (static and descriptor.workers): |
|
6026 argsPre.append("cx") |
|
6027 |
|
6028 needsUnwrap = False |
|
6029 argsPost = [] |
|
6030 if isConstructor: |
|
6031 needsUnwrap = True |
|
6032 needsUnwrappedVar = False |
|
6033 unwrappedVar = "obj" |
|
6034 elif descriptor.interface.isJSImplemented(): |
|
6035 needsUnwrap = True |
|
6036 needsUnwrappedVar = True |
|
6037 argsPost.append("js::GetObjectCompartment(unwrappedObj.empty() ? obj : unwrappedObj.ref())") |
|
6038 elif needScopeObject(returnType, arguments, self.extendedAttributes, |
|
6039 descriptor.wrapperCache, True, |
|
6040 idlNode.getExtendedAttribute("StoreInSlot")): |
|
6041 needsUnwrap = True |
|
6042 needsUnwrappedVar = True |
|
6043 argsPre.append("unwrappedObj.empty() ? obj : unwrappedObj.ref()") |
|
6044 |
|
6045 if needsUnwrap and needsUnwrappedVar: |
|
6046 # We cannot assign into obj because it's a Handle, not a |
|
6047 # MutableHandle, so we need a separate Rooted. |
|
6048 cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n")) |
|
6049 unwrappedVar = "unwrappedObj.ref()" |
|
6050 |
|
6051 if idlNode.isMethod() and idlNode.isLegacycaller(): |
|
6052 # If we can have legacycaller with identifier, we can't |
|
6053 # just use the idlNode to determine whether we're |
|
6054 # generating code for the legacycaller or not. |
|
6055 assert idlNode.isIdentifierLess() |
|
6056 # Pass in our thisVal |
|
6057 argsPre.append("args.thisv()") |
|
6058 |
|
6059 ourName = "%s.%s" % (descriptor.interface.identifier.name, |
|
6060 idlNode.identifier.name) |
|
6061 if idlNode.isMethod(): |
|
6062 argDescription = "argument %(index)d of " + ourName |
|
6063 elif setter: |
|
6064 argDescription = "value being assigned to %s" % ourName |
|
6065 else: |
|
6066 assert self.argCount == 0 |
|
6067 |
|
6068 if needsUnwrap: |
|
6069 # It's very important that we construct our unwrappedObj, if we need |
|
6070 # to do it, before we might start setting up Rooted things for our |
|
6071 # arguments, so that we don't violate the stack discipline Rooted |
|
6072 # depends on. |
|
6073 cgThings.append(CGGeneric( |
|
6074 "bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n")) |
|
6075 if needsUnwrappedVar: |
|
6076 cgThings.append(CGIfWrapper( |
|
6077 CGGeneric("unwrappedObj.construct(cx, obj);\n"), |
|
6078 "objIsXray")) |
|
6079 |
|
6080 for i in range(argConversionStartsAt, self.argCount): |
|
6081 cgThings.append( |
|
6082 CGArgumentConverter(arguments[i], i, self.descriptor, |
|
6083 argDescription % {"index": i + 1}, |
|
6084 invalidEnumValueFatal=not setter, |
|
6085 lenientFloatCode=lenientFloatCode)) |
|
6086 |
|
6087 if needsUnwrap: |
|
6088 # Something depends on having the unwrapped object, so unwrap it now. |
|
6089 xraySteps = [] |
|
6090 # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is |
|
6091 # not null. |
|
6092 xraySteps.append( |
|
6093 CGGeneric(string.Template(dedent(""" |
|
6094 ${obj} = js::CheckedUnwrap(${obj}); |
|
6095 if (!${obj}) { |
|
6096 return false; |
|
6097 } |
|
6098 """)).substitute({'obj': unwrappedVar}))) |
|
6099 if isConstructor: |
|
6100 # If we're called via an xray, we need to enter the underlying |
|
6101 # object's compartment and then wrap up all of our arguments into |
|
6102 # that compartment as needed. This is all happening after we've |
|
6103 # already done the conversions from JS values to WebIDL (C++) |
|
6104 # values, so we only need to worry about cases where there are 'any' |
|
6105 # or 'object' types, or other things that we represent as actual |
|
6106 # JSAPI types, present. Effectively, we're emulating a |
|
6107 # CrossCompartmentWrapper, but working with the C++ types, not the |
|
6108 # original list of JS::Values. |
|
6109 cgThings.append(CGGeneric("Maybe<JSAutoCompartment> ac;\n")) |
|
6110 xraySteps.append(CGGeneric("ac.construct(cx, obj);\n")) |
|
6111 xraySteps.extend( |
|
6112 wrapArgIntoCurrentCompartment(arg, argname, isMember=False) |
|
6113 for arg, argname in self.getArguments()) |
|
6114 |
|
6115 cgThings.append( |
|
6116 CGIfWrapper(CGList(xraySteps), |
|
6117 "objIsXray")) |
|
6118 |
|
6119 cgThings.append(CGCallGenerator( |
|
6120 self.getErrorReport() if self.isFallible() else None, |
|
6121 self.getArguments(), argsPre, returnType, |
|
6122 self.extendedAttributes, descriptor, nativeMethodName, |
|
6123 static, argsPost=argsPost)) |
|
6124 self.cgRoot = CGList(cgThings) |
|
6125 |
|
6126 def getArguments(self): |
|
6127 return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)] |
|
6128 |
|
6129 def isFallible(self): |
|
6130 return 'infallible' not in self.extendedAttributes |
|
6131 |
|
6132 def wrap_return_value(self): |
|
6133 returnsNewObject = memberReturnsNewObject(self.idlNode) |
|
6134 if returnsNewObject: |
|
6135 # We better be returning addrefed things! |
|
6136 assert(isResultAlreadyAddRefed(self.extendedAttributes) or |
|
6137 # NewObject can return raw pointers to owned objects |
|
6138 (self.returnType.isGeckoInterface() and |
|
6139 self.descriptor.getDescriptor(self.returnType.unroll().inner.identifier.name).nativeOwnership == 'owned')) |
|
6140 |
|
6141 setSlot = self.idlNode.isAttr() and self.idlNode.slotIndex is not None |
|
6142 if setSlot: |
|
6143 # For attributes in slots, we want to do some |
|
6144 # post-processing once we've wrapped them. |
|
6145 successCode = "break;\n" |
|
6146 else: |
|
6147 successCode = None |
|
6148 |
|
6149 resultTemplateValues = { |
|
6150 'jsvalRef': 'args.rval()', |
|
6151 'jsvalHandle': 'args.rval()', |
|
6152 'returnsNewObject': returnsNewObject, |
|
6153 'successCode': successCode, |
|
6154 'obj': "reflector" if setSlot else "obj" |
|
6155 } |
|
6156 try: |
|
6157 wrapCode = wrapForType(self.returnType, self.descriptor, resultTemplateValues) |
|
6158 except MethodNotNewObjectError, err: |
|
6159 assert not returnsNewObject |
|
6160 raise TypeError("%s being returned from non-NewObject method or property %s.%s" % |
|
6161 (err.typename, |
|
6162 self.descriptor.interface.identifier.name, |
|
6163 self.idlNode.identifier.name)) |
|
6164 if setSlot: |
|
6165 # We need to make sure that our initial wrapping is done in the |
|
6166 # reflector compartment, but that we finally set args.rval() in the |
|
6167 # caller compartment. We also need to make sure that the actual |
|
6168 # wrapping steps happen inside a do/while that they can break out |
|
6169 # of. |
|
6170 |
|
6171 # postSteps are the steps that run while we're still in the |
|
6172 # reflector compartment but after we've finished the initial |
|
6173 # wrapping into args.rval(). |
|
6174 postSteps = "" |
|
6175 if self.idlNode.getExtendedAttribute("Frozen"): |
|
6176 assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary() |
|
6177 freezeValue = CGGeneric( |
|
6178 "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n" |
|
6179 "if (!JS_FreezeObject(cx, rvalObj)) {\n" |
|
6180 " return false;\n" |
|
6181 "}\n") |
|
6182 if self.idlNode.type.nullable(): |
|
6183 freezeValue = CGIfWrapper(freezeValue, |
|
6184 "args.rval().isObject()") |
|
6185 postSteps += freezeValue.define() |
|
6186 postSteps += ("js::SetReservedSlot(reflector, %s, args.rval());\n" % |
|
6187 memberReservedSlot(self.idlNode)) |
|
6188 # For the case of Cached attributes, go ahead and preserve our |
|
6189 # wrapper if needed. We need to do this because otherwise the |
|
6190 # wrapper could get garbage-collected and the cached value would |
|
6191 # suddenly disappear, but the whole premise of cached values is that |
|
6192 # they never change without explicit action on someone's part. We |
|
6193 # don't do this for StoreInSlot, since those get dealt with during |
|
6194 # wrapper setup, and failure would involve us trying to clear an |
|
6195 # already-preserved wrapper. |
|
6196 if (self.idlNode.getExtendedAttribute("Cached") and |
|
6197 self.descriptor.wrapperCache): |
|
6198 postSteps += "PreserveWrapper(self);\n" |
|
6199 |
|
6200 wrapCode = fill( |
|
6201 """ |
|
6202 { // Make sure we wrap and store in the slot in reflector's compartment |
|
6203 JSAutoCompartment ac(cx, reflector); |
|
6204 do { // block we break out of when done wrapping |
|
6205 $*{wrapCode} |
|
6206 } while (0); |
|
6207 $*{postSteps} |
|
6208 } |
|
6209 // And now make sure args.rval() is in the caller compartment |
|
6210 return ${maybeWrap}(cx, args.rval()); |
|
6211 """, |
|
6212 wrapCode=wrapCode, |
|
6213 postSteps=postSteps, |
|
6214 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type)) |
|
6215 return wrapCode |
|
6216 |
|
6217 def getErrorReport(self): |
|
6218 jsImplemented = "" |
|
6219 if self.descriptor.interface.isJSImplemented(): |
|
6220 jsImplemented = ", true" |
|
6221 return CGGeneric('return ThrowMethodFailedWithDetails(cx, rv, "%s", "%s"%s);\n' |
|
6222 % (self.descriptor.interface.identifier.name, |
|
6223 self.idlNode.identifier.name, |
|
6224 jsImplemented)) |
|
6225 |
|
6226 def define(self): |
|
6227 return (self.cgRoot.define() + self.wrap_return_value()) |
|
6228 |
|
6229 |
|
6230 class CGSwitch(CGList): |
|
6231 """ |
|
6232 A class to generate code for a switch statement. |
|
6233 |
|
6234 Takes three constructor arguments: an expression, a list of cases, |
|
6235 and an optional default. |
|
6236 |
|
6237 Each case is a CGCase. The default is a CGThing for the body of |
|
6238 the default case, if any. |
|
6239 """ |
|
6240 def __init__(self, expression, cases, default=None): |
|
6241 CGList.__init__(self, [CGIndenter(c) for c in cases]) |
|
6242 self.prepend(CGGeneric("switch (" + expression + ") {\n")) |
|
6243 if default is not None: |
|
6244 self.append( |
|
6245 CGIndenter( |
|
6246 CGWrapper( |
|
6247 CGIndenter(default), |
|
6248 pre="default: {\n", |
|
6249 post=" break;\n}\n"))) |
|
6250 |
|
6251 self.append(CGGeneric("}\n")) |
|
6252 |
|
6253 |
|
6254 class CGCase(CGList): |
|
6255 """ |
|
6256 A class to generate code for a case statement. |
|
6257 |
|
6258 Takes three constructor arguments: an expression, a CGThing for |
|
6259 the body (allowed to be None if there is no body), and an optional |
|
6260 argument (defaulting to False) for whether to fall through. |
|
6261 """ |
|
6262 def __init__(self, expression, body, fallThrough=False): |
|
6263 CGList.__init__(self, []) |
|
6264 self.append(CGGeneric("case " + expression + ": {\n")) |
|
6265 bodyList = CGList([body]) |
|
6266 if fallThrough: |
|
6267 bodyList.append(CGGeneric("/* Fall through */\n")) |
|
6268 else: |
|
6269 bodyList.append(CGGeneric("break;\n")) |
|
6270 self.append(CGIndenter(bodyList)) |
|
6271 self.append(CGGeneric("}\n")) |
|
6272 |
|
6273 |
|
6274 class CGMethodCall(CGThing): |
|
6275 """ |
|
6276 A class to generate selection of a method signature from a set of |
|
6277 signatures and generation of a call to that signature. |
|
6278 """ |
|
6279 def __init__(self, nativeMethodName, static, descriptor, method, |
|
6280 isConstructor=False, constructorName=None): |
|
6281 CGThing.__init__(self) |
|
6282 |
|
6283 if isConstructor: |
|
6284 assert constructorName is not None |
|
6285 methodName = constructorName |
|
6286 else: |
|
6287 methodName = "%s.%s" % (descriptor.interface.identifier.name, method.identifier.name) |
|
6288 argDesc = "argument %d of " + methodName |
|
6289 |
|
6290 def requiredArgCount(signature): |
|
6291 arguments = signature[1] |
|
6292 if len(arguments) == 0: |
|
6293 return 0 |
|
6294 requiredArgs = len(arguments) |
|
6295 while requiredArgs and arguments[requiredArgs-1].optional: |
|
6296 requiredArgs -= 1 |
|
6297 return requiredArgs |
|
6298 |
|
6299 def getPerSignatureCall(signature, argConversionStartsAt=0): |
|
6300 return CGPerSignatureCall(signature[0], signature[1], |
|
6301 nativeMethodName, static, descriptor, |
|
6302 method, |
|
6303 argConversionStartsAt=argConversionStartsAt, |
|
6304 isConstructor=isConstructor) |
|
6305 |
|
6306 signatures = method.signatures() |
|
6307 if len(signatures) == 1: |
|
6308 # Special case: we can just do a per-signature method call |
|
6309 # here for our one signature and not worry about switching |
|
6310 # on anything. |
|
6311 signature = signatures[0] |
|
6312 self.cgRoot = CGList([CGIndenter(getPerSignatureCall(signature))]) |
|
6313 requiredArgs = requiredArgCount(signature) |
|
6314 |
|
6315 if requiredArgs > 0: |
|
6316 code = indent(fill( # BOGUS extra blank line |
|
6317 """ |
|
6318 |
|
6319 if (args.length() < ${requiredArgs}) { |
|
6320 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}"); |
|
6321 } |
|
6322 """, |
|
6323 requiredArgs=requiredArgs, |
|
6324 methodName=methodName)) |
|
6325 self.cgRoot.prepend(CGGeneric(code)) |
|
6326 return |
|
6327 |
|
6328 # Need to find the right overload |
|
6329 maxArgCount = method.maxArgCount |
|
6330 allowedArgCounts = method.allowedArgCounts |
|
6331 |
|
6332 argCountCases = [] |
|
6333 for argCountIdx, argCount in enumerate(allowedArgCounts): |
|
6334 possibleSignatures = method.signaturesForArgCount(argCount) |
|
6335 |
|
6336 # Try to optimize away cases when the next argCount in the list |
|
6337 # will have the same code as us; if it does, we can fall through to |
|
6338 # that case. |
|
6339 if argCountIdx+1 < len(allowedArgCounts): |
|
6340 nextPossibleSignatures = \ |
|
6341 method.signaturesForArgCount(allowedArgCounts[argCountIdx+1]) |
|
6342 else: |
|
6343 nextPossibleSignatures = None |
|
6344 if possibleSignatures == nextPossibleSignatures: |
|
6345 # Same set of signatures means we better have the same |
|
6346 # distinguishing index. So we can in fact just fall through to |
|
6347 # the next case here. |
|
6348 assert (len(possibleSignatures) == 1 or |
|
6349 (method.distinguishingIndexForArgCount(argCount) == |
|
6350 method.distinguishingIndexForArgCount(allowedArgCounts[argCountIdx+1]))) |
|
6351 argCountCases.append(CGCase(str(argCount), None, True)) |
|
6352 continue |
|
6353 |
|
6354 if len(possibleSignatures) == 1: |
|
6355 # easy case! |
|
6356 signature = possibleSignatures[0] |
|
6357 argCountCases.append( |
|
6358 CGCase(str(argCount), getPerSignatureCall(signature))) |
|
6359 continue |
|
6360 |
|
6361 distinguishingIndex = method.distinguishingIndexForArgCount(argCount) |
|
6362 |
|
6363 def distinguishingArgument(signature): |
|
6364 args = signature[1] |
|
6365 if distinguishingIndex < len(args): |
|
6366 return args[distinguishingIndex] |
|
6367 assert args[-1].variadic |
|
6368 return args[-1] |
|
6369 |
|
6370 def distinguishingType(signature): |
|
6371 return distinguishingArgument(signature).type |
|
6372 |
|
6373 for sig in possibleSignatures: |
|
6374 # We should not have "any" args at distinguishingIndex, |
|
6375 # since we have multiple possible signatures remaining, |
|
6376 # but "any" is never distinguishable from anything else. |
|
6377 assert not distinguishingType(sig).isAny() |
|
6378 # We can't handle unions at the distinguishing index. |
|
6379 if distinguishingType(sig).isUnion(): |
|
6380 raise TypeError("No support for unions as distinguishing " |
|
6381 "arguments yet: %s" % |
|
6382 distinguishingArgument(sig).location) |
|
6383 # We don't support variadics as the distinguishingArgument yet. |
|
6384 # If you want to add support, consider this case: |
|
6385 # |
|
6386 # void(long... foo); |
|
6387 # void(long bar, Int32Array baz); |
|
6388 # |
|
6389 # in which we have to convert argument 0 to long before picking |
|
6390 # an overload... but all the variadic stuff needs to go into a |
|
6391 # single array in case we pick that overload, so we have to have |
|
6392 # machinery for converting argument 0 to long and then either |
|
6393 # placing it in the variadic bit or not. Or something. We may |
|
6394 # be able to loosen this restriction if the variadic arg is in |
|
6395 # fact at distinguishingIndex, perhaps. Would need to |
|
6396 # double-check. |
|
6397 if distinguishingArgument(sig).variadic: |
|
6398 raise TypeError("No support for variadics as distinguishing " |
|
6399 "arguments yet: %s" % |
|
6400 distinguishingArgument(sig).location) |
|
6401 |
|
6402 # Convert all our arguments up to the distinguishing index. |
|
6403 # Doesn't matter which of the possible signatures we use, since |
|
6404 # they all have the same types up to that point; just use |
|
6405 # possibleSignatures[0] |
|
6406 caseBody = [CGArgumentConverter(possibleSignatures[0][1][i], |
|
6407 i, descriptor, |
|
6408 argDesc % (i + 1)) |
|
6409 for i in range(0, distinguishingIndex)] |
|
6410 |
|
6411 # Select the right overload from our set. |
|
6412 distinguishingArg = "args[%d]" % distinguishingIndex |
|
6413 |
|
6414 def tryCall(signature, indent, isDefinitelyObject=False, |
|
6415 isNullOrUndefined=False): |
|
6416 assert not isDefinitelyObject or not isNullOrUndefined |
|
6417 assert isDefinitelyObject or isNullOrUndefined |
|
6418 if isDefinitelyObject: |
|
6419 failureCode = "break;\n" |
|
6420 else: |
|
6421 failureCode = None |
|
6422 type = distinguishingType(signature) |
|
6423 # The argument at index distinguishingIndex can't possibly be |
|
6424 # unset here, because we've already checked that argc is large |
|
6425 # enough that we can examine this argument. But note that we |
|
6426 # still want to claim that optional arguments are optional, in |
|
6427 # case undefined was passed in. |
|
6428 argIsOptional = (distinguishingArgument(signature).optional and |
|
6429 not distinguishingArgument(signature).defaultValue) |
|
6430 testCode = instantiateJSToNativeConversion( |
|
6431 getJSToNativeConversionInfo(type, descriptor, |
|
6432 failureCode=failureCode, |
|
6433 isDefinitelyObject=isDefinitelyObject, |
|
6434 isNullOrUndefined=isNullOrUndefined, |
|
6435 isOptional=argIsOptional, |
|
6436 sourceDescription=(argDesc % (distinguishingIndex + 1))), |
|
6437 { |
|
6438 "declName": "arg%d" % distinguishingIndex, |
|
6439 "holderName": ("arg%d" % distinguishingIndex) + "_holder", |
|
6440 "val": distinguishingArg, |
|
6441 "mutableVal": distinguishingArg, |
|
6442 "obj": "obj", |
|
6443 "haveValue": "args.hasDefined(%d)" % distinguishingIndex |
|
6444 }, |
|
6445 checkForValue=argIsOptional) |
|
6446 caseBody.append(CGIndenter(testCode, indent)) |
|
6447 |
|
6448 # If we got this far, we know we unwrapped to the right |
|
6449 # C++ type, so just do the call. Start conversion with |
|
6450 # distinguishingIndex + 1, since we already converted |
|
6451 # distinguishingIndex. |
|
6452 caseBody.append(CGIndenter( |
|
6453 getPerSignatureCall(signature, distinguishingIndex + 1), |
|
6454 indent)) |
|
6455 |
|
6456 def hasConditionalConversion(type): |
|
6457 """ |
|
6458 Return whether the argument conversion for this type will be |
|
6459 conditional on the type of incoming JS value. For example, for |
|
6460 interface types the conversion is conditional on the incoming |
|
6461 value being isObject(). |
|
6462 |
|
6463 For the types for which this returns false, we do not have to |
|
6464 output extra isUndefined() or isNullOrUndefined() cases, because |
|
6465 null/undefined values will just fall through into our |
|
6466 unconditional conversion. |
|
6467 """ |
|
6468 if type.isString() or type.isEnum(): |
|
6469 return False |
|
6470 if type.isBoolean(): |
|
6471 distinguishingTypes = (distinguishingType(s) for s in |
|
6472 possibleSignatures) |
|
6473 return any(t.isString() or t.isEnum() or t.isNumeric() |
|
6474 for t in distinguishingTypes) |
|
6475 if type.isNumeric(): |
|
6476 distinguishingTypes = (distinguishingType(s) for s in |
|
6477 possibleSignatures) |
|
6478 return any(t.isString() or t.isEnum() |
|
6479 for t in distinguishingTypes) |
|
6480 return True |
|
6481 |
|
6482 def needsNullOrUndefinedCase(type): |
|
6483 """ |
|
6484 Return true if the type needs a special isNullOrUndefined() case |
|
6485 """ |
|
6486 return ((type.nullable() and |
|
6487 hasConditionalConversion(type)) or |
|
6488 type.isDictionary()) |
|
6489 |
|
6490 # First check for undefined and optional distinguishing arguments |
|
6491 # and output a special branch for that case. Note that we don't |
|
6492 # use distinguishingArgument here because we actualy want to |
|
6493 # exclude variadic arguments. Also note that we skip this check if |
|
6494 # we plan to output a isNullOrUndefined() special case for this |
|
6495 # argument anyway, since that will subsume our isUndefined() check. |
|
6496 # This is safe, because there can be at most one nullable |
|
6497 # distinguishing argument, so if we're it we'll definitely get |
|
6498 # picked up by the nullable handling. Also, we can skip this check |
|
6499 # if the argument has an unconditional conversion later on. |
|
6500 undefSigs = [s for s in possibleSignatures if |
|
6501 distinguishingIndex < len(s[1]) and |
|
6502 s[1][distinguishingIndex].optional and |
|
6503 hasConditionalConversion(s[1][distinguishingIndex].type) and |
|
6504 not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)] |
|
6505 # Can't have multiple signatures with an optional argument at the |
|
6506 # same index. |
|
6507 assert len(undefSigs) < 2 |
|
6508 if len(undefSigs) > 0: |
|
6509 caseBody.append(CGGeneric("if (%s.isUndefined()) {\n" % |
|
6510 distinguishingArg)) |
|
6511 tryCall(undefSigs[0], 2, isNullOrUndefined=True) |
|
6512 caseBody.append(CGGeneric("}\n")) |
|
6513 |
|
6514 # Next, check for null or undefined. That means looking for |
|
6515 # nullable arguments at the distinguishing index and outputting a |
|
6516 # separate branch for them. But if the nullable argument has an |
|
6517 # unconditional conversion, we don't need to do that. The reason |
|
6518 # for that is that at most one argument at the distinguishing index |
|
6519 # is nullable (since two nullable arguments are not |
|
6520 # distinguishable), and null/undefined values will always fall |
|
6521 # through to the unconditional conversion we have, if any, since |
|
6522 # they will fail whatever the conditions on the input value are for |
|
6523 # our other conversions. |
|
6524 nullOrUndefSigs = [s for s in possibleSignatures |
|
6525 if needsNullOrUndefinedCase(distinguishingType(s))] |
|
6526 # Can't have multiple nullable types here |
|
6527 assert len(nullOrUndefSigs) < 2 |
|
6528 if len(nullOrUndefSigs) > 0: |
|
6529 caseBody.append(CGGeneric("if (%s.isNullOrUndefined()) {\n" % |
|
6530 distinguishingArg)) |
|
6531 tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True) |
|
6532 caseBody.append(CGGeneric("}\n")) |
|
6533 |
|
6534 # Now check for distinguishingArg being various kinds of objects. |
|
6535 # The spec says to check for the following things in order: |
|
6536 # 1) A platform object that's not a platform array object, being |
|
6537 # passed to an interface or "object" arg. |
|
6538 # 2) A Date object being passed to a Date or "object" arg. |
|
6539 # 3) A RegExp object being passed to a RegExp or "object" arg. |
|
6540 # 4) A callable object being passed to a callback or "object" arg. |
|
6541 # 5) Any non-Date and non-RegExp object being passed to a |
|
6542 # array or sequence or callback interface dictionary or |
|
6543 # "object" arg. |
|
6544 # |
|
6545 # We can can coalesce these five cases together, as long as we make |
|
6546 # sure to check whether our object works as an interface argument |
|
6547 # before checking whether it works as an arraylike or dictionary or |
|
6548 # callback function or callback interface. |
|
6549 |
|
6550 # First grab all the overloads that have a non-callback interface |
|
6551 # (which includes typed arrays and arraybuffers) at the |
|
6552 # distinguishing index. We can also include the ones that have an |
|
6553 # "object" here, since if those are present no other object-typed |
|
6554 # argument will be. |
|
6555 objectSigs = [ |
|
6556 s for s in possibleSignatures |
|
6557 if (distinguishingType(s).isObject() or |
|
6558 distinguishingType(s).isNonCallbackInterface())] |
|
6559 |
|
6560 # And all the overloads that take Date |
|
6561 objectSigs.extend(s for s in possibleSignatures |
|
6562 if distinguishingType(s).isDate()) |
|
6563 |
|
6564 # And all the overloads that take callbacks |
|
6565 objectSigs.extend(s for s in possibleSignatures |
|
6566 if distinguishingType(s).isCallback()) |
|
6567 |
|
6568 # Now append all the overloads that take an array or sequence or |
|
6569 # dictionary or callback interface: |
|
6570 objectSigs.extend(s for s in possibleSignatures |
|
6571 if (distinguishingType(s).isArray() or |
|
6572 distinguishingType(s).isSequence() or |
|
6573 distinguishingType(s).isDictionary() or |
|
6574 distinguishingType(s).isCallbackInterface())) |
|
6575 |
|
6576 # There might be more than one thing in objectSigs; we need to check |
|
6577 # which ones we unwrap to. |
|
6578 if len(objectSigs) > 0: |
|
6579 # Here it's enough to guard on our argument being an object. The |
|
6580 # code for unwrapping non-callback interfaces, typed arrays, |
|
6581 # sequences, arrays, and Dates will just bail out and move on to |
|
6582 # the next overload if the object fails to unwrap correctly, |
|
6583 # while "object" accepts any object anyway. We could even not |
|
6584 # do the isObject() check up front here, but in cases where we |
|
6585 # have multiple object overloads it makes sense to do it only |
|
6586 # once instead of for each overload. That will also allow the |
|
6587 # unwrapping test to skip having to do codegen for the |
|
6588 # null-or-undefined case, which we already handled above. |
|
6589 caseBody.append(CGGeneric("if (%s.isObject()) {\n" % |
|
6590 distinguishingArg)) |
|
6591 for sig in objectSigs: |
|
6592 caseBody.append(CGIndenter(CGGeneric("do {\n"))) |
|
6593 # Indent by 4, since we need to indent further |
|
6594 # than our "do" statement |
|
6595 tryCall(sig, 4, isDefinitelyObject=True) |
|
6596 caseBody.append(CGIndenter(CGGeneric("} while (0);\n"))) |
|
6597 |
|
6598 caseBody.append(CGGeneric("}\n")) |
|
6599 |
|
6600 # Now we only have to consider booleans, numerics, and strings. If |
|
6601 # we only have one of them, then we can just output it. But if not, |
|
6602 # then we need to output some of the cases conditionally: if we have |
|
6603 # a string overload, then boolean and numeric are conditional, and |
|
6604 # if not then boolean is conditional if we have a numeric overload. |
|
6605 def findUniqueSignature(filterLambda): |
|
6606 sigs = filter(filterLambda, possibleSignatures) |
|
6607 assert len(sigs) < 2 |
|
6608 if len(sigs) > 0: |
|
6609 return sigs[0] |
|
6610 return None |
|
6611 |
|
6612 stringSignature = findUniqueSignature( |
|
6613 lambda s: (distinguishingType(s).isString() or |
|
6614 distinguishingType(s).isEnum())) |
|
6615 numericSignature = findUniqueSignature( |
|
6616 lambda s: distinguishingType(s).isNumeric()) |
|
6617 booleanSignature = findUniqueSignature( |
|
6618 lambda s: distinguishingType(s).isBoolean()) |
|
6619 |
|
6620 if stringSignature or numericSignature: |
|
6621 booleanCondition = "%s.isBoolean()" |
|
6622 else: |
|
6623 booleanCondition = None |
|
6624 |
|
6625 if stringSignature: |
|
6626 numericCondition = "%s.isNumber()" |
|
6627 else: |
|
6628 numericCondition = None |
|
6629 |
|
6630 def addCase(sig, condition): |
|
6631 sigCode = getPerSignatureCall(sig, distinguishingIndex) |
|
6632 if condition: |
|
6633 sigCode = CGIfWrapper(sigCode, |
|
6634 condition % distinguishingArg) |
|
6635 caseBody.append(sigCode) |
|
6636 |
|
6637 if booleanSignature: |
|
6638 addCase(booleanSignature, booleanCondition) |
|
6639 if numericSignature: |
|
6640 addCase(numericSignature, numericCondition) |
|
6641 if stringSignature: |
|
6642 addCase(stringSignature, None) |
|
6643 |
|
6644 if (not booleanSignature and not numericSignature and |
|
6645 not stringSignature): |
|
6646 # Just throw; we have no idea what we're supposed to |
|
6647 # do with this. |
|
6648 caseBody.append(CGGeneric( |
|
6649 'return ThrowErrorMessage(cx, MSG_OVERLOAD_RESOLUTION_FAILED, "%d", "%d", "%s");\n' % |
|
6650 (distinguishingIndex + 1, argCount, methodName))) |
|
6651 |
|
6652 argCountCases.append(CGCase(str(argCount), CGList(caseBody))) |
|
6653 |
|
6654 overloadCGThings = [] |
|
6655 overloadCGThings.append( |
|
6656 CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" % |
|
6657 maxArgCount)) |
|
6658 overloadCGThings.append( |
|
6659 CGSwitch("argcount", |
|
6660 argCountCases, |
|
6661 # BOGUS extra blank line at end of default block |
|
6662 CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n\n' % |
|
6663 methodName))) |
|
6664 overloadCGThings.append( |
|
6665 CGGeneric('MOZ_CRASH("We have an always-returning default case");\n' |
|
6666 'return false;\n')) |
|
6667 self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings)), |
|
6668 pre="\n") |
|
6669 |
|
6670 def define(self): |
|
6671 return self.cgRoot.define() |
|
6672 |
|
6673 |
|
6674 class CGGetterCall(CGPerSignatureCall): |
|
6675 """ |
|
6676 A class to generate a native object getter call for a particular IDL |
|
6677 getter. |
|
6678 """ |
|
6679 def __init__(self, returnType, nativeMethodName, descriptor, attr): |
|
6680 CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName, |
|
6681 attr.isStatic(), descriptor, attr, |
|
6682 getter=True) |
|
6683 |
|
6684 |
|
6685 class FakeArgument(): |
|
6686 """ |
|
6687 A class that quacks like an IDLArgument. This is used to make |
|
6688 setters look like method calls or for special operations. |
|
6689 """ |
|
6690 def __init__(self, type, interfaceMember, name="arg", allowTreatNonCallableAsNull=False): |
|
6691 self.type = type |
|
6692 self.optional = False |
|
6693 self.variadic = False |
|
6694 self.defaultValue = None |
|
6695 self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull |
|
6696 self.treatNullAs = interfaceMember.treatNullAs |
|
6697 if isinstance(interfaceMember, IDLAttribute): |
|
6698 self.enforceRange = interfaceMember.enforceRange |
|
6699 self.clamp = interfaceMember.clamp |
|
6700 else: |
|
6701 self.enforceRange = False |
|
6702 self.clamp = False |
|
6703 |
|
6704 class FakeIdentifier(): |
|
6705 def __init__(self): |
|
6706 self.name = name |
|
6707 self.identifier = FakeIdentifier() |
|
6708 |
|
6709 def allowTreatNonCallableAsNull(self): |
|
6710 return self._allowTreatNonCallableAsNull |
|
6711 |
|
6712 |
|
6713 class CGSetterCall(CGPerSignatureCall): |
|
6714 """ |
|
6715 A class to generate a native object setter call for a particular IDL |
|
6716 setter. |
|
6717 """ |
|
6718 def __init__(self, argType, nativeMethodName, descriptor, attr): |
|
6719 CGPerSignatureCall.__init__(self, None, [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)], |
|
6720 nativeMethodName, attr.isStatic(), |
|
6721 descriptor, attr, setter=True) |
|
6722 |
|
6723 def wrap_return_value(self): |
|
6724 attr = self.idlNode |
|
6725 if self.descriptor.wrapperCache and attr.slotIndex is not None: |
|
6726 if attr.getExtendedAttribute("StoreInSlot"): |
|
6727 args = "cx, self" |
|
6728 else: |
|
6729 args = "self" |
|
6730 clearSlot = ("ClearCached%sValue(%s);\n" % |
|
6731 (MakeNativeName(self.idlNode.identifier.name), args)) |
|
6732 else: |
|
6733 clearSlot = "" |
|
6734 |
|
6735 # We have no return value |
|
6736 return ("\n" |
|
6737 "%s" |
|
6738 "return true;\n" % clearSlot) |
|
6739 |
|
6740 |
|
6741 class CGAbstractBindingMethod(CGAbstractStaticMethod): |
|
6742 """ |
|
6743 Common class to generate the JSNatives for all our methods, getters, and |
|
6744 setters. This will generate the function declaration and unwrap the |
|
6745 |this| object. Subclasses are expected to override the generate_code |
|
6746 function to do the rest of the work. This function should return a |
|
6747 CGThing which is already properly indented. |
|
6748 |
|
6749 getThisObj should be code for getting a JSObject* for the binding |
|
6750 object. If this is None, we will auto-generate code based on |
|
6751 descriptor to do the right thing. "" can be passed in if the |
|
6752 binding object is already stored in 'obj'. |
|
6753 |
|
6754 callArgs should be code for getting a JS::CallArgs into a variable |
|
6755 called 'args'. This can be "" if there is already such a variable |
|
6756 around. |
|
6757 |
|
6758 If allowCrossOriginThis is true, then this-unwrapping will first do an |
|
6759 UncheckedUnwrap and after that operate on the result. |
|
6760 """ |
|
6761 def __init__(self, descriptor, name, args, unwrapFailureCode=None, |
|
6762 getThisObj=None, |
|
6763 callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n", |
|
6764 allowCrossOriginThis=False): |
|
6765 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) |
|
6766 |
|
6767 if unwrapFailureCode is None: |
|
6768 self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, "Value", "%s");\n' % descriptor.interface.identifier.name |
|
6769 else: |
|
6770 self.unwrapFailureCode = unwrapFailureCode |
|
6771 |
|
6772 if getThisObj == "": |
|
6773 self.getThisObj = None |
|
6774 else: |
|
6775 if getThisObj is None: |
|
6776 if descriptor.interface.isOnGlobalProtoChain(): |
|
6777 ensureCondition = "!args.thisv().isNullOrUndefined() && !args.thisv().isObject()" |
|
6778 getThisObj = "args.thisv().isObject() ? &args.thisv().toObject() : js::GetGlobalForObjectCrossCompartment(&args.callee())" |
|
6779 else: |
|
6780 ensureCondition = "!args.thisv().isObject()" |
|
6781 getThisObj = "&args.thisv().toObject()" |
|
6782 unwrapFailureCode = self.unwrapFailureCode % {'securityError': 'false'} |
|
6783 ensureThisObj = CGIfWrapper(CGGeneric(unwrapFailureCode), |
|
6784 ensureCondition) |
|
6785 else: |
|
6786 ensureThisObj = None |
|
6787 self.getThisObj = CGList( |
|
6788 [ensureThisObj, |
|
6789 CGGeneric("JS::Rooted<JSObject*> obj(cx, %s);\n" % |
|
6790 getThisObj)]) |
|
6791 self.callArgs = callArgs |
|
6792 self.allowCrossOriginThis = allowCrossOriginThis |
|
6793 |
|
6794 def definition_body(self): |
|
6795 body = self.callArgs |
|
6796 if self.getThisObj is not None: |
|
6797 body += self.getThisObj.define() + "\n" |
|
6798 body += "%s* self;\n" % self.descriptor.nativeType |
|
6799 |
|
6800 # Our descriptor might claim that we're not castable, simply because |
|
6801 # we're someone's consequential interface. But for this-unwrapping, we |
|
6802 # know that we're the real deal. So fake a descriptor here for |
|
6803 # consumption by CastableObjectUnwrapper. |
|
6804 body += str(CastableObjectUnwrapper( |
|
6805 self.descriptor, |
|
6806 "obj", "self", self.unwrapFailureCode, |
|
6807 allowCrossOriginObj=self.allowCrossOriginThis)) |
|
6808 |
|
6809 return indent(body) + self.generate_code().define() |
|
6810 |
|
6811 def generate_code(self): |
|
6812 assert False # Override me |
|
6813 |
|
6814 |
|
6815 class CGAbstractStaticBindingMethod(CGAbstractStaticMethod): |
|
6816 """ |
|
6817 Common class to generate the JSNatives for all our static methods, getters |
|
6818 and setters. This will generate the function declaration and unwrap the |
|
6819 global object. Subclasses are expected to override the generate_code |
|
6820 function to do the rest of the work. This function should return a |
|
6821 CGThing which is already properly indented. |
|
6822 """ |
|
6823 def __init__(self, descriptor, name): |
|
6824 args = [Argument('JSContext*', 'cx'), |
|
6825 Argument('unsigned', 'argc'), |
|
6826 Argument('JS::Value*', 'vp')] |
|
6827 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) |
|
6828 |
|
6829 def definition_body(self): |
|
6830 # Make sure that "obj" is in the same compartment as "cx", since we'll |
|
6831 # later use it to wrap return values. |
|
6832 unwrap = indent(dedent(""" |
|
6833 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
|
6834 JS::Rooted<JSObject*> obj(cx, &args.callee()); |
|
6835 |
|
6836 """)) |
|
6837 return unwrap + self.generate_code().define() |
|
6838 |
|
6839 def generate_code(self): |
|
6840 assert False # Override me |
|
6841 |
|
6842 |
|
6843 def MakeNativeName(name): |
|
6844 return name[0].upper() + name[1:] |
|
6845 |
|
6846 |
|
6847 class CGGenericMethod(CGAbstractBindingMethod): |
|
6848 """ |
|
6849 A class for generating the C++ code for an IDL method. |
|
6850 |
|
6851 If allowCrossOriginThis is true, then this-unwrapping will first do an |
|
6852 UncheckedUnwrap and after that operate on the result. |
|
6853 """ |
|
6854 def __init__(self, descriptor, allowCrossOriginThis=False): |
|
6855 args = [Argument('JSContext*', 'cx'), |
|
6856 Argument('unsigned', 'argc'), |
|
6857 Argument('JS::Value*', 'vp')] |
|
6858 unwrapFailureCode = ( |
|
6859 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForMethod(%%(securityError)s), "%s");\n' % |
|
6860 descriptor.interface.identifier.name) |
|
6861 name = "genericCrossOriginMethod" if allowCrossOriginThis else "genericMethod" |
|
6862 CGAbstractBindingMethod.__init__(self, descriptor, name, |
|
6863 args, |
|
6864 unwrapFailureCode=unwrapFailureCode, |
|
6865 allowCrossOriginThis=allowCrossOriginThis) |
|
6866 |
|
6867 def generate_code(self): |
|
6868 return CGGeneric(indent(dedent(""" |
|
6869 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
|
6870 MOZ_ASSERT(info->type() == JSJitInfo::Method); |
|
6871 JSJitMethodOp method = info->method; |
|
6872 return method(cx, obj, self, JSJitMethodCallArgs(args)); |
|
6873 """))) |
|
6874 |
|
6875 |
|
6876 class CGSpecializedMethod(CGAbstractStaticMethod): |
|
6877 """ |
|
6878 A class for generating the C++ code for a specialized method that the JIT |
|
6879 can call with lower overhead. |
|
6880 """ |
|
6881 def __init__(self, descriptor, method): |
|
6882 self.method = method |
|
6883 name = CppKeywords.checkMethodName(method.identifier.name) |
|
6884 args = [Argument('JSContext*', 'cx'), |
|
6885 Argument('JS::Handle<JSObject*>', 'obj'), |
|
6886 Argument('%s*' % descriptor.nativeType, 'self'), |
|
6887 Argument('const JSJitMethodCallArgs&', 'args')] |
|
6888 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args) |
|
6889 |
|
6890 def definition_body(self): |
|
6891 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, |
|
6892 self.method) |
|
6893 return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor, |
|
6894 self.method).define() |
|
6895 |
|
6896 @staticmethod |
|
6897 def makeNativeName(descriptor, method): |
|
6898 name = method.identifier.name |
|
6899 return MakeNativeName(descriptor.binaryNames.get(name, name)) |
|
6900 |
|
6901 |
|
6902 class CGMethodPromiseWrapper(CGAbstractStaticMethod): |
|
6903 """ |
|
6904 A class for generating a wrapper around another method that will |
|
6905 convert exceptions to promises. |
|
6906 """ |
|
6907 def __init__(self, descriptor, methodToWrap): |
|
6908 self.method = methodToWrap |
|
6909 name = self.makeName(methodToWrap.name) |
|
6910 args = list(methodToWrap.args) |
|
6911 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args) |
|
6912 |
|
6913 def definition_body(self): |
|
6914 return indent(fill( |
|
6915 """ |
|
6916 // Make sure to save the callee before someone maybe messes |
|
6917 // with rval(). |
|
6918 JS::Rooted<JSObject*> callee(cx, &args.callee()); |
|
6919 bool ok = ${methodName}(${args}); |
|
6920 if (ok) { |
|
6921 return true; |
|
6922 } |
|
6923 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee), |
|
6924 args.rval()); |
|
6925 """, |
|
6926 methodName=self.method.name, |
|
6927 args=", ".join(arg.name for arg in self.args))) |
|
6928 |
|
6929 @staticmethod |
|
6930 def makeName(methodName): |
|
6931 return methodName + "_promiseWrapper" |
|
6932 |
|
6933 |
|
6934 class CGJsonifierMethod(CGSpecializedMethod): |
|
6935 def __init__(self, descriptor, method): |
|
6936 assert method.isJsonifier() |
|
6937 CGSpecializedMethod.__init__(self, descriptor, method) |
|
6938 |
|
6939 def definition_body(self): |
|
6940 ret = dedent(""" |
|
6941 JS::Rooted<JSObject*> result(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); |
|
6942 if (!result) { |
|
6943 return false; |
|
6944 } |
|
6945 """) |
|
6946 for m in self.descriptor.interface.members: |
|
6947 if m.isAttr() and not m.isStatic() and m.type.isSerializable(): |
|
6948 ret += fill( |
|
6949 """ |
|
6950 { // scope for "temp" |
|
6951 JS::Rooted<JS::Value> temp(cx); |
|
6952 if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) { |
|
6953 return false; |
|
6954 } |
|
6955 if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) { |
|
6956 return false; |
|
6957 } |
|
6958 } |
|
6959 """, |
|
6960 name=m.identifier.name) |
|
6961 |
|
6962 ret += ('args.rval().setObject(*result);\n' |
|
6963 'return true;\n') |
|
6964 return indent(ret) |
|
6965 |
|
6966 |
|
6967 class CGLegacyCallHook(CGAbstractBindingMethod): |
|
6968 """ |
|
6969 Call hook for our object |
|
6970 """ |
|
6971 def __init__(self, descriptor): |
|
6972 self._legacycaller = descriptor.operations["LegacyCaller"] |
|
6973 args = [Argument('JSContext*', 'cx'), |
|
6974 Argument('unsigned', 'argc'), |
|
6975 Argument('JS::Value*', 'vp')] |
|
6976 # Our "self" is actually the callee in this case, not the thisval. |
|
6977 CGAbstractBindingMethod.__init__( |
|
6978 self, descriptor, LEGACYCALLER_HOOK_NAME, |
|
6979 args, getThisObj="&args.callee()") |
|
6980 |
|
6981 def define(self): |
|
6982 if not self._legacycaller: |
|
6983 return "" |
|
6984 return CGAbstractBindingMethod.define(self) |
|
6985 |
|
6986 def generate_code(self): |
|
6987 name = self._legacycaller.identifier.name |
|
6988 nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name)) |
|
6989 return CGMethodCall(nativeName, False, self.descriptor, |
|
6990 self._legacycaller) |
|
6991 |
|
6992 |
|
6993 class CGNewResolveHook(CGAbstractBindingMethod): |
|
6994 """ |
|
6995 NewResolve hook for objects with custom hooks. |
|
6996 """ |
|
6997 def __init__(self, descriptor): |
|
6998 assert descriptor.interface.getExtendedAttribute("NeedNewResolve") |
|
6999 |
|
7000 args = [Argument('JSContext*', 'cx'), |
|
7001 Argument('JS::Handle<JSObject*>', 'obj'), |
|
7002 Argument('JS::Handle<jsid>', 'id'), |
|
7003 Argument('JS::MutableHandle<JSObject*>', 'objp')] |
|
7004 # Our "self" is actually the "obj" argument in this case, not the thisval. |
|
7005 CGAbstractBindingMethod.__init__( |
|
7006 self, descriptor, NEWRESOLVE_HOOK_NAME, |
|
7007 args, getThisObj="", callArgs="") |
|
7008 |
|
7009 def generate_code(self): |
|
7010 return CGGeneric(indent(dedent(""" |
|
7011 JS::Rooted<JSPropertyDescriptor> desc(cx); |
|
7012 if (!self->DoNewResolve(cx, obj, id, &desc)) { |
|
7013 return false; |
|
7014 } |
|
7015 if (!desc.object()) { |
|
7016 return true; |
|
7017 } |
|
7018 // If desc.value() is undefined, then the DoNewResolve call |
|
7019 // has already defined it on the object. Don't try to also |
|
7020 // define it. |
|
7021 if (!desc.value().isUndefined() && |
|
7022 !JS_DefinePropertyById(cx, obj, id, desc.value(), |
|
7023 desc.getter(), desc.setter(), |
|
7024 desc.attributes())) { |
|
7025 return false; |
|
7026 } |
|
7027 objp.set(obj); |
|
7028 return true; |
|
7029 """))) |
|
7030 |
|
7031 def definition_body(self): |
|
7032 if self.descriptor.interface.getExtendedAttribute("Global"): |
|
7033 # Resolve standard classes |
|
7034 prefix = indent(dedent(""" |
|
7035 if (!ResolveGlobal(cx, obj, id, objp)) { |
|
7036 return false; |
|
7037 } |
|
7038 if (objp) { |
|
7039 return true; |
|
7040 } |
|
7041 |
|
7042 """)) |
|
7043 else: |
|
7044 prefix = "" |
|
7045 return prefix + CGAbstractBindingMethod.definition_body(self) |
|
7046 |
|
7047 |
|
7048 class CGEnumerateHook(CGAbstractBindingMethod): |
|
7049 """ |
|
7050 Enumerate hook for objects with custom hooks. |
|
7051 """ |
|
7052 def __init__(self, descriptor): |
|
7053 assert descriptor.interface.getExtendedAttribute("NeedNewResolve") |
|
7054 |
|
7055 args = [Argument('JSContext*', 'cx'), |
|
7056 Argument('JS::Handle<JSObject*>', 'obj')] |
|
7057 # Our "self" is actually the "obj" argument in this case, not the thisval. |
|
7058 CGAbstractBindingMethod.__init__( |
|
7059 self, descriptor, ENUMERATE_HOOK_NAME, |
|
7060 args, getThisObj="", callArgs="") |
|
7061 |
|
7062 def generate_code(self): |
|
7063 return CGGeneric(indent(dedent(""" |
|
7064 nsAutoTArray<nsString, 8> names; |
|
7065 ErrorResult rv; |
|
7066 self->GetOwnPropertyNames(cx, names, rv); |
|
7067 rv.WouldReportJSException(); |
|
7068 if (rv.Failed()) { |
|
7069 return ThrowMethodFailedWithDetails(cx, rv, "%s", "enumerate"); |
|
7070 } |
|
7071 JS::Rooted<JS::Value> dummy(cx); |
|
7072 for (uint32_t i = 0; i < names.Length(); ++i) { |
|
7073 if (!JS_LookupUCProperty(cx, obj, names[i].get(), names[i].Length(), &dummy)) { |
|
7074 return false; |
|
7075 } |
|
7076 } |
|
7077 return true; |
|
7078 """))) |
|
7079 |
|
7080 def definition_body(self): |
|
7081 if self.descriptor.interface.getExtendedAttribute("Global"): |
|
7082 # Enumerate standard classes |
|
7083 prefix = indent(dedent(""" |
|
7084 if (!EnumerateGlobal(cx, obj)) { |
|
7085 return false; |
|
7086 } |
|
7087 |
|
7088 """)) |
|
7089 else: |
|
7090 prefix = "" |
|
7091 return prefix + CGAbstractBindingMethod.definition_body(self) |
|
7092 |
|
7093 |
|
7094 class CppKeywords(): |
|
7095 """ |
|
7096 A class for checking if method names declared in webidl |
|
7097 are not in conflict with C++ keywords. |
|
7098 """ |
|
7099 keywords = frozenset([ |
|
7100 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'assert', 'auto', 'bitand', 'bitor', 'bool', |
|
7101 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const', |
|
7102 'constexpr', 'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double', |
|
7103 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'final', 'float', |
|
7104 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', |
|
7105 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'override', 'private', |
|
7106 'protected', 'public', 'register', 'reinterpret_cast', 'return', 'short', 'signed', |
|
7107 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this', |
|
7108 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union', |
|
7109 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq']) |
|
7110 |
|
7111 @staticmethod |
|
7112 def checkMethodName(name): |
|
7113 # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler. |
|
7114 # Bug 964892 and bug 963560. |
|
7115 if name in CppKeywords.keywords: |
|
7116 name = '_' + name + '_' |
|
7117 return name |
|
7118 |
|
7119 |
|
7120 class CGStaticMethod(CGAbstractStaticBindingMethod): |
|
7121 """ |
|
7122 A class for generating the C++ code for an IDL static method. |
|
7123 """ |
|
7124 def __init__(self, descriptor, method): |
|
7125 self.method = method |
|
7126 name = method.identifier.name |
|
7127 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) |
|
7128 |
|
7129 def generate_code(self): |
|
7130 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, |
|
7131 self.method) |
|
7132 return CGMethodCall(nativeName, True, self.descriptor, self.method) |
|
7133 |
|
7134 |
|
7135 class CGGenericGetter(CGAbstractBindingMethod): |
|
7136 """ |
|
7137 A class for generating the C++ code for an IDL attribute getter. |
|
7138 """ |
|
7139 def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False): |
|
7140 args = [Argument('JSContext*', 'cx'), |
|
7141 Argument('unsigned', 'argc'), |
|
7142 Argument('JS::Value*', 'vp')] |
|
7143 if lenientThis: |
|
7144 name = "genericLenientGetter" |
|
7145 unwrapFailureCode = dedent(""" |
|
7146 MOZ_ASSERT(!JS_IsExceptionPending(cx)); |
|
7147 if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) { |
|
7148 return false; |
|
7149 } |
|
7150 args.rval().set(JS::UndefinedValue()); |
|
7151 return true; |
|
7152 """) |
|
7153 else: |
|
7154 if allowCrossOriginThis: |
|
7155 name = "genericCrossOriginGetter" |
|
7156 else: |
|
7157 name = "genericGetter" |
|
7158 unwrapFailureCode = ( |
|
7159 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForGetter(%%(securityError)s), "%s");\n' % |
|
7160 descriptor.interface.identifier.name) |
|
7161 CGAbstractBindingMethod.__init__(self, descriptor, name, args, |
|
7162 unwrapFailureCode, |
|
7163 allowCrossOriginThis=allowCrossOriginThis) |
|
7164 |
|
7165 def generate_code(self): |
|
7166 return CGGeneric(indent(dedent(""" |
|
7167 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
|
7168 MOZ_ASSERT(info->type() == JSJitInfo::Getter); |
|
7169 JSJitGetterOp getter = info->getter; |
|
7170 return getter(cx, obj, self, JSJitGetterCallArgs(args)); |
|
7171 """))) |
|
7172 |
|
7173 |
|
7174 class CGSpecializedGetter(CGAbstractStaticMethod): |
|
7175 """ |
|
7176 A class for generating the code for a specialized attribute getter |
|
7177 that the JIT can call with lower overhead. |
|
7178 """ |
|
7179 def __init__(self, descriptor, attr): |
|
7180 self.attr = attr |
|
7181 name = 'get_' + attr.identifier.name |
|
7182 args = [ |
|
7183 Argument('JSContext*', 'cx'), |
|
7184 Argument('JS::Handle<JSObject*>', 'obj'), |
|
7185 Argument('%s*' % descriptor.nativeType, 'self'), |
|
7186 Argument('JSJitGetterCallArgs', 'args') |
|
7187 ] |
|
7188 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) |
|
7189 |
|
7190 def definition_body(self): |
|
7191 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, |
|
7192 self.attr) |
|
7193 if self.attr.slotIndex is not None: |
|
7194 if (self.descriptor.hasXPConnectImpls and |
|
7195 (self.descriptor.interface.identifier.name != 'Window' or |
|
7196 self.attr.identifier.name != 'document')): |
|
7197 raise TypeError("Interface '%s' has XPConnect impls, so we " |
|
7198 "can't use our slot for property '%s'!" % |
|
7199 (self.descriptor.interface.identifier.name, |
|
7200 self.attr.identifier.name)) |
|
7201 prefix = indent(fill( |
|
7202 """ |
|
7203 // Have to either root across the getter call or reget after. |
|
7204 JS::Rooted<JSObject*> reflector(cx); |
|
7205 // Safe to do an unchecked unwrap, since we've gotten this far. |
|
7206 // Also make sure to unwrap outer windows, since we want the |
|
7207 // real DOM object. |
|
7208 reflector = IsDOMObject(obj) ? obj : js::UncheckedUnwrap(obj, /* stopAtOuter = */ false); |
|
7209 { |
|
7210 // Scope for cachedVal |
|
7211 JS::Value cachedVal = js::GetReservedSlot(reflector, ${slot}); |
|
7212 if (!cachedVal.isUndefined()) { |
|
7213 args.rval().set(cachedVal); |
|
7214 // The cached value is in the compartment of reflector, |
|
7215 // so wrap into the caller compartment as needed. |
|
7216 return ${maybeWrap}(cx, args.rval()); |
|
7217 } |
|
7218 } |
|
7219 |
|
7220 """, |
|
7221 slot=memberReservedSlot(self.attr), |
|
7222 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))) |
|
7223 else: |
|
7224 prefix = "" |
|
7225 |
|
7226 return (prefix + |
|
7227 indent(CGGetterCall(self.attr.type, nativeName, |
|
7228 self.descriptor, self.attr).define())) |
|
7229 |
|
7230 @staticmethod |
|
7231 def makeNativeName(descriptor, attr): |
|
7232 name = attr.identifier.name |
|
7233 nativeName = MakeNativeName(descriptor.binaryNames.get(name, name)) |
|
7234 # resultOutParam does not depend on whether resultAlreadyAddRefed is set |
|
7235 _, resultOutParam, _, _ = getRetvalDeclarationForType(attr.type, |
|
7236 descriptor, |
|
7237 False) |
|
7238 infallible = ('infallible' in |
|
7239 descriptor.getExtendedAttributes(attr, getter=True)) |
|
7240 if resultOutParam or attr.type.nullable() or not infallible: |
|
7241 nativeName = "Get" + nativeName |
|
7242 return nativeName |
|
7243 |
|
7244 |
|
7245 class CGStaticGetter(CGAbstractStaticBindingMethod): |
|
7246 """ |
|
7247 A class for generating the C++ code for an IDL static attribute getter. |
|
7248 """ |
|
7249 def __init__(self, descriptor, attr): |
|
7250 self.attr = attr |
|
7251 name = 'get_' + attr.identifier.name |
|
7252 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) |
|
7253 |
|
7254 def generate_code(self): |
|
7255 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, |
|
7256 self.attr) |
|
7257 return CGIndenter(CGGetterCall(self.attr.type, nativeName, |
|
7258 self.descriptor, self.attr)) |
|
7259 |
|
7260 |
|
7261 class CGGenericSetter(CGAbstractBindingMethod): |
|
7262 """ |
|
7263 A class for generating the C++ code for an IDL attribute setter. |
|
7264 """ |
|
7265 def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False): |
|
7266 args = [Argument('JSContext*', 'cx'), |
|
7267 Argument('unsigned', 'argc'), |
|
7268 Argument('JS::Value*', 'vp')] |
|
7269 if lenientThis: |
|
7270 name = "genericLenientSetter" |
|
7271 unwrapFailureCode = dedent(""" |
|
7272 MOZ_ASSERT(!JS_IsExceptionPending(cx)); |
|
7273 if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) { |
|
7274 return false; |
|
7275 } |
|
7276 args.rval().set(JS::UndefinedValue()); |
|
7277 return true; |
|
7278 """) |
|
7279 else: |
|
7280 if allowCrossOriginThis: |
|
7281 name = "genericCrossOriginSetter" |
|
7282 else: |
|
7283 name = "genericSetter" |
|
7284 unwrapFailureCode = ( |
|
7285 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForSetter(%%(securityError)s), "%s");\n' % |
|
7286 descriptor.interface.identifier.name) |
|
7287 |
|
7288 CGAbstractBindingMethod.__init__(self, descriptor, name, args, |
|
7289 unwrapFailureCode, |
|
7290 allowCrossOriginThis=allowCrossOriginThis) |
|
7291 |
|
7292 def generate_code(self): |
|
7293 return CGGeneric(indent(fill( |
|
7294 """ |
|
7295 if (args.length() == 0) { |
|
7296 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} attribute setter"); |
|
7297 } |
|
7298 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
|
7299 MOZ_ASSERT(info->type() == JSJitInfo::Setter); |
|
7300 JSJitSetterOp setter = info->setter; |
|
7301 if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) { |
|
7302 return false; |
|
7303 } |
|
7304 args.rval().set(JSVAL_VOID); |
|
7305 return true; |
|
7306 """, |
|
7307 name=self.descriptor.interface.identifier.name))) |
|
7308 |
|
7309 |
|
7310 class CGSpecializedSetter(CGAbstractStaticMethod): |
|
7311 """ |
|
7312 A class for generating the code for a specialized attribute setter |
|
7313 that the JIT can call with lower overhead. |
|
7314 """ |
|
7315 def __init__(self, descriptor, attr): |
|
7316 self.attr = attr |
|
7317 name = 'set_' + attr.identifier.name |
|
7318 args = [Argument('JSContext*', 'cx'), |
|
7319 Argument('JS::Handle<JSObject*>', 'obj'), |
|
7320 Argument('%s*' % descriptor.nativeType, 'self'), |
|
7321 Argument('JSJitSetterCallArgs', 'args')] |
|
7322 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) |
|
7323 |
|
7324 def definition_body(self): |
|
7325 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, |
|
7326 self.attr) |
|
7327 return CGIndenter(CGSetterCall(self.attr.type, nativeName, |
|
7328 self.descriptor, self.attr)).define() |
|
7329 |
|
7330 @staticmethod |
|
7331 def makeNativeName(descriptor, attr): |
|
7332 name = attr.identifier.name |
|
7333 return "Set" + MakeNativeName(descriptor.binaryNames.get(name, name)) |
|
7334 |
|
7335 |
|
7336 class CGStaticSetter(CGAbstractStaticBindingMethod): |
|
7337 """ |
|
7338 A class for generating the C++ code for an IDL static attribute setter. |
|
7339 """ |
|
7340 def __init__(self, descriptor, attr): |
|
7341 self.attr = attr |
|
7342 name = 'set_' + attr.identifier.name |
|
7343 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) |
|
7344 |
|
7345 def generate_code(self): |
|
7346 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, |
|
7347 self.attr) |
|
7348 checkForArg = CGGeneric(fill( |
|
7349 """ |
|
7350 if (args.length() == 0) { |
|
7351 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} setter"); |
|
7352 } |
|
7353 """, |
|
7354 name=self.attr.identifier.name)) |
|
7355 call = CGSetterCall(self.attr.type, nativeName, self.descriptor, |
|
7356 self.attr) |
|
7357 return CGIndenter(CGList([checkForArg, call])) |
|
7358 |
|
7359 |
|
7360 class CGSpecializedForwardingSetter(CGSpecializedSetter): |
|
7361 """ |
|
7362 A class for generating the code for a specialized attribute setter with |
|
7363 PutForwards that the JIT can call with lower overhead. |
|
7364 """ |
|
7365 def __init__(self, descriptor, attr): |
|
7366 CGSpecializedSetter.__init__(self, descriptor, attr) |
|
7367 |
|
7368 def definition_body(self): |
|
7369 attrName = self.attr.identifier.name |
|
7370 forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0] |
|
7371 # JS_GetProperty and JS_SetProperty can only deal with ASCII |
|
7372 assert all(ord(c) < 128 for c in attrName) |
|
7373 assert all(ord(c) < 128 for c in forwardToAttrName) |
|
7374 return indent(fill( |
|
7375 """ |
|
7376 JS::Rooted<JS::Value> v(cx); |
|
7377 if (!JS_GetProperty(cx, obj, "${attr}", &v)) { |
|
7378 return false; |
|
7379 } |
|
7380 |
|
7381 if (!v.isObject()) { |
|
7382 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "${interface}.${attr}"); |
|
7383 } |
|
7384 |
|
7385 JS::Rooted<JSObject*> targetObj(cx, &v.toObject()); |
|
7386 return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]); |
|
7387 """, |
|
7388 attr=attrName, |
|
7389 interface=self.descriptor.interface.identifier.name, |
|
7390 forwardToAttrName=forwardToAttrName)) |
|
7391 |
|
7392 |
|
7393 class CGSpecializedReplaceableSetter(CGSpecializedSetter): |
|
7394 """ |
|
7395 A class for generating the code for a specialized attribute setter with |
|
7396 Replaceable that the JIT can call with lower overhead. |
|
7397 """ |
|
7398 def __init__(self, descriptor, attr): |
|
7399 CGSpecializedSetter.__init__(self, descriptor, attr) |
|
7400 |
|
7401 def definition_body(self): |
|
7402 attrName = self.attr.identifier.name |
|
7403 # JS_DefineProperty can only deal with ASCII |
|
7404 assert all(ord(c) < 128 for c in attrName) |
|
7405 return indent('return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n' % |
|
7406 attrName) |
|
7407 |
|
7408 |
|
7409 def memberReturnsNewObject(member): |
|
7410 return member.getExtendedAttribute("NewObject") is not None |
|
7411 |
|
7412 |
|
7413 class CGMemberJITInfo(CGThing): |
|
7414 """ |
|
7415 A class for generating the JITInfo for a property that points to |
|
7416 our specialized getter and setter. |
|
7417 """ |
|
7418 def __init__(self, descriptor, member): |
|
7419 self.member = member |
|
7420 self.descriptor = descriptor |
|
7421 |
|
7422 def declare(self): |
|
7423 return "" |
|
7424 |
|
7425 def defineJitInfo(self, infoName, opName, opType, infallible, movable, |
|
7426 aliasSet, hasSlot, slotIndex, returnTypes, args): |
|
7427 """ |
|
7428 aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit. |
|
7429 |
|
7430 args is None if we don't want to output argTypes for some |
|
7431 reason (e.g. we have overloads or we're not a method) and |
|
7432 otherwise an iterable of the arguments for this method. |
|
7433 """ |
|
7434 assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things |
|
7435 assert(not hasSlot or movable) # Things with slots had better be movable |
|
7436 |
|
7437 def jitInfoInitializer(isTypedMethod): |
|
7438 initializer = fill( |
|
7439 """ |
|
7440 { |
|
7441 { ${opName} }, |
|
7442 prototypes::id::${name}, |
|
7443 PrototypeTraits<prototypes::id::${name}>::Depth, |
|
7444 JSJitInfo::${opType}, |
|
7445 JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */ |
|
7446 ${returnType}, /* returnType. Not relevant for setters. */ |
|
7447 ${isInfallible}, /* isInfallible. False in setters. */ |
|
7448 ${isMovable}, /* isMovable. Not relevant for setters. */ |
|
7449 ${isInSlot}, /* isInSlot. Only relevant for getters. */ |
|
7450 ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */ |
|
7451 ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */ |
|
7452 } |
|
7453 """, |
|
7454 opName=opName, |
|
7455 name=self.descriptor.name, |
|
7456 opType=opType, |
|
7457 aliasSet=aliasSet, |
|
7458 returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes, |
|
7459 ""), |
|
7460 isInfallible=toStringBool(infallible), |
|
7461 isMovable=toStringBool(movable), |
|
7462 isInSlot=toStringBool(hasSlot), |
|
7463 isTypedMethod=toStringBool(isTypedMethod), |
|
7464 slotIndex=slotIndex) |
|
7465 return initializer.rstrip() |
|
7466 |
|
7467 if args is not None: |
|
7468 argTypes = "%s_argTypes" % infoName |
|
7469 args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args] |
|
7470 args.append("JSJitInfo::ArgTypeListEnd") |
|
7471 argTypesDecl = ( |
|
7472 "static const JSJitInfo::ArgType %s[] = { %s };\n" % |
|
7473 (argTypes, ", ".join(args))) |
|
7474 return fill( |
|
7475 """ |
|
7476 |
|
7477 $*{argTypesDecl} |
|
7478 static const JSTypedMethodJitInfo ${infoName} = { |
|
7479 ${jitInfo}, |
|
7480 ${argTypes} |
|
7481 }; |
|
7482 """, |
|
7483 argTypesDecl=argTypesDecl, |
|
7484 infoName=infoName, |
|
7485 jitInfo=jitInfoInitializer(True), |
|
7486 argTypes=argTypes) |
|
7487 |
|
7488 return ("\n" |
|
7489 "static const JSJitInfo %s = %s;\n" |
|
7490 % (infoName, jitInfoInitializer(False))) |
|
7491 |
|
7492 def define(self): |
|
7493 if self.member.isAttr(): |
|
7494 getterinfo = ("%s_getterinfo" % self.member.identifier.name) |
|
7495 # We need the cast here because JSJitGetterOp has a "void* self" |
|
7496 # while we have the right type. |
|
7497 getter = ("(JSJitGetterOp)get_%s" % self.member.identifier.name) |
|
7498 getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True) |
|
7499 getterconst = (self.member.getExtendedAttribute("SameObject") or |
|
7500 self.member.getExtendedAttribute("Constant")) |
|
7501 getterpure = getterconst or self.member.getExtendedAttribute("Pure") |
|
7502 if getterconst: |
|
7503 aliasSet = "AliasNone" |
|
7504 elif getterpure: |
|
7505 aliasSet = "AliasDOMSets" |
|
7506 else: |
|
7507 aliasSet = "AliasEverything" |
|
7508 movable = getterpure and getterinfal |
|
7509 |
|
7510 getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor) |
|
7511 isInSlot = self.member.getExtendedAttribute("StoreInSlot") |
|
7512 if isInSlot: |
|
7513 slotIndex = memberReservedSlot(self.member) |
|
7514 # We'll statically assert that this is not too big in |
|
7515 # CGUpdateMemberSlotsMethod |
|
7516 else: |
|
7517 slotIndex = "0" |
|
7518 |
|
7519 result = self.defineJitInfo(getterinfo, getter, "Getter", |
|
7520 getterinfal, movable, aliasSet, |
|
7521 isInSlot, slotIndex, |
|
7522 [self.member.type], None) |
|
7523 if (not self.member.readonly or |
|
7524 self.member.getExtendedAttribute("PutForwards") is not None or |
|
7525 self.member.getExtendedAttribute("Replaceable") is not None): |
|
7526 setterinfo = ("%s_setterinfo" % self.member.identifier.name) |
|
7527 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the |
|
7528 # union. |
|
7529 setter = ("(JSJitGetterOp)set_%s" % self.member.identifier.name) |
|
7530 # Setters are always fallible, since they have to do a typed unwrap. |
|
7531 result += self.defineJitInfo(setterinfo, setter, "Setter", |
|
7532 False, False, "AliasEverything", |
|
7533 False, "0", |
|
7534 [BuiltinTypes[IDLBuiltinType.Types.void]], |
|
7535 None) |
|
7536 return result |
|
7537 if self.member.isMethod(): |
|
7538 methodinfo = ("%s_methodinfo" % self.member.identifier.name) |
|
7539 name = CppKeywords.checkMethodName(self.member.identifier.name) |
|
7540 if self.member.returnsPromise(): |
|
7541 name = CGMethodPromiseWrapper.makeName(name) |
|
7542 # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union. |
|
7543 method = ("(JSJitGetterOp)%s" % name) |
|
7544 methodPure = self.member.getExtendedAttribute("Pure") |
|
7545 |
|
7546 # Methods are infallible if they are infallible, have no arguments |
|
7547 # to unwrap, and have a return type that's infallible to wrap up for |
|
7548 # return. |
|
7549 sigs = self.member.signatures() |
|
7550 if len(sigs) != 1: |
|
7551 # Don't handle overloading. If there's more than one signature, |
|
7552 # one of them must take arguments. |
|
7553 methodInfal = False |
|
7554 args = None |
|
7555 movable = False |
|
7556 else: |
|
7557 sig = sigs[0] |
|
7558 # For pure methods, it's OK to set movable to our notion of |
|
7559 # infallible on the C++ side, without considering argument |
|
7560 # conversions, since argument conversions that can reliably |
|
7561 # throw would be effectful anyway and the jit doesn't move |
|
7562 # effectful things. |
|
7563 hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member) |
|
7564 movable = methodPure and hasInfallibleImpl |
|
7565 # XXXbz can we move the smarts about fallibility due to arg |
|
7566 # conversions into the JIT, using our new args stuff? |
|
7567 if (len(sig[1]) != 0 or |
|
7568 not infallibleForMember(self.member, sig[0], self.descriptor)): |
|
7569 # We have arguments or our return-value boxing can fail |
|
7570 methodInfal = False |
|
7571 else: |
|
7572 methodInfal = hasInfallibleImpl |
|
7573 # For now, only bother to output args if we're pure |
|
7574 if methodPure: |
|
7575 args = sig[1] |
|
7576 else: |
|
7577 args = None |
|
7578 |
|
7579 if args is not None: |
|
7580 aliasSet = "AliasDOMSets" |
|
7581 else: |
|
7582 aliasSet = "AliasEverything" |
|
7583 result = self.defineJitInfo(methodinfo, method, "Method", |
|
7584 methodInfal, movable, aliasSet, False, "0", |
|
7585 [s[0] for s in sigs], args) |
|
7586 return result |
|
7587 raise TypeError("Illegal member type to CGPropertyJITInfo") |
|
7588 |
|
7589 @staticmethod |
|
7590 def getJSReturnTypeTag(t): |
|
7591 if t.nullable(): |
|
7592 # Sometimes it might return null, sometimes not |
|
7593 return "JSVAL_TYPE_UNKNOWN" |
|
7594 if t.isVoid(): |
|
7595 # No return, every time |
|
7596 return "JSVAL_TYPE_UNDEFINED" |
|
7597 if t.isArray(): |
|
7598 # No idea yet |
|
7599 assert False |
|
7600 if t.isSequence(): |
|
7601 return "JSVAL_TYPE_OBJECT" |
|
7602 if t.isMozMap(): |
|
7603 return "JSVAL_TYPE_OBJECT" |
|
7604 if t.isGeckoInterface(): |
|
7605 return "JSVAL_TYPE_OBJECT" |
|
7606 if t.isString(): |
|
7607 return "JSVAL_TYPE_STRING" |
|
7608 if t.isEnum(): |
|
7609 return "JSVAL_TYPE_STRING" |
|
7610 if t.isCallback(): |
|
7611 return "JSVAL_TYPE_OBJECT" |
|
7612 if t.isAny(): |
|
7613 # The whole point is to return various stuff |
|
7614 return "JSVAL_TYPE_UNKNOWN" |
|
7615 if t.isObject(): |
|
7616 return "JSVAL_TYPE_OBJECT" |
|
7617 if t.isSpiderMonkeyInterface(): |
|
7618 return "JSVAL_TYPE_OBJECT" |
|
7619 if t.isUnion(): |
|
7620 u = t.unroll() |
|
7621 if u.hasNullableType: |
|
7622 # Might be null or not |
|
7623 return "JSVAL_TYPE_UNKNOWN" |
|
7624 return reduce(CGMemberJITInfo.getSingleReturnType, |
|
7625 u.flatMemberTypes, "") |
|
7626 if t.isDictionary(): |
|
7627 return "JSVAL_TYPE_OBJECT" |
|
7628 if t.isDate(): |
|
7629 return "JSVAL_TYPE_OBJECT" |
|
7630 if not t.isPrimitive(): |
|
7631 raise TypeError("No idea what type " + str(t) + " is.") |
|
7632 tag = t.tag() |
|
7633 if tag == IDLType.Tags.bool: |
|
7634 return "JSVAL_TYPE_BOOLEAN" |
|
7635 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, |
|
7636 IDLType.Tags.int16, IDLType.Tags.uint16, |
|
7637 IDLType.Tags.int32]: |
|
7638 return "JSVAL_TYPE_INT32" |
|
7639 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64, |
|
7640 IDLType.Tags.unrestricted_float, IDLType.Tags.float, |
|
7641 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: |
|
7642 # These all use JS_NumberValue, which can return int or double. |
|
7643 # But TI treats "double" as meaning "int or double", so we're |
|
7644 # good to return JSVAL_TYPE_DOUBLE here. |
|
7645 return "JSVAL_TYPE_DOUBLE" |
|
7646 if tag != IDLType.Tags.uint32: |
|
7647 raise TypeError("No idea what type " + str(t) + " is.") |
|
7648 # uint32 is sometimes int and sometimes double. |
|
7649 return "JSVAL_TYPE_DOUBLE" |
|
7650 |
|
7651 @staticmethod |
|
7652 def getSingleReturnType(existingType, t): |
|
7653 type = CGMemberJITInfo.getJSReturnTypeTag(t) |
|
7654 if existingType == "": |
|
7655 # First element of the list; just return its type |
|
7656 return type |
|
7657 |
|
7658 if type == existingType: |
|
7659 return existingType |
|
7660 if ((type == "JSVAL_TYPE_DOUBLE" and |
|
7661 existingType == "JSVAL_TYPE_INT32") or |
|
7662 (existingType == "JSVAL_TYPE_DOUBLE" and |
|
7663 type == "JSVAL_TYPE_INT32")): |
|
7664 # Promote INT32 to DOUBLE as needed |
|
7665 return "JSVAL_TYPE_DOUBLE" |
|
7666 # Different types |
|
7667 return "JSVAL_TYPE_UNKNOWN" |
|
7668 |
|
7669 @staticmethod |
|
7670 def getJSArgType(t): |
|
7671 assert not t.isVoid() |
|
7672 if t.nullable(): |
|
7673 # Sometimes it might return null, sometimes not |
|
7674 return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner) |
|
7675 if t.isArray(): |
|
7676 # No idea yet |
|
7677 assert False |
|
7678 if t.isSequence(): |
|
7679 return "JSJitInfo::Object" |
|
7680 if t.isGeckoInterface(): |
|
7681 return "JSJitInfo::Object" |
|
7682 if t.isString(): |
|
7683 return "JSJitInfo::String" |
|
7684 if t.isEnum(): |
|
7685 return "JSJitInfo::String" |
|
7686 if t.isCallback(): |
|
7687 return "JSJitInfo::Object" |
|
7688 if t.isAny(): |
|
7689 # The whole point is to return various stuff |
|
7690 return "JSJitInfo::Any" |
|
7691 if t.isObject(): |
|
7692 return "JSJitInfo::Object" |
|
7693 if t.isSpiderMonkeyInterface(): |
|
7694 return "JSJitInfo::Object" |
|
7695 if t.isUnion(): |
|
7696 u = t.unroll() |
|
7697 type = "JSJitInfo::Null" if u.hasNullableType else "" |
|
7698 return ("JSJitInfo::ArgType(%s)" % |
|
7699 reduce(CGMemberJITInfo.getSingleArgType, |
|
7700 u.flatMemberTypes, type)) |
|
7701 if t.isDictionary(): |
|
7702 return "JSJitInfo::Object" |
|
7703 if t.isDate(): |
|
7704 return "JSJitInfo::Object" |
|
7705 if not t.isPrimitive(): |
|
7706 raise TypeError("No idea what type " + str(t) + " is.") |
|
7707 tag = t.tag() |
|
7708 if tag == IDLType.Tags.bool: |
|
7709 return "JSJitInfo::Boolean" |
|
7710 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, |
|
7711 IDLType.Tags.int16, IDLType.Tags.uint16, |
|
7712 IDLType.Tags.int32]: |
|
7713 return "JSJitInfo::Integer" |
|
7714 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64, |
|
7715 IDLType.Tags.unrestricted_float, IDLType.Tags.float, |
|
7716 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: |
|
7717 # These all use JS_NumberValue, which can return int or double. |
|
7718 # But TI treats "double" as meaning "int or double", so we're |
|
7719 # good to return JSVAL_TYPE_DOUBLE here. |
|
7720 return "JSJitInfo::Double" |
|
7721 if tag != IDLType.Tags.uint32: |
|
7722 raise TypeError("No idea what type " + str(t) + " is.") |
|
7723 # uint32 is sometimes int and sometimes double. |
|
7724 return "JSJitInfo::Double" |
|
7725 |
|
7726 @staticmethod |
|
7727 def getSingleArgType(existingType, t): |
|
7728 type = CGMemberJITInfo.getJSArgType(t) |
|
7729 if existingType == "": |
|
7730 # First element of the list; just return its type |
|
7731 return type |
|
7732 |
|
7733 if type == existingType: |
|
7734 return existingType |
|
7735 return "%s | %s" % (existingType, type) |
|
7736 |
|
7737 |
|
7738 class CGStaticMethodJitinfo(CGGeneric): |
|
7739 """ |
|
7740 A class for generating the JITInfo for a promise-returning static method. |
|
7741 """ |
|
7742 def __init__(self, method): |
|
7743 CGGeneric.__init__( |
|
7744 self, |
|
7745 "\n" |
|
7746 "static const JSJitInfo %s_methodinfo = {\n" |
|
7747 " { (JSJitGetterOp)%s },\n" |
|
7748 " prototypes::id::_ID_Count, 0, JSJitInfo::StaticMethod,\n" |
|
7749 " JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n" |
|
7750 " false, false, 0\n" |
|
7751 "};\n" % |
|
7752 (method.identifier.name, method.identifier.name)) |
|
7753 |
|
7754 |
|
7755 def getEnumValueName(value): |
|
7756 # Some enum values can be empty strings. Others might have weird |
|
7757 # characters in them. Deal with the former by returning "_empty", |
|
7758 # deal with possible name collisions from that by throwing if the |
|
7759 # enum value is actually "_empty", and throw on any value |
|
7760 # containing non-ASCII chars for now. Replace all chars other than |
|
7761 # [0-9A-Za-z_] with '_'. |
|
7762 if re.match("[^\x20-\x7E]", value): |
|
7763 raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters') |
|
7764 if re.match("^[0-9]", value): |
|
7765 return '_' + value |
|
7766 value = re.sub(r'[^0-9A-Za-z_]', '_', value) |
|
7767 if re.match("^_[A-Z]|__", value): |
|
7768 raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec') |
|
7769 if value == "_empty": |
|
7770 raise SyntaxError('"_empty" is not an IDL enum value we support yet') |
|
7771 if value == "": |
|
7772 return "_empty" |
|
7773 return MakeNativeName(value) |
|
7774 |
|
7775 |
|
7776 class CGEnum(CGThing): |
|
7777 def __init__(self, enum): |
|
7778 CGThing.__init__(self) |
|
7779 self.enum = enum |
|
7780 |
|
7781 def stringsNamespace(self): |
|
7782 return self.enum.identifier.name + "Values" |
|
7783 |
|
7784 def nEnumStrings(self): |
|
7785 return len(self.enum.values()) + 1 |
|
7786 |
|
7787 def declare(self): |
|
7788 decl = fill( # BOGUS extra newline at top |
|
7789 """ |
|
7790 |
|
7791 MOZ_BEGIN_ENUM_CLASS(${name}, uint32_t) |
|
7792 $*{enums} |
|
7793 MOZ_END_ENUM_CLASS(${name}) |
|
7794 """, |
|
7795 name=self.enum.identifier.name, |
|
7796 enums=",\n".join(map(getEnumValueName, self.enum.values())) + "\n") |
|
7797 strings = CGNamespace(self.stringsNamespace(), |
|
7798 CGGeneric(declare="extern const EnumEntry %s[%d];\n" |
|
7799 % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings()))) |
|
7800 return decl + "\n" + strings.declare() |
|
7801 |
|
7802 def define(self): |
|
7803 strings = fill( # BOGUS extra newline at top |
|
7804 """ |
|
7805 |
|
7806 extern const EnumEntry ${name}[${count}] = { |
|
7807 $*{entries} |
|
7808 { nullptr, 0 } |
|
7809 }; |
|
7810 """, |
|
7811 name=ENUM_ENTRY_VARIABLE_NAME, |
|
7812 count=self.nEnumStrings(), |
|
7813 entries=''.join('{"%s", %d},\n' % (val, len(val)) |
|
7814 for val in self.enum.values())) |
|
7815 # BOGUS - CGNamespace automatically indents; the extra indent() below causes |
|
7816 # the output to be indented 4 spaces. |
|
7817 return CGNamespace(self.stringsNamespace(), |
|
7818 CGGeneric(define=indent(strings))).define() |
|
7819 |
|
7820 def deps(self): |
|
7821 return self.enum.getDeps() |
|
7822 |
|
7823 |
|
7824 def getUnionAccessorSignatureType(type, descriptorProvider): |
|
7825 """ |
|
7826 Returns the types that are used in the getter and setter signatures for |
|
7827 union types |
|
7828 """ |
|
7829 if type.isArray(): |
|
7830 raise TypeError("Can't handle array arguments yet") |
|
7831 |
|
7832 if type.isSequence(): |
|
7833 nullable = type.nullable() |
|
7834 if nullable: |
|
7835 type = type.inner.inner |
|
7836 else: |
|
7837 type = type.inner |
|
7838 # We don't use the returned template here, so it's OK to just pass no |
|
7839 # sourceDescription. |
|
7840 elementInfo = getJSToNativeConversionInfo(type, descriptorProvider, |
|
7841 isMember="Sequence") |
|
7842 typeName = CGTemplatedType("Sequence", elementInfo.declType, |
|
7843 isReference=True) |
|
7844 if nullable: |
|
7845 typeName = CGTemplatedType("Nullable", typeName, isReference=True) |
|
7846 |
|
7847 return typeName |
|
7848 |
|
7849 if type.isUnion(): |
|
7850 typeName = CGGeneric(type.name) |
|
7851 if type.nullable(): |
|
7852 typeName = CGTemplatedType("Nullable", typeName, isReference=True) |
|
7853 |
|
7854 return typeName |
|
7855 |
|
7856 if type.isGeckoInterface(): |
|
7857 descriptor = descriptorProvider.getDescriptor( |
|
7858 type.unroll().inner.identifier.name) |
|
7859 typeName = CGGeneric(descriptor.nativeType) |
|
7860 # Allow null pointers for nullable types and old-binding classes |
|
7861 if type.nullable() or type.unroll().inner.isExternal(): |
|
7862 typeName = CGWrapper(typeName, post="*") |
|
7863 else: |
|
7864 typeName = CGWrapper(typeName, post="&") |
|
7865 return typeName |
|
7866 |
|
7867 if type.isSpiderMonkeyInterface(): |
|
7868 typeName = CGGeneric(type.name) |
|
7869 if type.nullable(): |
|
7870 typeName = CGTemplatedType("Nullable", typeName) |
|
7871 return CGWrapper(typeName, post="&") |
|
7872 |
|
7873 if type.isDOMString(): |
|
7874 return CGGeneric("const nsAString&") |
|
7875 |
|
7876 if type.isByteString(): |
|
7877 return CGGeneric("const nsCString&") |
|
7878 |
|
7879 if type.isEnum(): |
|
7880 if type.nullable(): |
|
7881 raise TypeError("We don't support nullable enumerated arguments or " |
|
7882 "union members yet") |
|
7883 return CGGeneric(type.inner.identifier.name) |
|
7884 |
|
7885 if type.isCallback(): |
|
7886 if type.nullable(): |
|
7887 typeName = "%s*" |
|
7888 else: |
|
7889 typeName = "%s&" |
|
7890 return CGGeneric(typeName % type.unroll().identifier.name) |
|
7891 |
|
7892 if type.isAny(): |
|
7893 return CGGeneric("JS::Value") |
|
7894 |
|
7895 if type.isObject(): |
|
7896 return CGGeneric("JSObject*") |
|
7897 |
|
7898 if type.isDictionary(): |
|
7899 return CGGeneric("const %s&" % type.inner.identifier.name) |
|
7900 |
|
7901 if not type.isPrimitive(): |
|
7902 raise TypeError("Need native type for argument type '%s'" % str(type)) |
|
7903 |
|
7904 typeName = CGGeneric(builtinNames[type.tag()]) |
|
7905 if type.nullable(): |
|
7906 typeName = CGTemplatedType("Nullable", typeName, isReference=True) |
|
7907 return typeName |
|
7908 |
|
7909 |
|
7910 def getUnionTypeTemplateVars(unionType, type, descriptorProvider, |
|
7911 ownsMembers=False): |
|
7912 # For dictionaries and sequences we need to pass None as the failureCode |
|
7913 # for getJSToNativeConversionInfo. |
|
7914 # Also, for dictionaries we would need to handle conversion of |
|
7915 # null/undefined to the dictionary correctly. |
|
7916 if type.isSequence(): |
|
7917 raise TypeError("Can't handle sequences in unions") |
|
7918 |
|
7919 name = getUnionMemberName(type) |
|
7920 |
|
7921 # By the time tryNextCode is invoked, we're guaranteed the union has been |
|
7922 # constructed as some type, since we've been trying to convert into the |
|
7923 # corresponding member. |
|
7924 prefix = "" if ownsMembers else "mUnion." |
|
7925 tryNextCode = ("%sDestroy%s();\n" |
|
7926 "tryNext = true;\n" |
|
7927 "return true;\n" % (prefix, name)) |
|
7928 conversionInfo = getJSToNativeConversionInfo( |
|
7929 type, descriptorProvider, failureCode=tryNextCode, |
|
7930 isDefinitelyObject=not type.isDictionary(), |
|
7931 isMember=("OwningUnion" if ownsMembers else None), |
|
7932 sourceDescription="member of %s" % unionType) |
|
7933 |
|
7934 ctorNeedsCx = conversionInfo.declArgs == "cx" |
|
7935 ctorArgs = "cx" if ctorNeedsCx else "" |
|
7936 |
|
7937 # This is ugly, but UnionMember needs to call a constructor with no |
|
7938 # arguments so the type can't be const. |
|
7939 structType = conversionInfo.declType.define() |
|
7940 if structType.startswith("const "): |
|
7941 structType = structType[6:] |
|
7942 externalType = getUnionAccessorSignatureType(type, descriptorProvider).define() |
|
7943 |
|
7944 if type.isObject(): |
|
7945 if ownsMembers: |
|
7946 body = dedent(""" |
|
7947 MOZ_ASSERT(mType == eUninitialized); |
|
7948 mValue.mObject.SetValue(obj); |
|
7949 mType = eObject; |
|
7950 """) |
|
7951 else: |
|
7952 body = dedent(""" |
|
7953 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized); |
|
7954 mUnion.mValue.mObject.SetValue(cx, obj); |
|
7955 mUnion.mType = mUnion.eObject; |
|
7956 """) |
|
7957 setter = ClassMethod("SetToObject", "void", |
|
7958 [Argument("JSContext*", "cx"), |
|
7959 Argument("JSObject*", "obj")], |
|
7960 inline=True, bodyInHeader=True, |
|
7961 body=body) |
|
7962 |
|
7963 else: |
|
7964 # Important: we need to not have our declName involve |
|
7965 # maybe-GCing operations. |
|
7966 jsConversion = string.Template(conversionInfo.template).substitute({ |
|
7967 "val": "value", |
|
7968 "mutableVal": "pvalue", |
|
7969 "declName": "memberSlot", |
|
7970 "holderName": "m" + name + "Holder", |
|
7971 }) |
|
7972 jsConversion = fill( |
|
7973 """ |
|
7974 tryNext = false; |
|
7975 { // scope for memberSlot |
|
7976 ${structType}& memberSlot = RawSetAs${name}(${ctorArgs}); |
|
7977 $*{jsConversion} |
|
7978 } |
|
7979 return true; |
|
7980 """, |
|
7981 structType=structType, |
|
7982 name=name, |
|
7983 ctorArgs=ctorArgs, |
|
7984 jsConversion=jsConversion) |
|
7985 |
|
7986 setter = ClassMethod("TrySetTo" + name, "bool", |
|
7987 [Argument("JSContext*", "cx"), |
|
7988 Argument("JS::Handle<JS::Value>", "value"), |
|
7989 Argument("JS::MutableHandle<JS::Value>", "pvalue"), |
|
7990 Argument("bool&", "tryNext")], |
|
7991 inline=not ownsMembers, |
|
7992 bodyInHeader=not ownsMembers, |
|
7993 body=jsConversion) |
|
7994 |
|
7995 return { |
|
7996 "name": name, |
|
7997 "structType": structType, |
|
7998 "externalType": externalType, |
|
7999 "setter": setter, |
|
8000 "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None, |
|
8001 "ctorArgs": ctorArgs, |
|
8002 "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else [] |
|
8003 } |
|
8004 |
|
8005 |
|
8006 class CGUnionStruct(CGThing): |
|
8007 def __init__(self, type, descriptorProvider, ownsMembers=False): |
|
8008 CGThing.__init__(self) |
|
8009 self.type = type.unroll() |
|
8010 self.descriptorProvider = descriptorProvider |
|
8011 self.ownsMembers = ownsMembers |
|
8012 self.struct = self.getStruct() |
|
8013 |
|
8014 def declare(self): |
|
8015 return self.struct.declare() |
|
8016 |
|
8017 def define(self): |
|
8018 return self.struct.define() |
|
8019 |
|
8020 def getStruct(self): |
|
8021 |
|
8022 members = [ClassMember("mType", "Type", body="eUninitialized"), |
|
8023 ClassMember("mValue", "Value")] |
|
8024 ctor = ClassConstructor([], bodyInHeader=True, visibility="public", |
|
8025 explicit=True) |
|
8026 |
|
8027 methods = [] |
|
8028 enumValues = ["eUninitialized"] |
|
8029 toJSValCases = [CGCase("eUninitialized", CGGeneric("return false;\n"))] |
|
8030 destructorCases = [CGCase("eUninitialized", None)] |
|
8031 assignmentCases = [ |
|
8032 CGCase("eUninitialized", |
|
8033 CGGeneric('MOZ_ASSERT(mType == eUninitialized,\n' |
|
8034 ' "We need to destroy ourselves?");\n'))] |
|
8035 traceCases = [] |
|
8036 unionValues = [] |
|
8037 if self.type.hasNullableType: |
|
8038 enumValues.append("eNull") |
|
8039 methods.append(ClassMethod("IsNull", "bool", [], const=True, inline=True, |
|
8040 body="return mType == eNull;\n", |
|
8041 bodyInHeader=True)) |
|
8042 methods.append(ClassMethod("SetNull", "void", [], inline=True, |
|
8043 body=("Uninit();\n" |
|
8044 "mType = eNull;\n"), |
|
8045 bodyInHeader=True)) |
|
8046 destructorCases.append(CGCase("eNull", None)) |
|
8047 assignmentCases.append(CGCase("eNull", |
|
8048 CGGeneric("MOZ_ASSERT(mType == eUninitialized);\n" |
|
8049 "mType = eNull;\n"))) |
|
8050 toJSValCases.append(CGCase("eNull", CGGeneric("rval.setNull();\n" |
|
8051 "return true;\n"))) |
|
8052 |
|
8053 hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes) |
|
8054 for t in self.type.flatMemberTypes: |
|
8055 vars = getUnionTypeTemplateVars(self.type, |
|
8056 t, self.descriptorProvider, |
|
8057 ownsMembers=self.ownsMembers) |
|
8058 if vars["name"] != "Object" or self.ownsMembers: |
|
8059 body = fill( |
|
8060 """ |
|
8061 if (mType == e${name}) { |
|
8062 return mValue.m${name}.Value(); |
|
8063 } |
|
8064 %s |
|
8065 mType = e${name}; |
|
8066 return mValue.m${name}.SetValue(${ctorArgs}); |
|
8067 """, |
|
8068 **vars) |
|
8069 |
|
8070 # bodyInHeader must be false for return values because they own |
|
8071 # their union members and we don't want include headers in |
|
8072 # UnionTypes.h just to call Addref/Release |
|
8073 methods.append(ClassMethod( |
|
8074 "RawSetAs" + vars["name"], |
|
8075 vars["structType"] + "&", |
|
8076 vars["ctorArgList"], |
|
8077 bodyInHeader=not self.ownsMembers, |
|
8078 body=body % "MOZ_ASSERT(mType == eUninitialized);")) |
|
8079 uninit = "Uninit();" |
|
8080 if hasObjectType and not self.ownsMembers: |
|
8081 uninit = 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n' + uninit |
|
8082 methods.append(ClassMethod( |
|
8083 "SetAs" + vars["name"], |
|
8084 vars["structType"] + "&", |
|
8085 vars["ctorArgList"], |
|
8086 bodyInHeader=not self.ownsMembers, |
|
8087 body=body % uninit)) |
|
8088 if self.ownsMembers: |
|
8089 methods.append(vars["setter"]) |
|
8090 if t.isString(): |
|
8091 methods.append( |
|
8092 ClassMethod("SetStringData", "void", |
|
8093 [Argument("const nsString::char_type*", "aData"), |
|
8094 Argument("nsString::size_type", "aLength")], |
|
8095 inline=True, bodyInHeader=True, |
|
8096 body="RawSetAsString().Assign(aData, aLength);\n")) |
|
8097 |
|
8098 body = fill( |
|
8099 """ |
|
8100 MOZ_ASSERT(Is${name}(), "Wrong type!"); |
|
8101 mValue.m${name}.Destroy(); |
|
8102 mType = eUninitialized; |
|
8103 """, |
|
8104 **vars) |
|
8105 methods.append(ClassMethod("Destroy" + vars["name"], |
|
8106 "void", |
|
8107 [], |
|
8108 visibility="private", |
|
8109 bodyInHeader=not self.ownsMembers, |
|
8110 body=body)) |
|
8111 |
|
8112 body = fill("return mType == e${name};\n", **vars) |
|
8113 methods.append(ClassMethod("Is" + vars["name"], |
|
8114 "bool", |
|
8115 [], |
|
8116 const=True, |
|
8117 bodyInHeader=True, |
|
8118 body=body)) |
|
8119 |
|
8120 body = fill( |
|
8121 """ |
|
8122 MOZ_ASSERT(Is${name}(), "Wrong type!"); |
|
8123 return const_cast<${structType}&>(mValue.m${name}.Value()); |
|
8124 """, |
|
8125 **vars) |
|
8126 if self.ownsMembers: |
|
8127 getterReturnType = "%s&" % vars["structType"] |
|
8128 else: |
|
8129 getterReturnType = vars["externalType"] |
|
8130 methods.append(ClassMethod("GetAs" + vars["name"], |
|
8131 getterReturnType, |
|
8132 [], |
|
8133 const=True, |
|
8134 bodyInHeader=True, |
|
8135 body=body)) |
|
8136 |
|
8137 unionValues.append( |
|
8138 fill("UnionMember<${structType} > m${name}", **vars)) |
|
8139 enumValues.append("e" + vars["name"]) |
|
8140 |
|
8141 toJSValCases.append( |
|
8142 CGCase("e" + vars["name"], |
|
8143 self.getConversionToJS(vars, t))) |
|
8144 destructorCases.append( |
|
8145 CGCase("e" + vars["name"], |
|
8146 CGGeneric("Destroy%s();\n" % vars["name"]))) |
|
8147 assignmentCases.append( |
|
8148 CGCase("e" + vars["name"], |
|
8149 CGGeneric("SetAs%s() = aOther.GetAs%s();\n" % |
|
8150 (vars["name"], vars["name"])))) |
|
8151 if self.ownsMembers and typeNeedsRooting(t): |
|
8152 if t.isObject(): |
|
8153 traceCases.append( |
|
8154 CGCase("e" + vars["name"], |
|
8155 CGGeneric('JS_CallObjectTracer(trc, %s, "%s");\n' % |
|
8156 ("&mValue.m" + vars["name"] + ".Value()", |
|
8157 "mValue.m" + vars["name"])))) |
|
8158 elif t.isDictionary(): |
|
8159 traceCases.append( |
|
8160 CGCase("e" + vars["name"], |
|
8161 CGGeneric("mValue.m%s.Value().TraceDictionary(trc);\n" % |
|
8162 vars["name"]))) |
|
8163 else: |
|
8164 assert t.isSpiderMonkeyInterface() |
|
8165 traceCases.append( |
|
8166 CGCase("e" + vars["name"], |
|
8167 CGGeneric("mValue.m%s.Value().TraceSelf(trc);\n" % |
|
8168 vars["name"]))) |
|
8169 |
|
8170 dtor = CGSwitch("mType", destructorCases).define() |
|
8171 |
|
8172 methods.append(ClassMethod("Uninit", "void", [], |
|
8173 visibility="private", body=dtor, |
|
8174 bodyInHeader=not self.ownsMembers, |
|
8175 inline=not self.ownsMembers)) |
|
8176 |
|
8177 methods.append( |
|
8178 ClassMethod( |
|
8179 "ToJSVal", |
|
8180 "bool", |
|
8181 [ |
|
8182 Argument("JSContext*", "cx"), |
|
8183 Argument("JS::Handle<JSObject*>", "scopeObj"), |
|
8184 Argument("JS::MutableHandle<JS::Value>", "rval") |
|
8185 ], |
|
8186 body=CGSwitch("mType", toJSValCases, |
|
8187 default=CGGeneric("return false;\n")).define(), |
|
8188 const=True)) |
|
8189 |
|
8190 constructors = [ctor] |
|
8191 selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers) |
|
8192 if self.ownsMembers: |
|
8193 if traceCases: |
|
8194 # BOGUS blank line in default case |
|
8195 traceBody = CGSwitch("mType", traceCases, |
|
8196 default=CGGeneric("\n")).define() |
|
8197 else: |
|
8198 # BOGUS blank line in method |
|
8199 traceBody = "\n" |
|
8200 methods.append(ClassMethod("TraceUnion", "void", |
|
8201 [Argument("JSTracer*", "trc")], |
|
8202 body=traceBody)) |
|
8203 if CGUnionStruct.isUnionCopyConstructible(self.type): |
|
8204 constructors.append( |
|
8205 ClassConstructor( |
|
8206 [Argument("const %s&" % selfName, "aOther")], |
|
8207 bodyInHeader=True, |
|
8208 visibility="public", |
|
8209 explicit=True, |
|
8210 body="*this = aOther;\n")) |
|
8211 methods.append(ClassMethod( |
|
8212 "operator=", "void", |
|
8213 [Argument("const %s&" % selfName, "aOther")], |
|
8214 body=CGSwitch("aOther.mType", assignmentCases).define())) |
|
8215 disallowCopyConstruction = False |
|
8216 else: |
|
8217 disallowCopyConstruction = True |
|
8218 else: |
|
8219 disallowCopyConstruction = True |
|
8220 |
|
8221 friend = " friend class %sArgument;\n" % str(self.type) if not self.ownsMembers else "" |
|
8222 bases = [ClassBase("AllOwningUnionBase")] if self.ownsMembers else [] |
|
8223 return CGClass(selfName, |
|
8224 bases=bases, |
|
8225 members=members, |
|
8226 constructors=constructors, |
|
8227 methods=methods, |
|
8228 disallowCopyConstruction=disallowCopyConstruction, |
|
8229 extradeclarations=friend, |
|
8230 destructor=ClassDestructor(visibility="public", |
|
8231 body="Uninit();", |
|
8232 bodyInHeader=True), |
|
8233 enums=[ClassEnum("Type", enumValues, visibility="private")], |
|
8234 unions=[ClassUnion("Value", unionValues, visibility="private")]) |
|
8235 |
|
8236 def getConversionToJS(self, templateVars, type): |
|
8237 assert not type.nullable() # flatMemberTypes never has nullable types |
|
8238 val = "mValue.m%(name)s.Value()" % templateVars |
|
8239 wrapCode = wrapForType( |
|
8240 type, self.descriptorProvider, |
|
8241 { |
|
8242 "jsvalRef": "rval", |
|
8243 "jsvalHandle": "rval", |
|
8244 "obj": "scopeObj", |
|
8245 "result": val, |
|
8246 "typedArraysAreStructs": True |
|
8247 }) |
|
8248 return CGGeneric(wrapCode) |
|
8249 |
|
8250 @staticmethod |
|
8251 def isUnionCopyConstructible(type): |
|
8252 return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes) |
|
8253 |
|
8254 @staticmethod |
|
8255 def unionTypeName(type, ownsMembers): |
|
8256 """ |
|
8257 Returns a string name for this known union type. |
|
8258 """ |
|
8259 assert type.isUnion() and not type.nullable() |
|
8260 return ("Owning" if ownsMembers else "") + type.name |
|
8261 |
|
8262 @staticmethod |
|
8263 def unionTypeDecl(type, ownsMembers): |
|
8264 """ |
|
8265 Returns a string for declaring this possibly-nullable union type. |
|
8266 """ |
|
8267 assert type.isUnion() |
|
8268 nullable = type.nullable() |
|
8269 if nullable: |
|
8270 type = type.inner |
|
8271 decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers)) |
|
8272 if nullable: |
|
8273 decl = CGTemplatedType("Nullable", decl) |
|
8274 return decl.define() |
|
8275 |
|
8276 |
|
8277 class CGUnionConversionStruct(CGThing): |
|
8278 def __init__(self, type, descriptorProvider): |
|
8279 CGThing.__init__(self) |
|
8280 self.type = type.unroll() |
|
8281 self.descriptorProvider = descriptorProvider |
|
8282 |
|
8283 def declare(self): |
|
8284 |
|
8285 structName = str(self.type) |
|
8286 members = [ClassMember("mUnion", structName + "&", |
|
8287 body="const_cast<%s&>(aUnion)" % structName)] |
|
8288 # Argument needs to be a const ref because that's all Maybe<> allows |
|
8289 ctor = ClassConstructor([Argument("const %s&" % structName, "aUnion")], |
|
8290 bodyInHeader=True, |
|
8291 visibility="public", |
|
8292 explicit=True) |
|
8293 methods = [] |
|
8294 |
|
8295 if self.type.hasNullableType: |
|
8296 methods.append(ClassMethod("SetNull", "bool", [], |
|
8297 body=("MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);\n" |
|
8298 "mUnion.mType = mUnion.eNull;\n" |
|
8299 "return true;\n"), |
|
8300 inline=True, bodyInHeader=True)) |
|
8301 |
|
8302 for t in self.type.flatMemberTypes: |
|
8303 vars = getUnionTypeTemplateVars(self.type, |
|
8304 t, self.descriptorProvider) |
|
8305 methods.append(vars["setter"]) |
|
8306 if vars["name"] != "Object": |
|
8307 body = fill( |
|
8308 """ |
|
8309 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized); |
|
8310 mUnion.mType = mUnion.e${name}; |
|
8311 return mUnion.mValue.m${name}.SetValue(${ctorArgs}); |
|
8312 """, |
|
8313 **vars) |
|
8314 methods.append(ClassMethod("RawSetAs" + vars["name"], |
|
8315 vars["structType"] + "&", |
|
8316 vars["ctorArgList"], |
|
8317 bodyInHeader=True, |
|
8318 body=body, |
|
8319 visibility="private")) |
|
8320 if t.isString(): |
|
8321 methods.append( |
|
8322 ClassMethod("SetStringData", "void", |
|
8323 [Argument("const nsDependentString::char_type*", "aData"), |
|
8324 Argument("nsDependentString::size_type", "aLength")], |
|
8325 inline=True, bodyInHeader=True, |
|
8326 body="RawSetAsString().SetData(aData, aLength);\n")) |
|
8327 |
|
8328 if vars["holderType"] is not None: |
|
8329 members.append(ClassMember("m%sHolder" % vars["name"], |
|
8330 vars["holderType"])) |
|
8331 |
|
8332 return CGClass(structName + "Argument", |
|
8333 members=members, |
|
8334 constructors=[ctor], |
|
8335 methods=methods, |
|
8336 disallowCopyConstruction=True).declare() |
|
8337 |
|
8338 def define(self): |
|
8339 return "\n" |
|
8340 |
|
8341 |
|
8342 class ClassItem: |
|
8343 """ Use with CGClass """ |
|
8344 def __init__(self, name, visibility): |
|
8345 self.name = name |
|
8346 self.visibility = visibility |
|
8347 |
|
8348 def declare(self, cgClass): |
|
8349 assert False |
|
8350 |
|
8351 def define(self, cgClass): |
|
8352 assert False |
|
8353 |
|
8354 |
|
8355 class ClassBase(ClassItem): |
|
8356 def __init__(self, name, visibility='public'): |
|
8357 ClassItem.__init__(self, name, visibility) |
|
8358 |
|
8359 def declare(self, cgClass): |
|
8360 return '%s %s' % (self.visibility, self.name) |
|
8361 |
|
8362 def define(self, cgClass): |
|
8363 # Only in the header |
|
8364 return '' |
|
8365 |
|
8366 |
|
8367 class ClassMethod(ClassItem): |
|
8368 def __init__(self, name, returnType, args, inline=False, static=False, |
|
8369 virtual=False, const=False, bodyInHeader=False, |
|
8370 templateArgs=None, visibility='public', body=None, |
|
8371 breakAfterReturnDecl="\n", |
|
8372 breakAfterSelf="\n", override=False): |
|
8373 """ |
|
8374 override indicates whether to flag the method as MOZ_OVERRIDE |
|
8375 """ |
|
8376 assert not override or virtual |
|
8377 assert not (override and static) |
|
8378 self.returnType = returnType |
|
8379 self.args = args |
|
8380 self.inline = inline or bodyInHeader |
|
8381 self.static = static |
|
8382 self.virtual = virtual |
|
8383 self.const = const |
|
8384 self.bodyInHeader = bodyInHeader |
|
8385 self.templateArgs = templateArgs |
|
8386 self.body = body |
|
8387 self.breakAfterReturnDecl = breakAfterReturnDecl |
|
8388 self.breakAfterSelf = breakAfterSelf |
|
8389 self.override = override |
|
8390 ClassItem.__init__(self, name, visibility) |
|
8391 |
|
8392 def getDecorators(self, declaring): |
|
8393 decorators = [] |
|
8394 if self.inline: |
|
8395 decorators.append('inline') |
|
8396 if declaring: |
|
8397 if self.static: |
|
8398 decorators.append('static') |
|
8399 if self.virtual: |
|
8400 decorators.append('virtual') |
|
8401 if decorators: |
|
8402 return ' '.join(decorators) + ' ' |
|
8403 return '' |
|
8404 |
|
8405 def getBody(self): |
|
8406 # Override me or pass a string to constructor |
|
8407 assert self.body is not None |
|
8408 return self.body |
|
8409 |
|
8410 def declare(self, cgClass): |
|
8411 templateClause = ('template <%s>\n' % ', '.join(self.templateArgs) |
|
8412 if self.bodyInHeader and self.templateArgs else '') |
|
8413 args = ', '.join([a.declare() for a in self.args]) |
|
8414 if self.bodyInHeader: |
|
8415 body = indent(self.getBody()) |
|
8416 body = '\n{\n' + body + '}\n' |
|
8417 else: |
|
8418 body = ';\n' |
|
8419 |
|
8420 return fill( |
|
8421 "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}" |
|
8422 "${name}(${args})${const}${override}${body}" |
|
8423 "${breakAfterSelf}", |
|
8424 templateClause=templateClause, |
|
8425 decorators=self.getDecorators(True), |
|
8426 returnType=self.returnType, |
|
8427 breakAfterReturnDecl=self.breakAfterReturnDecl, |
|
8428 name=self.name, |
|
8429 args=args, |
|
8430 const=' const' if self.const else '', |
|
8431 override=' MOZ_OVERRIDE' if self.override else '', |
|
8432 body=body, |
|
8433 breakAfterSelf=self.breakAfterSelf) |
|
8434 |
|
8435 def define(self, cgClass): |
|
8436 if self.bodyInHeader: |
|
8437 return '' |
|
8438 |
|
8439 templateArgs = cgClass.templateArgs |
|
8440 if templateArgs: |
|
8441 if cgClass.templateSpecialization: |
|
8442 templateArgs = \ |
|
8443 templateArgs[len(cgClass.templateSpecialization):] |
|
8444 |
|
8445 if templateArgs: |
|
8446 templateClause = \ |
|
8447 'template <%s>\n' % ', '.join([str(a) for a in templateArgs]) |
|
8448 else: |
|
8449 templateClause = '' |
|
8450 |
|
8451 return fill( |
|
8452 """ |
|
8453 ${templateClause}${decorators}${returnType} |
|
8454 ${className}::${name}(${args})${const} |
|
8455 { |
|
8456 $*{body} |
|
8457 } |
|
8458 """, |
|
8459 templateClause=templateClause, |
|
8460 decorators=self.getDecorators(False), |
|
8461 returnType=self.returnType, |
|
8462 className=cgClass.getNameString(), |
|
8463 name=self.name, |
|
8464 args=', '.join([a.define() for a in self.args]), |
|
8465 const=' const' if self.const else '', |
|
8466 body=self.getBody()) |
|
8467 |
|
8468 |
|
8469 class ClassUsingDeclaration(ClassItem): |
|
8470 """ |
|
8471 Used for importing a name from a base class into a CGClass |
|
8472 |
|
8473 baseClass is the name of the base class to import the name from |
|
8474 |
|
8475 name is the name to import |
|
8476 |
|
8477 visibility determines the visibility of the name (public, |
|
8478 protected, private), defaults to public. |
|
8479 """ |
|
8480 def __init__(self, baseClass, name, visibility='public'): |
|
8481 self.baseClass = baseClass |
|
8482 ClassItem.__init__(self, name, visibility) |
|
8483 |
|
8484 def declare(self, cgClass): |
|
8485 return "using %s::%s;\n\n" % (self.baseClass, self.name) |
|
8486 |
|
8487 def define(self, cgClass): |
|
8488 return '' |
|
8489 |
|
8490 |
|
8491 class ClassConstructor(ClassItem): |
|
8492 """ |
|
8493 Used for adding a constructor to a CGClass. |
|
8494 |
|
8495 args is a list of Argument objects that are the arguments taken by the |
|
8496 constructor. |
|
8497 |
|
8498 inline should be True if the constructor should be marked inline. |
|
8499 |
|
8500 bodyInHeader should be True if the body should be placed in the class |
|
8501 declaration in the header. |
|
8502 |
|
8503 visibility determines the visibility of the constructor (public, |
|
8504 protected, private), defaults to private. |
|
8505 |
|
8506 explicit should be True if the constructor should be marked explicit. |
|
8507 |
|
8508 baseConstructors is a list of strings containing calls to base constructors, |
|
8509 defaults to None. |
|
8510 |
|
8511 body contains a string with the code for the constructor, defaults to empty. |
|
8512 """ |
|
8513 def __init__(self, args, inline=False, bodyInHeader=False, |
|
8514 visibility="private", explicit=False, baseConstructors=None, |
|
8515 body=""): |
|
8516 self.args = args |
|
8517 self.inline = inline or bodyInHeader |
|
8518 self.bodyInHeader = bodyInHeader |
|
8519 self.explicit = explicit |
|
8520 self.baseConstructors = baseConstructors or [] |
|
8521 self.body = body |
|
8522 ClassItem.__init__(self, None, visibility) |
|
8523 |
|
8524 def getDecorators(self, declaring): |
|
8525 decorators = [] |
|
8526 if self.explicit: |
|
8527 decorators.append('explicit') |
|
8528 if self.inline and declaring: |
|
8529 decorators.append('inline') |
|
8530 if decorators: |
|
8531 return ' '.join(decorators) + ' ' |
|
8532 return '' |
|
8533 |
|
8534 def getInitializationList(self, cgClass): |
|
8535 items = [str(c) for c in self.baseConstructors] |
|
8536 for m in cgClass.members: |
|
8537 if not m.static: |
|
8538 initialize = m.body |
|
8539 if initialize: |
|
8540 items.append(m.name + "(" + initialize + ")") |
|
8541 |
|
8542 if len(items) > 0: |
|
8543 return '\n : ' + ',\n '.join(items) |
|
8544 return '' |
|
8545 |
|
8546 def getBody(self): |
|
8547 return self.body |
|
8548 |
|
8549 def declare(self, cgClass): |
|
8550 args = ', '.join([a.declare() for a in self.args]) |
|
8551 if self.bodyInHeader: |
|
8552 body = self.getInitializationList(cgClass) + '\n{\n' + indent(self.getBody()) + '}\n' |
|
8553 else: |
|
8554 body = ';\n' |
|
8555 |
|
8556 return fill( |
|
8557 "${decorators}${className}(${args})${body}\n", |
|
8558 decorators=self.getDecorators(True), |
|
8559 className=cgClass.getNameString(), |
|
8560 args=args, |
|
8561 body=body) |
|
8562 |
|
8563 def define(self, cgClass): |
|
8564 if self.bodyInHeader: |
|
8565 return '' |
|
8566 |
|
8567 return fill( |
|
8568 """ |
|
8569 ${decorators} |
|
8570 ${className}::${className}(${args})${initializationList} |
|
8571 { |
|
8572 $*{body} |
|
8573 } |
|
8574 """, |
|
8575 decorators=self.getDecorators(False), |
|
8576 className=cgClass.getNameString(), |
|
8577 args=', '.join([a.define() for a in self.args]), |
|
8578 initializationList=self.getInitializationList(cgClass), |
|
8579 body=self.getBody()) |
|
8580 |
|
8581 |
|
8582 class ClassDestructor(ClassItem): |
|
8583 """ |
|
8584 Used for adding a destructor to a CGClass. |
|
8585 |
|
8586 inline should be True if the destructor should be marked inline. |
|
8587 |
|
8588 bodyInHeader should be True if the body should be placed in the class |
|
8589 declaration in the header. |
|
8590 |
|
8591 visibility determines the visibility of the destructor (public, |
|
8592 protected, private), defaults to private. |
|
8593 |
|
8594 body contains a string with the code for the destructor, defaults to empty. |
|
8595 |
|
8596 virtual determines whether the destructor is virtual, defaults to False. |
|
8597 """ |
|
8598 def __init__(self, inline=False, bodyInHeader=False, |
|
8599 visibility="private", body='', virtual=False): |
|
8600 self.inline = inline or bodyInHeader |
|
8601 self.bodyInHeader = bodyInHeader |
|
8602 self.body = body |
|
8603 self.virtual = virtual |
|
8604 ClassItem.__init__(self, None, visibility) |
|
8605 |
|
8606 def getDecorators(self, declaring): |
|
8607 decorators = [] |
|
8608 if self.virtual and declaring: |
|
8609 decorators.append('virtual') |
|
8610 if self.inline and declaring: |
|
8611 decorators.append('inline') |
|
8612 if decorators: |
|
8613 return ' '.join(decorators) + ' ' |
|
8614 return '' |
|
8615 |
|
8616 def getBody(self): |
|
8617 return self.body |
|
8618 |
|
8619 def declare(self, cgClass): |
|
8620 if self.bodyInHeader: |
|
8621 body = '\n{\n' + indent(self.getBody()) + '}\n' |
|
8622 else: |
|
8623 body = ';\n' |
|
8624 |
|
8625 return fill( |
|
8626 "${decorators}~${className}()${body}\n", |
|
8627 decorators=self.getDecorators(True), |
|
8628 className=cgClass.getNameString(), |
|
8629 body=body) |
|
8630 |
|
8631 def define(self, cgClass): |
|
8632 if self.bodyInHeader: |
|
8633 return '' |
|
8634 return fill( |
|
8635 """ |
|
8636 ${decorators} |
|
8637 ${className}::~${className}() |
|
8638 { |
|
8639 $*{body} |
|
8640 } |
|
8641 """, |
|
8642 decorators=self.getDecorators(False), |
|
8643 className=cgClass.getNameString(), |
|
8644 body=self.getBody() or "\n") # BOGUS extra blank line if empty |
|
8645 |
|
8646 |
|
8647 class ClassMember(ClassItem): |
|
8648 def __init__(self, name, type, visibility="private", static=False, |
|
8649 body=None): |
|
8650 self.type = type |
|
8651 self.static = static |
|
8652 self.body = body |
|
8653 ClassItem.__init__(self, name, visibility) |
|
8654 |
|
8655 def declare(self, cgClass): |
|
8656 return '%s%s %s;\n' % ('static ' if self.static else '', self.type, |
|
8657 self.name) |
|
8658 |
|
8659 def define(self, cgClass): |
|
8660 if not self.static: |
|
8661 return '' |
|
8662 if self.body: |
|
8663 body = " = " + self.body |
|
8664 else: |
|
8665 body = "" |
|
8666 return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(), |
|
8667 self.name, body) |
|
8668 |
|
8669 |
|
8670 class ClassTypedef(ClassItem): |
|
8671 def __init__(self, name, type, visibility="public"): |
|
8672 self.type = type |
|
8673 ClassItem.__init__(self, name, visibility) |
|
8674 |
|
8675 def declare(self, cgClass): |
|
8676 return 'typedef %s %s;\n' % (self.type, self.name) |
|
8677 |
|
8678 def define(self, cgClass): |
|
8679 # Only goes in the header |
|
8680 return '' |
|
8681 |
|
8682 |
|
8683 class ClassEnum(ClassItem): |
|
8684 def __init__(self, name, entries, values=None, visibility="public"): |
|
8685 self.entries = entries |
|
8686 self.values = values |
|
8687 ClassItem.__init__(self, name, visibility) |
|
8688 |
|
8689 def declare(self, cgClass): |
|
8690 entries = [] |
|
8691 for i in range(0, len(self.entries)): |
|
8692 if not self.values or i >= len(self.values): |
|
8693 entry = '%s' % self.entries[i] |
|
8694 else: |
|
8695 entry = '%s = %s' % (self.entries[i], self.values[i]) |
|
8696 entries.append(entry) |
|
8697 name = '' if not self.name else ' ' + self.name |
|
8698 return 'enum%s\n{\n%s\n};\n' % (name, indent(',\n'.join(entries))) |
|
8699 |
|
8700 def define(self, cgClass): |
|
8701 # Only goes in the header |
|
8702 return '' |
|
8703 |
|
8704 |
|
8705 class ClassUnion(ClassItem): |
|
8706 def __init__(self, name, entries, visibility="public"): |
|
8707 self.entries = [entry + ";\n" for entry in entries] |
|
8708 ClassItem.__init__(self, name, visibility) |
|
8709 |
|
8710 def declare(self, cgClass): |
|
8711 return "union %s\n{\n%s\n};\n" % (self.name, indent(''.join(self.entries))) |
|
8712 |
|
8713 def define(self, cgClass): |
|
8714 # Only goes in the header |
|
8715 return '' |
|
8716 |
|
8717 |
|
8718 class CGClass(CGThing): |
|
8719 def __init__(self, name, bases=[], members=[], constructors=[], |
|
8720 destructor=None, methods=[], |
|
8721 typedefs=[], enums=[], unions=[], templateArgs=[], |
|
8722 templateSpecialization=[], isStruct=False, |
|
8723 disallowCopyConstruction=False, indent='', |
|
8724 decorators='', |
|
8725 extradeclarations='', |
|
8726 extradefinitions=''): |
|
8727 CGThing.__init__(self) |
|
8728 self.name = name |
|
8729 self.bases = bases |
|
8730 self.members = members |
|
8731 self.constructors = constructors |
|
8732 # We store our single destructor in a list, since all of our |
|
8733 # code wants lists of members. |
|
8734 self.destructors = [destructor] if destructor else [] |
|
8735 self.methods = methods |
|
8736 self.typedefs = typedefs |
|
8737 self.enums = enums |
|
8738 self.unions = unions |
|
8739 self.templateArgs = templateArgs |
|
8740 self.templateSpecialization = templateSpecialization |
|
8741 self.isStruct = isStruct |
|
8742 self.disallowCopyConstruction = disallowCopyConstruction |
|
8743 self.indent = indent |
|
8744 self.defaultVisibility = 'public' if isStruct else 'private' |
|
8745 self.decorators = decorators |
|
8746 self.extradeclarations = extradeclarations |
|
8747 self.extradefinitions = extradefinitions |
|
8748 |
|
8749 def getNameString(self): |
|
8750 className = self.name |
|
8751 if self.templateSpecialization: |
|
8752 className += '<%s>' % ', '.join([str(a) |
|
8753 for a in self.templateSpecialization]) |
|
8754 return className |
|
8755 |
|
8756 def declare(self): |
|
8757 result = '' |
|
8758 if self.templateArgs: |
|
8759 templateArgs = [a.declare() for a in self.templateArgs] |
|
8760 templateArgs = templateArgs[len(self.templateSpecialization):] |
|
8761 result += ('template <%s>\n' % |
|
8762 ','.join([str(a) for a in templateArgs])) |
|
8763 |
|
8764 type = 'struct' if self.isStruct else 'class' |
|
8765 |
|
8766 if self.templateSpecialization: |
|
8767 specialization = \ |
|
8768 '<%s>' % ', '.join([str(a) for a in self.templateSpecialization]) |
|
8769 else: |
|
8770 specialization = '' |
|
8771 |
|
8772 myself = '%s %s%s' % (type, self.name, specialization) |
|
8773 if self.decorators != '': |
|
8774 myself += " " + self.decorators |
|
8775 result += myself |
|
8776 |
|
8777 if self.bases: |
|
8778 inherit = ' : ' |
|
8779 result += inherit |
|
8780 # Grab our first base |
|
8781 baseItems = [CGGeneric(b.declare(self)) for b in self.bases] |
|
8782 bases = baseItems[:1] |
|
8783 # Indent the rest |
|
8784 bases.extend(CGIndenter(b, len(myself) + len(inherit)) |
|
8785 for b in baseItems[1:]) |
|
8786 result += ",\n".join(b.define() for b in bases) |
|
8787 |
|
8788 result += '\n{\n' |
|
8789 |
|
8790 result += self.extradeclarations |
|
8791 |
|
8792 def declareMembers(cgClass, memberList, defaultVisibility): |
|
8793 members = {'private': [], 'protected': [], 'public': []} |
|
8794 |
|
8795 for member in memberList: |
|
8796 members[member.visibility].append(member) |
|
8797 |
|
8798 if defaultVisibility == 'public': |
|
8799 order = ['public', 'protected', 'private'] |
|
8800 else: |
|
8801 order = ['private', 'protected', 'public'] |
|
8802 |
|
8803 result = '' |
|
8804 |
|
8805 lastVisibility = defaultVisibility |
|
8806 for visibility in order: |
|
8807 list = members[visibility] |
|
8808 if list: |
|
8809 if visibility != lastVisibility: |
|
8810 result += visibility + ':\n' |
|
8811 for member in list: |
|
8812 result += indent(member.declare(cgClass)) |
|
8813 lastVisibility = visibility |
|
8814 return (result, lastVisibility) |
|
8815 |
|
8816 if self.disallowCopyConstruction: |
|
8817 class DisallowedCopyConstructor(object): |
|
8818 def __init__(self): |
|
8819 self.visibility = "private" |
|
8820 |
|
8821 def declare(self, cgClass): |
|
8822 name = cgClass.getNameString() |
|
8823 return ("%s(const %s&) MOZ_DELETE;\n" |
|
8824 "void operator=(const %s) MOZ_DELETE;\n" % (name, name, name)) |
|
8825 |
|
8826 disallowedCopyConstructors = [DisallowedCopyConstructor()] |
|
8827 else: |
|
8828 disallowedCopyConstructors = [] |
|
8829 |
|
8830 order = [self.enums, self.unions, |
|
8831 self.typedefs, self.members, |
|
8832 self.constructors + disallowedCopyConstructors, |
|
8833 self.destructors, self.methods] |
|
8834 |
|
8835 lastVisibility = self.defaultVisibility |
|
8836 pieces = [] |
|
8837 for memberList in order: |
|
8838 code, lastVisibility = declareMembers(self, memberList, lastVisibility) |
|
8839 |
|
8840 if code: |
|
8841 code = code.rstrip() + "\n" # remove extra blank lines at the end |
|
8842 pieces.append(code) |
|
8843 |
|
8844 result += '\n'.join(pieces) |
|
8845 result += '};\n' |
|
8846 result = indent(result, len(self.indent)) |
|
8847 return result |
|
8848 |
|
8849 def define(self): |
|
8850 def defineMembers(cgClass, memberList, itemCount, separator=''): |
|
8851 result = '' |
|
8852 for member in memberList: |
|
8853 if itemCount != 0: |
|
8854 result = result + separator |
|
8855 definition = member.define(cgClass) |
|
8856 if definition: |
|
8857 # Member variables would only produce empty lines here. |
|
8858 result += definition |
|
8859 itemCount += 1 |
|
8860 return (result, itemCount) |
|
8861 |
|
8862 order = [(self.members, ''), (self.constructors, '\n'), |
|
8863 (self.destructors, '\n'), (self.methods, '\n')] |
|
8864 |
|
8865 result = self.extradefinitions |
|
8866 itemCount = 0 |
|
8867 for memberList, separator in order: |
|
8868 memberString, itemCount = defineMembers(self, memberList, |
|
8869 itemCount, separator) |
|
8870 result = result + memberString |
|
8871 return result |
|
8872 |
|
8873 |
|
8874 class CGResolveOwnProperty(CGAbstractStaticMethod): |
|
8875 def __init__(self, descriptor): |
|
8876 args = [Argument('JSContext*', 'cx'), |
|
8877 Argument('JS::Handle<JSObject*>', 'wrapper'), |
|
8878 Argument('JS::Handle<JSObject*>', 'obj'), |
|
8879 Argument('JS::Handle<jsid>', 'id'), |
|
8880 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'), |
|
8881 ] |
|
8882 CGAbstractStaticMethod.__init__(self, descriptor, "ResolveOwnProperty", |
|
8883 "bool", args) |
|
8884 |
|
8885 def definition_body(self): |
|
8886 # BOGUS extra blank line at end of function |
|
8887 return " return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n\n" |
|
8888 |
|
8889 |
|
8890 class CGResolveOwnPropertyViaNewresolve(CGAbstractBindingMethod): |
|
8891 """ |
|
8892 An implementation of Xray ResolveOwnProperty stuff for things that have a |
|
8893 newresolve hook. |
|
8894 """ |
|
8895 def __init__(self, descriptor): |
|
8896 args = [Argument('JSContext*', 'cx'), |
|
8897 Argument('JS::Handle<JSObject*>', 'wrapper'), |
|
8898 Argument('JS::Handle<JSObject*>', 'obj'), |
|
8899 Argument('JS::Handle<jsid>', 'id'), |
|
8900 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc')] |
|
8901 CGAbstractBindingMethod.__init__(self, descriptor, |
|
8902 "ResolveOwnPropertyViaNewresolve", |
|
8903 args, getThisObj="", |
|
8904 callArgs="") |
|
8905 |
|
8906 def generate_code(self): |
|
8907 return CGGeneric(indent(dedent(""" |
|
8908 { |
|
8909 // Since we're dealing with an Xray, do the resolve on the |
|
8910 // underlying object first. That gives it a chance to |
|
8911 // define properties on the actual object as needed, and |
|
8912 // then use the fact that it created the objects as a flag |
|
8913 // to avoid re-resolving the properties if someone deletes |
|
8914 // them. |
|
8915 JSAutoCompartment ac(cx, obj); |
|
8916 JS::Rooted<JSPropertyDescriptor> objDesc(cx); |
|
8917 if (!self->DoNewResolve(cx, obj, id, &objDesc)) { |
|
8918 return false; |
|
8919 } |
|
8920 // If desc.value() is undefined, then the DoNewResolve call |
|
8921 // has already defined the property on the object. Don't |
|
8922 // try to also define it. |
|
8923 if (objDesc.object() && |
|
8924 !objDesc.value().isUndefined() && |
|
8925 !JS_DefinePropertyById(cx, obj, id, objDesc.value(), |
|
8926 objDesc.getter(), objDesc.setter(), |
|
8927 objDesc.attributes())) { |
|
8928 return false; |
|
8929 } |
|
8930 } |
|
8931 return self->DoNewResolve(cx, wrapper, id, desc); |
|
8932 """))) |
|
8933 |
|
8934 |
|
8935 class CGEnumerateOwnProperties(CGAbstractStaticMethod): |
|
8936 def __init__(self, descriptor): |
|
8937 args = [Argument('JSContext*', 'cx'), |
|
8938 Argument('JS::Handle<JSObject*>', 'wrapper'), |
|
8939 Argument('JS::Handle<JSObject*>', 'obj'), |
|
8940 Argument('JS::AutoIdVector&', 'props')] |
|
8941 CGAbstractStaticMethod.__init__(self, descriptor, |
|
8942 "EnumerateOwnProperties", "bool", args) |
|
8943 |
|
8944 def definition_body(self): |
|
8945 # BOGUS extra newline |
|
8946 return " return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n\n" |
|
8947 |
|
8948 |
|
8949 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod): |
|
8950 """ |
|
8951 An implementation of Xray EnumerateOwnProperties stuff for things |
|
8952 that have a newresolve hook. |
|
8953 """ |
|
8954 def __init__(self, descriptor): |
|
8955 args = [Argument('JSContext*', 'cx'), |
|
8956 Argument('JS::Handle<JSObject*>', 'wrapper'), |
|
8957 Argument('JS::Handle<JSObject*>', 'obj'), |
|
8958 Argument('JS::AutoIdVector&', 'props')] |
|
8959 CGAbstractBindingMethod.__init__(self, descriptor, |
|
8960 "EnumerateOwnPropertiesViaGetOwnPropertyNames", |
|
8961 args, getThisObj="", |
|
8962 callArgs="") |
|
8963 |
|
8964 def generate_code(self): |
|
8965 return CGIndenter(CGGeneric(dedent(""" |
|
8966 nsAutoTArray<nsString, 8> names; |
|
8967 ErrorResult rv; |
|
8968 self->GetOwnPropertyNames(cx, names, rv); |
|
8969 rv.WouldReportJSException(); |
|
8970 if (rv.Failed()) { |
|
8971 return ThrowMethodFailedWithDetails(cx, rv, "%s", "enumerate"); |
|
8972 } |
|
8973 // OK to pass null as "proxy" because it's ignored if |
|
8974 // shadowPrototypeProperties is true |
|
8975 return AppendNamedPropertyIds(cx, JS::NullPtr(), names, true, props); |
|
8976 """))) |
|
8977 |
|
8978 |
|
8979 class CGPrototypeTraitsClass(CGClass): |
|
8980 def __init__(self, descriptor, indent=''): |
|
8981 templateArgs = [Argument('prototypes::ID', 'PrototypeID')] |
|
8982 templateSpecialization = ['prototypes::id::' + descriptor.name] |
|
8983 enums = [ClassEnum('', ['Depth'], |
|
8984 [descriptor.interface.inheritanceDepth()])] |
|
8985 CGClass.__init__(self, 'PrototypeTraits', indent=indent, |
|
8986 templateArgs=templateArgs, |
|
8987 templateSpecialization=templateSpecialization, |
|
8988 enums=enums, isStruct=True) |
|
8989 |
|
8990 def deps(self): |
|
8991 return set() |
|
8992 |
|
8993 |
|
8994 class CGClassForwardDeclare(CGThing): |
|
8995 def __init__(self, name, isStruct=False): |
|
8996 CGThing.__init__(self) |
|
8997 self.name = name |
|
8998 self.isStruct = isStruct |
|
8999 |
|
9000 def declare(self): |
|
9001 type = 'struct' if self.isStruct else 'class' |
|
9002 return '%s %s;\n' % (type, self.name) |
|
9003 |
|
9004 def define(self): |
|
9005 # Header only |
|
9006 return '' |
|
9007 |
|
9008 def deps(self): |
|
9009 return set() |
|
9010 |
|
9011 |
|
9012 class CGProxySpecialOperation(CGPerSignatureCall): |
|
9013 """ |
|
9014 Base class for classes for calling an indexed or named special operation |
|
9015 (don't use this directly, use the derived classes below). |
|
9016 |
|
9017 If checkFound is False, will just assert that the prop is found instead of |
|
9018 checking that it is before wrapping the value. |
|
9019 """ |
|
9020 def __init__(self, descriptor, operation, checkFound=True, argumentMutableValue=None): |
|
9021 self.checkFound = checkFound |
|
9022 |
|
9023 nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation)) |
|
9024 operation = descriptor.operations[operation] |
|
9025 assert len(operation.signatures()) == 1 |
|
9026 signature = operation.signatures()[0] |
|
9027 |
|
9028 returnType, arguments = signature |
|
9029 |
|
9030 # We pass len(arguments) as the final argument so that the |
|
9031 # CGPerSignatureCall won't do any argument conversion of its own. |
|
9032 CGPerSignatureCall.__init__(self, returnType, arguments, nativeName, |
|
9033 False, descriptor, operation, |
|
9034 len(arguments)) |
|
9035 |
|
9036 if operation.isSetter() or operation.isCreator(): |
|
9037 # arguments[0] is the index or name of the item that we're setting. |
|
9038 argument = arguments[1] |
|
9039 info = getJSToNativeConversionInfo( |
|
9040 argument.type, descriptor, |
|
9041 treatNullAs=argument.treatNullAs, |
|
9042 sourceDescription=("value being assigned to %s setter" % |
|
9043 descriptor.interface.identifier.name)) |
|
9044 if argumentMutableValue is None: |
|
9045 argumentMutableValue = "desc.value()" |
|
9046 templateValues = { |
|
9047 "declName": argument.identifier.name, |
|
9048 "holderName": argument.identifier.name + "_holder", |
|
9049 "val": argumentMutableValue, |
|
9050 "mutableVal": argumentMutableValue, |
|
9051 "obj": "obj" |
|
9052 } |
|
9053 self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues)) |
|
9054 elif operation.isGetter() or operation.isDeleter(): |
|
9055 self.cgRoot.prepend(CGGeneric("bool found;\n")) |
|
9056 |
|
9057 def getArguments(self): |
|
9058 args = [(a, a.identifier.name) for a in self.arguments] |
|
9059 if self.idlNode.isGetter() or self.idlNode.isDeleter(): |
|
9060 args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean], |
|
9061 self.idlNode), |
|
9062 "found")) |
|
9063 return args |
|
9064 |
|
9065 def wrap_return_value(self): |
|
9066 if not self.idlNode.isGetter() or self.templateValues is None: |
|
9067 return "" |
|
9068 |
|
9069 wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues)) |
|
9070 if self.checkFound: |
|
9071 wrap = CGIfWrapper(wrap, "found") |
|
9072 else: |
|
9073 wrap = CGList([CGGeneric("MOZ_ASSERT(found);\n"), wrap]) |
|
9074 return "\n" + wrap.define() |
|
9075 |
|
9076 |
|
9077 class CGProxyIndexedOperation(CGProxySpecialOperation): |
|
9078 """ |
|
9079 Class to generate a call to an indexed operation. |
|
9080 |
|
9081 If doUnwrap is False, the caller is responsible for making sure a variable |
|
9082 named 'self' holds the C++ object somewhere where the code we generate |
|
9083 will see it. |
|
9084 |
|
9085 If checkFound is False, will just assert that the prop is found instead of |
|
9086 checking that it is before wrapping the value. |
|
9087 """ |
|
9088 def __init__(self, descriptor, name, doUnwrap=True, checkFound=True, |
|
9089 argumentMutableValue=None): |
|
9090 self.doUnwrap = doUnwrap |
|
9091 CGProxySpecialOperation.__init__(self, descriptor, name, checkFound, |
|
9092 argumentMutableValue=argumentMutableValue) |
|
9093 |
|
9094 def define(self): |
|
9095 # Our first argument is the id we're getting. |
|
9096 argName = self.arguments[0].identifier.name |
|
9097 if argName == "index": |
|
9098 # We already have our index in a variable with that name |
|
9099 setIndex = "" |
|
9100 else: |
|
9101 setIndex = "uint32_t %s = index;\n" % argName |
|
9102 if self.doUnwrap: |
|
9103 unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType |
|
9104 else: |
|
9105 unwrap = "" |
|
9106 return (setIndex + unwrap + |
|
9107 CGProxySpecialOperation.define(self)) |
|
9108 |
|
9109 |
|
9110 class CGProxyIndexedGetter(CGProxyIndexedOperation): |
|
9111 """ |
|
9112 Class to generate a call to an indexed getter. If templateValues is not None |
|
9113 the returned value will be wrapped with wrapForType using templateValues. |
|
9114 |
|
9115 If doUnwrap is False, the caller is responsible for making sure a variable |
|
9116 named 'self' holds the C++ object somewhere where the code we generate |
|
9117 will see it. |
|
9118 |
|
9119 If checkFound is False, will just assert that the prop is found instead of |
|
9120 checking that it is before wrapping the value. |
|
9121 """ |
|
9122 def __init__(self, descriptor, templateValues=None, doUnwrap=True, |
|
9123 checkFound=True): |
|
9124 self.templateValues = templateValues |
|
9125 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter', |
|
9126 doUnwrap, checkFound) |
|
9127 |
|
9128 |
|
9129 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter): |
|
9130 """ |
|
9131 Class to generate a call that checks whether an indexed property exists. |
|
9132 |
|
9133 For now, we just delegate to CGProxyIndexedGetter |
|
9134 """ |
|
9135 def __init__(self, descriptor): |
|
9136 CGProxyIndexedGetter.__init__(self, descriptor) |
|
9137 self.cgRoot.append(CGGeneric("(void)result;\n")) |
|
9138 |
|
9139 |
|
9140 class CGProxyIndexedSetter(CGProxyIndexedOperation): |
|
9141 """ |
|
9142 Class to generate a call to an indexed setter. |
|
9143 """ |
|
9144 def __init__(self, descriptor, argumentMutableValue=None): |
|
9145 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter', |
|
9146 argumentMutableValue=argumentMutableValue) |
|
9147 |
|
9148 |
|
9149 class CGProxyIndexedDeleter(CGProxyIndexedOperation): |
|
9150 """ |
|
9151 Class to generate a call to an indexed deleter. |
|
9152 """ |
|
9153 def __init__(self, descriptor): |
|
9154 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedDeleter') |
|
9155 |
|
9156 |
|
9157 class CGProxyNamedOperation(CGProxySpecialOperation): |
|
9158 """ |
|
9159 Class to generate a call to a named operation. |
|
9160 |
|
9161 'value' is the jsval to use for the name; None indicates that it should be |
|
9162 gotten from the property id. |
|
9163 """ |
|
9164 def __init__(self, descriptor, name, value=None, argumentMutableValue=None): |
|
9165 CGProxySpecialOperation.__init__(self, descriptor, name, |
|
9166 argumentMutableValue=argumentMutableValue) |
|
9167 self.value = value |
|
9168 |
|
9169 def define(self): |
|
9170 # Our first argument is the id we're getting. |
|
9171 argName = self.arguments[0].identifier.name |
|
9172 if argName == "id": |
|
9173 # deal with the name collision |
|
9174 idDecl = "JS::Rooted<jsid> id_(cx, id);\n" |
|
9175 idName = "id_" |
|
9176 else: |
|
9177 idDecl = "" |
|
9178 idName = "id" |
|
9179 unwrapString = fill( |
|
9180 """ |
|
9181 if (!ConvertJSValueToString(cx, nameVal, &nameVal, |
|
9182 eStringify, eStringify, ${argName})) { |
|
9183 return false; |
|
9184 } |
|
9185 """, |
|
9186 argName=argName) |
|
9187 if self.value is None: |
|
9188 # We're just using 'id', and if it's an atom we can take a |
|
9189 # fast path here. |
|
9190 unwrapString = fill( |
|
9191 """ |
|
9192 if (MOZ_LIKELY(JSID_IS_ATOM(${idName}))) { |
|
9193 ${argName}.SetData(js::GetAtomChars(JSID_TO_ATOM(${idName})), js::GetAtomLength(JSID_TO_ATOM(${idName}))); |
|
9194 } else { |
|
9195 nameVal = js::IdToValue(${idName}); |
|
9196 $*{unwrapString} |
|
9197 } |
|
9198 """, |
|
9199 idName=idName, |
|
9200 argName=argName, |
|
9201 unwrapString=unwrapString) |
|
9202 else: |
|
9203 unwrapString = ("nameVal = %s;\n" % self.value) + unwrapString |
|
9204 |
|
9205 # Sadly, we have to set up nameVal even if we have an atom id, |
|
9206 # because we don't know for sure, and we can end up needing it |
|
9207 # so it needs to be higher up the stack. Using a Maybe here |
|
9208 # seems like probable overkill. |
|
9209 return fill( |
|
9210 """ |
|
9211 JS::Rooted<JS::Value> nameVal(cx); |
|
9212 $*{idDecl} |
|
9213 binding_detail::FakeDependentString ${argName}; |
|
9214 $*{unwrapString} |
|
9215 |
|
9216 ${nativeType}* self = UnwrapProxy(proxy); |
|
9217 $*{op} |
|
9218 """, |
|
9219 idDecl=idDecl, |
|
9220 argName=argName, |
|
9221 unwrapString=unwrapString, |
|
9222 nativeType=self.descriptor.nativeType, |
|
9223 op=CGProxySpecialOperation.define(self)) |
|
9224 |
|
9225 |
|
9226 class CGProxyNamedGetter(CGProxyNamedOperation): |
|
9227 """ |
|
9228 Class to generate a call to an named getter. If templateValues is not None |
|
9229 the returned value will be wrapped with wrapForType using templateValues. |
|
9230 'value' is the jsval to use for the name; None indicates that it should be |
|
9231 gotten from the property id. |
|
9232 """ |
|
9233 def __init__(self, descriptor, templateValues=None, value=None): |
|
9234 self.templateValues = templateValues |
|
9235 CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value) |
|
9236 |
|
9237 |
|
9238 class CGProxyNamedPresenceChecker(CGProxyNamedGetter): |
|
9239 """ |
|
9240 Class to generate a call that checks whether a named property exists. |
|
9241 |
|
9242 For now, we just delegate to CGProxyNamedGetter |
|
9243 """ |
|
9244 def __init__(self, descriptor): |
|
9245 CGProxyNamedGetter.__init__(self, descriptor) |
|
9246 self.cgRoot.append(CGGeneric("(void)result;\n")) |
|
9247 |
|
9248 |
|
9249 class CGProxyNamedSetter(CGProxyNamedOperation): |
|
9250 """ |
|
9251 Class to generate a call to a named setter. |
|
9252 """ |
|
9253 def __init__(self, descriptor, argumentMutableValue=None): |
|
9254 CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter', |
|
9255 argumentMutableValue=argumentMutableValue) |
|
9256 |
|
9257 |
|
9258 class CGProxyNamedDeleter(CGProxyNamedOperation): |
|
9259 """ |
|
9260 Class to generate a call to a named deleter. |
|
9261 """ |
|
9262 def __init__(self, descriptor): |
|
9263 CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter') |
|
9264 |
|
9265 |
|
9266 class CGProxyIsProxy(CGAbstractMethod): |
|
9267 def __init__(self, descriptor): |
|
9268 args = [Argument('JSObject*', 'obj')] |
|
9269 CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True) |
|
9270 |
|
9271 def declare(self): |
|
9272 return "" |
|
9273 |
|
9274 def definition_body(self): |
|
9275 return " return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n" |
|
9276 |
|
9277 |
|
9278 class CGProxyUnwrap(CGAbstractMethod): |
|
9279 def __init__(self, descriptor): |
|
9280 args = [Argument('JSObject*', 'obj')] |
|
9281 CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True) |
|
9282 |
|
9283 def declare(self): |
|
9284 return "" |
|
9285 |
|
9286 def definition_body(self): |
|
9287 return indent(fill( |
|
9288 """ |
|
9289 MOZ_ASSERT(js::IsProxy(obj)); |
|
9290 if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) { |
|
9291 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj)); |
|
9292 obj = js::UncheckedUnwrap(obj); |
|
9293 } |
|
9294 MOZ_ASSERT(IsProxy(obj)); |
|
9295 return static_cast<${type}*>(js::GetProxyPrivate(obj).toPrivate()); |
|
9296 """, |
|
9297 type=self.descriptor.nativeType)) |
|
9298 |
|
9299 |
|
9300 class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod): |
|
9301 def __init__(self, descriptor): |
|
9302 args = [Argument('JSContext*', 'cx'), |
|
9303 Argument('JS::Handle<JSObject*>', 'proxy'), |
|
9304 Argument('JS::Handle<jsid>', 'id'), |
|
9305 Argument('bool', 'ignoreNamedProps'), |
|
9306 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc')] |
|
9307 ClassMethod.__init__(self, "getOwnPropDescriptor", "bool", args, |
|
9308 virtual=True, override=True) |
|
9309 self.descriptor = descriptor |
|
9310 |
|
9311 def getBody(self): |
|
9312 indexedGetter = self.descriptor.operations['IndexedGetter'] |
|
9313 indexedSetter = self.descriptor.operations['IndexedSetter'] |
|
9314 |
|
9315 if self.descriptor.supportsIndexedProperties(): |
|
9316 readonly = toStringBool(indexedSetter is None) |
|
9317 fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;\n" % readonly |
|
9318 templateValues = { |
|
9319 'jsvalRef': 'desc.value()', |
|
9320 'jsvalHandle': 'desc.value()', |
|
9321 'obj': 'proxy', |
|
9322 'successCode': fillDescriptor |
|
9323 } |
|
9324 getIndexed = fill( |
|
9325 """ |
|
9326 int32_t index = GetArrayIndexFromId(cx, id); |
|
9327 if (IsArrayIndex(index)) { |
|
9328 $*{callGetter} |
|
9329 } |
|
9330 |
|
9331 """, |
|
9332 callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define()) |
|
9333 else: |
|
9334 getIndexed = "" |
|
9335 |
|
9336 if UseHolderForUnforgeable(self.descriptor): |
|
9337 tryHolder = dedent(""" |
|
9338 if (!JS_GetPropertyDescriptorById(cx, ${holder}, id, desc)) { |
|
9339 return false; |
|
9340 } |
|
9341 MOZ_ASSERT_IF(desc.object(), desc.object() == ${holder}); |
|
9342 """) |
|
9343 |
|
9344 # We don't want to look at the unforgeable holder at all |
|
9345 # in the xray case; that part got handled already. |
|
9346 getUnforgeable = fill( |
|
9347 """ |
|
9348 if (!isXray) { |
|
9349 $*{callOnUnforgeable} |
|
9350 if (desc.object()) { |
|
9351 desc.object().set(proxy); |
|
9352 return true; |
|
9353 } |
|
9354 } |
|
9355 |
|
9356 """, |
|
9357 callOnUnforgeable=CallOnUnforgeableHolder(self.descriptor, tryHolder)) |
|
9358 else: |
|
9359 getUnforgeable = "" |
|
9360 |
|
9361 if self.descriptor.supportsNamedProperties(): |
|
9362 operations = self.descriptor.operations |
|
9363 readonly = toStringBool(operations['NamedSetter'] is None) |
|
9364 enumerable = ( |
|
9365 "self->NameIsEnumerable(Constify(%s))" % |
|
9366 # First [0] means first (and only) signature, [1] means |
|
9367 # "arguments" as opposed to return type, [0] means first (and |
|
9368 # only) argument. |
|
9369 operations['NamedGetter'].signatures()[0][1][0].identifier.name) |
|
9370 fillDescriptor = ( |
|
9371 "FillPropertyDescriptor(desc, proxy, %s, %s);\n" |
|
9372 "return true;\n" % (readonly, enumerable)) |
|
9373 templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()', |
|
9374 'obj': 'proxy', 'successCode': fillDescriptor} |
|
9375 condition = "!HasPropertyOnPrototype(cx, proxy, id)" |
|
9376 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): |
|
9377 condition = "(!isXray || %s)" % condition |
|
9378 condition = "!ignoreNamedProps && " + condition |
|
9379 if self.descriptor.supportsIndexedProperties(): |
|
9380 condition = "!IsArrayIndex(index) && " + condition |
|
9381 namedGet = (CGIfWrapper(CGProxyNamedGetter(self.descriptor, templateValues), |
|
9382 condition).define() + |
|
9383 "\n") |
|
9384 else: |
|
9385 namedGet = "" |
|
9386 |
|
9387 return fill( |
|
9388 """ |
|
9389 bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy); |
|
9390 $*{getIndexed} |
|
9391 $*{getUnforgeable} |
|
9392 JS::Rooted<JSObject*> expando(cx); |
|
9393 if (!isXray && (expando = GetExpandoObject(proxy))) { |
|
9394 if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) { |
|
9395 return false; |
|
9396 } |
|
9397 if (desc.object()) { |
|
9398 // Pretend the property lives on the wrapper. |
|
9399 desc.object().set(proxy); |
|
9400 return true; |
|
9401 } |
|
9402 } |
|
9403 |
|
9404 $*{namedGet} |
|
9405 desc.object().set(nullptr); |
|
9406 return true; |
|
9407 """, |
|
9408 getIndexed=getIndexed, |
|
9409 getUnforgeable=getUnforgeable, |
|
9410 namedGet=namedGet) |
|
9411 |
|
9412 |
|
9413 class CGDOMJSProxyHandler_defineProperty(ClassMethod): |
|
9414 def __init__(self, descriptor): |
|
9415 args = [Argument('JSContext*', 'cx'), |
|
9416 Argument('JS::Handle<JSObject*>', 'proxy'), |
|
9417 Argument('JS::Handle<jsid>', 'id'), |
|
9418 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'), |
|
9419 Argument('bool*', 'defined')] |
|
9420 ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True) |
|
9421 self.descriptor = descriptor |
|
9422 |
|
9423 def getBody(self): |
|
9424 set = "" |
|
9425 |
|
9426 indexedSetter = self.descriptor.operations['IndexedSetter'] |
|
9427 if indexedSetter: |
|
9428 if self.descriptor.operations['IndexedCreator'] is not indexedSetter: |
|
9429 raise TypeError("Can't handle creator that's different from the setter") |
|
9430 set += fill( |
|
9431 """ |
|
9432 int32_t index = GetArrayIndexFromId(cx, id); |
|
9433 if (IsArrayIndex(index)) { |
|
9434 *defined = true; |
|
9435 $*{callSetter} |
|
9436 return true; |
|
9437 } |
|
9438 """, |
|
9439 callSetter=CGProxyIndexedSetter(self.descriptor).define()) |
|
9440 elif self.descriptor.supportsIndexedProperties(): |
|
9441 set += fill( |
|
9442 """ |
|
9443 if (IsArrayIndex(GetArrayIndexFromId(cx, id))) { |
|
9444 return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}"); |
|
9445 } |
|
9446 """, |
|
9447 name=self.descriptor.name) |
|
9448 |
|
9449 if UseHolderForUnforgeable(self.descriptor): |
|
9450 defineOnUnforgeable = ("bool hasUnforgeable;\n" |
|
9451 "if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n" |
|
9452 " return false;\n" |
|
9453 "}\n" |
|
9454 "if (hasUnforgeable) {\n" |
|
9455 " *defined = true;" # SUPER BOGUS missing newline |
|
9456 " bool unused;\n" |
|
9457 " return js_DefineOwnProperty(cx, ${holder}, id, desc, &unused);\n" |
|
9458 "}\n" |
|
9459 "\n") # BOGUS extra blank line at end of block or method |
|
9460 set += CallOnUnforgeableHolder(self.descriptor, |
|
9461 defineOnUnforgeable, |
|
9462 "xpc::WrapperFactory::IsXrayWrapper(proxy)") |
|
9463 |
|
9464 namedSetter = self.descriptor.operations['NamedSetter'] |
|
9465 if namedSetter: |
|
9466 if self.descriptor.operations['NamedCreator'] is not namedSetter: |
|
9467 raise TypeError("Can't handle creator that's different from the setter") |
|
9468 # If we support indexed properties, we won't get down here for |
|
9469 # indices, so we can just do our setter unconditionally here. |
|
9470 set += fill( |
|
9471 """ |
|
9472 *defined = true; |
|
9473 $*{callSetter} |
|
9474 |
|
9475 return true; |
|
9476 |
|
9477 """, # BOGUS extra blank line at end of method |
|
9478 callSetter=CGProxyNamedSetter(self.descriptor).define()) |
|
9479 else: |
|
9480 if self.descriptor.supportsNamedProperties(): |
|
9481 set += fill( |
|
9482 """ |
|
9483 $*{presenceChecker} |
|
9484 |
|
9485 if (found) { |
|
9486 return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}"); |
|
9487 } |
|
9488 """, |
|
9489 presenceChecker=CGProxyNamedPresenceChecker(self.descriptor).define(), |
|
9490 name=self.descriptor.name) |
|
9491 set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" % |
|
9492 ", ".join(a.name for a in self.args)) |
|
9493 return set |
|
9494 |
|
9495 |
|
9496 class CGDOMJSProxyHandler_delete(ClassMethod): |
|
9497 def __init__(self, descriptor): |
|
9498 args = [Argument('JSContext*', 'cx'), |
|
9499 Argument('JS::Handle<JSObject*>', 'proxy'), |
|
9500 Argument('JS::Handle<jsid>', 'id'), |
|
9501 Argument('bool*', 'bp')] |
|
9502 ClassMethod.__init__(self, "delete_", "bool", args, |
|
9503 virtual=True, override=True) |
|
9504 self.descriptor = descriptor |
|
9505 |
|
9506 def getBody(self): |
|
9507 def getDeleterBody(type): |
|
9508 """ |
|
9509 type should be "Named" or "Indexed" |
|
9510 """ |
|
9511 assert type in ("Named", "Indexed") |
|
9512 deleter = self.descriptor.operations[type + 'Deleter'] |
|
9513 if deleter: |
|
9514 if (not deleter.signatures()[0][0].isPrimitive() or |
|
9515 deleter.signatures()[0][0].nullable() or |
|
9516 deleter.signatures()[0][0].tag() != IDLType.Tags.bool): |
|
9517 setBp = "*bp = true;\n" |
|
9518 else: |
|
9519 setBp = dedent(""" |
|
9520 if (found) { |
|
9521 *bp = result; |
|
9522 } else { |
|
9523 *bp = true; |
|
9524 } |
|
9525 """) |
|
9526 body = (eval("CGProxy%sDeleter" % type)(self.descriptor).define() + |
|
9527 setBp) |
|
9528 elif eval("self.descriptor.supports%sProperties()" % type): |
|
9529 body = (eval("CGProxy%sPresenceChecker" % type)(self.descriptor).define() + |
|
9530 dedent(""" |
|
9531 if (found) { |
|
9532 *bp = false; |
|
9533 } else { |
|
9534 *bp = true; |
|
9535 } |
|
9536 """)) |
|
9537 else: |
|
9538 body = None |
|
9539 return body |
|
9540 |
|
9541 delete = dedent(""" |
|
9542 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), |
|
9543 "Should not have a XrayWrapper here"); |
|
9544 |
|
9545 """) |
|
9546 |
|
9547 indexedBody = getDeleterBody("Indexed") |
|
9548 if indexedBody is not None: |
|
9549 delete += fill( |
|
9550 """ |
|
9551 int32_t index = GetArrayIndexFromId(cx, id); |
|
9552 if (IsArrayIndex(index)) { |
|
9553 $*{indexedBody} |
|
9554 // We always return here, even if the property was not found |
|
9555 return true; |
|
9556 } |
|
9557 """, |
|
9558 indexedBody=indexedBody) |
|
9559 |
|
9560 if UseHolderForUnforgeable(self.descriptor): |
|
9561 unforgeable = dedent(""" |
|
9562 bool hasUnforgeable; |
|
9563 if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) { |
|
9564 return false; |
|
9565 } |
|
9566 if (hasUnforgeable) { |
|
9567 *bp = false; |
|
9568 return true; |
|
9569 } |
|
9570 """) |
|
9571 delete += CallOnUnforgeableHolder(self.descriptor, unforgeable) |
|
9572 delete += "\n" |
|
9573 |
|
9574 namedBody = getDeleterBody("Named") |
|
9575 if namedBody is not None: |
|
9576 # We always return above for an index id in the case when we support |
|
9577 # indexed properties, so we can just treat the id as a name |
|
9578 # unconditionally here. |
|
9579 delete += (namedBody + |
|
9580 "if (found) {\n" |
|
9581 " return true;\n" |
|
9582 "}\n\n") # BOGUS extra blank line |
|
9583 if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): |
|
9584 delete = CGIfWrapper(CGGeneric(delete), |
|
9585 "!HasPropertyOnPrototype(cx, proxy, id)").define() |
|
9586 else: |
|
9587 delete += "\n" # BOGUS extra blank line |
|
9588 |
|
9589 delete += dedent(""" |
|
9590 |
|
9591 return dom::DOMProxyHandler::delete_(cx, proxy, id, bp); |
|
9592 """) |
|
9593 |
|
9594 return delete |
|
9595 |
|
9596 |
|
9597 class CGDOMJSProxyHandler_ownPropNames(ClassMethod): |
|
9598 def __init__(self, descriptor, ): |
|
9599 args = [Argument('JSContext*', 'cx'), |
|
9600 Argument('JS::Handle<JSObject*>', 'proxy'), |
|
9601 Argument('unsigned', 'flags'), |
|
9602 Argument('JS::AutoIdVector&', 'props')] |
|
9603 ClassMethod.__init__(self, "ownPropNames", "bool", args, |
|
9604 virtual=True, override=True) |
|
9605 self.descriptor = descriptor |
|
9606 |
|
9607 def getBody(self): |
|
9608 # Per spec, we do indices, then named props, then everything else |
|
9609 if self.descriptor.supportsIndexedProperties(): |
|
9610 addIndices = dedent(""" |
|
9611 |
|
9612 uint32_t length = UnwrapProxy(proxy)->Length(); |
|
9613 MOZ_ASSERT(int32_t(length) >= 0); |
|
9614 for (int32_t i = 0; i < int32_t(length); ++i) { |
|
9615 if (!props.append(INT_TO_JSID(i))) { |
|
9616 return false; |
|
9617 } |
|
9618 } |
|
9619 """) |
|
9620 else: |
|
9621 addIndices = "" |
|
9622 |
|
9623 if UseHolderForUnforgeable(self.descriptor): |
|
9624 addUnforgeable = dedent(""" |
|
9625 if (!js::GetPropertyNames(cx, ${holder}, flags, &props)) { |
|
9626 return false; |
|
9627 } |
|
9628 """) |
|
9629 addUnforgeable = CallOnUnforgeableHolder(self.descriptor, |
|
9630 addUnforgeable, |
|
9631 "isXray") |
|
9632 else: |
|
9633 addUnforgeable = "" |
|
9634 |
|
9635 if self.descriptor.supportsNamedProperties(): |
|
9636 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): |
|
9637 shadow = "!isXray" |
|
9638 else: |
|
9639 shadow = "false" |
|
9640 addNames = fill( |
|
9641 """ |
|
9642 |
|
9643 nsTArray<nsString> names; |
|
9644 UnwrapProxy(proxy)->GetSupportedNames(flags, names); |
|
9645 if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) { |
|
9646 return false; |
|
9647 } |
|
9648 """, |
|
9649 shadow=shadow) |
|
9650 else: |
|
9651 addNames = "" |
|
9652 |
|
9653 return fill( |
|
9654 """ |
|
9655 bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy); |
|
9656 $*{addIndices} |
|
9657 $*{addUnforgeable} |
|
9658 $*{addNames} |
|
9659 |
|
9660 JS::Rooted<JSObject*> expando(cx); |
|
9661 if (!isXray && (expando = DOMProxyHandler::GetExpandoObject(proxy)) && |
|
9662 !js::GetPropertyNames(cx, expando, flags, &props)) { |
|
9663 return false; |
|
9664 } |
|
9665 |
|
9666 return true; |
|
9667 """, |
|
9668 addIndices=addIndices, |
|
9669 addUnforgeable=addUnforgeable, |
|
9670 addNames=addNames) |
|
9671 |
|
9672 |
|
9673 class CGDOMJSProxyHandler_hasOwn(ClassMethod): |
|
9674 def __init__(self, descriptor): |
|
9675 args = [Argument('JSContext*', 'cx'), |
|
9676 Argument('JS::Handle<JSObject*>', 'proxy'), |
|
9677 Argument('JS::Handle<jsid>', 'id'), |
|
9678 Argument('bool*', 'bp')] |
|
9679 ClassMethod.__init__(self, "hasOwn", "bool", args, |
|
9680 virtual=True, override=True) |
|
9681 self.descriptor = descriptor |
|
9682 |
|
9683 def getBody(self): |
|
9684 if self.descriptor.supportsIndexedProperties(): |
|
9685 indexed = fill( |
|
9686 """ |
|
9687 int32_t index = GetArrayIndexFromId(cx, id); |
|
9688 if (IsArrayIndex(index)) { |
|
9689 $*{presenceChecker} |
|
9690 |
|
9691 *bp = found; |
|
9692 return true; |
|
9693 } |
|
9694 |
|
9695 """, |
|
9696 presenceChecker=CGProxyIndexedPresenceChecker(self.descriptor).define()) |
|
9697 else: |
|
9698 indexed = "" |
|
9699 |
|
9700 if UseHolderForUnforgeable(self.descriptor): |
|
9701 unforgeable = dedent(""" |
|
9702 bool b = true; |
|
9703 bool ok = JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &b); |
|
9704 *bp = !!b; |
|
9705 if (!ok || *bp) { |
|
9706 return ok; |
|
9707 } |
|
9708 """) |
|
9709 unforgeable = CallOnUnforgeableHolder(self.descriptor, unforgeable) |
|
9710 else: |
|
9711 unforgeable = "" |
|
9712 |
|
9713 if self.descriptor.supportsNamedProperties(): |
|
9714 # If we support indexed properties we always return above for index |
|
9715 # property names, so no need to check for those here. |
|
9716 named = (CGProxyNamedPresenceChecker(self.descriptor).define() + |
|
9717 "\n" + |
|
9718 "*bp = found;\n") |
|
9719 if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): |
|
9720 # BOGUS extra blank line at end of block |
|
9721 named = CGIfWrapper(CGGeneric(named + "return true;\n\n"), |
|
9722 "!HasPropertyOnPrototype(cx, proxy, id)").define() |
|
9723 named += "*bp = false;\n" |
|
9724 else: |
|
9725 named += "\n" |
|
9726 else: |
|
9727 named = "*bp = false;\n" |
|
9728 |
|
9729 return fill( |
|
9730 """ |
|
9731 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), |
|
9732 "Should not have a XrayWrapper here"); |
|
9733 |
|
9734 $*{indexed} |
|
9735 $*{unforgeable} |
|
9736 |
|
9737 JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy)); |
|
9738 if (expando) { |
|
9739 bool b = true; |
|
9740 bool ok = JS_HasPropertyById(cx, expando, id, &b); |
|
9741 *bp = !!b; |
|
9742 if (!ok || *bp) { |
|
9743 return ok; |
|
9744 } |
|
9745 } |
|
9746 |
|
9747 $*{named} |
|
9748 return true; |
|
9749 """, |
|
9750 indexed=indexed, |
|
9751 unforgeable=unforgeable, |
|
9752 named=named) |
|
9753 |
|
9754 |
|
9755 class CGDOMJSProxyHandler_get(ClassMethod): |
|
9756 def __init__(self, descriptor): |
|
9757 args = [Argument('JSContext*', 'cx'), |
|
9758 Argument('JS::Handle<JSObject*>', 'proxy'), |
|
9759 Argument('JS::Handle<JSObject*>', 'receiver'), |
|
9760 Argument('JS::Handle<jsid>', 'id'), |
|
9761 Argument('JS::MutableHandle<JS::Value>', 'vp')] |
|
9762 ClassMethod.__init__(self, "get", "bool", args, |
|
9763 virtual=True, override=True) |
|
9764 self.descriptor = descriptor |
|
9765 |
|
9766 def getBody(self): |
|
9767 getUnforgeableOrExpando = "JS::Rooted<JSObject*> sharedRoot(cx);\n" |
|
9768 if UseHolderForUnforgeable(self.descriptor): |
|
9769 hasUnforgeable = dedent(""" |
|
9770 bool hasUnforgeable; |
|
9771 if (!JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &hasUnforgeable)) { |
|
9772 return false; |
|
9773 } |
|
9774 if (hasUnforgeable) { |
|
9775 return JS_ForwardGetPropertyTo(cx, ${holder}, id, proxy, vp); |
|
9776 } |
|
9777 """) |
|
9778 getUnforgeableOrExpando += CallOnUnforgeableHolder(self.descriptor, |
|
9779 hasUnforgeable, |
|
9780 useSharedRoot=True) |
|
9781 getUnforgeableOrExpando += dedent(""" |
|
9782 { // Scope for expando |
|
9783 JS::Rooted<JSObject*>& expando(sharedRoot); |
|
9784 expando = DOMProxyHandler::GetExpandoObject(proxy); |
|
9785 if (expando) { |
|
9786 bool hasProp; |
|
9787 if (!JS_HasPropertyById(cx, expando, id, &hasProp)) { |
|
9788 return false; |
|
9789 } |
|
9790 |
|
9791 if (hasProp) { |
|
9792 // Forward the get to the expando object, but our receiver is whatever our |
|
9793 // receiver is. |
|
9794 return JS_ForwardGetPropertyTo(cx, expando, id, receiver, vp); |
|
9795 } |
|
9796 } |
|
9797 } |
|
9798 """) |
|
9799 |
|
9800 templateValues = {'jsvalRef': 'vp', 'jsvalHandle': 'vp', 'obj': 'proxy'} |
|
9801 |
|
9802 if self.descriptor.supportsIndexedProperties(): |
|
9803 getIndexedOrExpando = fill( |
|
9804 """ |
|
9805 int32_t index = GetArrayIndexFromId(cx, id); |
|
9806 if (IsArrayIndex(index)) { |
|
9807 $*{callGetter} |
|
9808 // Even if we don't have this index, we don't forward the |
|
9809 // get on to our expando object. |
|
9810 } else { |
|
9811 $*{getUnforgeableOrExpando} |
|
9812 } |
|
9813 """, |
|
9814 callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define(), |
|
9815 getUnforgeableOrExpando=getUnforgeableOrExpando) |
|
9816 else: |
|
9817 getIndexedOrExpando = getUnforgeableOrExpando |
|
9818 |
|
9819 if self.descriptor.supportsNamedProperties(): |
|
9820 getNamed = CGProxyNamedGetter(self.descriptor, templateValues) |
|
9821 if self.descriptor.supportsIndexedProperties(): |
|
9822 getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)") |
|
9823 getNamed = getNamed.define() + "\n" |
|
9824 else: |
|
9825 getNamed = "" |
|
9826 |
|
9827 getOnPrototype = dedent(""" |
|
9828 bool foundOnPrototype; |
|
9829 if (!GetPropertyOnPrototype(cx, proxy, id, &foundOnPrototype, vp.address())) { |
|
9830 return false; |
|
9831 } |
|
9832 |
|
9833 if (foundOnPrototype) { |
|
9834 return true; |
|
9835 } |
|
9836 |
|
9837 """) |
|
9838 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): |
|
9839 getNamed = getNamed + getOnPrototype |
|
9840 else: |
|
9841 getNamed = getOnPrototype + getNamed |
|
9842 |
|
9843 return fill( |
|
9844 """ |
|
9845 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), |
|
9846 "Should not have a XrayWrapper here"); |
|
9847 |
|
9848 $*{indexedOrExpando} |
|
9849 |
|
9850 $*{named} |
|
9851 vp.setUndefined(); |
|
9852 return true; |
|
9853 """, |
|
9854 indexedOrExpando=getIndexedOrExpando, |
|
9855 named=getNamed) |
|
9856 |
|
9857 |
|
9858 class CGDOMJSProxyHandler_setCustom(ClassMethod): |
|
9859 def __init__(self, descriptor): |
|
9860 args = [Argument('JSContext*', 'cx'), |
|
9861 Argument('JS::Handle<JSObject*>', 'proxy'), |
|
9862 Argument('JS::Handle<jsid>', 'id'), |
|
9863 Argument('JS::MutableHandle<JS::Value>', 'vp'), |
|
9864 Argument('bool*', 'done')] |
|
9865 ClassMethod.__init__(self, "setCustom", "bool", args, virtual=True, override=True) |
|
9866 self.descriptor = descriptor |
|
9867 |
|
9868 def getBody(self): |
|
9869 assertion = ("MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n" |
|
9870 ' "Should not have a XrayWrapper here");\n') |
|
9871 |
|
9872 # Correctness first. If we have a NamedSetter and [OverrideBuiltins], |
|
9873 # always call the NamedSetter and never do anything else. |
|
9874 namedSetter = self.descriptor.operations['NamedSetter'] |
|
9875 if (namedSetter is not None and |
|
9876 self.descriptor.interface.getExtendedAttribute('OverrideBuiltins')): |
|
9877 # Check assumptions. |
|
9878 if self.descriptor.supportsIndexedProperties(): |
|
9879 raise ValueError("In interface " + self.descriptor.name + ": " + |
|
9880 "Can't cope with [OverrideBuiltins] and an indexed getter") |
|
9881 if self.descriptor.operations['NamedCreator'] is not namedSetter: |
|
9882 raise ValueError("In interface " + self.descriptor.name + ": " + |
|
9883 "Can't cope with named setter that is not also a named creator") |
|
9884 if UseHolderForUnforgeable(self.descriptor): |
|
9885 raise ValueError("In interface " + self.descriptor.name + ": " + |
|
9886 "Can't cope with [OverrideBuiltins] and unforgeable members") |
|
9887 |
|
9888 callSetter = CGProxyNamedSetter(self.descriptor, argumentMutableValue="vp") |
|
9889 return (assertion + |
|
9890 callSetter.define() + |
|
9891 "*done = true;\n" |
|
9892 "return true;\n") |
|
9893 |
|
9894 # As an optimization, if we are going to call an IndexedSetter, go |
|
9895 # ahead and call it and have done. |
|
9896 indexedSetter = self.descriptor.operations['IndexedSetter'] |
|
9897 if indexedSetter is not None: |
|
9898 if self.descriptor.operations['IndexedCreator'] is not indexedSetter: |
|
9899 raise ValueError("In interface " + self.descriptor.name + ": " + |
|
9900 "Can't cope with indexed setter that is not " + |
|
9901 "also an indexed creator") |
|
9902 setIndexed = fill( |
|
9903 """ |
|
9904 int32_t index = GetArrayIndexFromId(cx, id); |
|
9905 if (IsArrayIndex(index)) { |
|
9906 $*{callSetter} |
|
9907 *done = true; |
|
9908 return true; |
|
9909 } |
|
9910 |
|
9911 """, |
|
9912 callSetter=CGProxyIndexedSetter(self.descriptor, |
|
9913 argumentMutableValue="vp").define()) |
|
9914 else: |
|
9915 setIndexed = "" |
|
9916 |
|
9917 return (assertion + |
|
9918 setIndexed + |
|
9919 "*done = false;\n" |
|
9920 "return true;\n") |
|
9921 |
|
9922 |
|
9923 class CGDOMJSProxyHandler_className(ClassMethod): |
|
9924 def __init__(self, descriptor): |
|
9925 args = [Argument('JSContext*', 'cx'), |
|
9926 Argument('JS::Handle<JSObject*>', 'proxy')] |
|
9927 ClassMethod.__init__(self, "className", "const char*", args, |
|
9928 virtual=True, override=True) |
|
9929 self.descriptor = descriptor |
|
9930 |
|
9931 def getBody(self): |
|
9932 return 'return "%s";\n' % self.descriptor.name |
|
9933 |
|
9934 |
|
9935 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod): |
|
9936 def __init__(self, descriptor): |
|
9937 args = [Argument('JS::Value', 'priv')] |
|
9938 ClassMethod.__init__(self, "finalizeInBackground", "bool", args, |
|
9939 virtual=True, override=True) |
|
9940 self.descriptor = descriptor |
|
9941 |
|
9942 def getBody(self): |
|
9943 return "return false;\n" |
|
9944 |
|
9945 |
|
9946 class CGDOMJSProxyHandler_finalize(ClassMethod): |
|
9947 def __init__(self, descriptor): |
|
9948 args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')] |
|
9949 ClassMethod.__init__(self, "finalize", "void", args, |
|
9950 virtual=True, override=True) |
|
9951 self.descriptor = descriptor |
|
9952 |
|
9953 def getBody(self): |
|
9954 return ("%s* self = UnwrapProxy(proxy);\n\n" % self.descriptor.nativeType + |
|
9955 finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define()) |
|
9956 |
|
9957 |
|
9958 class CGDOMJSProxyHandler_slice(ClassMethod): |
|
9959 def __init__(self, descriptor): |
|
9960 assert descriptor.supportsIndexedProperties() |
|
9961 |
|
9962 args = [Argument('JSContext*', 'cx'), |
|
9963 Argument('JS::Handle<JSObject*>', 'proxy'), |
|
9964 Argument('uint32_t', 'begin'), |
|
9965 Argument('uint32_t', 'end'), |
|
9966 Argument('JS::Handle<JSObject*>', 'array')] |
|
9967 ClassMethod.__init__(self, "slice", "bool", args, virtual=True, override=True) |
|
9968 self.descriptor = descriptor |
|
9969 |
|
9970 def getBody(self): |
|
9971 # Just like getOwnPropertyNames we'll assume that we have no holes, so |
|
9972 # we have all properties from 0 to length. If that ever changes |
|
9973 # (unlikely), we'll need to do something a bit more clever with how we |
|
9974 # forward on to our ancestor. |
|
9975 |
|
9976 templateValues = { |
|
9977 'jsvalRef': 'temp', |
|
9978 'jsvalHandle': '&temp', |
|
9979 'obj': 'proxy', |
|
9980 'successCode': ("js::UnsafeDefineElement(cx, array, index - begin, temp);\n" |
|
9981 "continue;\n") |
|
9982 } |
|
9983 get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define() |
|
9984 |
|
9985 return fill( |
|
9986 """ |
|
9987 JS::Rooted<JS::Value> temp(cx); |
|
9988 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), |
|
9989 "Should not have a XrayWrapper here"); |
|
9990 |
|
9991 ${nativeType}* self = UnwrapProxy(proxy); |
|
9992 uint32_t length = self->Length(); |
|
9993 // Compute the end of the indices we'll get ourselves |
|
9994 uint32_t ourEnd = std::max(begin, std::min(end, length)); |
|
9995 |
|
9996 for (uint32_t index = begin; index < ourEnd; ++index) { |
|
9997 $*{get} |
|
9998 } |
|
9999 |
|
10000 if (end > ourEnd) { |
|
10001 JS::Rooted<JSObject*> proto(cx); |
|
10002 if (!js::GetObjectProto(cx, proxy, &proto)) { |
|
10003 return false; |
|
10004 } |
|
10005 return js::SliceSlowly(cx, proto, proxy, ourEnd, end, array); |
|
10006 } |
|
10007 |
|
10008 return true; |
|
10009 """, |
|
10010 nativeType=self.descriptor.nativeType, |
|
10011 get=get) |
|
10012 |
|
10013 |
|
10014 class CGDOMJSProxyHandler_getInstance(ClassMethod): |
|
10015 def __init__(self): |
|
10016 ClassMethod.__init__(self, "getInstance", "DOMProxyHandler*", [], static=True) |
|
10017 |
|
10018 def getBody(self): |
|
10019 return dedent(""" |
|
10020 static DOMProxyHandler instance; |
|
10021 return &instance; |
|
10022 """) |
|
10023 |
|
10024 |
|
10025 class CGDOMJSProxyHandler(CGClass): |
|
10026 def __init__(self, descriptor): |
|
10027 assert (descriptor.supportsIndexedProperties() or |
|
10028 descriptor.supportsNamedProperties()) |
|
10029 methods = [CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor), |
|
10030 CGDOMJSProxyHandler_defineProperty(descriptor), |
|
10031 ClassUsingDeclaration("mozilla::dom::DOMProxyHandler", |
|
10032 "defineProperty"), |
|
10033 CGDOMJSProxyHandler_ownPropNames(descriptor), |
|
10034 CGDOMJSProxyHandler_hasOwn(descriptor), |
|
10035 CGDOMJSProxyHandler_get(descriptor), |
|
10036 CGDOMJSProxyHandler_className(descriptor), |
|
10037 CGDOMJSProxyHandler_finalizeInBackground(descriptor), |
|
10038 CGDOMJSProxyHandler_finalize(descriptor), |
|
10039 CGDOMJSProxyHandler_getInstance(), |
|
10040 CGDOMJSProxyHandler_delete(descriptor)] |
|
10041 if descriptor.supportsIndexedProperties(): |
|
10042 methods.append(CGDOMJSProxyHandler_slice(descriptor)) |
|
10043 if (descriptor.operations['IndexedSetter'] is not None or |
|
10044 (descriptor.operations['NamedSetter'] is not None and |
|
10045 descriptor.interface.getExtendedAttribute('OverrideBuiltins'))): |
|
10046 methods.append(CGDOMJSProxyHandler_setCustom(descriptor)) |
|
10047 |
|
10048 CGClass.__init__(self, 'DOMProxyHandler', |
|
10049 bases=[ClassBase('mozilla::dom::DOMProxyHandler')], |
|
10050 methods=methods) |
|
10051 |
|
10052 |
|
10053 class CGDOMJSProxyHandlerDeclarer(CGThing): |
|
10054 """ |
|
10055 A class for declaring a DOMProxyHandler. |
|
10056 """ |
|
10057 def __init__(self, handlerThing): |
|
10058 self.handlerThing = handlerThing |
|
10059 |
|
10060 def declare(self): |
|
10061 # Our class declaration should happen when we're defining |
|
10062 return "" |
|
10063 |
|
10064 def define(self): |
|
10065 return self.handlerThing.declare() |
|
10066 |
|
10067 |
|
10068 class CGDOMJSProxyHandlerDefiner(CGThing): |
|
10069 """ |
|
10070 A class for defining a DOMProxyHandler. |
|
10071 """ |
|
10072 def __init__(self, handlerThing): |
|
10073 self.handlerThing = handlerThing |
|
10074 |
|
10075 def declare(self): |
|
10076 return "" |
|
10077 |
|
10078 def define(self): |
|
10079 return self.handlerThing.define() |
|
10080 |
|
10081 |
|
10082 def stripTrailingWhitespace(text): |
|
10083 tail = '\n' if text.endswith('\n') else '' |
|
10084 lines = text.splitlines() |
|
10085 return '\n'.join(line.rstrip() for line in lines) + tail |
|
10086 |
|
10087 |
|
10088 class CGDescriptor(CGThing): |
|
10089 def __init__(self, descriptor): |
|
10090 CGThing.__init__(self) |
|
10091 |
|
10092 assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject() |
|
10093 |
|
10094 if descriptor.nativeOwnership == 'owned' and ( |
|
10095 descriptor.interface.hasChildInterfaces() or |
|
10096 descriptor.interface.parent): |
|
10097 raise TypeError("Owned interface cannot have a parent or children") |
|
10098 |
|
10099 self._deps = descriptor.interface.getDeps() |
|
10100 |
|
10101 cgThings = [] |
|
10102 cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" % |
|
10103 descriptor.nativeType)) |
|
10104 # These are set to true if at least one non-static |
|
10105 # method/getter/setter or jsonifier exist on the interface. |
|
10106 (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasJsonifier, |
|
10107 hasLenientSetter) = False, False, False, False, False, False |
|
10108 crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set() |
|
10109 for n in descriptor.interface.namedConstructors: |
|
10110 cgThings.append(CGClassConstructor(descriptor, n, |
|
10111 NamedConstructorName(n))) |
|
10112 for m in descriptor.interface.members: |
|
10113 if m.isMethod() and m.identifier.name == 'queryInterface': |
|
10114 continue |
|
10115 if m.isMethod() and m == descriptor.operations['Jsonifier']: |
|
10116 hasJsonifier = True |
|
10117 hasMethod = descriptor.needsSpecialGenericOps() |
|
10118 jsonifierMethod = m |
|
10119 elif (m.isMethod() and |
|
10120 (not m.isIdentifierLess() or m == descriptor.operations['Stringifier'])): |
|
10121 if m.isStatic(): |
|
10122 assert descriptor.interface.hasInterfaceObject |
|
10123 cgThings.append(CGStaticMethod(descriptor, m)) |
|
10124 if m.returnsPromise(): |
|
10125 cgThings.append(CGStaticMethodJitinfo(m)) |
|
10126 elif descriptor.interface.hasInterfacePrototypeObject(): |
|
10127 specializedMethod = CGSpecializedMethod(descriptor, m) |
|
10128 cgThings.append(specializedMethod) |
|
10129 if m.returnsPromise(): |
|
10130 cgThings.append(CGMethodPromiseWrapper(descriptor, specializedMethod)) |
|
10131 cgThings.append(CGMemberJITInfo(descriptor, m)) |
|
10132 if m.getExtendedAttribute("CrossOriginCallable"): |
|
10133 crossOriginMethods.add(m.identifier.name) |
|
10134 elif descriptor.needsSpecialGenericOps(): |
|
10135 hasMethod = True |
|
10136 elif m.isAttr(): |
|
10137 if m.stringifier: |
|
10138 raise TypeError("Stringifier attributes not supported yet. " |
|
10139 "See bug 824857.\n" |
|
10140 "%s" % m.location) |
|
10141 if m.isStatic(): |
|
10142 assert descriptor.interface.hasInterfaceObject |
|
10143 cgThings.append(CGStaticGetter(descriptor, m)) |
|
10144 elif descriptor.interface.hasInterfacePrototypeObject(): |
|
10145 cgThings.append(CGSpecializedGetter(descriptor, m)) |
|
10146 if m.hasLenientThis(): |
|
10147 hasLenientGetter = True |
|
10148 elif m.getExtendedAttribute("CrossOriginReadable"): |
|
10149 crossOriginGetters.add(m.identifier.name) |
|
10150 elif descriptor.needsSpecialGenericOps(): |
|
10151 hasGetter = True |
|
10152 if not m.readonly: |
|
10153 for extAttr in ["PutForwards", "Replaceable"]: |
|
10154 if m.getExtendedAttribute(extAttr): |
|
10155 raise TypeError("Writable attributes should not " |
|
10156 "have %s specified.\n" |
|
10157 "%s" % |
|
10158 (extAttr, m.location)) |
|
10159 if m.isStatic(): |
|
10160 assert descriptor.interface.hasInterfaceObject |
|
10161 cgThings.append(CGStaticSetter(descriptor, m)) |
|
10162 elif descriptor.interface.hasInterfacePrototypeObject(): |
|
10163 cgThings.append(CGSpecializedSetter(descriptor, m)) |
|
10164 if m.hasLenientThis(): |
|
10165 hasLenientSetter = True |
|
10166 elif m.getExtendedAttribute("CrossOriginWritable"): |
|
10167 crossOriginSetters.add(m.identifier.name) |
|
10168 elif descriptor.needsSpecialGenericOps(): |
|
10169 hasSetter = True |
|
10170 elif m.getExtendedAttribute("PutForwards"): |
|
10171 cgThings.append(CGSpecializedForwardingSetter(descriptor, m)) |
|
10172 if m.getExtendedAttribute("CrossOriginWritable"): |
|
10173 crossOriginSetters.add(m.identifier.name) |
|
10174 elif descriptor.needsSpecialGenericOps(): |
|
10175 hasSetter = True |
|
10176 elif m.getExtendedAttribute("Replaceable"): |
|
10177 cgThings.append(CGSpecializedReplaceableSetter(descriptor, m)) |
|
10178 if descriptor.needsSpecialGenericOps(): |
|
10179 hasSetter = True |
|
10180 if (not m.isStatic() and |
|
10181 descriptor.interface.hasInterfacePrototypeObject()): |
|
10182 cgThings.append(CGMemberJITInfo(descriptor, m)) |
|
10183 if hasJsonifier: |
|
10184 cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod)) |
|
10185 cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod)) |
|
10186 if hasMethod: |
|
10187 cgThings.append(CGGenericMethod(descriptor)) |
|
10188 if len(crossOriginMethods): |
|
10189 cgThings.append(CGGenericMethod(descriptor, |
|
10190 allowCrossOriginThis=True)) |
|
10191 if hasGetter: |
|
10192 cgThings.append(CGGenericGetter(descriptor)) |
|
10193 if hasLenientGetter: |
|
10194 cgThings.append(CGGenericGetter(descriptor, lenientThis=True)) |
|
10195 if len(crossOriginGetters): |
|
10196 cgThings.append(CGGenericGetter(descriptor, |
|
10197 allowCrossOriginThis=True)) |
|
10198 if hasSetter: |
|
10199 cgThings.append(CGGenericSetter(descriptor)) |
|
10200 if hasLenientSetter: |
|
10201 cgThings.append(CGGenericSetter(descriptor, lenientThis=True)) |
|
10202 if len(crossOriginSetters): |
|
10203 cgThings.append(CGGenericSetter(descriptor, |
|
10204 allowCrossOriginThis=True)) |
|
10205 |
|
10206 if descriptor.interface.getNavigatorProperty(): |
|
10207 cgThings.append(CGConstructNavigatorObjectHelper(descriptor)) |
|
10208 cgThings.append(CGConstructNavigatorObject(descriptor)) |
|
10209 |
|
10210 if descriptor.concrete and not descriptor.proxy: |
|
10211 if wantsAddProperty(descriptor): |
|
10212 cgThings.append(CGAddPropertyHook(descriptor)) |
|
10213 |
|
10214 # Always have a finalize hook, regardless of whether the class |
|
10215 # wants a custom hook. |
|
10216 cgThings.append(CGClassFinalizeHook(descriptor)) |
|
10217 |
|
10218 properties = PropertyArrays(descriptor) |
|
10219 cgThings.append(CGGeneric(define=str(properties))) |
|
10220 cgThings.append(CGNativeProperties(descriptor, properties)) |
|
10221 |
|
10222 # Set up our Xray callbacks as needed. Note that we don't need to do |
|
10223 # it in workers. |
|
10224 if not descriptor.workers and descriptor.concrete and descriptor.proxy: |
|
10225 cgThings.append(CGResolveOwnProperty(descriptor)) |
|
10226 cgThings.append(CGEnumerateOwnProperties(descriptor)) |
|
10227 elif descriptor.needsXrayResolveHooks(): |
|
10228 cgThings.append(CGResolveOwnPropertyViaNewresolve(descriptor)) |
|
10229 cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor)) |
|
10230 |
|
10231 # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff |
|
10232 # done, set up our NativePropertyHooks. |
|
10233 cgThings.append(CGNativePropertyHooks(descriptor, properties)) |
|
10234 |
|
10235 if descriptor.interface.hasInterfaceObject(): |
|
10236 cgThings.append(CGClassConstructor(descriptor, |
|
10237 descriptor.interface.ctor())) |
|
10238 cgThings.append(CGClassHasInstanceHook(descriptor)) |
|
10239 cgThings.append(CGInterfaceObjectJSClass(descriptor, properties)) |
|
10240 if descriptor.needsConstructHookHolder(): |
|
10241 cgThings.append(CGClassConstructHookHolder(descriptor)) |
|
10242 cgThings.append(CGNamedConstructors(descriptor)) |
|
10243 |
|
10244 cgThings.append(CGLegacyCallHook(descriptor)) |
|
10245 if descriptor.interface.getExtendedAttribute("NeedNewResolve"): |
|
10246 cgThings.append(CGNewResolveHook(descriptor)) |
|
10247 cgThings.append(CGEnumerateHook(descriptor)) |
|
10248 |
|
10249 if descriptor.interface.hasInterfacePrototypeObject(): |
|
10250 cgThings.append(CGPrototypeJSClass(descriptor, properties)) |
|
10251 |
|
10252 if descriptor.interface.hasInterfaceObject(): |
|
10253 cgThings.append(CGDefineDOMInterfaceMethod(descriptor)) |
|
10254 |
|
10255 if ((descriptor.interface.hasInterfaceObject() or descriptor.interface.getNavigatorProperty()) and |
|
10256 not descriptor.interface.isExternal() and |
|
10257 descriptor.isExposedConditionally() and |
|
10258 # Workers stuff is never conditional |
|
10259 not descriptor.workers): |
|
10260 cgThings.append(CGConstructorEnabled(descriptor)) |
|
10261 |
|
10262 if descriptor.concrete: |
|
10263 if descriptor.proxy: |
|
10264 if descriptor.interface.totalMembersInSlots != 0: |
|
10265 raise TypeError("We can't have extra reserved slots for " |
|
10266 "proxy interface %s" % |
|
10267 descriptor.interface.identifier.name) |
|
10268 cgThings.append(CGGeneric(fill( |
|
10269 """ |
|
10270 static_assert(IsBaseOf<nsISupports, ${nativeType} >::value, |
|
10271 "We don't support non-nsISupports native classes for " |
|
10272 "proxy-based bindings yet"); |
|
10273 |
|
10274 """, |
|
10275 nativeType=descriptor.nativeType))) |
|
10276 if not descriptor.wrapperCache: |
|
10277 raise TypeError("We need a wrappercache to support expandos for proxy-based " |
|
10278 "bindings (" + descriptor.name + ")") |
|
10279 handlerThing = CGDOMJSProxyHandler(descriptor) |
|
10280 cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing)) |
|
10281 cgThings.append(CGProxyIsProxy(descriptor)) |
|
10282 cgThings.append(CGProxyUnwrap(descriptor)) |
|
10283 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing)) |
|
10284 cgThings.append(CGDOMProxyJSClass(descriptor)) |
|
10285 else: |
|
10286 cgThings.append(CGDOMJSClass(descriptor)) |
|
10287 cgThings.append(CGGetJSClassMethod(descriptor)) |
|
10288 if descriptor.interface.hasMembersInSlots(): |
|
10289 if descriptor.interface.hasChildInterfaces(): |
|
10290 raise TypeError("We don't support members in slots on " |
|
10291 "non-leaf interfaces like %s" % |
|
10292 descriptor.interface.identifier.name) |
|
10293 cgThings.append(CGUpdateMemberSlotsMethod(descriptor)) |
|
10294 |
|
10295 if descriptor.interface.getExtendedAttribute("Global"): |
|
10296 assert descriptor.wrapperCache |
|
10297 cgThings.append(CGWrapGlobalMethod(descriptor, properties)) |
|
10298 elif descriptor.wrapperCache: |
|
10299 cgThings.append(CGWrapWithCacheMethod(descriptor, properties)) |
|
10300 cgThings.append(CGWrapMethod(descriptor)) |
|
10301 else: |
|
10302 cgThings.append(CGWrapNonWrapperCacheMethod(descriptor, |
|
10303 properties)) |
|
10304 |
|
10305 # If we're not wrappercached, we don't know how to clear our |
|
10306 # cached values, since we can't get at the JSObject. |
|
10307 if descriptor.wrapperCache: |
|
10308 cgThings.extend(CGClearCachedValueMethod(descriptor, m) for |
|
10309 m in descriptor.interface.members if |
|
10310 m.isAttr() and |
|
10311 # Constants should never need clearing! |
|
10312 not m.getExtendedAttribute("Constant") and |
|
10313 not m.getExtendedAttribute("SameObject") and |
|
10314 m.slotIndex is not None) |
|
10315 |
|
10316 # CGCreateInterfaceObjectsMethod needs to come after our |
|
10317 # CGDOMJSClass, if any. |
|
10318 cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties)) |
|
10319 |
|
10320 # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need |
|
10321 # to come after CGCreateInterfaceObjectsMethod. |
|
10322 if descriptor.interface.hasInterfacePrototypeObject(): |
|
10323 cgThings.append(CGGetProtoObjectMethod(descriptor)) |
|
10324 if descriptor.interface.hasInterfaceObject(): |
|
10325 cgThings.append(CGGetConstructorObjectMethod(descriptor)) |
|
10326 |
|
10327 # See whether we need we need to generate an IsPermitted method |
|
10328 if crossOriginGetters or crossOriginSetters or crossOriginMethods: |
|
10329 cgThings.append(CGIsPermittedMethod(descriptor, |
|
10330 crossOriginGetters, |
|
10331 crossOriginSetters, |
|
10332 crossOriginMethods)) |
|
10333 |
|
10334 cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n") |
|
10335 cgThings = CGWrapper(cgThings, pre='\n', post='\n') |
|
10336 self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name), |
|
10337 cgThings), |
|
10338 post='\n') |
|
10339 |
|
10340 def declare(self): |
|
10341 return self.cgRoot.declare() |
|
10342 |
|
10343 def define(self): |
|
10344 return self.cgRoot.define() |
|
10345 |
|
10346 def deps(self): |
|
10347 return self._deps |
|
10348 |
|
10349 |
|
10350 class CGNamespacedEnum(CGThing): |
|
10351 def __init__(self, namespace, enumName, names, values, comment=""): |
|
10352 |
|
10353 if not values: |
|
10354 values = [] |
|
10355 |
|
10356 # Account for explicit enum values. |
|
10357 entries = [] |
|
10358 for i in range(0, len(names)): |
|
10359 if len(values) > i and values[i] is not None: |
|
10360 entry = "%s = %s" % (names[i], values[i]) |
|
10361 else: |
|
10362 entry = names[i] |
|
10363 entries.append(entry) |
|
10364 |
|
10365 # Append a Count. |
|
10366 entries.append('_' + enumName + '_Count') |
|
10367 |
|
10368 # Indent. |
|
10369 entries = [' ' + e for e in entries] |
|
10370 |
|
10371 # Build the enum body. |
|
10372 enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries)) |
|
10373 curr = CGGeneric(declare=enumstr) |
|
10374 |
|
10375 # Add some whitespace padding. |
|
10376 curr = CGWrapper(curr, pre='\n', post='\n') |
|
10377 |
|
10378 # Add the namespace. |
|
10379 curr = CGNamespace(namespace, curr) |
|
10380 |
|
10381 # Add the typedef |
|
10382 typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName) |
|
10383 curr = CGList([curr, CGGeneric(declare=typedef)]) |
|
10384 |
|
10385 # Save the result. |
|
10386 self.node = curr |
|
10387 |
|
10388 def declare(self): |
|
10389 return self.node.declare() |
|
10390 |
|
10391 def define(self): |
|
10392 return "" |
|
10393 |
|
10394 |
|
10395 class CGDictionary(CGThing): |
|
10396 def __init__(self, dictionary, descriptorProvider): |
|
10397 self.dictionary = dictionary |
|
10398 self.descriptorProvider = descriptorProvider |
|
10399 self.needToInitIds = len(dictionary.members) > 0 |
|
10400 self.memberInfo = [ |
|
10401 (member, |
|
10402 getJSToNativeConversionInfo( |
|
10403 member.type, |
|
10404 descriptorProvider, |
|
10405 isEnforceRange=member.enforceRange, |
|
10406 isClamp=member.clamp, |
|
10407 isMember="Dictionary", |
|
10408 isOptional=(not member.defaultValue), |
|
10409 defaultValue=member.defaultValue, |
|
10410 sourceDescription=("'%s' member of %s" % |
|
10411 (member.identifier.name, dictionary.identifier.name)))) |
|
10412 for member in dictionary.members] |
|
10413 |
|
10414 # If we have a union member containing something in the same |
|
10415 # file as us, bail: the C++ includes won't work out. |
|
10416 for member in dictionary.members: |
|
10417 type = member.type.unroll() |
|
10418 if type.isUnion(): |
|
10419 for t in type.flatMemberTypes: |
|
10420 if (t.isDictionary() and |
|
10421 CGHeaders.getDeclarationFilename(t.inner) == |
|
10422 CGHeaders.getDeclarationFilename(dictionary)): |
|
10423 raise TypeError( |
|
10424 "Dictionary contains a union that contains a " |
|
10425 "dictionary in the same WebIDL file. This won't " |
|
10426 "compile. Move the inner dictionary to a " |
|
10427 "different file.\n%s\n%s" % |
|
10428 (t.location, t.inner.location)) |
|
10429 self.structs = self.getStructs() |
|
10430 |
|
10431 def declare(self): |
|
10432 return self.structs.declare() |
|
10433 |
|
10434 def define(self): |
|
10435 return self.structs.define() |
|
10436 |
|
10437 def base(self): |
|
10438 if self.dictionary.parent: |
|
10439 return self.makeClassName(self.dictionary.parent) |
|
10440 return "DictionaryBase" |
|
10441 |
|
10442 def initMethod(self): |
|
10443 body = dedent(""" |
|
10444 // Passing a null JSContext is OK only if we're initing from null, |
|
10445 // Since in that case we will not have to do any property gets |
|
10446 MOZ_ASSERT_IF(!cx, val.isNull()); |
|
10447 """) |
|
10448 |
|
10449 if self.needToInitIds: |
|
10450 body += fill( |
|
10451 """ |
|
10452 ${dictName}Atoms* atomsCache = nullptr; |
|
10453 if (cx) { |
|
10454 atomsCache = GetAtomCache<${dictName}Atoms>(cx); |
|
10455 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) { |
|
10456 return false; |
|
10457 } |
|
10458 } |
|
10459 |
|
10460 """, |
|
10461 dictName=self.makeClassName(self.dictionary)) |
|
10462 |
|
10463 if self.dictionary.parent: |
|
10464 body += fill( |
|
10465 """ |
|
10466 // Per spec, we init the parent's members first |
|
10467 if (!${dictName}::Init(cx, val)) { |
|
10468 return false; |
|
10469 } |
|
10470 MOZ_ASSERT(IsConvertibleToDictionary(cx, val)); |
|
10471 |
|
10472 """, |
|
10473 dictName=self.makeClassName(self.dictionary.parent)) |
|
10474 else: |
|
10475 body += fill( |
|
10476 """ |
|
10477 if (!IsConvertibleToDictionary(cx, val)) { |
|
10478 return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription); |
|
10479 } |
|
10480 |
|
10481 """) |
|
10482 |
|
10483 memberInits = [self.getMemberConversion(m).define() |
|
10484 for m in self.memberInfo] |
|
10485 if memberInits: |
|
10486 body += fill( |
|
10487 """ |
|
10488 bool isNull = val.isNullOrUndefined(); |
|
10489 // We only need these if !isNull, in which case we have |cx|. |
|
10490 Maybe<JS::Rooted<JSObject *> > object; |
|
10491 Maybe<JS::Rooted<JS::Value> > temp; |
|
10492 if (!isNull) { |
|
10493 object.construct(cx, &val.toObject()); |
|
10494 temp.construct(cx); |
|
10495 } |
|
10496 $*{memberInits} |
|
10497 """, |
|
10498 memberInits="\n".join(memberInits)) |
|
10499 |
|
10500 body += "return true;\n" |
|
10501 |
|
10502 return ClassMethod("Init", "bool", [ |
|
10503 Argument('JSContext*', 'cx'), |
|
10504 Argument('JS::Handle<JS::Value>', 'val'), |
|
10505 Argument('const char*', 'sourceDescription', default='"Value"') |
|
10506 ], body=body) |
|
10507 |
|
10508 def initFromJSONMethod(self): |
|
10509 return ClassMethod( |
|
10510 "Init", "bool", |
|
10511 [Argument('const nsAString&', 'aJSON')], |
|
10512 body=dedent(""" |
|
10513 MOZ_ASSERT(NS_IsMainThread()); |
|
10514 AutoSafeJSContext cx; |
|
10515 JS::Rooted<JS::Value> json(cx); |
|
10516 bool ok = ParseJSON(cx, aJSON, &json); |
|
10517 NS_ENSURE_TRUE(ok, false); |
|
10518 return Init(cx, json); |
|
10519 """)) |
|
10520 |
|
10521 def toObjectMethod(self): |
|
10522 body = "" |
|
10523 if self.needToInitIds: |
|
10524 body += fill( |
|
10525 """ |
|
10526 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx); |
|
10527 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) { |
|
10528 return false; |
|
10529 } |
|
10530 |
|
10531 """, |
|
10532 dictName=self.makeClassName(self.dictionary)) |
|
10533 |
|
10534 if self.dictionary.parent: |
|
10535 body += fill( |
|
10536 """ |
|
10537 // Per spec, we define the parent's members first |
|
10538 if (!${dictName}::ToObject(cx, rval)) { |
|
10539 return false; |
|
10540 } |
|
10541 JS::Rooted<JSObject*> obj(cx, &rval.toObject()); |
|
10542 |
|
10543 """, |
|
10544 dictName=self.makeClassName(self.dictionary.parent)) |
|
10545 else: |
|
10546 body += fill( |
|
10547 """ |
|
10548 JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); |
|
10549 if (!obj) { |
|
10550 return false; |
|
10551 } |
|
10552 rval.set(JS::ObjectValue(*obj)); |
|
10553 |
|
10554 """) |
|
10555 |
|
10556 if self.memberInfo: |
|
10557 body += "\n".join(self.getMemberDefinition(m).define() |
|
10558 for m in self.memberInfo) |
|
10559 else: |
|
10560 body += "\n" # BOGUS extra blank line |
|
10561 body += "\nreturn true;\n" |
|
10562 |
|
10563 return ClassMethod("ToObject", "bool", [ |
|
10564 Argument('JSContext*', 'cx'), |
|
10565 Argument('JS::MutableHandle<JS::Value>', 'rval'), |
|
10566 ], const=True, body=body) |
|
10567 |
|
10568 def initIdsMethod(self): |
|
10569 assert self.needToInitIds |
|
10570 idinit = ['!atomsCache->%s.init(cx, "%s")' % |
|
10571 (CGDictionary.makeIdName(m.identifier.name), |
|
10572 m.identifier.name) |
|
10573 for m in self.dictionary.members] |
|
10574 idinit.reverse() |
|
10575 body = fill( |
|
10576 """ |
|
10577 MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache)); |
|
10578 |
|
10579 // Initialize these in reverse order so that any failure leaves the first one |
|
10580 // uninitialized. |
|
10581 if (${idinit}) { |
|
10582 return false; |
|
10583 } |
|
10584 return true; |
|
10585 """, |
|
10586 idinit=" ||\n ".join(idinit)) |
|
10587 |
|
10588 return ClassMethod("InitIds", "bool", [ |
|
10589 Argument("JSContext*", "cx"), |
|
10590 Argument("%sAtoms*" % self.makeClassName(self.dictionary), |
|
10591 "atomsCache"), |
|
10592 ], static=True, body=body, visibility="private") |
|
10593 |
|
10594 def traceDictionaryMethod(self): |
|
10595 body = "" |
|
10596 if self.dictionary.parent: |
|
10597 cls = self.makeClassName(self.dictionary.parent) |
|
10598 body += "%s::TraceDictionary(trc);\n" % cls |
|
10599 |
|
10600 memberTraces = [self.getMemberTrace(m) |
|
10601 for m in self.dictionary.members |
|
10602 if typeNeedsRooting(m.type)] |
|
10603 |
|
10604 if memberTraces: |
|
10605 body += "\n".join(memberTraces) |
|
10606 else: |
|
10607 body += "\n" # BOGUS extra newline |
|
10608 |
|
10609 return ClassMethod("TraceDictionary", "void", [ |
|
10610 Argument("JSTracer*", "trc"), |
|
10611 ], body=body) |
|
10612 |
|
10613 def assignmentOperator(self): |
|
10614 body = CGList([]) |
|
10615 if self.dictionary.parent: |
|
10616 body.append(CGGeneric( |
|
10617 "%s::operator=(aOther);\n" % |
|
10618 self.makeClassName(self.dictionary.parent))) |
|
10619 for m, _ in self.memberInfo: |
|
10620 memberName = self.makeMemberName(m.identifier.name) |
|
10621 if not m.defaultValue: |
|
10622 memberAssign = CGGeneric(fill( |
|
10623 """ |
|
10624 if (aOther.${name}.WasPassed()) { |
|
10625 ${name}.Construct(); |
|
10626 ${name}.Value() = aOther.${name}.Value(); |
|
10627 } else { |
|
10628 ${name}.Reset(); |
|
10629 } |
|
10630 """, |
|
10631 name=memberName)) |
|
10632 else: |
|
10633 memberAssign = CGGeneric( |
|
10634 "%s = aOther.%s;\n" % (memberName, memberName)) |
|
10635 body.append(memberAssign) |
|
10636 return ClassMethod( |
|
10637 "operator=", "void", |
|
10638 [Argument("const %s&" % self.makeClassName(self.dictionary), |
|
10639 "aOther")], |
|
10640 body=body.define() or "\n") # BOGUS blank line when empty |
|
10641 |
|
10642 def getStructs(self): |
|
10643 d = self.dictionary |
|
10644 selfName = self.makeClassName(d) |
|
10645 members = [ClassMember(self.makeMemberName(m[0].identifier.name), |
|
10646 self.getMemberType(m), |
|
10647 visibility="public", |
|
10648 body=self.getMemberInitializer(m)) |
|
10649 for m in self.memberInfo] |
|
10650 ctors = [ |
|
10651 ClassConstructor( |
|
10652 [], |
|
10653 visibility="public", |
|
10654 body=( |
|
10655 "// Safe to pass a null context if we pass a null value\n" |
|
10656 "Init(nullptr, JS::NullHandleValue);\n")), |
|
10657 ClassConstructor( |
|
10658 [Argument("int", "")], |
|
10659 visibility="protected", |
|
10660 explicit=True, |
|
10661 bodyInHeader=True, |
|
10662 body='// Do nothing here; this is used by our "Fast" subclass\n') |
|
10663 ] |
|
10664 methods = [] |
|
10665 |
|
10666 if self.needToInitIds: |
|
10667 methods.append(self.initIdsMethod()) |
|
10668 |
|
10669 methods.append(self.initMethod()) |
|
10670 methods.append(self.initFromJSONMethod()) |
|
10671 try: |
|
10672 methods.append(self.toObjectMethod()) |
|
10673 except MethodNotNewObjectError: |
|
10674 # If we can't have a ToObject() because one of our members can only |
|
10675 # be returned from [NewObject] methods, then just skip generating |
|
10676 # ToObject(). |
|
10677 pass |
|
10678 methods.append(self.traceDictionaryMethod()) |
|
10679 |
|
10680 if CGDictionary.isDictionaryCopyConstructible(d): |
|
10681 disallowCopyConstruction = False |
|
10682 # Note: no base constructors because our operator= will |
|
10683 # deal with that. |
|
10684 ctors.append(ClassConstructor([Argument("const %s&" % selfName, |
|
10685 "aOther")], |
|
10686 bodyInHeader=True, |
|
10687 visibility="public", |
|
10688 explicit=True, |
|
10689 body="*this = aOther;\n")) |
|
10690 methods.append(self.assignmentOperator()) |
|
10691 else: |
|
10692 disallowCopyConstruction = True |
|
10693 |
|
10694 struct = CGClass(selfName, |
|
10695 bases=[ClassBase(self.base())], |
|
10696 members=members, |
|
10697 constructors=ctors, |
|
10698 methods=methods, |
|
10699 isStruct=True, |
|
10700 disallowCopyConstruction=disallowCopyConstruction) |
|
10701 |
|
10702 fastDictionaryCtor = ClassConstructor( |
|
10703 [], |
|
10704 visibility="public", |
|
10705 bodyInHeader=True, |
|
10706 baseConstructors=["%s(42)" % selfName], |
|
10707 body="// Doesn't matter what int we pass to the parent constructor\n") |
|
10708 |
|
10709 fastStruct = CGClass("Fast" + selfName, |
|
10710 bases=[ClassBase(selfName)], |
|
10711 constructors=[fastDictionaryCtor], |
|
10712 isStruct=True) |
|
10713 |
|
10714 return CGList([struct, |
|
10715 CGNamespace('binding_detail', fastStruct)], |
|
10716 "\n") |
|
10717 |
|
10718 def deps(self): |
|
10719 return self.dictionary.getDeps() |
|
10720 |
|
10721 @staticmethod |
|
10722 def makeDictionaryName(dictionary): |
|
10723 return dictionary.identifier.name |
|
10724 |
|
10725 def makeClassName(self, dictionary): |
|
10726 return self.makeDictionaryName(dictionary) |
|
10727 |
|
10728 @staticmethod |
|
10729 def makeMemberName(name): |
|
10730 return "m" + name[0].upper() + name[1:] |
|
10731 |
|
10732 def getMemberType(self, memberInfo): |
|
10733 _, conversionInfo = memberInfo |
|
10734 # We can't handle having a holderType here |
|
10735 assert conversionInfo.holderType is None |
|
10736 declType = conversionInfo.declType |
|
10737 if conversionInfo.dealWithOptional: |
|
10738 declType = CGTemplatedType("Optional", declType) |
|
10739 return declType.define() |
|
10740 |
|
10741 def getMemberConversion(self, memberInfo): |
|
10742 member, conversionInfo = memberInfo |
|
10743 replacements = { |
|
10744 "val": "temp.ref()", |
|
10745 "mutableVal": "&temp.ref()", |
|
10746 "declName": self.makeMemberName(member.identifier.name), |
|
10747 # We need a holder name for external interfaces, but |
|
10748 # it's scoped down to the conversion so we can just use |
|
10749 # anything we want. |
|
10750 "holderName": "holder" |
|
10751 } |
|
10752 # We can't handle having a holderType here |
|
10753 assert conversionInfo.holderType is None |
|
10754 if conversionInfo.dealWithOptional: |
|
10755 replacements["declName"] = "(" + replacements["declName"] + ".Value())" |
|
10756 if member.defaultValue: |
|
10757 replacements["haveValue"] = "!isNull && !temp.ref().isUndefined()" |
|
10758 |
|
10759 propId = self.makeIdName(member.identifier.name) |
|
10760 propGet = ("JS_GetPropertyById(cx, object.ref(), atomsCache->%s, &temp.ref())" % |
|
10761 propId) |
|
10762 |
|
10763 conversionReplacements = { |
|
10764 "prop": self.makeMemberName(member.identifier.name), |
|
10765 "convert": string.Template(conversionInfo.template).substitute(replacements), |
|
10766 "propGet": propGet |
|
10767 } |
|
10768 conversion = ("if (!isNull && !${propGet}) {\n" |
|
10769 " return false;\n" |
|
10770 "}\n") |
|
10771 if member.defaultValue: |
|
10772 conversion += "${convert}" |
|
10773 else: |
|
10774 conversion += ( |
|
10775 "if (!isNull && !temp.ref().isUndefined()) {\n" |
|
10776 " ${prop}.Construct();\n" |
|
10777 "${convert}" |
|
10778 "}\n") |
|
10779 conversionReplacements["convert"] = indent(conversionReplacements["convert"]) |
|
10780 |
|
10781 return CGGeneric( |
|
10782 string.Template(conversion).substitute(conversionReplacements)) |
|
10783 |
|
10784 def getMemberDefinition(self, memberInfo): |
|
10785 member = memberInfo[0] |
|
10786 declType = memberInfo[1].declType |
|
10787 memberLoc = self.makeMemberName(member.identifier.name) |
|
10788 if member.defaultValue: |
|
10789 memberData = memberLoc |
|
10790 else: |
|
10791 # The data is inside the Optional<> |
|
10792 memberData = "%s.InternalValue()" % memberLoc |
|
10793 |
|
10794 # If you have to change this list (which you shouldn't!), make sure it |
|
10795 # continues to match the list in test_Object.prototype_props.html |
|
10796 if (member.identifier.name in |
|
10797 ["constructor", "toSource", "toString", "toLocaleString", "valueOf", |
|
10798 "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", |
|
10799 "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", |
|
10800 "__lookupGetter__", "__lookupSetter__", "__proto__"]): |
|
10801 raise TypeError("'%s' member of %s dictionary shadows " |
|
10802 "a property of Object.prototype, and Xrays to " |
|
10803 "Object can't handle that.\n" |
|
10804 "%s" % |
|
10805 (member.identifier.name, |
|
10806 self.dictionary.identifier.name, |
|
10807 member.location)) |
|
10808 |
|
10809 propDef = ( |
|
10810 'JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, nullptr, nullptr, JSPROP_ENUMERATE)' % |
|
10811 self.makeIdName(member.identifier.name)) |
|
10812 |
|
10813 innerTemplate = wrapForType( |
|
10814 member.type, self.descriptorProvider, |
|
10815 { |
|
10816 'result': "currentValue", |
|
10817 'successCode': ("if (!%s) {\n" |
|
10818 " return false;\n" |
|
10819 "}\n" |
|
10820 "break;\n" % propDef), |
|
10821 'jsvalRef': "temp", |
|
10822 'jsvalHandle': "&temp", |
|
10823 'returnsNewObject': False, |
|
10824 # 'obj' can just be allowed to be the string "obj", since that |
|
10825 # will be our dictionary object, which is presumably itself in |
|
10826 # the right scope. |
|
10827 'typedArraysAreStructs': True |
|
10828 }) |
|
10829 conversion = CGGeneric(innerTemplate) |
|
10830 conversion = CGWrapper(conversion, |
|
10831 pre=("JS::Rooted<JS::Value> temp(cx);\n" |
|
10832 "%s const & currentValue = %s;\n" % |
|
10833 (declType.define(), memberData) |
|
10834 )) |
|
10835 |
|
10836 # Now make sure that our successCode can actually break out of the |
|
10837 # conversion. This incidentally gives us a scope for 'temp' and |
|
10838 # 'currentValue'. |
|
10839 conversion = CGWrapper( |
|
10840 CGIndenter(conversion), |
|
10841 pre=("do {\n" |
|
10842 " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"), |
|
10843 post="} while(0);\n") |
|
10844 if not member.defaultValue: |
|
10845 # Only do the conversion if we have a value |
|
10846 conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc) |
|
10847 return conversion |
|
10848 |
|
10849 def getMemberTrace(self, member): |
|
10850 type = member.type |
|
10851 assert typeNeedsRooting(type) |
|
10852 memberLoc = self.makeMemberName(member.identifier.name) |
|
10853 if member.defaultValue: |
|
10854 memberData = memberLoc |
|
10855 else: |
|
10856 # The data is inside the Optional<> |
|
10857 memberData = "%s.Value()" % memberLoc |
|
10858 |
|
10859 memberName = "%s.%s" % (self.makeClassName(self.dictionary), |
|
10860 memberLoc) |
|
10861 |
|
10862 if type.isObject(): |
|
10863 trace = CGGeneric('JS_CallObjectTracer(trc, %s, "%s");\n' % |
|
10864 ("&"+memberData, memberName)) |
|
10865 if type.nullable(): |
|
10866 trace = CGIfWrapper(trace, memberData) |
|
10867 elif type.isAny(): |
|
10868 trace = CGGeneric('JS_CallValueTracer(trc, %s, "%s");\n' % |
|
10869 ("&"+memberData, memberName)) |
|
10870 elif (type.isSequence() or type.isDictionary() or |
|
10871 type.isSpiderMonkeyInterface() or type.isUnion()): |
|
10872 if type.nullable(): |
|
10873 memberNullable = memberData |
|
10874 memberData = "%s.Value()" % memberData |
|
10875 if type.isSequence(): |
|
10876 trace = CGGeneric('DoTraceSequence(trc, %s);\n' % memberData) |
|
10877 elif type.isDictionary(): |
|
10878 trace = CGGeneric('%s.TraceDictionary(trc);\n' % memberData) |
|
10879 elif type.isUnion(): |
|
10880 trace = CGGeneric('%s.TraceUnion(trc);\n' % memberData) |
|
10881 else: |
|
10882 assert type.isSpiderMonkeyInterface() |
|
10883 trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData) |
|
10884 if type.nullable(): |
|
10885 trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable) |
|
10886 else: |
|
10887 assert False # unknown type |
|
10888 |
|
10889 if not member.defaultValue: |
|
10890 trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc) |
|
10891 |
|
10892 return trace.define() |
|
10893 |
|
10894 def getMemberInitializer(self, memberInfo): |
|
10895 """ |
|
10896 Get the right initializer for the member. Most members don't need one, |
|
10897 but we need to pre-initialize 'any' and 'object' that have a default |
|
10898 value, so they're safe to trace at all times. |
|
10899 """ |
|
10900 member, _ = memberInfo |
|
10901 if not member.defaultValue: |
|
10902 # No default value means no need to set it up front, since it's |
|
10903 # inside an Optional and won't get traced until it's actually set |
|
10904 # up. |
|
10905 return None |
|
10906 type = member.type |
|
10907 if type.isAny(): |
|
10908 return "JS::UndefinedValue()" |
|
10909 if type.isObject(): |
|
10910 return "nullptr" |
|
10911 return None |
|
10912 |
|
10913 @staticmethod |
|
10914 def makeIdName(name): |
|
10915 return name + "_id" |
|
10916 |
|
10917 @staticmethod |
|
10918 def getDictionaryDependenciesFromType(type): |
|
10919 if type.isDictionary(): |
|
10920 return set([type.unroll().inner]) |
|
10921 if type.isSequence() or type.isArray(): |
|
10922 return CGDictionary.getDictionaryDependenciesFromType(type.unroll()) |
|
10923 return set() |
|
10924 |
|
10925 @staticmethod |
|
10926 def getDictionaryDependencies(dictionary): |
|
10927 deps = set() |
|
10928 if dictionary.parent: |
|
10929 deps.add(dictionary.parent) |
|
10930 for member in dictionary.members: |
|
10931 deps |= CGDictionary.getDictionaryDependenciesFromType(member.type) |
|
10932 return deps |
|
10933 |
|
10934 @staticmethod |
|
10935 def isDictionaryCopyConstructible(dictionary): |
|
10936 if (dictionary.parent and |
|
10937 not CGDictionary.isDictionaryCopyConstructible(dictionary.parent)): |
|
10938 return False |
|
10939 return all(isTypeCopyConstructible(m.type) for m in dictionary.members) |
|
10940 |
|
10941 |
|
10942 class CGRegisterProtos(CGAbstractMethod): |
|
10943 def __init__(self, config): |
|
10944 CGAbstractMethod.__init__(self, None, 'Register', 'void', |
|
10945 [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')]) |
|
10946 self.config = config |
|
10947 |
|
10948 def _defineMacro(self): |
|
10949 return dedent(""" |
|
10950 #define REGISTER_PROTO(_dom_class, _ctor_check) \\ |
|
10951 aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_class), _dom_class##Binding::DefineDOMInterface, _ctor_check); |
|
10952 #define REGISTER_CONSTRUCTOR(_dom_constructor, _dom_class, _ctor_check) \\ |
|
10953 aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_constructor), _dom_class##Binding::DefineDOMInterface, _ctor_check); |
|
10954 #define REGISTER_NAVIGATOR_CONSTRUCTOR(_prop, _dom_class, _ctor_check) \\ |
|
10955 aNameSpaceManager->RegisterNavigatorDOMConstructor(MOZ_UTF16(_prop), _dom_class##Binding::ConstructNavigatorObject, _ctor_check); |
|
10956 """) |
|
10957 |
|
10958 def _undefineMacro(self): |
|
10959 return dedent(""" |
|
10960 #undef REGISTER_CONSTRUCTOR |
|
10961 #undef REGISTER_PROTO |
|
10962 #undef REGISTER_NAVIGATOR_CONSTRUCTOR |
|
10963 """) |
|
10964 |
|
10965 def _registerProtos(self): |
|
10966 def getCheck(desc): |
|
10967 if not desc.isExposedConditionally(): |
|
10968 return "nullptr" |
|
10969 return "%sBinding::ConstructorEnabled" % desc.name |
|
10970 lines = [] |
|
10971 for desc in self.config.getDescriptors(hasInterfaceObject=True, |
|
10972 isExternal=False, |
|
10973 workers=False, |
|
10974 register=True): |
|
10975 lines.append("REGISTER_PROTO(%s, %s);\n" % (desc.name, getCheck(desc))) |
|
10976 lines.extend("REGISTER_CONSTRUCTOR(%s, %s, %s);\n" % (n.identifier.name, desc.name, getCheck(desc)) |
|
10977 for n in desc.interface.namedConstructors) |
|
10978 for desc in self.config.getDescriptors(isNavigatorProperty=True, register=True): |
|
10979 propName = desc.interface.getNavigatorProperty() |
|
10980 assert propName |
|
10981 lines.append('REGISTER_NAVIGATOR_CONSTRUCTOR("%s", %s, %s);\n' % (propName, desc.name, getCheck(desc))) |
|
10982 return ''.join(lines) |
|
10983 |
|
10984 def definition_body(self): |
|
10985 return "\n" + self._defineMacro() + "\n" + self._registerProtos() + "\n" + self._undefineMacro() |
|
10986 |
|
10987 |
|
10988 def dependencySortObjects(objects, dependencyGetter, nameGetter): |
|
10989 """ |
|
10990 Sort IDL objects with dependencies on each other such that if A |
|
10991 depends on B then B will come before A. This is needed for |
|
10992 declaring C++ classes in the right order, for example. Objects |
|
10993 that have no dependencies are just sorted by name. |
|
10994 |
|
10995 objects should be something that can produce a set of objects |
|
10996 (e.g. a set, iterator, list, etc). |
|
10997 |
|
10998 dependencyGetter is something that, given an object, should return |
|
10999 the set of objects it depends on. |
|
11000 """ |
|
11001 # XXXbz this will fail if we have two webidl files F1 and F2 such that F1 |
|
11002 # declares an object which depends on an object in F2, and F2 declares an |
|
11003 # object (possibly a different one!) that depends on an object in F1. The |
|
11004 # good news is that I expect this to never happen. |
|
11005 sortedObjects = [] |
|
11006 objects = set(objects) |
|
11007 while len(objects) != 0: |
|
11008 # Find the dictionaries that don't depend on anything else |
|
11009 # anymore and move them over. |
|
11010 toMove = [o for o in objects if |
|
11011 len(dependencyGetter(o) & objects) == 0] |
|
11012 if len(toMove) == 0: |
|
11013 raise TypeError("Loop in dependency graph\n" + |
|
11014 "\n".join(o.location for o in objects)) |
|
11015 objects = objects - set(toMove) |
|
11016 sortedObjects.extend(sorted(toMove, key=nameGetter)) |
|
11017 return sortedObjects |
|
11018 |
|
11019 |
|
11020 class ForwardDeclarationBuilder: |
|
11021 """ |
|
11022 Create a canonical representation of a set of namespaced forward |
|
11023 declarations. |
|
11024 """ |
|
11025 def __init__(self): |
|
11026 """ |
|
11027 The set of declarations is represented as a tree of nested namespaces. |
|
11028 Each tree node has a set of declarations |decls| and a dict |children|. |
|
11029 Each declaration is a pair consisting of the class name and a boolean |
|
11030 that is true iff the class is really a struct. |children| maps the |
|
11031 names of inner namespaces to the declarations in that namespace. |
|
11032 """ |
|
11033 self.decls = set() |
|
11034 self.children = {} |
|
11035 |
|
11036 def _listAdd(self, namespaces, name, isStruct=False): |
|
11037 """ |
|
11038 Add a forward declaration, where |namespaces| is a list of namespaces. |
|
11039 |name| should not contain any other namespaces. |
|
11040 """ |
|
11041 if namespaces: |
|
11042 child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder()) |
|
11043 child._listAdd(namespaces[1:], name, isStruct) |
|
11044 else: |
|
11045 assert '::' not in name |
|
11046 self.decls.add((name, isStruct)) |
|
11047 |
|
11048 def addInMozillaDom(self, name, isStruct=False): |
|
11049 """ |
|
11050 Add a forward declaration to the mozilla::dom:: namespace. |name| should not |
|
11051 contain any other namespaces. |
|
11052 """ |
|
11053 self._listAdd(["mozilla", "dom"], name, isStruct) |
|
11054 |
|
11055 def add(self, nativeType, isStruct=False): |
|
11056 """ |
|
11057 Add a forward declaration, where |nativeType| is a string containing |
|
11058 the type and its namespaces, in the usual C++ way. |
|
11059 """ |
|
11060 components = nativeType.split('::') |
|
11061 self._listAdd(components[:-1], components[-1], isStruct) |
|
11062 |
|
11063 def _build(self, atTopLevel): |
|
11064 """ |
|
11065 Return a codegenerator for the forward declarations. |
|
11066 """ |
|
11067 decls = [] |
|
11068 if self.decls: |
|
11069 decls.append(CGList([CGClassForwardDeclare(cname, isStruct) |
|
11070 for cname, isStruct in sorted(self.decls)])) |
|
11071 for namespace, child in sorted(self.children.iteritems()): |
|
11072 decls.append(CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True)) |
|
11073 |
|
11074 cg = CGList(decls, "\n") |
|
11075 if not atTopLevel and len(decls) + len(self.decls) > 1: |
|
11076 cg = CGWrapper(cg, pre='\n', post='\n') |
|
11077 return cg |
|
11078 |
|
11079 def build(self): |
|
11080 return self._build(atTopLevel=True) |
|
11081 |
|
11082 |
|
11083 class CGForwardDeclarations(CGWrapper): |
|
11084 """ |
|
11085 Code generate the forward declarations for a header file. |
|
11086 """ |
|
11087 def __init__(self, config, descriptors, mainCallbacks, workerCallbacks, |
|
11088 dictionaries, callbackInterfaces): |
|
11089 builder = ForwardDeclarationBuilder() |
|
11090 |
|
11091 def forwardDeclareForType(t, workerness='both'): |
|
11092 t = t.unroll() |
|
11093 if t.isGeckoInterface(): |
|
11094 name = t.inner.identifier.name |
|
11095 # Find and add the non-worker implementation, if any. |
|
11096 if workerness != 'workeronly': |
|
11097 try: |
|
11098 desc = config.getDescriptor(name, False) |
|
11099 builder.add(desc.nativeType) |
|
11100 except NoSuchDescriptorError: |
|
11101 pass |
|
11102 # Find and add the worker implementation, if any. |
|
11103 if workerness != 'mainthreadonly': |
|
11104 try: |
|
11105 desc = config.getDescriptor(name, True) |
|
11106 builder.add(desc.nativeType) |
|
11107 except NoSuchDescriptorError: |
|
11108 pass |
|
11109 elif t.isCallback(): |
|
11110 builder.addInMozillaDom(str(t)) |
|
11111 elif t.isDictionary(): |
|
11112 builder.addInMozillaDom(t.inner.identifier.name, isStruct=True) |
|
11113 elif t.isCallbackInterface(): |
|
11114 builder.addInMozillaDom(t.inner.identifier.name) |
|
11115 elif t.isUnion(): |
|
11116 # Forward declare both the owning and non-owning version, |
|
11117 # since we don't know which one we might want |
|
11118 builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False)) |
|
11119 builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, True)) |
|
11120 elif t.isMozMap(): |
|
11121 forwardDeclareForType(t.inner, workerness) |
|
11122 # Don't need to do anything for void, primitive, string, any or object. |
|
11123 # There may be some other cases we are missing. |
|
11124 |
|
11125 # Needed for at least Wrap. |
|
11126 for d in descriptors: |
|
11127 builder.add(d.nativeType) |
|
11128 |
|
11129 # We just about always need NativePropertyHooks |
|
11130 builder.addInMozillaDom("NativePropertyHooks") |
|
11131 builder.addInMozillaDom("ProtoAndIfaceCache") |
|
11132 |
|
11133 for callback in mainCallbacks: |
|
11134 forwardDeclareForType(callback) |
|
11135 for t in getTypesFromCallback(callback): |
|
11136 forwardDeclareForType(t, workerness='mainthreadonly') |
|
11137 |
|
11138 for callback in workerCallbacks: |
|
11139 forwardDeclareForType(callback) |
|
11140 for t in getTypesFromCallback(callback): |
|
11141 forwardDeclareForType(t, workerness='workeronly') |
|
11142 |
|
11143 for d in callbackInterfaces: |
|
11144 builder.add(d.nativeType) |
|
11145 for t in getTypesFromDescriptor(d): |
|
11146 forwardDeclareForType(t) |
|
11147 |
|
11148 for d in dictionaries: |
|
11149 if len(d.members) > 0: |
|
11150 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True) |
|
11151 for t in getTypesFromDictionary(d): |
|
11152 forwardDeclareForType(t) |
|
11153 |
|
11154 CGWrapper.__init__(self, builder.build()) |
|
11155 |
|
11156 |
|
11157 class CGBindingRoot(CGThing): |
|
11158 """ |
|
11159 Root codegen class for binding generation. Instantiate the class, and call |
|
11160 declare or define to generate header or cpp code (respectively). |
|
11161 """ |
|
11162 def __init__(self, config, prefix, webIDLFile): |
|
11163 bindingHeaders = {} |
|
11164 descriptors = config.getDescriptors(webIDLFile=webIDLFile, |
|
11165 hasInterfaceOrInterfacePrototypeObject=True, |
|
11166 skipGen=False) |
|
11167 |
|
11168 def descriptorRequiresPreferences(desc): |
|
11169 iface = desc.interface |
|
11170 return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface]) |
|
11171 |
|
11172 bindingHeaders["mozilla/Preferences.h"] = any( |
|
11173 descriptorRequiresPreferences(d) for d in descriptors) |
|
11174 bindingHeaders["mozilla/dom/NonRefcountedDOMObject.h"] = any( |
|
11175 d.nativeOwnership == 'owned' for d in descriptors) |
|
11176 bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any( |
|
11177 d.concrete and d.proxy for d in descriptors) |
|
11178 |
|
11179 def descriptorHasChromeOnly(desc): |
|
11180 return (any(isChromeOnly(a) for a in desc.interface.members) or |
|
11181 desc.interface.getExtendedAttribute("ChromeOnly") is not None or |
|
11182 # JS-implemented interfaces with an interface object get a |
|
11183 # chromeonly _create method. |
|
11184 (desc.interface.isJSImplemented() and |
|
11185 desc.interface.hasInterfaceObject())) |
|
11186 |
|
11187 bindingHeaders["nsContentUtils.h"] = any( |
|
11188 descriptorHasChromeOnly(d) for d in descriptors) |
|
11189 # XXXkhuey ugly hack but this is going away soon. |
|
11190 bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl") |
|
11191 hasWorkerStuff = len(config.getDescriptors(webIDLFile=webIDLFile, |
|
11192 workers=True)) != 0 |
|
11193 bindingHeaders["WorkerPrivate.h"] = hasWorkerStuff |
|
11194 bindingHeaders["nsThreadUtils.h"] = hasWorkerStuff |
|
11195 |
|
11196 dictionaries = config.getDictionaries(webIDLFile=webIDLFile) |
|
11197 bindingHeaders["nsCxPusher.h"] = dictionaries |
|
11198 bindingHeaders["AtomList.h"] = any( |
|
11199 len(dict.members) > 0 for dict in dictionaries) |
|
11200 mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile, |
|
11201 workers=False) |
|
11202 workerCallbacks = config.getCallbacks(webIDLFile=webIDLFile, |
|
11203 workers=True) |
|
11204 callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile, |
|
11205 isCallback=True) |
|
11206 jsImplemented = config.getDescriptors(webIDLFile=webIDLFile, |
|
11207 isJSImplemented=True) |
|
11208 bindingHeaders["nsPIDOMWindow.h"] = jsImplemented |
|
11209 |
|
11210 def addHeaderBasedOnTypes(header, typeChecker): |
|
11211 bindingHeaders[header] = ( |
|
11212 bindingHeaders.get(header, False) or |
|
11213 any(map(typeChecker, |
|
11214 getAllTypes(descriptors + callbackDescriptors, |
|
11215 dictionaries, |
|
11216 mainCallbacks + workerCallbacks)))) |
|
11217 |
|
11218 bindingHeaders["nsDOMQS.h"] = any(d.hasXPConnectImpls for d in descriptors) |
|
11219 # Only mainthread things can have hasXPConnectImpls |
|
11220 provider = config.getDescriptorProvider(False) |
|
11221 |
|
11222 def checkForXPConnectImpls(typeInfo): |
|
11223 type, _, _ = typeInfo |
|
11224 type = type.unroll() |
|
11225 while type.isMozMap(): |
|
11226 type = type.inner.unroll() |
|
11227 if not type.isInterface() or not type.isGeckoInterface(): |
|
11228 return False |
|
11229 try: |
|
11230 typeDesc = provider.getDescriptor(type.inner.identifier.name) |
|
11231 except NoSuchDescriptorError: |
|
11232 return False |
|
11233 return typeDesc.hasXPConnectImpls |
|
11234 addHeaderBasedOnTypes("nsDOMQS.h", checkForXPConnectImpls) |
|
11235 |
|
11236 def descriptorClearsPropsInSlots(descriptor): |
|
11237 if not descriptor.wrapperCache: |
|
11238 return False |
|
11239 return any(m.isAttr() and m.getExtendedAttribute("StoreInSlot") |
|
11240 for m in descriptor.interface.members) |
|
11241 bindingHeaders["nsJSUtils.h"] = any(descriptorClearsPropsInSlots(d) for d in descriptors) |
|
11242 |
|
11243 # Do codegen for all the enums |
|
11244 enums = config.getEnums(webIDLFile) |
|
11245 cgthings = [CGEnum(e) for e in enums] |
|
11246 |
|
11247 hasCode = (descriptors or callbackDescriptors or dictionaries or |
|
11248 mainCallbacks or workerCallbacks) |
|
11249 bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode |
|
11250 bindingHeaders["mozilla/dom/OwningNonNull.h"] = hasCode |
|
11251 bindingHeaders["mozilla/dom/BindingDeclarations.h"] = ( |
|
11252 not hasCode and enums) |
|
11253 |
|
11254 bindingHeaders["WrapperFactory.h"] = descriptors |
|
11255 bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors |
|
11256 |
|
11257 # Do codegen for all the dictionaries. We have to be a bit careful |
|
11258 # here, because we have to generate these in order from least derived |
|
11259 # to most derived so that class inheritance works out. We also have to |
|
11260 # generate members before the dictionary that contains them. |
|
11261 cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False)) |
|
11262 for d in |
|
11263 dependencySortObjects(dictionaries, |
|
11264 CGDictionary.getDictionaryDependencies, |
|
11265 lambda d: d.identifier.name)]) |
|
11266 |
|
11267 # Do codegen for all the callbacks. |
|
11268 cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(False)) |
|
11269 for c in mainCallbacks) |
|
11270 |
|
11271 cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(True)) |
|
11272 for c in workerCallbacks if c not in mainCallbacks) |
|
11273 |
|
11274 # Do codegen for all the descriptors |
|
11275 cgthings.extend([CGDescriptor(x) for x in descriptors]) |
|
11276 |
|
11277 # Do codegen for all the callback interfaces. Skip worker callbacks. |
|
11278 cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors if |
|
11279 not x.workers]) |
|
11280 |
|
11281 # Do codegen for JS implemented classes |
|
11282 def getParentDescriptor(desc): |
|
11283 if not desc.interface.parent: |
|
11284 return set() |
|
11285 return {desc.getDescriptor(desc.interface.parent.identifier.name)} |
|
11286 for x in dependencySortObjects(jsImplemented, getParentDescriptor, |
|
11287 lambda d: d.interface.identifier.name): |
|
11288 cgthings.append(CGCallbackInterface(x)) |
|
11289 cgthings.append(CGJSImplClass(x)) |
|
11290 |
|
11291 # And make sure we have the right number of newlines at the end |
|
11292 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") |
|
11293 |
|
11294 # Wrap all of that in our namespaces. |
|
11295 curr = CGNamespace.build(['mozilla', 'dom'], |
|
11296 CGWrapper(curr, pre="\n")) |
|
11297 |
|
11298 curr = CGList([CGForwardDeclarations(config, descriptors, |
|
11299 mainCallbacks, workerCallbacks, |
|
11300 dictionaries, |
|
11301 callbackDescriptors + jsImplemented), |
|
11302 curr], |
|
11303 "\n") |
|
11304 |
|
11305 # Add header includes. |
|
11306 bindingHeaders = [header |
|
11307 for header, include in bindingHeaders.iteritems() |
|
11308 if include] |
|
11309 declareIncludes = [ |
|
11310 'mozilla/dom/BindingDeclarations.h', |
|
11311 'mozilla/dom/Nullable.h', |
|
11312 'mozilla/ErrorResult.h', |
|
11313 'jspubtd.h', |
|
11314 'js/RootingAPI.h', |
|
11315 ] |
|
11316 if jsImplemented: |
|
11317 declareIncludes.append('nsWeakReference.h') |
|
11318 |
|
11319 curr = CGHeaders(descriptors, |
|
11320 dictionaries, |
|
11321 mainCallbacks + workerCallbacks, |
|
11322 callbackDescriptors, |
|
11323 declareIncludes, |
|
11324 bindingHeaders, |
|
11325 prefix, |
|
11326 curr, |
|
11327 config, |
|
11328 jsImplemented) |
|
11329 |
|
11330 # Add include guards. |
|
11331 curr = CGIncludeGuard(prefix, curr) |
|
11332 |
|
11333 # Add the auto-generated comment. |
|
11334 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) |
|
11335 |
|
11336 # Store the final result. |
|
11337 self.root = curr |
|
11338 |
|
11339 def declare(self): |
|
11340 return stripTrailingWhitespace(self.root.declare()) |
|
11341 |
|
11342 def define(self): |
|
11343 return stripTrailingWhitespace(self.root.define()) |
|
11344 |
|
11345 def deps(self): |
|
11346 return self.root.deps() |
|
11347 |
|
11348 |
|
11349 class CGNativeMember(ClassMethod): |
|
11350 def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, |
|
11351 breakAfter=True, passJSBitsAsNeeded=True, visibility="public", |
|
11352 jsObjectsArePtr=False, variadicIsSequence=False): |
|
11353 """ |
|
11354 If jsObjectsArePtr is true, typed arrays and "object" will be |
|
11355 passed as JSObject*. |
|
11356 |
|
11357 If passJSBitsAsNeeded is false, we don't automatically pass in a |
|
11358 JSContext* or a JSObject* based on the return and argument types. We |
|
11359 can still pass it based on 'implicitJSContext' annotations. |
|
11360 """ |
|
11361 self.descriptorProvider = descriptorProvider |
|
11362 self.member = member |
|
11363 self.extendedAttrs = extendedAttrs |
|
11364 self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.extendedAttrs) |
|
11365 self.passJSBitsAsNeeded = passJSBitsAsNeeded |
|
11366 self.jsObjectsArePtr = jsObjectsArePtr |
|
11367 self.variadicIsSequence = variadicIsSequence |
|
11368 breakAfterSelf = "\n" if breakAfter else "" |
|
11369 ClassMethod.__init__(self, name, |
|
11370 self.getReturnType(signature[0], False), |
|
11371 self.getArgs(signature[0], signature[1]), |
|
11372 static=member.isStatic(), |
|
11373 # Mark our getters, which are attrs that |
|
11374 # have a non-void return type, as const. |
|
11375 const=(not member.isStatic() and member.isAttr() and |
|
11376 not signature[0].isVoid()), |
|
11377 breakAfterReturnDecl=" ", |
|
11378 breakAfterSelf=breakAfterSelf, |
|
11379 visibility=visibility) |
|
11380 |
|
11381 def getReturnType(self, type, isMember): |
|
11382 return self.getRetvalInfo(type, isMember)[0] |
|
11383 |
|
11384 def getRetvalInfo(self, type, isMember): |
|
11385 """ |
|
11386 Returns a tuple: |
|
11387 |
|
11388 The first element is the type declaration for the retval |
|
11389 |
|
11390 The second element is a default value that can be used on error returns. |
|
11391 For cases whose behavior depends on isMember, the second element will be |
|
11392 None if isMember is true. |
|
11393 |
|
11394 The third element is a template for actually returning a value stored in |
|
11395 "${declName}" and "${holderName}". This means actually returning it if |
|
11396 we're not outparam, else assigning to the "retval" outparam. If |
|
11397 isMember is true, this can be None, since in that case the caller will |
|
11398 never examine this value. |
|
11399 """ |
|
11400 if type.isVoid(): |
|
11401 return "void", "", "" |
|
11402 if type.isPrimitive() and type.tag() in builtinNames: |
|
11403 result = CGGeneric(builtinNames[type.tag()]) |
|
11404 defaultReturnArg = "0" |
|
11405 if type.nullable(): |
|
11406 result = CGTemplatedType("Nullable", result) |
|
11407 defaultReturnArg = "" |
|
11408 return (result.define(), |
|
11409 "%s(%s)" % (result.define(), defaultReturnArg), |
|
11410 "return ${declName};\n") |
|
11411 if type.isDOMString(): |
|
11412 if isMember: |
|
11413 # No need for a third element in the isMember case |
|
11414 return "nsString", None, None |
|
11415 # Outparam |
|
11416 return "void", "", "aRetVal = ${declName};\n" |
|
11417 if type.isByteString(): |
|
11418 if isMember: |
|
11419 # No need for a third element in the isMember case |
|
11420 return "nsCString", None, None |
|
11421 # Outparam |
|
11422 return "void", "", "aRetVal = ${declName};\n" |
|
11423 if type.isEnum(): |
|
11424 enumName = type.unroll().inner.identifier.name |
|
11425 if type.nullable(): |
|
11426 enumName = CGTemplatedType("Nullable", |
|
11427 CGGeneric(enumName)).define() |
|
11428 defaultValue = "%s()" % enumName |
|
11429 else: |
|
11430 defaultValue = "%s(0)" % enumName |
|
11431 return enumName, defaultValue, "return ${declName};\n" |
|
11432 if type.isGeckoInterface(): |
|
11433 iface = type.unroll().inner |
|
11434 result = CGGeneric(self.descriptorProvider.getDescriptor( |
|
11435 iface.identifier.name).prettyNativeType) |
|
11436 if self.resultAlreadyAddRefed: |
|
11437 if isMember: |
|
11438 holder = "nsRefPtr" |
|
11439 else: |
|
11440 holder = "already_AddRefed" |
|
11441 if memberReturnsNewObject(self.member): |
|
11442 warning = "" |
|
11443 else: |
|
11444 warning = "// Mark this as resultNotAddRefed to return raw pointers\n" |
|
11445 result = CGWrapper(result, |
|
11446 pre=("%s%s<" % (warning, holder)), |
|
11447 post=">") |
|
11448 else: |
|
11449 result = CGWrapper(result, post="*") |
|
11450 # Since we always force an owning type for callback return values, |
|
11451 # our ${declName} is an OwningNonNull or nsRefPtr. So we can just |
|
11452 # .forget() to get our already_AddRefed. |
|
11453 return result.define(), "nullptr", "return ${declName}.forget();\n" |
|
11454 if type.isCallback(): |
|
11455 return ("already_AddRefed<%s>" % type.unroll().identifier.name, |
|
11456 "nullptr", "return ${declName}.forget();\n") |
|
11457 if type.isAny(): |
|
11458 if isMember: |
|
11459 # No need for a third element in the isMember case |
|
11460 return "JS::Value", None, None |
|
11461 # Outparam |
|
11462 return "void", "", "aRetVal.set(${declName});\n" |
|
11463 |
|
11464 if type.isObject(): |
|
11465 if isMember: |
|
11466 # No need for a third element in the isMember case |
|
11467 return "JSObject*", None, None |
|
11468 return "void", "", "aRetVal.set(${declName});\n" |
|
11469 if type.isSpiderMonkeyInterface(): |
|
11470 if isMember: |
|
11471 # No need for a third element in the isMember case |
|
11472 return "JSObject*", None, None |
|
11473 if type.nullable(): |
|
11474 returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();\n" |
|
11475 else: |
|
11476 returnCode = "${declName}.Obj();\n" |
|
11477 return "void", "", "aRetVal.set(%s);\n" % returnCode |
|
11478 if type.isSequence(): |
|
11479 # If we want to handle sequence-of-sequences return values, we're |
|
11480 # going to need to fix example codegen to not produce nsTArray<void> |
|
11481 # for the relevant argument... |
|
11482 assert not isMember |
|
11483 # Outparam. |
|
11484 if type.nullable(): |
|
11485 returnCode = dedent(""" |
|
11486 if (${declName}.IsNull()) { |
|
11487 aRetVal.SetNull(); |
|
11488 } else { |
|
11489 aRetVal.SetValue().SwapElements(${declName}.Value()); |
|
11490 } |
|
11491 """) |
|
11492 else: |
|
11493 returnCode = "aRetVal.SwapElements(${declName});\n" |
|
11494 return "void", "", returnCode |
|
11495 if type.isMozMap(): |
|
11496 # If we want to handle MozMap-of-MozMap return values, we're |
|
11497 # going to need to fix example codegen to not produce MozMap<void> |
|
11498 # for the relevant argument... |
|
11499 assert not isMember |
|
11500 # In this case we convert directly into our outparam to start with |
|
11501 return "void", "", "" |
|
11502 if type.isDate(): |
|
11503 result = CGGeneric("Date") |
|
11504 if type.nullable(): |
|
11505 result = CGTemplatedType("Nullable", result) |
|
11506 return (result.define(), "%s()" % result.define(), |
|
11507 "return ${declName};\n") |
|
11508 if type.isDictionary(): |
|
11509 if isMember: |
|
11510 # Only the first member of the tuple matters here, but return |
|
11511 # bogus values for the others in case someone decides to use |
|
11512 # them. |
|
11513 return CGDictionary.makeDictionaryName(type.inner), None, None |
|
11514 # In this case we convert directly into our outparam to start with |
|
11515 return "void", "", "" |
|
11516 if type.isUnion(): |
|
11517 if isMember: |
|
11518 # Only the first member of the tuple matters here, but return |
|
11519 # bogus values for the others in case someone decides to use |
|
11520 # them. |
|
11521 return CGUnionStruct.unionTypeDecl(type, True), None, None |
|
11522 # In this case we convert directly into our outparam to start with |
|
11523 return "void", "", "" |
|
11524 |
|
11525 raise TypeError("Don't know how to declare return value for %s" % |
|
11526 type) |
|
11527 |
|
11528 def getArgs(self, returnType, argList): |
|
11529 args = [self.getArg(arg) for arg in argList] |
|
11530 # Now the outparams |
|
11531 if returnType.isDOMString(): |
|
11532 args.append(Argument("nsString&", "aRetVal")) |
|
11533 elif returnType.isByteString(): |
|
11534 args.append(Argument("nsCString&", "aRetVal")) |
|
11535 elif returnType.isSequence(): |
|
11536 nullable = returnType.nullable() |
|
11537 if nullable: |
|
11538 returnType = returnType.inner |
|
11539 # And now the actual underlying type |
|
11540 elementDecl = self.getReturnType(returnType.inner, True) |
|
11541 type = CGTemplatedType("nsTArray", CGGeneric(elementDecl)) |
|
11542 if nullable: |
|
11543 type = CGTemplatedType("Nullable", type) |
|
11544 args.append(Argument("%s&" % type.define(), "aRetVal")) |
|
11545 elif returnType.isMozMap(): |
|
11546 nullable = returnType.nullable() |
|
11547 if nullable: |
|
11548 returnType = returnType.inner |
|
11549 # And now the actual underlying type |
|
11550 elementDecl = self.getReturnType(returnType.inner, True) |
|
11551 type = CGTemplatedType("MozMap", CGGeneric(elementDecl)) |
|
11552 if nullable: |
|
11553 type = CGTemplatedType("Nullable", type) |
|
11554 args.append(Argument("%s&" % type.define(), "aRetVal")) |
|
11555 elif returnType.isDictionary(): |
|
11556 nullable = returnType.nullable() |
|
11557 if nullable: |
|
11558 returnType = returnType.inner |
|
11559 dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner)) |
|
11560 if nullable: |
|
11561 dictType = CGTemplatedType("Nullable", dictType) |
|
11562 args.append(Argument("%s&" % dictType.define(), "aRetVal")) |
|
11563 elif returnType.isUnion(): |
|
11564 args.append(Argument("%s&" % |
|
11565 CGUnionStruct.unionTypeDecl(returnType, True), |
|
11566 "aRetVal")) |
|
11567 elif returnType.isAny(): |
|
11568 args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal")) |
|
11569 elif returnType.isObject() or returnType.isSpiderMonkeyInterface(): |
|
11570 args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal")) |
|
11571 |
|
11572 # And the ErrorResult |
|
11573 if 'infallible' not in self.extendedAttrs: |
|
11574 # Use aRv so it won't conflict with local vars named "rv" |
|
11575 args.append(Argument("ErrorResult&", "aRv")) |
|
11576 # The legacycaller thisval |
|
11577 if self.member.isMethod() and self.member.isLegacycaller(): |
|
11578 # If it has an identifier, we can't deal with it yet |
|
11579 assert self.member.isIdentifierLess() |
|
11580 args.insert(0, Argument("JS::Value", "aThisVal")) |
|
11581 # And jscontext bits. |
|
11582 if needCx(returnType, argList, self.extendedAttrs, |
|
11583 self.passJSBitsAsNeeded): |
|
11584 args.insert(0, Argument("JSContext*", "cx")) |
|
11585 if needScopeObject(returnType, argList, self.extendedAttrs, |
|
11586 self.descriptorProvider.wrapperCache, |
|
11587 self.passJSBitsAsNeeded, |
|
11588 self.member.getExtendedAttribute("StoreInSlot")): |
|
11589 args.insert(1, Argument("JS::Handle<JSObject*>", "obj")) |
|
11590 # And if we're static, a global |
|
11591 if self.member.isStatic(): |
|
11592 args.insert(0, Argument("const GlobalObject&", "global")) |
|
11593 return args |
|
11594 |
|
11595 def doGetArgType(self, type, optional, isMember): |
|
11596 """ |
|
11597 The main work of getArgType. Returns a string type decl, whether this |
|
11598 is a const ref, as well as whether the type should be wrapped in |
|
11599 Nullable as needed. |
|
11600 |
|
11601 isMember can be false or one of the strings "Sequence", "Variadic", |
|
11602 "MozMap" |
|
11603 """ |
|
11604 if type.isArray(): |
|
11605 raise TypeError("Can't handle array arguments yet") |
|
11606 |
|
11607 if type.isSequence(): |
|
11608 nullable = type.nullable() |
|
11609 if nullable: |
|
11610 type = type.inner |
|
11611 elementType = type.inner |
|
11612 argType = self.getArgType(elementType, False, "Sequence")[0] |
|
11613 decl = CGTemplatedType("Sequence", argType) |
|
11614 return decl.define(), True, True |
|
11615 |
|
11616 if type.isMozMap(): |
|
11617 nullable = type.nullable() |
|
11618 if nullable: |
|
11619 type = type.inner |
|
11620 elementType = type.inner |
|
11621 argType = self.getArgType(elementType, False, "MozMap")[0] |
|
11622 decl = CGTemplatedType("MozMap", argType) |
|
11623 return decl.define(), True, True |
|
11624 |
|
11625 if type.isUnion(): |
|
11626 # unionTypeDecl will handle nullable types, so return False for |
|
11627 # auto-wrapping in Nullable |
|
11628 return CGUnionStruct.unionTypeDecl(type, isMember), True, False |
|
11629 |
|
11630 if type.isGeckoInterface() and not type.isCallbackInterface(): |
|
11631 iface = type.unroll().inner |
|
11632 argIsPointer = type.nullable() or iface.isExternal() |
|
11633 forceOwningType = iface.isCallback() or isMember |
|
11634 if argIsPointer: |
|
11635 if (optional or isMember) and forceOwningType: |
|
11636 typeDecl = "nsRefPtr<%s>" |
|
11637 else: |
|
11638 typeDecl = "%s*" |
|
11639 else: |
|
11640 if optional or isMember: |
|
11641 if forceOwningType: |
|
11642 typeDecl = "OwningNonNull<%s>" |
|
11643 else: |
|
11644 typeDecl = "NonNull<%s>" |
|
11645 else: |
|
11646 typeDecl = "%s&" |
|
11647 return ((typeDecl % |
|
11648 self.descriptorProvider.getDescriptor(iface.identifier.name).prettyNativeType), |
|
11649 False, False) |
|
11650 |
|
11651 if type.isSpiderMonkeyInterface(): |
|
11652 if self.jsObjectsArePtr: |
|
11653 return "JSObject*", False, False |
|
11654 |
|
11655 return type.name, True, True |
|
11656 |
|
11657 if type.isDOMString(): |
|
11658 if isMember: |
|
11659 declType = "nsString" |
|
11660 else: |
|
11661 declType = "nsAString" |
|
11662 return declType, True, False |
|
11663 |
|
11664 if type.isByteString(): |
|
11665 declType = "nsCString" |
|
11666 return declType, True, False |
|
11667 |
|
11668 if type.isEnum(): |
|
11669 return type.unroll().inner.identifier.name, False, True |
|
11670 |
|
11671 if type.isCallback() or type.isCallbackInterface(): |
|
11672 forceOwningType = optional or isMember |
|
11673 if type.nullable(): |
|
11674 if forceOwningType: |
|
11675 declType = "nsRefPtr<%s>" |
|
11676 else: |
|
11677 declType = "%s*" |
|
11678 else: |
|
11679 if forceOwningType: |
|
11680 declType = "OwningNonNull<%s>" |
|
11681 else: |
|
11682 declType = "%s&" |
|
11683 if type.isCallback(): |
|
11684 name = type.unroll().identifier.name |
|
11685 else: |
|
11686 name = type.unroll().inner.identifier.name |
|
11687 return declType % name, False, False |
|
11688 |
|
11689 if type.isAny(): |
|
11690 # Don't do the rooting stuff for variadics for now |
|
11691 if isMember: |
|
11692 declType = "JS::Value" |
|
11693 else: |
|
11694 declType = "JS::Handle<JS::Value>" |
|
11695 return declType, False, False |
|
11696 |
|
11697 if type.isObject(): |
|
11698 if isMember: |
|
11699 declType = "JSObject*" |
|
11700 else: |
|
11701 declType = "JS::Handle<JSObject*>" |
|
11702 return declType, False, False |
|
11703 |
|
11704 if type.isDictionary(): |
|
11705 typeName = CGDictionary.makeDictionaryName(type.inner) |
|
11706 return typeName, True, True |
|
11707 |
|
11708 if type.isDate(): |
|
11709 return "Date", False, True |
|
11710 |
|
11711 assert type.isPrimitive() |
|
11712 |
|
11713 return builtinNames[type.tag()], False, True |
|
11714 |
|
11715 def getArgType(self, type, optional, isMember): |
|
11716 """ |
|
11717 Get the type of an argument declaration. Returns the type CGThing, and |
|
11718 whether this should be a const ref. |
|
11719 |
|
11720 isMember can be False, "Sequence", or "Variadic" |
|
11721 """ |
|
11722 decl, ref, handleNullable = self.doGetArgType(type, optional, isMember) |
|
11723 decl = CGGeneric(decl) |
|
11724 if handleNullable and type.nullable(): |
|
11725 decl = CGTemplatedType("Nullable", decl) |
|
11726 ref = True |
|
11727 if isMember == "Variadic": |
|
11728 arrayType = "Sequence" if self.variadicIsSequence else "nsTArray" |
|
11729 decl = CGTemplatedType(arrayType, decl) |
|
11730 ref = True |
|
11731 elif optional: |
|
11732 # Note: All variadic args claim to be optional, but we can just use |
|
11733 # empty arrays to represent them not being present. |
|
11734 decl = CGTemplatedType("Optional", decl) |
|
11735 ref = True |
|
11736 return (decl, ref) |
|
11737 |
|
11738 def getArg(self, arg): |
|
11739 """ |
|
11740 Get the full argument declaration for an argument |
|
11741 """ |
|
11742 decl, ref = self.getArgType(arg.type, |
|
11743 arg.optional and not arg.defaultValue, |
|
11744 "Variadic" if arg.variadic else False) |
|
11745 if ref: |
|
11746 decl = CGWrapper(decl, pre="const ", post="&") |
|
11747 |
|
11748 return Argument(decl.define(), arg.identifier.name) |
|
11749 |
|
11750 |
|
11751 class CGExampleMethod(CGNativeMember): |
|
11752 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): |
|
11753 CGNativeMember.__init__(self, descriptor, method, |
|
11754 CGSpecializedMethod.makeNativeName(descriptor, |
|
11755 method), |
|
11756 signature, |
|
11757 descriptor.getExtendedAttributes(method), |
|
11758 breakAfter=breakAfter, |
|
11759 variadicIsSequence=True) |
|
11760 |
|
11761 def define(self, cgClass): |
|
11762 return '' |
|
11763 |
|
11764 |
|
11765 class CGExampleGetter(CGNativeMember): |
|
11766 def __init__(self, descriptor, attr): |
|
11767 CGNativeMember.__init__(self, descriptor, attr, |
|
11768 CGSpecializedGetter.makeNativeName(descriptor, |
|
11769 attr), |
|
11770 (attr.type, []), |
|
11771 descriptor.getExtendedAttributes(attr, |
|
11772 getter=True)) |
|
11773 |
|
11774 def define(self, cgClass): |
|
11775 return '' |
|
11776 |
|
11777 |
|
11778 class CGExampleSetter(CGNativeMember): |
|
11779 def __init__(self, descriptor, attr): |
|
11780 CGNativeMember.__init__(self, descriptor, attr, |
|
11781 CGSpecializedSetter.makeNativeName(descriptor, |
|
11782 attr), |
|
11783 (BuiltinTypes[IDLBuiltinType.Types.void], |
|
11784 [FakeArgument(attr.type, attr)]), |
|
11785 descriptor.getExtendedAttributes(attr, |
|
11786 setter=True)) |
|
11787 |
|
11788 def define(self, cgClass): |
|
11789 return '' |
|
11790 |
|
11791 |
|
11792 class CGBindingImplClass(CGClass): |
|
11793 """ |
|
11794 Common codegen for generating a C++ implementation of a WebIDL interface |
|
11795 """ |
|
11796 def __init__(self, descriptor, cgMethod, cgGetter, cgSetter, wantGetParent=True): |
|
11797 """ |
|
11798 cgMethod, cgGetter and cgSetter are classes used to codegen methods, |
|
11799 getters and setters. |
|
11800 """ |
|
11801 self.descriptor = descriptor |
|
11802 self._deps = descriptor.interface.getDeps() |
|
11803 |
|
11804 iface = descriptor.interface |
|
11805 |
|
11806 self.methodDecls = [] |
|
11807 |
|
11808 def appendMethod(m, isConstructor=False): |
|
11809 sigs = m.signatures() |
|
11810 for s in sigs[:-1]: |
|
11811 # Don't put a blank line after overloads, until we |
|
11812 # get to the last one. |
|
11813 self.methodDecls.append(cgMethod(descriptor, m, s, |
|
11814 isConstructor, |
|
11815 breakAfter=False)) |
|
11816 self.methodDecls.append(cgMethod(descriptor, m, sigs[-1], |
|
11817 isConstructor)) |
|
11818 |
|
11819 if iface.ctor(): |
|
11820 appendMethod(iface.ctor(), isConstructor=True) |
|
11821 for n in iface.namedConstructors: |
|
11822 appendMethod(n, isConstructor=True) |
|
11823 for m in iface.members: |
|
11824 if m.isMethod(): |
|
11825 if m.isIdentifierLess(): |
|
11826 continue |
|
11827 appendMethod(m) |
|
11828 elif m.isAttr(): |
|
11829 self.methodDecls.append(cgGetter(descriptor, m)) |
|
11830 if not m.readonly: |
|
11831 self.methodDecls.append(cgSetter(descriptor, m)) |
|
11832 |
|
11833 # Now do the special operations |
|
11834 def appendSpecialOperation(name, op): |
|
11835 if op is None: |
|
11836 return |
|
11837 if name == "IndexedCreator" or name == "NamedCreator": |
|
11838 # These are identical to the setters |
|
11839 return |
|
11840 assert len(op.signatures()) == 1 |
|
11841 returnType, args = op.signatures()[0] |
|
11842 # Make a copy of the args, since we plan to modify them. |
|
11843 args = list(args) |
|
11844 if op.isGetter() or op.isDeleter(): |
|
11845 # This is a total hack. The '&' belongs with the |
|
11846 # type, not the name! But it works, and is simpler |
|
11847 # than trying to somehow make this pretty. |
|
11848 args.append(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean], |
|
11849 op, name="&found")) |
|
11850 if name == "Stringifier": |
|
11851 if op.isIdentifierLess(): |
|
11852 # XXXbz I wish we were consistent about our renaming here. |
|
11853 name = "Stringify" |
|
11854 else: |
|
11855 # We already added this method |
|
11856 return |
|
11857 if name == "LegacyCaller": |
|
11858 if op.isIdentifierLess(): |
|
11859 # XXXbz I wish we were consistent about our renaming here. |
|
11860 name = "LegacyCall" |
|
11861 else: |
|
11862 # We already added this method |
|
11863 return |
|
11864 if name == "Jsonifier": |
|
11865 # We already added this method |
|
11866 return |
|
11867 self.methodDecls.append( |
|
11868 CGNativeMember(descriptor, op, |
|
11869 name, |
|
11870 (returnType, args), |
|
11871 descriptor.getExtendedAttributes(op))) |
|
11872 # Sort things by name so we get stable ordering in the output. |
|
11873 ops = descriptor.operations.items() |
|
11874 ops.sort(key=lambda x: x[0]) |
|
11875 for name, op in ops: |
|
11876 appendSpecialOperation(name, op) |
|
11877 # If we support indexed properties, then we need a Length() |
|
11878 # method so we know which indices are supported. |
|
11879 if descriptor.supportsIndexedProperties(): |
|
11880 # But we don't need it if we already have an infallible |
|
11881 # "length" attribute, which we often do. |
|
11882 haveLengthAttr = any( |
|
11883 m for m in iface.members if m.isAttr() and |
|
11884 CGSpecializedGetter.makeNativeName(descriptor, m) == "Length") |
|
11885 if not haveLengthAttr: |
|
11886 self.methodDecls.append( |
|
11887 CGNativeMember(descriptor, FakeMember(), |
|
11888 "Length", |
|
11889 (BuiltinTypes[IDLBuiltinType.Types.unsigned_long], |
|
11890 []), |
|
11891 {"infallible": True})) |
|
11892 # And if we support named properties we need to be able to |
|
11893 # enumerate the supported names and test whether they're enumerable. |
|
11894 if descriptor.supportsNamedProperties(): |
|
11895 self.methodDecls.append( |
|
11896 CGNativeMember( |
|
11897 descriptor, FakeMember(), |
|
11898 "GetSupportedNames", |
|
11899 (IDLSequenceType(None, |
|
11900 BuiltinTypes[IDLBuiltinType.Types.domstring]), |
|
11901 # Let's use unsigned long for the type here, though really |
|
11902 # it's just a C++ "unsigned"... |
|
11903 [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long], |
|
11904 FakeMember(), |
|
11905 name="aFlags")]), |
|
11906 {"infallible": True})) |
|
11907 self.methodDecls.append( |
|
11908 CGNativeMember( |
|
11909 descriptor, FakeMember(), |
|
11910 "NameIsEnumerable", |
|
11911 (BuiltinTypes[IDLBuiltinType.Types.boolean], |
|
11912 [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring], |
|
11913 FakeMember(), |
|
11914 name="aName")]), |
|
11915 { "infallible": True })) |
|
11916 |
|
11917 wrapArgs = [Argument('JSContext*', 'aCx')] |
|
11918 self.methodDecls.insert(0, |
|
11919 ClassMethod("WrapObject", "JSObject*", |
|
11920 wrapArgs, virtual=descriptor.wrapperCache, |
|
11921 breakAfterReturnDecl=" ", |
|
11922 override=descriptor.wrapperCache, |
|
11923 body=self.getWrapObjectBody())) |
|
11924 if wantGetParent: |
|
11925 self.methodDecls.insert(0, |
|
11926 ClassMethod("GetParentObject", |
|
11927 self.getGetParentObjectReturnType(), |
|
11928 [], const=True, |
|
11929 breakAfterReturnDecl=" ", |
|
11930 body=self.getGetParentObjectBody())) |
|
11931 |
|
11932 # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen. |
|
11933 |
|
11934 def getWrapObjectBody(self): |
|
11935 return None |
|
11936 |
|
11937 def getGetParentObjectReturnType(self): |
|
11938 return ("// TODO: return something sensible here, and change the return type\n" |
|
11939 "%s*" % self.descriptor.nativeType.split('::')[-1]) |
|
11940 |
|
11941 def getGetParentObjectBody(self): |
|
11942 return None |
|
11943 |
|
11944 def deps(self): |
|
11945 return self._deps |
|
11946 |
|
11947 |
|
11948 class CGExampleClass(CGBindingImplClass): |
|
11949 """ |
|
11950 Codegen for the actual example class implementation for this descriptor |
|
11951 """ |
|
11952 def __init__(self, descriptor): |
|
11953 CGBindingImplClass.__init__(self, descriptor, |
|
11954 CGExampleMethod, CGExampleGetter, CGExampleSetter, |
|
11955 wantGetParent=descriptor.wrapperCache) |
|
11956 |
|
11957 self.refcounted = descriptor.nativeOwnership == "refcounted" |
|
11958 |
|
11959 self.parentIface = descriptor.interface.parent |
|
11960 if self.parentIface: |
|
11961 self.parentDesc = descriptor.getDescriptor( |
|
11962 self.parentIface.identifier.name) |
|
11963 bases = [ClassBase(self.nativeLeafName(self.parentDesc))] |
|
11964 else: |
|
11965 bases = [] |
|
11966 if self.refcounted: |
|
11967 bases.append(ClassBase("nsISupports /* Change nativeOwnership in the binding configuration if you don't want this */")) |
|
11968 if descriptor.wrapperCache: |
|
11969 bases.append(ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */")) |
|
11970 else: |
|
11971 bases.append(ClassBase("NonRefcountedDOMObject")) |
|
11972 |
|
11973 if self.refcounted: |
|
11974 if self.parentIface: |
|
11975 extradeclarations = ( |
|
11976 "public:\n" |
|
11977 " NS_DECL_ISUPPORTS_INHERITED\n" |
|
11978 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n" |
|
11979 "\n" % (self.nativeLeafName(descriptor), |
|
11980 self.nativeLeafName(self.parentDesc))) |
|
11981 else: |
|
11982 extradeclarations = ( |
|
11983 "public:\n" |
|
11984 " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n" |
|
11985 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" |
|
11986 "\n" % self.nativeLeafName(descriptor)) |
|
11987 else: |
|
11988 extradeclarations = "" |
|
11989 |
|
11990 if descriptor.interface.hasChildInterfaces(): |
|
11991 decorators = "" |
|
11992 else: |
|
11993 decorators = "MOZ_FINAL" |
|
11994 |
|
11995 CGClass.__init__(self, self.nativeLeafName(descriptor), |
|
11996 bases=bases, |
|
11997 constructors=[ClassConstructor([], |
|
11998 visibility="public")], |
|
11999 destructor=ClassDestructor(visibility="public"), |
|
12000 methods=self.methodDecls, |
|
12001 decorators=decorators, |
|
12002 extradeclarations=extradeclarations) |
|
12003 |
|
12004 def define(self): |
|
12005 # Just override CGClass and do our own thing |
|
12006 if self.descriptor.wrapperCache: |
|
12007 setDOMBinding = " SetIsDOMBinding();\n" |
|
12008 else: |
|
12009 setDOMBinding = "" |
|
12010 if self.refcounted: |
|
12011 ctordtor = dedent(""" |
|
12012 ${nativeType}::${nativeType}() |
|
12013 { |
|
12014 %s} |
|
12015 |
|
12016 ${nativeType}::~${nativeType}() |
|
12017 { |
|
12018 } |
|
12019 """) % setDOMBinding |
|
12020 else: |
|
12021 ctordtor = dedent(""" |
|
12022 ${nativeType}::${nativeType}() |
|
12023 { |
|
12024 MOZ_COUNT_CTOR(${nativeType}); |
|
12025 } |
|
12026 |
|
12027 ${nativeType}::~${nativeType}() |
|
12028 { |
|
12029 MOZ_COUNT_DTOR(${nativeType}); |
|
12030 } |
|
12031 """) |
|
12032 |
|
12033 if self.refcounted: |
|
12034 if self.parentIface: |
|
12035 ccImpl = dedent(""" |
|
12036 |
|
12037 NS_IMPL_CYCLE_COLLECTION_INHERITED_0(${nativeType}, ${parentType}) |
|
12038 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType}) |
|
12039 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType}) |
|
12040 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType}) |
|
12041 NS_INTERFACE_MAP_END_INHERITING(${parentType}) |
|
12042 |
|
12043 """) |
|
12044 else: |
|
12045 ccImpl = dedent(""" |
|
12046 |
|
12047 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType}) |
|
12048 NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType}) |
|
12049 NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType}) |
|
12050 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType}) |
|
12051 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
12052 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
12053 NS_INTERFACE_MAP_END |
|
12054 |
|
12055 """) |
|
12056 else: |
|
12057 ccImpl = "" |
|
12058 |
|
12059 classImpl = ccImpl + ctordtor + "\n" + dedent(""" |
|
12060 JSObject* |
|
12061 ${nativeType}::WrapObject(JSContext* aCx) |
|
12062 { |
|
12063 return ${ifaceName}Binding::Wrap(aCx, this); |
|
12064 } |
|
12065 |
|
12066 """) |
|
12067 return string.Template(classImpl).substitute( |
|
12068 ifaceName=self.descriptor.name, |
|
12069 nativeType=self.nativeLeafName(self.descriptor), |
|
12070 parentType=self.nativeLeafName(self.parentDesc) if self.parentIface else "") |
|
12071 |
|
12072 @staticmethod |
|
12073 def nativeLeafName(descriptor): |
|
12074 return descriptor.nativeType.split('::')[-1] |
|
12075 |
|
12076 |
|
12077 class CGExampleRoot(CGThing): |
|
12078 """ |
|
12079 Root codegen class for example implementation generation. Instantiate the |
|
12080 class and call declare or define to generate header or cpp code, |
|
12081 respectively. |
|
12082 """ |
|
12083 def __init__(self, config, interfaceName): |
|
12084 # Let's assume we're not doing workers stuff |
|
12085 descriptor = config.getDescriptor(interfaceName, False) |
|
12086 |
|
12087 self.root = CGWrapper(CGExampleClass(descriptor), |
|
12088 pre="\n", post="\n") |
|
12089 |
|
12090 self.root = CGNamespace.build(["mozilla", "dom"], self.root) |
|
12091 |
|
12092 self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True), |
|
12093 self.root], "\n") |
|
12094 |
|
12095 # Throw in our #includes |
|
12096 self.root = CGHeaders([], [], [], [], |
|
12097 ["nsWrapperCache.h", |
|
12098 "nsCycleCollectionParticipant.h", |
|
12099 "mozilla/Attributes.h", |
|
12100 "mozilla/ErrorResult.h"], |
|
12101 ["mozilla/dom/%s.h" % interfaceName, |
|
12102 ("mozilla/dom/%s" % |
|
12103 CGHeaders.getDeclarationFilename(descriptor.interface))], "", self.root) |
|
12104 |
|
12105 # And now some include guards |
|
12106 self.root = CGIncludeGuard(interfaceName, self.root) |
|
12107 |
|
12108 # And our license block comes before everything else |
|
12109 self.root = CGWrapper(self.root, pre=dedent(""" |
|
12110 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
12111 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
12112 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
12113 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
12114 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
12115 |
|
12116 """)) |
|
12117 |
|
12118 def declare(self): |
|
12119 return self.root.declare() |
|
12120 |
|
12121 def define(self): |
|
12122 return self.root.define() |
|
12123 |
|
12124 |
|
12125 def jsImplName(name): |
|
12126 return name + "JSImpl" |
|
12127 |
|
12128 |
|
12129 class CGJSImplMember(CGNativeMember): |
|
12130 """ |
|
12131 Base class for generating code for the members of the implementation class |
|
12132 for a JS-implemented WebIDL interface. |
|
12133 """ |
|
12134 def __init__(self, descriptorProvider, member, name, signature, |
|
12135 extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True, |
|
12136 visibility="public", jsObjectsArePtr=False, |
|
12137 variadicIsSequence=False): |
|
12138 CGNativeMember.__init__(self, descriptorProvider, member, name, |
|
12139 signature, extendedAttrs, breakAfter=breakAfter, |
|
12140 passJSBitsAsNeeded=passJSBitsAsNeeded, |
|
12141 visibility=visibility, |
|
12142 jsObjectsArePtr=jsObjectsArePtr, |
|
12143 variadicIsSequence=variadicIsSequence) |
|
12144 self.body = self.getImpl() |
|
12145 |
|
12146 def getArgs(self, returnType, argList): |
|
12147 args = CGNativeMember.getArgs(self, returnType, argList) |
|
12148 args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) |
|
12149 return args |
|
12150 |
|
12151 |
|
12152 class CGJSImplMethod(CGJSImplMember): |
|
12153 """ |
|
12154 Class for generating code for the methods for a JS-implemented WebIDL |
|
12155 interface. |
|
12156 """ |
|
12157 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): |
|
12158 self.signature = signature |
|
12159 self.descriptor = descriptor |
|
12160 self.isConstructor = isConstructor |
|
12161 CGJSImplMember.__init__(self, descriptor, method, |
|
12162 CGSpecializedMethod.makeNativeName(descriptor, |
|
12163 method), |
|
12164 signature, |
|
12165 descriptor.getExtendedAttributes(method), |
|
12166 breakAfter=breakAfter, |
|
12167 variadicIsSequence=True, |
|
12168 passJSBitsAsNeeded=False) |
|
12169 |
|
12170 def getArgs(self, returnType, argList): |
|
12171 if self.isConstructor: |
|
12172 # Skip the JSCompartment bits for constructors; it's handled |
|
12173 # manually in getImpl. |
|
12174 return CGNativeMember.getArgs(self, returnType, argList) |
|
12175 return CGJSImplMember.getArgs(self, returnType, argList) |
|
12176 |
|
12177 def getImpl(self): |
|
12178 args = self.getArgs(self.signature[0], self.signature[1]) |
|
12179 if not self.isConstructor: |
|
12180 return 'return mImpl->%s(%s);\n' % (self.name, ", ".join(arg.name for arg in args)) |
|
12181 |
|
12182 assert self.descriptor.interface.isJSImplemented() |
|
12183 if self.name != 'Constructor': |
|
12184 raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.") |
|
12185 if len(self.signature[1]) != 0: |
|
12186 # The first two arguments to the constructor implementation are not |
|
12187 # arguments to the WebIDL constructor, so don't pass them to __Init() |
|
12188 assert args[0].argType == 'const GlobalObject&' |
|
12189 assert args[1].argType == 'JSContext*' |
|
12190 constructorArgs = [arg.name for arg in args[2:]] |
|
12191 constructorArgs.append("js::GetObjectCompartment(scopeObj)") |
|
12192 initCall = fill( |
|
12193 """ |
|
12194 // Wrap the object before calling __Init so that __DOM_IMPL__ is available. |
|
12195 nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(window); |
|
12196 JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject()); |
|
12197 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx)); |
|
12198 JS::Rooted<JS::Value> wrappedVal(cx); |
|
12199 if (!WrapNewBindingObject(cx, impl, &wrappedVal)) { |
|
12200 //XXX Assertion disabled for now, see bug 991271. |
|
12201 MOZ_ASSERT(true || JS_IsExceptionPending(cx)); |
|
12202 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
12203 return nullptr; |
|
12204 } |
|
12205 // Initialize the object with the constructor arguments. |
|
12206 impl->mImpl->__Init(${args}); |
|
12207 if (aRv.Failed()) { |
|
12208 return nullptr; |
|
12209 } |
|
12210 """, |
|
12211 args=", ".join(constructorArgs)) |
|
12212 else: |
|
12213 initCall = "" |
|
12214 return genConstructorBody(self.descriptor, initCall) |
|
12215 |
|
12216 |
|
12217 def genConstructorBody(descriptor, initCall=""): |
|
12218 return fill( |
|
12219 """ |
|
12220 JS::Rooted<JSObject*> jsImplObj(cx); |
|
12221 nsCOMPtr<nsPIDOMWindow> window = |
|
12222 ConstructJSImplementation(cx, "${contractId}", global, &jsImplObj, aRv); |
|
12223 if (aRv.Failed()) { |
|
12224 return nullptr; |
|
12225 } |
|
12226 // Build the C++ implementation. |
|
12227 nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, window); |
|
12228 $*{initCall} |
|
12229 return impl.forget(); |
|
12230 """, |
|
12231 contractId=descriptor.interface.getJSImplementation(), |
|
12232 implClass=descriptor.name, |
|
12233 initCall=initCall) |
|
12234 |
|
12235 |
|
12236 # We're always fallible |
|
12237 def callbackGetterName(attr, descriptor): |
|
12238 return "Get" + MakeNativeName( |
|
12239 descriptor.binaryNames.get(attr.identifier.name, attr.identifier.name)) |
|
12240 |
|
12241 |
|
12242 def callbackSetterName(attr, descriptor): |
|
12243 return "Set" + MakeNativeName( |
|
12244 descriptor.binaryNames.get(attr.identifier.name, attr.identifier.name)) |
|
12245 |
|
12246 |
|
12247 class CGJSImplGetter(CGJSImplMember): |
|
12248 """ |
|
12249 Class for generating code for the getters of attributes for a JS-implemented |
|
12250 WebIDL interface. |
|
12251 """ |
|
12252 def __init__(self, descriptor, attr): |
|
12253 CGJSImplMember.__init__(self, descriptor, attr, |
|
12254 CGSpecializedGetter.makeNativeName(descriptor, |
|
12255 attr), |
|
12256 (attr.type, []), |
|
12257 descriptor.getExtendedAttributes(attr, |
|
12258 getter=True), |
|
12259 passJSBitsAsNeeded=False) |
|
12260 |
|
12261 def getImpl(self): |
|
12262 callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])] |
|
12263 return 'return mImpl->%s(%s);\n' % ( |
|
12264 callbackGetterName(self.member, self.descriptorProvider), |
|
12265 ", ".join(callbackArgs)) |
|
12266 |
|
12267 |
|
12268 class CGJSImplSetter(CGJSImplMember): |
|
12269 """ |
|
12270 Class for generating code for the setters of attributes for a JS-implemented |
|
12271 WebIDL interface. |
|
12272 """ |
|
12273 def __init__(self, descriptor, attr): |
|
12274 CGJSImplMember.__init__(self, descriptor, attr, |
|
12275 CGSpecializedSetter.makeNativeName(descriptor, |
|
12276 attr), |
|
12277 (BuiltinTypes[IDLBuiltinType.Types.void], |
|
12278 [FakeArgument(attr.type, attr)]), |
|
12279 descriptor.getExtendedAttributes(attr, |
|
12280 setter=True), |
|
12281 passJSBitsAsNeeded=False) |
|
12282 |
|
12283 def getImpl(self): |
|
12284 callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void], |
|
12285 [FakeArgument(self.member.type, self.member)])] |
|
12286 return 'mImpl->%s(%s);\n' % ( |
|
12287 callbackSetterName(self.member, self.descriptorProvider), |
|
12288 ", ".join(callbackArgs)) |
|
12289 |
|
12290 |
|
12291 class CGJSImplClass(CGBindingImplClass): |
|
12292 def __init__(self, descriptor): |
|
12293 CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter) |
|
12294 |
|
12295 if descriptor.interface.parent: |
|
12296 parentClass = descriptor.getDescriptor( |
|
12297 descriptor.interface.parent.identifier.name).jsImplParent |
|
12298 baseClasses = [ClassBase(parentClass)] |
|
12299 isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n" |
|
12300 ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" % |
|
12301 (descriptor.name, parentClass)) |
|
12302 constructorBody = dedent(""" |
|
12303 // Make sure we're an nsWrapperCache already |
|
12304 MOZ_ASSERT(static_cast<nsWrapperCache*>(this)); |
|
12305 // And that our ancestor has called SetIsDOMBinding() |
|
12306 MOZ_ASSERT(IsDOMBinding()); |
|
12307 """) |
|
12308 extradefinitions = fill( |
|
12309 """ |
|
12310 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent) |
|
12311 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass}) |
|
12312 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass}) |
|
12313 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${ifaceName}) |
|
12314 NS_INTERFACE_MAP_END_INHERITING(${parentClass}) |
|
12315 """, |
|
12316 ifaceName=self.descriptor.name, |
|
12317 parentClass=parentClass) |
|
12318 else: |
|
12319 baseClasses = [ClassBase("nsSupportsWeakReference"), |
|
12320 ClassBase("nsWrapperCache")] |
|
12321 isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n" |
|
12322 ccDecl = ("NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" % |
|
12323 descriptor.name) |
|
12324 constructorBody = "SetIsDOMBinding();\n" |
|
12325 extradefinitions = fill( |
|
12326 """ |
|
12327 NS_IMPL_CYCLE_COLLECTION_CLASS(${ifaceName}) |
|
12328 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName}) |
|
12329 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl) |
|
12330 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) |
|
12331 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
|
12332 tmp->ClearWeakReferences(); |
|
12333 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
12334 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName}) |
|
12335 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl) |
|
12336 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) |
|
12337 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
12338 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
12339 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(${ifaceName}) |
|
12340 NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName}) |
|
12341 NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName}) |
|
12342 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName}) |
|
12343 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
12344 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
12345 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) |
|
12346 NS_INTERFACE_MAP_END |
|
12347 """, |
|
12348 ifaceName=self.descriptor.name) |
|
12349 |
|
12350 extradeclarations = fill( |
|
12351 """ |
|
12352 public: |
|
12353 $*{isupportsDecl} |
|
12354 $*{ccDecl} |
|
12355 |
|
12356 private: |
|
12357 nsRefPtr<${jsImplName}> mImpl; |
|
12358 nsCOMPtr<nsISupports> mParent; |
|
12359 |
|
12360 """, |
|
12361 isupportsDecl=isupportsDecl, |
|
12362 ccDecl=ccDecl, |
|
12363 jsImplName=jsImplName(descriptor.name)) |
|
12364 |
|
12365 if descriptor.interface.hasChildInterfaces(): |
|
12366 decorators = "" |
|
12367 # We need a public virtual destructor our subclasses can use |
|
12368 destructor = ClassDestructor(virtual=True, visibility="public") |
|
12369 else: |
|
12370 decorators = "MOZ_FINAL" |
|
12371 destructor = None |
|
12372 |
|
12373 baseConstructors = [ |
|
12374 ("mImpl(new %s(aJSImplObject, /* aIncumbentGlobal = */ nullptr))" % |
|
12375 jsImplName(descriptor.name)), |
|
12376 "mParent(aParent)"] |
|
12377 parentInterface = descriptor.interface.parent |
|
12378 while parentInterface: |
|
12379 if parentInterface.isJSImplemented(): |
|
12380 baseConstructors.insert( |
|
12381 0, "%s(aJSImplObject, aParent)" % parentClass) |
|
12382 break |
|
12383 parentInterface = parentInterface.parent |
|
12384 if not parentInterface and descriptor.interface.parent: |
|
12385 # We only have C++ ancestors, so only pass along the window |
|
12386 baseConstructors.insert(0, |
|
12387 "%s(aParent)" % parentClass) |
|
12388 |
|
12389 constructor = ClassConstructor( |
|
12390 [Argument("JS::Handle<JSObject*>", "aJSImplObject"), |
|
12391 Argument("nsPIDOMWindow*", "aParent")], |
|
12392 visibility="public", |
|
12393 baseConstructors=baseConstructors, |
|
12394 body=constructorBody) |
|
12395 |
|
12396 self.methodDecls.append( |
|
12397 ClassMethod("_Create", |
|
12398 "bool", |
|
12399 [Argument("JSContext*", "cx"), |
|
12400 Argument("unsigned", "argc"), |
|
12401 Argument("JS::Value*", "vp")], |
|
12402 static=True, |
|
12403 body=self.getCreateFromExistingBody())) |
|
12404 |
|
12405 CGClass.__init__(self, descriptor.name, |
|
12406 bases=baseClasses, |
|
12407 constructors=[constructor], |
|
12408 destructor=destructor, |
|
12409 methods=self.methodDecls, |
|
12410 decorators=decorators, |
|
12411 extradeclarations=extradeclarations, |
|
12412 extradefinitions=extradefinitions) |
|
12413 |
|
12414 def getWrapObjectBody(self): |
|
12415 return fill( |
|
12416 """ |
|
12417 JS::Rooted<JSObject*> obj(aCx, ${name}Binding::Wrap(aCx, this)); |
|
12418 if (!obj) { |
|
12419 return nullptr; |
|
12420 } |
|
12421 |
|
12422 // Now define it on our chrome object |
|
12423 JSAutoCompartment ac(aCx, mImpl->Callback()); |
|
12424 if (!JS_WrapObject(aCx, &obj)) { |
|
12425 return nullptr; |
|
12426 } |
|
12427 if (!JS_DefineProperty(aCx, mImpl->Callback(), "__DOM_IMPL__", obj, 0)) { |
|
12428 return nullptr; |
|
12429 } |
|
12430 return obj; |
|
12431 """, |
|
12432 name=self.descriptor.name) |
|
12433 |
|
12434 def getGetParentObjectReturnType(self): |
|
12435 return "nsISupports*" |
|
12436 |
|
12437 def getGetParentObjectBody(self): |
|
12438 return "return mParent;\n" |
|
12439 |
|
12440 def getCreateFromExistingBody(self): |
|
12441 # XXXbz we could try to get parts of this (e.g. the argument |
|
12442 # conversions) auto-generated by somehow creating an IDLMethod and |
|
12443 # adding it to our interface, but we'd still need to special-case the |
|
12444 # implementation slightly to have it not try to forward to the JS |
|
12445 # object... |
|
12446 return fill( |
|
12447 """ |
|
12448 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
|
12449 if (args.length() < 2) { |
|
12450 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${ifaceName}._create"); |
|
12451 } |
|
12452 if (!args[0].isObject()) { |
|
12453 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 1 of ${ifaceName}._create"); |
|
12454 } |
|
12455 if (!args[1].isObject()) { |
|
12456 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 2 of ${ifaceName}._create"); |
|
12457 } |
|
12458 |
|
12459 // GlobalObject will go through wrappers as needed for us, and |
|
12460 // is simpler than the right UnwrapArg incantation. |
|
12461 GlobalObject global(cx, &args[0].toObject()); |
|
12462 if (global.Failed()) { |
|
12463 return false; |
|
12464 } |
|
12465 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.GetAsSupports()); |
|
12466 if (!window) { |
|
12467 return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "Argument 1 of ${ifaceName}._create", "Window"); |
|
12468 } |
|
12469 JS::Rooted<JSObject*> arg(cx, &args[1].toObject()); |
|
12470 nsRefPtr<${implName}> impl = new ${implName}(arg, window); |
|
12471 MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx)); |
|
12472 return WrapNewBindingObject(cx, impl, args.rval()); |
|
12473 """, |
|
12474 ifaceName=self.descriptor.interface.identifier.name, |
|
12475 implName=self.descriptor.name) |
|
12476 |
|
12477 |
|
12478 def isJSImplementedDescriptor(descriptorProvider): |
|
12479 return (isinstance(descriptorProvider, Descriptor) and |
|
12480 descriptorProvider.interface.isJSImplemented()) |
|
12481 |
|
12482 |
|
12483 class CGCallback(CGClass): |
|
12484 def __init__(self, idlObject, descriptorProvider, baseName, methods, |
|
12485 getters=[], setters=[]): |
|
12486 self.baseName = baseName |
|
12487 self._deps = idlObject.getDeps() |
|
12488 self.idlObject = idlObject |
|
12489 name = idlObject.identifier.name |
|
12490 if isJSImplementedDescriptor(descriptorProvider): |
|
12491 name = jsImplName(name) |
|
12492 # For our public methods that needThisHandling we want most of the |
|
12493 # same args and the same return type as what CallbackMember |
|
12494 # generates. So we want to take advantage of all its |
|
12495 # CGNativeMember infrastructure, but that infrastructure can't deal |
|
12496 # with templates and most especially template arguments. So just |
|
12497 # cheat and have CallbackMember compute all those things for us. |
|
12498 realMethods = [] |
|
12499 for method in methods: |
|
12500 if not method.needThisHandling: |
|
12501 realMethods.append(method) |
|
12502 else: |
|
12503 realMethods.extend(self.getMethodImpls(method)) |
|
12504 realMethods.append( |
|
12505 ClassMethod("operator==", "bool", |
|
12506 [Argument("const %s&" % name, "aOther")], |
|
12507 inline=True, bodyInHeader=True, |
|
12508 const=True, |
|
12509 body=("return %s::operator==(aOther);\n" % baseName))) |
|
12510 CGClass.__init__(self, name, |
|
12511 bases=[ClassBase(baseName)], |
|
12512 constructors=self.getConstructors(), |
|
12513 methods=realMethods+getters+setters) |
|
12514 |
|
12515 def getConstructors(self): |
|
12516 if (not self.idlObject.isInterface() and |
|
12517 not self.idlObject._treatNonObjectAsNull): |
|
12518 body = "MOZ_ASSERT(JS_ObjectIsCallable(nullptr, mCallback));\n" |
|
12519 else: |
|
12520 # Not much we can assert about it, other than not being null, and |
|
12521 # CallbackObject does that already. |
|
12522 body = "" |
|
12523 return [ClassConstructor( |
|
12524 [Argument("JS::Handle<JSObject*>", "aCallback"), |
|
12525 Argument("nsIGlobalObject*", "aIncumbentGlobal")], |
|
12526 bodyInHeader=True, |
|
12527 visibility="public", |
|
12528 explicit=True, |
|
12529 baseConstructors=[ |
|
12530 "%s(aCallback, aIncumbentGlobal)" % self.baseName, |
|
12531 ], |
|
12532 body=body)] |
|
12533 |
|
12534 def getMethodImpls(self, method): |
|
12535 assert method.needThisHandling |
|
12536 args = list(method.args) |
|
12537 # Strip out the JSContext*/JSObject* args |
|
12538 # that got added. |
|
12539 assert args[0].name == "cx" and args[0].argType == "JSContext*" |
|
12540 assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>" |
|
12541 args = args[2:] |
|
12542 # Record the names of all the arguments, so we can use them when we call |
|
12543 # the private method. |
|
12544 argnames = [arg.name for arg in args] |
|
12545 argnamesWithThis = ["s.GetContext()", "thisValJS"] + argnames |
|
12546 argnamesWithoutThis = ["s.GetContext()", "JS::UndefinedHandleValue"] + argnames |
|
12547 # Now that we've recorded the argnames for our call to our private |
|
12548 # method, insert our optional argument for deciding whether the |
|
12549 # CallSetup should re-throw exceptions on aRv. |
|
12550 args.append(Argument("ExceptionHandling", "aExceptionHandling", |
|
12551 "eReportExceptions")) |
|
12552 # And now insert our template argument. |
|
12553 argsWithoutThis = list(args) |
|
12554 args.insert(0, Argument("const T&", "thisObjPtr")) |
|
12555 errorReturn = method.getDefaultRetval() |
|
12556 |
|
12557 setupCall = fill( |
|
12558 """ |
|
12559 CallSetup s(this, aRv, aExceptionHandling); |
|
12560 if (!s.GetContext()) { |
|
12561 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
12562 return${errorReturn}; |
|
12563 } |
|
12564 """, |
|
12565 errorReturn=errorReturn) |
|
12566 |
|
12567 bodyWithThis = fill( |
|
12568 """ |
|
12569 $*{setupCall} |
|
12570 JS::Rooted<JSObject*> thisObjJS(s.GetContext(), |
|
12571 WrapCallThisObject(s.GetContext(), thisObjPtr)); |
|
12572 if (!thisObjJS) { |
|
12573 aRv.Throw(NS_ERROR_FAILURE); |
|
12574 return${errorReturn}; |
|
12575 } |
|
12576 JS::Rooted<JS::Value> thisValJS(s.GetContext(), |
|
12577 JS::ObjectValue(*thisObjJS)); |
|
12578 return ${methodName}(${callArgs}); |
|
12579 """, |
|
12580 setupCall=setupCall, |
|
12581 errorReturn=errorReturn, |
|
12582 methodName=method.name, |
|
12583 callArgs=", ".join(argnamesWithThis)) |
|
12584 bodyWithoutThis = fill( |
|
12585 """ |
|
12586 $*{setupCall} |
|
12587 return ${methodName}(${callArgs}); |
|
12588 """, |
|
12589 setupCall=setupCall, |
|
12590 errorReturn=errorReturn, |
|
12591 methodName=method.name, |
|
12592 callArgs=", ".join(argnamesWithoutThis)) |
|
12593 |
|
12594 return [ClassMethod(method.name, method.returnType, args, |
|
12595 bodyInHeader=True, |
|
12596 templateArgs=["typename T"], |
|
12597 body=bodyWithThis), |
|
12598 ClassMethod(method.name, method.returnType, argsWithoutThis, |
|
12599 bodyInHeader=True, |
|
12600 body=bodyWithoutThis), |
|
12601 method] |
|
12602 |
|
12603 def deps(self): |
|
12604 return self._deps |
|
12605 |
|
12606 |
|
12607 class CGCallbackFunction(CGCallback): |
|
12608 def __init__(self, callback, descriptorProvider): |
|
12609 self.callback = callback |
|
12610 CGCallback.__init__(self, callback, descriptorProvider, |
|
12611 "CallbackFunction", |
|
12612 methods=[CallCallback(callback, descriptorProvider)]) |
|
12613 |
|
12614 def getConstructors(self): |
|
12615 return CGCallback.getConstructors(self) + [ |
|
12616 ClassConstructor( |
|
12617 [Argument("CallbackFunction*", "aOther")], |
|
12618 bodyInHeader=True, |
|
12619 visibility="public", |
|
12620 explicit=True, |
|
12621 baseConstructors=["CallbackFunction(aOther)"])] |
|
12622 |
|
12623 |
|
12624 class CGCallbackInterface(CGCallback): |
|
12625 def __init__(self, descriptor): |
|
12626 iface = descriptor.interface |
|
12627 attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()] |
|
12628 getters = [CallbackGetter(a, descriptor) for a in attrs] |
|
12629 setters = [CallbackSetter(a, descriptor) for a in attrs |
|
12630 if not a.readonly] |
|
12631 methods = [m for m in iface.members |
|
12632 if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()] |
|
12633 methods = [CallbackOperation(m, sig, descriptor) for m in methods |
|
12634 for sig in m.signatures()] |
|
12635 if iface.isJSImplemented() and iface.ctor(): |
|
12636 sigs = descriptor.interface.ctor().signatures() |
|
12637 if len(sigs) != 1: |
|
12638 raise TypeError("We only handle one constructor. See bug 869268.") |
|
12639 methods.append(CGJSImplInitOperation(sigs[0], descriptor)) |
|
12640 CGCallback.__init__(self, iface, descriptor, "CallbackInterface", |
|
12641 methods, getters=getters, setters=setters) |
|
12642 |
|
12643 |
|
12644 class FakeMember(): |
|
12645 def __init__(self): |
|
12646 self.treatNullAs = "Default" |
|
12647 |
|
12648 def isStatic(self): |
|
12649 return False |
|
12650 |
|
12651 def isAttr(self): |
|
12652 return False |
|
12653 |
|
12654 def isMethod(self): |
|
12655 return False |
|
12656 |
|
12657 def getExtendedAttribute(self, name): |
|
12658 # Claim to be a [NewObject] so we can avoid the "mark this |
|
12659 # resultNotAddRefed" comments CGNativeMember codegen would |
|
12660 # otherwise stick in. |
|
12661 if name == "NewObject": |
|
12662 return True |
|
12663 return None |
|
12664 |
|
12665 |
|
12666 class CallbackMember(CGNativeMember): |
|
12667 def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False): |
|
12668 """ |
|
12669 needThisHandling is True if we need to be able to accept a specified |
|
12670 thisObj, False otherwise. |
|
12671 """ |
|
12672 assert not rethrowContentException or not needThisHandling |
|
12673 |
|
12674 self.retvalType = sig[0] |
|
12675 self.originalSig = sig |
|
12676 args = sig[1] |
|
12677 self.argCount = len(args) |
|
12678 if self.argCount > 0: |
|
12679 # Check for variadic arguments |
|
12680 lastArg = args[self.argCount-1] |
|
12681 if lastArg.variadic: |
|
12682 self.argCountStr = ("(%d - 1) + %s.Length()" % |
|
12683 (self.argCount, lastArg.identifier.name)) |
|
12684 else: |
|
12685 self.argCountStr = "%d" % self.argCount |
|
12686 self.needThisHandling = needThisHandling |
|
12687 # If needThisHandling, we generate ourselves as private and the caller |
|
12688 # will handle generating public versions that handle the "this" stuff. |
|
12689 visibility = "private" if needThisHandling else "public" |
|
12690 self.rethrowContentException = rethrowContentException |
|
12691 # We don't care, for callback codegen, whether our original member was |
|
12692 # a method or attribute or whatnot. Just always pass FakeMember() |
|
12693 # here. |
|
12694 CGNativeMember.__init__(self, descriptorProvider, FakeMember(), |
|
12695 name, (self.retvalType, args), |
|
12696 extendedAttrs={}, |
|
12697 passJSBitsAsNeeded=False, |
|
12698 visibility=visibility, |
|
12699 jsObjectsArePtr=True) |
|
12700 # We have to do all the generation of our body now, because |
|
12701 # the caller relies on us throwing if we can't manage it. |
|
12702 self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n" |
|
12703 "return%s;\n" % self.getDefaultRetval()) |
|
12704 self.body = self.getImpl() |
|
12705 |
|
12706 def getImpl(self): |
|
12707 setupCall = self.getCallSetup() |
|
12708 declRval = self.getRvalDecl() |
|
12709 if self.argCount > 0: |
|
12710 argvDecl = fill( |
|
12711 """ |
|
12712 JS::AutoValueVector argv(cx); |
|
12713 if (!argv.resize(${argCount})) { |
|
12714 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
12715 return${errorReturn}; |
|
12716 } |
|
12717 """, |
|
12718 argCount=self.argCountStr, |
|
12719 errorReturn=self.getDefaultRetval()) |
|
12720 else: |
|
12721 # Avoid weird 0-sized arrays |
|
12722 argvDecl = "" |
|
12723 convertArgs = self.getArgConversions() |
|
12724 doCall = self.getCall() |
|
12725 returnResult = self.getResultConversion() |
|
12726 |
|
12727 return setupCall + declRval + argvDecl + convertArgs + doCall + returnResult |
|
12728 |
|
12729 def getResultConversion(self): |
|
12730 replacements = { |
|
12731 "val": "rval", |
|
12732 "mutableVal": "&rval", |
|
12733 "holderName": "rvalHolder", |
|
12734 "declName": "rvalDecl", |
|
12735 # We actually want to pass in a null scope object here, because |
|
12736 # wrapping things into our current compartment (that of mCallback) |
|
12737 # is what we want. |
|
12738 "obj": "nullptr" |
|
12739 } |
|
12740 |
|
12741 if isJSImplementedDescriptor(self.descriptorProvider): |
|
12742 isCallbackReturnValue = "JSImpl" |
|
12743 else: |
|
12744 isCallbackReturnValue = "Callback" |
|
12745 sourceDescription = "return value of %s" % self.getPrettyName() |
|
12746 convertType = instantiateJSToNativeConversion( |
|
12747 getJSToNativeConversionInfo(self.retvalType, |
|
12748 self.descriptorProvider, |
|
12749 exceptionCode=self.exceptionCode, |
|
12750 isCallbackReturnValue=isCallbackReturnValue, |
|
12751 sourceDescription=sourceDescription), |
|
12752 replacements) |
|
12753 assignRetval = string.Template( |
|
12754 self.getRetvalInfo(self.retvalType, |
|
12755 False)[2]).substitute(replacements) |
|
12756 type = convertType.define() |
|
12757 if type == "": |
|
12758 type = "\n" # BOGUS extra blank line |
|
12759 if assignRetval == "": |
|
12760 assignRetval = "\n" # BOGUS extra blank line |
|
12761 return type + assignRetval |
|
12762 |
|
12763 def getArgConversions(self): |
|
12764 # Just reget the arglist from self.originalSig, because our superclasses |
|
12765 # just have way to many members they like to clobber, so I can't find a |
|
12766 # safe member name to store it in. |
|
12767 argConversions = [self.getArgConversion(i, arg) |
|
12768 for i, arg in enumerate(self.originalSig[1])] |
|
12769 if not argConversions: |
|
12770 return "\n\n" # BOGUS extra blank line |
|
12771 |
|
12772 # Do them back to front, so our argc modifications will work |
|
12773 # correctly, because we examine trailing arguments first. |
|
12774 argConversions.reverse() |
|
12775 # Wrap each one in a scope so that any locals it has don't leak out, and |
|
12776 # also so that we can just "break;" for our successCode. |
|
12777 argConversions = [CGWrapper(CGIndenter(CGGeneric(c)), |
|
12778 pre="do {\n", |
|
12779 post="} while (0);\n") |
|
12780 for c in argConversions] |
|
12781 if self.argCount > 0: |
|
12782 argConversions.insert(0, self.getArgcDecl()) |
|
12783 # And slap them together. |
|
12784 return CGList(argConversions, "\n").define() + "\n" |
|
12785 |
|
12786 def getArgConversion(self, i, arg): |
|
12787 argval = arg.identifier.name |
|
12788 |
|
12789 if arg.variadic: |
|
12790 argval = argval + "[idx]" |
|
12791 jsvalIndex = "%d + idx" % i |
|
12792 else: |
|
12793 jsvalIndex = "%d" % i |
|
12794 if arg.optional and not arg.defaultValue: |
|
12795 argval += ".Value()" |
|
12796 if arg.type.isDOMString(): |
|
12797 # XPConnect string-to-JS conversion wants to mutate the string. So |
|
12798 # let's give it a string it can mutate |
|
12799 # XXXbz if we try to do a sequence of strings, this will kinda fail. |
|
12800 result = "mutableStr" |
|
12801 prepend = "nsString mutableStr(%s);\n" % argval |
|
12802 else: |
|
12803 result = argval |
|
12804 prepend = "" |
|
12805 |
|
12806 try: |
|
12807 conversion = prepend + wrapForType( |
|
12808 arg.type, self.descriptorProvider, |
|
12809 { |
|
12810 'result': result, |
|
12811 'successCode': "continue;\n" if arg.variadic else "break;\n", |
|
12812 'jsvalRef': "argv.handleAt(%s)" % jsvalIndex, |
|
12813 'jsvalHandle': "argv.handleAt(%s)" % jsvalIndex, |
|
12814 # XXXbz we don't have anything better to use for 'obj', |
|
12815 # really... It's OK to use CallbackPreserveColor because |
|
12816 # CallSetup already handled the unmark-gray bits for us. |
|
12817 'obj': 'CallbackPreserveColor()', |
|
12818 'returnsNewObject': False, |
|
12819 'exceptionCode': self.exceptionCode |
|
12820 }) |
|
12821 except MethodNotNewObjectError as err: |
|
12822 raise TypeError("%s being passed as an argument to %s but is not " |
|
12823 "wrapper cached, so can't be reliably converted to " |
|
12824 "a JS object." % |
|
12825 (err.typename, self.getPrettyName())) |
|
12826 if arg.variadic: |
|
12827 conversion = fill( |
|
12828 """ |
|
12829 for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) { |
|
12830 $*{conversion} |
|
12831 } |
|
12832 break; |
|
12833 """, |
|
12834 arg=arg.identifier.name, |
|
12835 conversion=conversion) |
|
12836 elif arg.optional and not arg.defaultValue: |
|
12837 conversion = fill( |
|
12838 """ |
|
12839 if (${argName}.WasPassed()) { |
|
12840 $*{conversion} |
|
12841 } else if (argc == ${iPlus1}) { |
|
12842 // This is our current trailing argument; reduce argc |
|
12843 --argc; |
|
12844 } else { |
|
12845 argv[${i}] = JS::UndefinedValue(); |
|
12846 } |
|
12847 """, |
|
12848 argName=arg.identifier.name, |
|
12849 conversion=conversion, |
|
12850 iPlus1=i + 1, |
|
12851 i=i) |
|
12852 return conversion |
|
12853 |
|
12854 def getDefaultRetval(self): |
|
12855 default = self.getRetvalInfo(self.retvalType, False)[1] |
|
12856 if len(default) != 0: |
|
12857 default = " " + default |
|
12858 return default |
|
12859 |
|
12860 def getArgs(self, returnType, argList): |
|
12861 args = CGNativeMember.getArgs(self, returnType, argList) |
|
12862 if not self.needThisHandling: |
|
12863 # Since we don't need this handling, we're the actual method that |
|
12864 # will be called, so we need an aRethrowExceptions argument. |
|
12865 if self.rethrowContentException: |
|
12866 args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) |
|
12867 else: |
|
12868 args.append(Argument("ExceptionHandling", "aExceptionHandling", |
|
12869 "eReportExceptions")) |
|
12870 return args |
|
12871 # We want to allow the caller to pass in a "this" value, as |
|
12872 # well as a JSContext. |
|
12873 return [Argument("JSContext*", "cx"), |
|
12874 Argument("JS::Handle<JS::Value>", "aThisVal")] + args |
|
12875 |
|
12876 def getCallSetup(self): |
|
12877 if self.needThisHandling: |
|
12878 # It's been done for us already |
|
12879 return "" |
|
12880 callSetup = "CallSetup s(this, aRv" |
|
12881 if self.rethrowContentException: |
|
12882 # getArgs doesn't add the aExceptionHandling argument but does add |
|
12883 # aCompartment for us. |
|
12884 callSetup += ", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ " |
|
12885 callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider)) |
|
12886 else: |
|
12887 callSetup += ", aExceptionHandling" |
|
12888 callSetup += ");\n" |
|
12889 return fill( |
|
12890 """ |
|
12891 $*{callSetup} |
|
12892 JSContext* cx = s.GetContext(); |
|
12893 if (!cx) { |
|
12894 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
12895 return${errorReturn}; |
|
12896 } |
|
12897 """, |
|
12898 callSetup=callSetup, |
|
12899 errorReturn=self.getDefaultRetval()) |
|
12900 |
|
12901 def getArgcDecl(self): |
|
12902 return CGGeneric("unsigned argc = %s;\n" % self.argCountStr) |
|
12903 |
|
12904 @staticmethod |
|
12905 def ensureASCIIName(idlObject): |
|
12906 type = "attribute" if idlObject.isAttr() else "operation" |
|
12907 if re.match("[^\x20-\x7E]", idlObject.identifier.name): |
|
12908 raise SyntaxError('Callback %s name "%s" contains non-ASCII ' |
|
12909 "characters. We can't handle that. %s" % |
|
12910 (type, idlObject.identifier.name, |
|
12911 idlObject.location)) |
|
12912 if re.match('"', idlObject.identifier.name): |
|
12913 raise SyntaxError("Callback %s name '%s' contains " |
|
12914 "double-quote character. We can't handle " |
|
12915 "that. %s" % |
|
12916 (type, idlObject.identifier.name, |
|
12917 idlObject.location)) |
|
12918 |
|
12919 |
|
12920 class CallbackMethod(CallbackMember): |
|
12921 def __init__(self, sig, name, descriptorProvider, needThisHandling, |
|
12922 rethrowContentException=False): |
|
12923 CallbackMember.__init__(self, sig, name, descriptorProvider, |
|
12924 needThisHandling, rethrowContentException) |
|
12925 |
|
12926 def getRvalDecl(self): |
|
12927 return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" |
|
12928 |
|
12929 def getCall(self): |
|
12930 if self.argCount > 0: |
|
12931 args = "JS::HandleValueArray::subarray(argv, 0, argc)" |
|
12932 else: |
|
12933 args = "JS::HandleValueArray::empty()" |
|
12934 |
|
12935 return fill( |
|
12936 """ |
|
12937 $*{declCallable} |
|
12938 $*{declThis} |
|
12939 if (${callGuard}!JS::Call(cx, ${thisVal}, callable, |
|
12940 ${args}, &rval)) { |
|
12941 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
12942 return${errorReturn}; |
|
12943 } |
|
12944 """, |
|
12945 declCallable=self.getCallableDecl(), |
|
12946 declThis=self.getThisDecl(), |
|
12947 callGuard=self.getCallGuard(), |
|
12948 thisVal=self.getThisVal(), |
|
12949 args=args, |
|
12950 errorReturn=self.getDefaultRetval()) |
|
12951 |
|
12952 |
|
12953 class CallCallback(CallbackMethod): |
|
12954 def __init__(self, callback, descriptorProvider): |
|
12955 self.callback = callback |
|
12956 CallbackMethod.__init__(self, callback.signatures()[0], "Call", |
|
12957 descriptorProvider, needThisHandling=True) |
|
12958 |
|
12959 def getThisDecl(self): |
|
12960 return "" |
|
12961 |
|
12962 def getThisVal(self): |
|
12963 return "aThisVal" |
|
12964 |
|
12965 def getCallableDecl(self): |
|
12966 return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n" |
|
12967 |
|
12968 def getPrettyName(self): |
|
12969 return self.callback.identifier.name |
|
12970 |
|
12971 def getCallGuard(self): |
|
12972 if self.callback._treatNonObjectAsNull: |
|
12973 return "JS_ObjectIsCallable(cx, mCallback) && " |
|
12974 return "" |
|
12975 |
|
12976 |
|
12977 class CallbackOperationBase(CallbackMethod): |
|
12978 """ |
|
12979 Common class for implementing various callback operations. |
|
12980 """ |
|
12981 def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False): |
|
12982 self.singleOperation = singleOperation |
|
12983 self.methodName = descriptor.binaryNames.get(jsName, jsName) |
|
12984 CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException) |
|
12985 |
|
12986 def getThisDecl(self): |
|
12987 if not self.singleOperation: |
|
12988 return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n" |
|
12989 # This relies on getCallableDecl declaring a boolean |
|
12990 # isCallable in the case when we're a single-operation |
|
12991 # interface. |
|
12992 return dedent(""" |
|
12993 JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get() |
|
12994 : JS::ObjectValue(*mCallback)); |
|
12995 """) |
|
12996 |
|
12997 def getThisVal(self): |
|
12998 return "thisValue" |
|
12999 |
|
13000 def getCallableDecl(self): |
|
13001 getCallableFromProp = fill( |
|
13002 """ |
|
13003 if (!GetCallableProperty(cx, "${methodName}", &callable)) { |
|
13004 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
13005 return${errorReturn}; |
|
13006 } |
|
13007 """, |
|
13008 methodName=self.methodName, |
|
13009 errorReturn=self.getDefaultRetval()) |
|
13010 if not self.singleOperation: |
|
13011 return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp |
|
13012 return fill( |
|
13013 """ |
|
13014 bool isCallable = JS_ObjectIsCallable(cx, mCallback); |
|
13015 JS::Rooted<JS::Value> callable(cx); |
|
13016 if (isCallable) { |
|
13017 callable = JS::ObjectValue(*mCallback); |
|
13018 } else { |
|
13019 $*{getCallableFromProp} |
|
13020 } |
|
13021 """, |
|
13022 getCallableFromProp=getCallableFromProp) |
|
13023 |
|
13024 def getCallGuard(self): |
|
13025 return "" |
|
13026 |
|
13027 |
|
13028 class CallbackOperation(CallbackOperationBase): |
|
13029 """ |
|
13030 Codegen actual WebIDL operations on callback interfaces. |
|
13031 """ |
|
13032 def __init__(self, method, signature, descriptor): |
|
13033 self.ensureASCIIName(method) |
|
13034 self.method = method |
|
13035 jsName = method.identifier.name |
|
13036 CallbackOperationBase.__init__(self, signature, |
|
13037 jsName, |
|
13038 MakeNativeName(descriptor.binaryNames.get(jsName, jsName)), |
|
13039 descriptor, descriptor.interface.isSingleOperationInterface(), |
|
13040 rethrowContentException=descriptor.interface.isJSImplemented()) |
|
13041 |
|
13042 def getPrettyName(self): |
|
13043 return "%s.%s" % (self.descriptorProvider.interface.identifier.name, |
|
13044 self.method.identifier.name) |
|
13045 |
|
13046 |
|
13047 class CallbackAccessor(CallbackMember): |
|
13048 """ |
|
13049 Shared superclass for CallbackGetter and CallbackSetter. |
|
13050 """ |
|
13051 def __init__(self, attr, sig, name, descriptor): |
|
13052 self.ensureASCIIName(attr) |
|
13053 self.attrName = attr.identifier.name |
|
13054 CallbackMember.__init__(self, sig, name, descriptor, |
|
13055 needThisHandling=False, |
|
13056 rethrowContentException=descriptor.interface.isJSImplemented()) |
|
13057 |
|
13058 def getPrettyName(self): |
|
13059 return "%s.%s" % (self.descriptorProvider.interface.identifier.name, |
|
13060 self.attrName) |
|
13061 |
|
13062 |
|
13063 class CallbackGetter(CallbackAccessor): |
|
13064 def __init__(self, attr, descriptor): |
|
13065 CallbackAccessor.__init__(self, attr, |
|
13066 (attr.type, []), |
|
13067 callbackGetterName(attr, descriptor), |
|
13068 descriptor) |
|
13069 |
|
13070 def getRvalDecl(self): |
|
13071 return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" |
|
13072 |
|
13073 def getCall(self): |
|
13074 return fill( |
|
13075 """ |
|
13076 JS::Rooted<JSObject *> callback(cx, mCallback); |
|
13077 if (!JS_GetProperty(cx, callback, "${attrName}", &rval)) { |
|
13078 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
13079 return${errorReturn}; |
|
13080 } |
|
13081 """, |
|
13082 attrName=self.descriptorProvider.binaryNames.get(self.attrName, |
|
13083 self.attrName), |
|
13084 errorReturn=self.getDefaultRetval()) |
|
13085 |
|
13086 |
|
13087 class CallbackSetter(CallbackAccessor): |
|
13088 def __init__(self, attr, descriptor): |
|
13089 CallbackAccessor.__init__(self, attr, |
|
13090 (BuiltinTypes[IDLBuiltinType.Types.void], |
|
13091 [FakeArgument(attr.type, attr)]), |
|
13092 callbackSetterName(attr, descriptor), |
|
13093 descriptor) |
|
13094 |
|
13095 def getRvalDecl(self): |
|
13096 # We don't need an rval |
|
13097 return "" |
|
13098 |
|
13099 def getCall(self): |
|
13100 return fill( |
|
13101 """ |
|
13102 MOZ_ASSERT(argv.length() == 1); |
|
13103 if (!JS_SetProperty(cx, CallbackPreserveColor(), "${attrName}", argv.handleAt(0))) { |
|
13104 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
13105 return${errorReturn}; |
|
13106 } |
|
13107 """, |
|
13108 attrName=self.descriptorProvider.binaryNames.get(self.attrName, |
|
13109 self.attrName), |
|
13110 errorReturn=self.getDefaultRetval()) |
|
13111 |
|
13112 def getArgcDecl(self): |
|
13113 return None |
|
13114 |
|
13115 |
|
13116 class CGJSImplInitOperation(CallbackOperationBase): |
|
13117 """ |
|
13118 Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL. |
|
13119 """ |
|
13120 def __init__(self, sig, descriptor): |
|
13121 assert sig in descriptor.interface.ctor().signatures() |
|
13122 CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]), |
|
13123 "__init", "__Init", descriptor, False, True) |
|
13124 |
|
13125 def getPrettyName(self): |
|
13126 return "__init" |
|
13127 |
|
13128 |
|
13129 class GlobalGenRoots(): |
|
13130 """ |
|
13131 Roots for global codegen. |
|
13132 |
|
13133 To generate code, call the method associated with the target, and then |
|
13134 call the appropriate define/declare method. |
|
13135 """ |
|
13136 |
|
13137 @staticmethod |
|
13138 def GeneratedAtomList(config): |
|
13139 # Atom enum |
|
13140 dictionaries = config.dictionaries |
|
13141 |
|
13142 structs = [] |
|
13143 |
|
13144 for dict in dictionaries: |
|
13145 dictMembers = dict.members |
|
13146 if len(dictMembers) == 0: |
|
13147 continue |
|
13148 |
|
13149 classMembers = [ClassMember(CGDictionary.makeIdName(m.identifier.name), |
|
13150 "InternedStringId", |
|
13151 visibility="public") for m in dictMembers] |
|
13152 |
|
13153 structName = dict.identifier.name + "Atoms" |
|
13154 structs.append((structName, |
|
13155 CGWrapper(CGClass(structName, |
|
13156 bases=None, |
|
13157 isStruct=True, |
|
13158 members=classMembers), post='\n'))) |
|
13159 |
|
13160 structs.sort() |
|
13161 generatedStructs = [struct for structName, struct in structs] |
|
13162 structNames = [structName for structName, struct in structs] |
|
13163 |
|
13164 mainStruct = CGWrapper(CGClass("PerThreadAtomCache", |
|
13165 bases=[ClassBase(structName) for structName in structNames], |
|
13166 isStruct=True), |
|
13167 post='\n') |
|
13168 |
|
13169 structs = CGList(generatedStructs + [mainStruct]) |
|
13170 |
|
13171 # Wrap all of that in our namespaces. |
|
13172 curr = CGNamespace.build(['mozilla', 'dom'], |
|
13173 CGWrapper(structs, pre='\n')) |
|
13174 curr = CGWrapper(curr, post='\n') |
|
13175 |
|
13176 # Add include statement for InternedStringId. |
|
13177 declareIncludes = ['mozilla/dom/BindingUtils.h'] |
|
13178 curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList', |
|
13179 curr) |
|
13180 |
|
13181 # Add include guards. |
|
13182 curr = CGIncludeGuard('GeneratedAtomList', curr) |
|
13183 |
|
13184 # Add the auto-generated comment. |
|
13185 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) |
|
13186 |
|
13187 # Done. |
|
13188 return curr |
|
13189 |
|
13190 @staticmethod |
|
13191 def PrototypeList(config): |
|
13192 |
|
13193 # Prototype ID enum. |
|
13194 descriptorsWithPrototype = config.getDescriptors(hasInterfacePrototypeObject=True) |
|
13195 protos = [d.name for d in descriptorsWithPrototype] |
|
13196 idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + protos, |
|
13197 [0, '_ID_Start']) |
|
13198 idEnum = CGList([idEnum]) |
|
13199 |
|
13200 # This is only used by DOM worker code, once there are no more consumers |
|
13201 # of INTERFACE_CHAIN_* this code should be removed. |
|
13202 def ifaceChainMacro(ifaceCount): |
|
13203 supplied = [CGGeneric(declare="_iface_" + str(i + 1)) for i in range(ifaceCount)] |
|
13204 remaining = [CGGeneric(declare="prototypes::id::_ID_Count")] * (config.maxProtoChainLength - ifaceCount) |
|
13205 macro = CGWrapper(CGList(supplied, ", "), |
|
13206 pre="#define INTERFACE_CHAIN_" + str(ifaceCount) + "(", |
|
13207 post=") \\\n", |
|
13208 declareOnly=True) |
|
13209 macroContent = CGIndenter(CGList(supplied + remaining, ", \\\n")) |
|
13210 macroContent = CGIndenter(CGWrapper(macroContent, pre="{ \\\n", |
|
13211 post=" \\\n}", |
|
13212 declareOnly=True)) |
|
13213 return CGWrapper(CGList([macro, macroContent]), post="\n\n", |
|
13214 declareOnly=True) |
|
13215 |
|
13216 idEnum.append(ifaceChainMacro(1)) |
|
13217 |
|
13218 def fieldSizeAssert(amount, jitInfoField, message): |
|
13219 maxFieldValue = "(uint64_t(1) << (sizeof(((JSJitInfo*)nullptr)->%s) * 8))" % jitInfoField |
|
13220 return CGGeneric(declare="static_assert(%s < %s, \"%s\");\n\n" |
|
13221 % (amount, maxFieldValue, message)) |
|
13222 |
|
13223 idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID", |
|
13224 "Too many prototypes!")) |
|
13225 |
|
13226 # Wrap all of that in our namespaces. |
|
13227 idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'], |
|
13228 CGWrapper(idEnum, pre='\n')) |
|
13229 idEnum = CGWrapper(idEnum, post='\n') |
|
13230 |
|
13231 curr = CGList([CGGeneric(define="#include <stdint.h>\n\n"), |
|
13232 idEnum]) |
|
13233 |
|
13234 # Let things know the maximum length of the prototype chain. |
|
13235 maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH" |
|
13236 maxMacro = CGGeneric(declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength)) |
|
13237 curr.append(CGWrapper(maxMacro, post='\n\n')) |
|
13238 curr.append(fieldSizeAssert(maxMacroName, "depth", |
|
13239 "Some inheritance chain is too long!")) |
|
13240 |
|
13241 # Constructor ID enum. |
|
13242 constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)] |
|
13243 idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors, |
|
13244 ['prototypes::id::_ID_Count', '_ID_Start']) |
|
13245 |
|
13246 # Wrap all of that in our namespaces. |
|
13247 idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'], |
|
13248 CGWrapper(idEnum, pre='\n')) |
|
13249 idEnum = CGWrapper(idEnum, post='\n') |
|
13250 |
|
13251 curr.append(idEnum) |
|
13252 |
|
13253 traitsDecls = [CGGeneric(declare=dedent(""" |
|
13254 template <prototypes::ID PrototypeID> |
|
13255 struct PrototypeTraits; |
|
13256 """))] |
|
13257 traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype) |
|
13258 |
|
13259 ifaceNamesWithProto = [d.interface.identifier.name |
|
13260 for d in descriptorsWithPrototype] |
|
13261 traitsDecls.append(CGStringTable("NamesOfInterfacesWithProtos", |
|
13262 ifaceNamesWithProto)) |
|
13263 |
|
13264 traitsDecl = CGNamespace.build(['mozilla', 'dom'], |
|
13265 CGList(traitsDecls)) |
|
13266 |
|
13267 curr.append(traitsDecl) |
|
13268 |
|
13269 # Add include guards. |
|
13270 curr = CGIncludeGuard('PrototypeList', curr) |
|
13271 |
|
13272 # Add the auto-generated comment. |
|
13273 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) |
|
13274 |
|
13275 # Done. |
|
13276 return curr |
|
13277 |
|
13278 @staticmethod |
|
13279 def RegisterBindings(config): |
|
13280 |
|
13281 # TODO - Generate the methods we want |
|
13282 curr = CGRegisterProtos(config) |
|
13283 |
|
13284 # Wrap all of that in our namespaces. |
|
13285 curr = CGNamespace.build(['mozilla', 'dom'], |
|
13286 CGWrapper(curr, post='\n')) |
|
13287 curr = CGWrapper(curr, post='\n') |
|
13288 |
|
13289 # Add the includes |
|
13290 defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface) |
|
13291 for desc in config.getDescriptors(hasInterfaceObject=True, |
|
13292 workers=False, |
|
13293 register=True)] |
|
13294 defineIncludes.append('nsScriptNameSpaceManager.h') |
|
13295 defineIncludes.extend([CGHeaders.getDeclarationFilename(desc.interface) |
|
13296 for desc in config.getDescriptors(isNavigatorProperty=True, |
|
13297 workers=False, |
|
13298 register=True)]) |
|
13299 curr = CGHeaders([], [], [], [], [], defineIncludes, 'RegisterBindings', |
|
13300 curr) |
|
13301 |
|
13302 # Add include guards. |
|
13303 curr = CGIncludeGuard('RegisterBindings', curr) |
|
13304 |
|
13305 # Done. |
|
13306 return curr |
|
13307 |
|
13308 @staticmethod |
|
13309 def UnionTypes(config): |
|
13310 |
|
13311 (includes, implincludes, |
|
13312 declarations, unions) = UnionTypes(config.getDescriptors(), |
|
13313 config.getDictionaries(), |
|
13314 config.getCallbacks(), |
|
13315 config) |
|
13316 includes.add("mozilla/dom/OwningNonNull.h") |
|
13317 includes.add("mozilla/dom/UnionMember.h") |
|
13318 includes.add("mozilla/dom/BindingDeclarations.h") |
|
13319 # Need BindingUtils.h for FakeDependentString |
|
13320 includes.add("mozilla/dom/BindingUtils.h") |
|
13321 implincludes.add("mozilla/dom/PrimitiveConversions.h") |
|
13322 |
|
13323 # Wrap all of that in our namespaces. |
|
13324 curr = CGNamespace.build(['mozilla', 'dom'], unions) |
|
13325 |
|
13326 curr = CGWrapper(curr, post='\n') |
|
13327 |
|
13328 namespaces = [] |
|
13329 stack = [CGList([])] |
|
13330 for clazz, isStruct in sorted(declarations): |
|
13331 elements = clazz.split("::") |
|
13332 clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct) |
|
13333 i = 0 |
|
13334 if len(elements) > 0: |
|
13335 common = min(len(namespaces), len(elements)) |
|
13336 while i < common and namespaces[i] == elements[i]: |
|
13337 i += 1 |
|
13338 |
|
13339 # pop all the namespaces that should be closed |
|
13340 namespaces = namespaces[:i] |
|
13341 |
|
13342 # add all the namespaces that should be opened |
|
13343 for j, namespace in enumerate(elements[i:]): |
|
13344 namespaces.append(namespace) |
|
13345 # every CGNamespace that we add holds a CGList |
|
13346 list = CGList([]) |
|
13347 # add the new namespace to the list on top of the stack |
|
13348 stack[i + j].append(CGNamespace(namespace, list)) |
|
13349 # set the top of the namespace stack to the list of the new |
|
13350 # namespace |
|
13351 stack[i + j + 1:] = [list] |
|
13352 |
|
13353 stack[len(elements)].append(clazz) |
|
13354 |
|
13355 curr = CGList([stack[0], curr], "\n") |
|
13356 |
|
13357 curr = CGHeaders([], [], [], [], includes, implincludes, 'UnionTypes', |
|
13358 curr) |
|
13359 |
|
13360 # Add include guards. |
|
13361 curr = CGIncludeGuard('UnionTypes', curr) |
|
13362 |
|
13363 # Done. |
|
13364 return curr |
|
13365 |
|
13366 @staticmethod |
|
13367 def UnionConversions(config): |
|
13368 |
|
13369 headers, unions = UnionConversions(config.getDescriptors(), |
|
13370 config.getDictionaries(), |
|
13371 config.getCallbacks(), |
|
13372 config) |
|
13373 |
|
13374 # Wrap all of that in our namespaces. |
|
13375 curr = CGNamespace.build(['mozilla', 'dom'], unions) |
|
13376 |
|
13377 curr = CGWrapper(curr, post='\n') |
|
13378 |
|
13379 headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h"]) |
|
13380 curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr) |
|
13381 |
|
13382 # Add include guards. |
|
13383 curr = CGIncludeGuard('UnionConversions', curr) |
|
13384 |
|
13385 # Done. |
|
13386 return curr |
|
13387 |
|
13388 |
|
13389 # Code generator for simple events |
|
13390 class CGEventGetter(CGNativeMember): |
|
13391 def __init__(self, descriptor, attr): |
|
13392 ea = descriptor.getExtendedAttributes(attr, getter=True) |
|
13393 ea.append('resultNotAddRefed') |
|
13394 CGNativeMember.__init__(self, descriptor, attr, |
|
13395 CGSpecializedGetter.makeNativeName(descriptor, |
|
13396 attr), |
|
13397 (attr.type, []), |
|
13398 ea) |
|
13399 self.body = self.getMethodBody() |
|
13400 |
|
13401 def getArgs(self, returnType, argList): |
|
13402 if 'infallible' not in self.extendedAttrs: |
|
13403 raise TypeError("Event code generator does not support [Throws]!") |
|
13404 if not self.member.isAttr(): |
|
13405 raise TypeError("Event code generator does not support methods") |
|
13406 if self.member.isStatic(): |
|
13407 raise TypeError("Event code generators does not support static attributes") |
|
13408 return CGNativeMember.getArgs(self, returnType, argList) |
|
13409 |
|
13410 def getMethodBody(self): |
|
13411 type = self.member.type |
|
13412 memberName = CGDictionary.makeMemberName(self.member.identifier.name) |
|
13413 if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface(): |
|
13414 return "return " + memberName + ";\n" |
|
13415 if type.isDOMString() or type.isByteString(): |
|
13416 return "aRetVal = " + memberName + ";\n" |
|
13417 if type.isSpiderMonkeyInterface() or type.isObject(): |
|
13418 return fill( |
|
13419 """ |
|
13420 if (${memberName}) { |
|
13421 JS::ExposeObjectToActiveJS(${memberName}); |
|
13422 } |
|
13423 aRetVal.set(${memberName}); |
|
13424 return; |
|
13425 """, |
|
13426 memberName=memberName) |
|
13427 if type.isAny(): |
|
13428 return fill( |
|
13429 """ |
|
13430 JS::ExposeValueToActiveJS(${memberName}); |
|
13431 aRetVal.set(${memberName}); |
|
13432 return; |
|
13433 """, |
|
13434 memberName=memberName) |
|
13435 if type.isUnion(): |
|
13436 return "aRetVal = " + memberName + ";\n" |
|
13437 raise TypeError("Event code generator does not support this type!") |
|
13438 |
|
13439 def declare(self, cgClass): |
|
13440 if getattr(self.member, "originatingInterface", |
|
13441 cgClass.descriptor.interface) != cgClass.descriptor.interface: |
|
13442 return "" |
|
13443 return CGNativeMember.declare(self, cgClass) |
|
13444 |
|
13445 def define(self, cgClass): |
|
13446 if getattr(self.member, "originatingInterface", |
|
13447 cgClass.descriptor.interface) != cgClass.descriptor.interface: |
|
13448 return "" |
|
13449 return CGNativeMember.define(self, cgClass) |
|
13450 |
|
13451 |
|
13452 class CGEventSetter(CGNativeMember): |
|
13453 def __init__(self): |
|
13454 raise TypeError("Event code generator does not support setters!") |
|
13455 |
|
13456 |
|
13457 class CGEventMethod(CGNativeMember): |
|
13458 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): |
|
13459 if not isConstructor: |
|
13460 raise TypeError("Event code generator does not support methods!") |
|
13461 self.wantsConstructorForNativeCaller = True |
|
13462 CGNativeMember.__init__(self, descriptor, method, |
|
13463 CGSpecializedMethod.makeNativeName(descriptor, |
|
13464 method), |
|
13465 signature, |
|
13466 descriptor.getExtendedAttributes(method), |
|
13467 breakAfter=breakAfter, |
|
13468 variadicIsSequence=True) |
|
13469 self.originalArgs = list(self.args) |
|
13470 |
|
13471 def getArgs(self, returnType, argList): |
|
13472 args = [self.getArg(arg) for arg in argList] |
|
13473 return args |
|
13474 |
|
13475 def getArg(self, arg): |
|
13476 decl, ref = self.getArgType(arg.type, |
|
13477 arg.optional and not arg.defaultValue, |
|
13478 "Variadic" if arg.variadic else False) |
|
13479 if ref: |
|
13480 decl = CGWrapper(decl, pre="const ", post="&") |
|
13481 |
|
13482 name = arg.identifier.name |
|
13483 name = "a" + name[0].upper() + name[1:] |
|
13484 return Argument(decl.define(), name) |
|
13485 |
|
13486 def declare(self, cgClass): |
|
13487 self.args = list(self.originalArgs) |
|
13488 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner")) |
|
13489 constructorForNativeCaller = CGNativeMember.declare(self, cgClass) + "\n" |
|
13490 self.args = list(self.originalArgs) |
|
13491 if needCx(None, self.descriptorProvider.interface.members, [], True): |
|
13492 self.args.insert(0, Argument("JSContext*", "aCx")) |
|
13493 self.args.insert(0, Argument("const GlobalObject&", "aGlobal")) |
|
13494 self.args.append(Argument('ErrorResult&', 'aRv')) |
|
13495 return constructorForNativeCaller + CGNativeMember.declare(self, cgClass) |
|
13496 |
|
13497 def define(self, cgClass): |
|
13498 self.args = list(self.originalArgs) |
|
13499 members = "" |
|
13500 holdJS = "" |
|
13501 iface = self.descriptorProvider.interface |
|
13502 while iface.identifier.name != "Event": |
|
13503 for m in self.descriptorProvider.getDescriptor(iface.identifier.name).interface.members: |
|
13504 if m.isAttr(): |
|
13505 # We initialize all the other member variables in the |
|
13506 # Constructor except those ones coming from the Event. |
|
13507 if getattr(m, "originatingInterface", |
|
13508 cgClass.descriptor.interface).identifier.name == "Event": |
|
13509 continue |
|
13510 name = CGDictionary.makeMemberName(m.identifier.name) |
|
13511 members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name) |
|
13512 if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface(): |
|
13513 holdJS = "mozilla::HoldJSObjects(e.get());\n" |
|
13514 iface = iface.parent |
|
13515 |
|
13516 self.body = fill( |
|
13517 """ |
|
13518 nsRefPtr<${nativeType}> e = new ${nativeType}(aOwner); |
|
13519 bool trusted = e->Init(aOwner); |
|
13520 e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable); |
|
13521 $*{members} |
|
13522 e->SetTrusted(trusted); |
|
13523 $*{holdJS} |
|
13524 return e.forget(); |
|
13525 """, |
|
13526 nativeType=self.descriptorProvider.nativeType.split('::')[-1], |
|
13527 eventType=self.args[0].name, |
|
13528 eventInit=self.args[1].name, |
|
13529 members=members, |
|
13530 holdJS=holdJS) |
|
13531 |
|
13532 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner")) |
|
13533 constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n" |
|
13534 self.args = list(self.originalArgs) |
|
13535 self.body = fill( |
|
13536 """ |
|
13537 nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports()); |
|
13538 return Constructor(owner, ${arg0}, ${arg1}); |
|
13539 """, |
|
13540 arg0=self.args[0].name, |
|
13541 arg1=self.args[1].name) |
|
13542 if needCx(None, self.descriptorProvider.interface.members, [], True): |
|
13543 self.args.insert(0, Argument("JSContext*", "aCx")) |
|
13544 self.args.insert(0, Argument("const GlobalObject&", "aGlobal")) |
|
13545 self.args.append(Argument('ErrorResult&', 'aRv')) |
|
13546 return constructorForNativeCaller + CGNativeMember.define(self, cgClass) |
|
13547 |
|
13548 |
|
13549 class CGEventClass(CGBindingImplClass): |
|
13550 """ |
|
13551 Codegen for the actual Event class implementation for this descriptor |
|
13552 """ |
|
13553 def __init__(self, descriptor): |
|
13554 CGBindingImplClass.__init__(self, descriptor, CGEventMethod, CGEventGetter, CGEventSetter, False) |
|
13555 members = [] |
|
13556 for m in descriptor.interface.members: |
|
13557 if m.isAttr(): |
|
13558 if getattr(m, "originatingInterface", |
|
13559 descriptor.interface) != descriptor.interface: |
|
13560 continue |
|
13561 if m.type.isPrimitive() and m.type.tag() in builtinNames: |
|
13562 nativeType = CGGeneric(builtinNames[m.type.tag()]) |
|
13563 if m.type.nullable(): |
|
13564 nativeType = CGTemplatedType("Nullable", nativeType) |
|
13565 nativeType = nativeType.define() |
|
13566 elif m.type.isEnum(): |
|
13567 nativeType = m.type.unroll().inner.identifier.name |
|
13568 if m.type.nullable(): |
|
13569 nativeType = CGTemplatedType("Nullable", |
|
13570 CGGeneric(nativeType)).define() |
|
13571 elif m.type.isDOMString(): |
|
13572 nativeType = "nsString" |
|
13573 elif m.type.isByteString(): |
|
13574 nativeType = "nsCString" |
|
13575 elif m.type.isGeckoInterface(): |
|
13576 iface = m.type.unroll().inner |
|
13577 nativeType = self.descriptor.getDescriptor( |
|
13578 iface.identifier.name).nativeType |
|
13579 # Now trim off unnecessary namespaces |
|
13580 nativeType = nativeType.split("::") |
|
13581 if nativeType[0] == "mozilla": |
|
13582 nativeType.pop(0) |
|
13583 if nativeType[0] == "dom": |
|
13584 nativeType.pop(0) |
|
13585 nativeType = CGWrapper(CGGeneric("::".join(nativeType)), pre="nsRefPtr<", post=">").define() |
|
13586 elif m.type.isAny(): |
|
13587 nativeType = "JS::Heap<JS::Value>" |
|
13588 elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): |
|
13589 nativeType = "JS::Heap<JSObject*>" |
|
13590 elif m.type.isUnion(): |
|
13591 nativeType = CGUnionStruct.unionTypeDecl(m.type, True) |
|
13592 else: |
|
13593 raise TypeError("Don't know how to declare member of type %s" % |
|
13594 m.type) |
|
13595 members.append(ClassMember(CGDictionary.makeMemberName(m.identifier.name), |
|
13596 nativeType, |
|
13597 visibility="private", |
|
13598 body="body")) |
|
13599 |
|
13600 parent = self.descriptor.interface.parent |
|
13601 self.parentType = self.descriptor.getDescriptor(parent.identifier.name).nativeType.split('::')[-1] |
|
13602 baseDeclarations = fill( |
|
13603 """ |
|
13604 public: |
|
13605 NS_DECL_ISUPPORTS_INHERITED |
|
13606 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType}) |
|
13607 virtual ~${nativeType}(); |
|
13608 protected: |
|
13609 ${nativeType}(mozilla::dom::EventTarget* aOwner); |
|
13610 |
|
13611 """, |
|
13612 nativeType=self.descriptor.nativeType.split('::')[-1], |
|
13613 parentType=self.parentType) |
|
13614 |
|
13615 className = descriptor.nativeType.split('::')[-1] |
|
13616 asConcreteTypeMethod = ClassMethod("As%s" % className, |
|
13617 "%s*" % className, |
|
13618 [], |
|
13619 virtual=True, |
|
13620 body="return this;\n", |
|
13621 breakAfterReturnDecl=" ") |
|
13622 |
|
13623 CGClass.__init__(self, className, |
|
13624 bases=[ClassBase(self.parentType)], |
|
13625 methods=[asConcreteTypeMethod]+self.methodDecls, |
|
13626 members=members, |
|
13627 extradeclarations=baseDeclarations) |
|
13628 |
|
13629 def getWrapObjectBody(self): |
|
13630 return "return %sBinding::Wrap(aCx, this);\n" % self.descriptor.name |
|
13631 |
|
13632 def implTraverse(self): |
|
13633 retVal = "" |
|
13634 for m in self.descriptor.interface.members: |
|
13635 if m.isAttr() and m.type.isGeckoInterface(): |
|
13636 retVal += (" NS_IMPL_CYCLE_COLLECTION_TRAVERSE(" + |
|
13637 CGDictionary.makeMemberName(m.identifier.name) + |
|
13638 ")\n") |
|
13639 return retVal |
|
13640 |
|
13641 def implUnlink(self): |
|
13642 retVal = "" |
|
13643 for m in self.descriptor.interface.members: |
|
13644 if m.isAttr(): |
|
13645 name = CGDictionary.makeMemberName(m.identifier.name) |
|
13646 if m.type.isGeckoInterface(): |
|
13647 retVal += " NS_IMPL_CYCLE_COLLECTION_UNLINK(" + name + ")\n" |
|
13648 elif m.type.isAny(): |
|
13649 retVal += " tmp->" + name + ".setUndefined();\n" |
|
13650 elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): |
|
13651 retVal += " tmp->" + name + " = nullptr;\n" |
|
13652 return retVal |
|
13653 |
|
13654 def implTrace(self): |
|
13655 retVal = "" |
|
13656 for m in self.descriptor.interface.members: |
|
13657 if m.isAttr(): |
|
13658 name = CGDictionary.makeMemberName(m.identifier.name) |
|
13659 if m.type.isAny(): |
|
13660 retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(" + name + ")\n" |
|
13661 elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): |
|
13662 retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n" |
|
13663 elif typeNeedsRooting(m.type): |
|
13664 raise TypeError("Need to implement tracing for event " |
|
13665 "member of type %s" % m.type) |
|
13666 return retVal |
|
13667 |
|
13668 def define(self): |
|
13669 dropJS = "" |
|
13670 for m in self.descriptor.interface.members: |
|
13671 if m.isAttr(): |
|
13672 member = CGDictionary.makeMemberName(m.identifier.name) |
|
13673 if m.type.isAny(): |
|
13674 dropJS += member + " = JS::UndefinedValue();\n" |
|
13675 elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): |
|
13676 dropJS += member + " = nullptr;\n" |
|
13677 if dropJS != "": |
|
13678 dropJS += "mozilla::DropJSObjects(this);\n" |
|
13679 # Just override CGClass and do our own thing |
|
13680 nativeType = self.descriptor.nativeType.split('::')[-1] |
|
13681 ctorParams = ("aOwner, nullptr, nullptr" if self.parentType == "Event" |
|
13682 else "aOwner") |
|
13683 |
|
13684 classImpl = fill( |
|
13685 """ |
|
13686 |
|
13687 NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType}) |
|
13688 |
|
13689 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType}) |
|
13690 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType}) |
|
13691 |
|
13692 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType}) |
|
13693 $*{traverse} |
|
13694 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
13695 |
|
13696 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType}) |
|
13697 $*{trace} |
|
13698 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
13699 |
|
13700 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType}) |
|
13701 $*{unlink} |
|
13702 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
13703 |
|
13704 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${nativeType}) |
|
13705 NS_INTERFACE_MAP_END_INHERITING(${parentType}) |
|
13706 |
|
13707 ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner) |
|
13708 : ${parentType}(${ctorParams}) |
|
13709 { |
|
13710 } |
|
13711 |
|
13712 ${nativeType}::~${nativeType}() |
|
13713 { |
|
13714 $*{dropJS} |
|
13715 } |
|
13716 |
|
13717 """, |
|
13718 ifaceName=self.descriptor.name, |
|
13719 nativeType=nativeType, |
|
13720 ctorParams=ctorParams, |
|
13721 parentType=self.parentType, |
|
13722 traverse=self.implTraverse(), |
|
13723 unlink=self.implUnlink(), |
|
13724 trace=self.implTrace(), |
|
13725 dropJS=dropJS) |
|
13726 return classImpl + CGBindingImplClass.define(self) |
|
13727 |
|
13728 |
|
13729 class CGEventRoot(CGThing): |
|
13730 def __init__(self, config, interfaceName): |
|
13731 # Let's assume we're not doing workers stuff, for now |
|
13732 descriptor = config.getDescriptor(interfaceName, False) |
|
13733 |
|
13734 self.root = CGWrapper(CGEventClass(descriptor), |
|
13735 pre="\n", post="\n") |
|
13736 |
|
13737 self.root = CGNamespace.build(["mozilla", "dom"], self.root) |
|
13738 |
|
13739 self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True), |
|
13740 self.root]) |
|
13741 |
|
13742 parent = descriptor.interface.parent.identifier.name |
|
13743 |
|
13744 # Throw in our #includes |
|
13745 self.root = CGHeaders([descriptor], [], [], [], |
|
13746 [ |
|
13747 config.getDescriptor(parent, False).headerFile, |
|
13748 "mozilla/Attributes.h", |
|
13749 "mozilla/ErrorResult.h", |
|
13750 "mozilla/dom/%sBinding.h" % interfaceName, |
|
13751 'mozilla/dom/BindingUtils.h', |
|
13752 ], |
|
13753 [ |
|
13754 "%s.h" % interfaceName, |
|
13755 "js/GCAPI.h", |
|
13756 'mozilla/dom/Nullable.h', |
|
13757 'nsDOMQS.h' |
|
13758 ], |
|
13759 "", self.root) |
|
13760 |
|
13761 # And now some include guards |
|
13762 self.root = CGIncludeGuard(interfaceName, self.root) |
|
13763 |
|
13764 self.root = CGWrapper(self.root, pre=AUTOGENERATED_WARNING_COMMENT) |
|
13765 |
|
13766 self.root = CGWrapper(self.root, pre=dedent(""" |
|
13767 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
13768 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
13769 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
13770 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
13771 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
13772 |
|
13773 """)) |
|
13774 |
|
13775 def declare(self): |
|
13776 return self.root.declare() |
|
13777 |
|
13778 def define(self): |
|
13779 return self.root.define() |