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 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5# Common codegen classes. 6 7from collections import defaultdict 8from itertools import groupby 9 10import operator 11import os 12import re 13import string 14import textwrap 15import functools 16 17from WebIDL import ( 18 BuiltinTypes, 19 IDLBuiltinType, 20 IDLInterfaceMember, 21 IDLNullableType, 22 IDLNullValue, 23 IDLObject, 24 IDLPromiseType, 25 IDLType, 26 IDLUndefinedValue, 27 IDLWrapperType, 28) 29 30from Configuration import ( 31 MakeNativeName, 32 MemberIsUnforgeable, 33 getModuleFromObject, 34 getTypesFromCallback, 35 getTypesFromDescriptor, 36 getTypesFromDictionary, 37 iteratorNativeType 38) 39 40AUTOGENERATED_WARNING_COMMENT = \ 41 "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" 42FINALIZE_HOOK_NAME = '_finalize' 43TRACE_HOOK_NAME = '_trace' 44CONSTRUCT_HOOK_NAME = '_constructor' 45HASINSTANCE_HOOK_NAME = '_hasInstance' 46 47RUST_KEYWORDS = {"abstract", "alignof", "as", "become", "box", "break", "const", "continue", 48 "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in", 49 "let", "loop", "macro", "match", "mod", "move", "mut", "offsetof", "override", 50 "priv", "proc", "pub", "pure", "ref", "return", "static", "self", "sizeof", 51 "struct", "super", "true", "trait", "type", "typeof", "unsafe", "unsized", 52 "use", "virtual", "where", "while", "yield"} 53 54 55def replaceFileIfChanged(filename, newContents): 56 """ 57 Read a copy of the old file, so that we don't touch it if it hasn't changed. 58 Returns True if the file was updated, false otherwise. 59 """ 60 # XXXjdm This doesn't play well with make right now. 61 # Force the file to always be updated, or else changing CodegenRust.py 62 # will cause many autogenerated bindings to be regenerated perpetually 63 # until the result is actually different. 64 65 # oldFileContents = "" 66 # try: 67 # with open(filename, 'rb') as oldFile: 68 # oldFileContents = ''.join(oldFile.readlines()) 69 # except: 70 # pass 71 72 # if newContents == oldFileContents: 73 # return False 74 75 with open(filename, 'wb') as f: 76 f.write(newContents) 77 78 return True 79 80 81def toStringBool(arg): 82 return str(not not arg).lower() 83 84 85def toBindingNamespace(arg): 86 return re.sub("((_workers)?$)", "Binding\\1", MakeNativeName(arg)) 87 88 89def stripTrailingWhitespace(text): 90 tail = '\n' if text.endswith('\n') else '' 91 lines = text.splitlines() 92 for i in range(len(lines)): 93 lines[i] = lines[i].rstrip() 94 return '\n'.join(lines) + tail 95 96 97def innerContainerType(type): 98 assert type.isSequence() or type.isRecord() 99 return type.inner.inner if type.nullable() else type.inner 100 101 102def wrapInNativeContainerType(type, inner): 103 if type.isSequence(): 104 containerType = "Vec" 105 elif type.isRecord(): 106 containerType = "MozMap" 107 else: 108 raise TypeError("Unexpected container type %s", type) 109 110 return CGWrapper(inner, pre=containerType + "<", post=">") 111 112 113builtinNames = { 114 IDLType.Tags.bool: 'bool', 115 IDLType.Tags.int8: 'i8', 116 IDLType.Tags.int16: 'i16', 117 IDLType.Tags.int32: 'i32', 118 IDLType.Tags.int64: 'i64', 119 IDLType.Tags.uint8: 'u8', 120 IDLType.Tags.uint16: 'u16', 121 IDLType.Tags.uint32: 'u32', 122 IDLType.Tags.uint64: 'u64', 123 IDLType.Tags.unrestricted_float: 'f32', 124 IDLType.Tags.float: 'Finite<f32>', 125 IDLType.Tags.unrestricted_double: 'f64', 126 IDLType.Tags.double: 'Finite<f64>' 127} 128 129numericTags = [ 130 IDLType.Tags.int8, IDLType.Tags.uint8, 131 IDLType.Tags.int16, IDLType.Tags.uint16, 132 IDLType.Tags.int32, IDLType.Tags.uint32, 133 IDLType.Tags.int64, IDLType.Tags.uint64, 134 IDLType.Tags.unrestricted_float, 135 IDLType.Tags.unrestricted_double 136] 137 138 139# We'll want to insert the indent at the beginnings of lines, but we 140# don't want to indent empty lines. So only indent lines that have a 141# non-newline character on them. 142lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE) 143 144 145def indent(s, indentLevel=2): 146 """ 147 Indent C++ code. 148 149 Weird secret feature: this doesn't indent lines that start with # (such as 150 #include lines or #ifdef/#endif). 151 """ 152 if s == "": 153 return s 154 return re.sub(lineStartDetector, indentLevel * " ", s) 155 156 157# dedent() and fill() are often called on the same string multiple 158# times. We want to memoize their return values so we don't keep 159# recomputing them all the time. 160def memoize(fn): 161 """ 162 Decorator to memoize a function of one argument. The cache just 163 grows without bound. 164 """ 165 cache = {} 166 167 @functools.wraps(fn) 168 def wrapper(arg): 169 retval = cache.get(arg) 170 if retval is None: 171 retval = cache[arg] = fn(arg) 172 return retval 173 return wrapper 174 175 176@memoize 177def dedent(s): 178 """ 179 Remove all leading whitespace from s, and remove a blank line 180 at the beginning. 181 """ 182 if s.startswith('\n'): 183 s = s[1:] 184 return textwrap.dedent(s) 185 186 187# This works by transforming the fill()-template to an equivalent 188# string.Template. 189fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?") 190 191 192@memoize 193def compile_fill_template(template): 194 """ 195 Helper function for fill(). Given the template string passed to fill(), 196 do the reusable part of template processing and return a pair (t, 197 argModList) that can be used every time fill() is called with that 198 template argument. 199 200 argsModList is list of tuples that represent modifications to be 201 made to args. Each modification has, in order: i) the arg name, 202 ii) the modified name, iii) the indent depth. 203 """ 204 t = dedent(template) 205 assert t.endswith("\n") or "\n" not in t 206 argModList = [] 207 208 def replace(match): 209 """ 210 Replaces a line like ' $*{xyz}\n' with '${xyz_n}', 211 where n is the indent depth, and add a corresponding entry to 212 argModList. 213 214 Note that this needs to close over argModList, so it has to be 215 defined inside compile_fill_template(). 216 """ 217 indentation, name, nl = match.groups() 218 depth = len(indentation) 219 220 # Check that $*{xyz} appears by itself on a line. 221 prev = match.string[:match.start()] 222 if (prev and not prev.endswith("\n")) or nl is None: 223 raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name) 224 225 # Now replace this whole line of template with the indented equivalent. 226 modified_name = name + "_" + str(depth) 227 argModList.append((name, modified_name, depth)) 228 return "${" + modified_name + "}" 229 230 t = re.sub(fill_multiline_substitution_re, replace, t) 231 return (string.Template(t), argModList) 232 233 234def fill(template, **args): 235 """ 236 Convenience function for filling in a multiline template. 237 238 `fill(template, name1=v1, name2=v2)` is a lot like 239 `string.Template(template).substitute({"name1": v1, "name2": v2})`. 240 241 However, it's shorter, and has a few nice features: 242 243 * If `template` is indented, fill() automatically dedents it! 244 This makes code using fill() with Python's multiline strings 245 much nicer to look at. 246 247 * If `template` starts with a blank line, fill() strips it off. 248 (Again, convenient with multiline strings.) 249 250 * fill() recognizes a special kind of substitution 251 of the form `$*{name}`. 252 253 Use this to paste in, and automatically indent, multiple lines. 254 (Mnemonic: The `*` is for "multiple lines"). 255 256 A `$*` substitution must appear by itself on a line, with optional 257 preceding indentation (spaces only). The whole line is replaced by the 258 corresponding keyword argument, indented appropriately. If the 259 argument is an empty string, no output is generated, not even a blank 260 line. 261 """ 262 263 t, argModList = compile_fill_template(template) 264 # Now apply argModList to args 265 for (name, modified_name, depth) in argModList: 266 if not (args[name] == "" or args[name].endswith("\n")): 267 raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name])) 268 args[modified_name] = indent(args[name], depth) 269 270 return t.substitute(args) 271 272 273class CGThing(): 274 """ 275 Abstract base class for things that spit out code. 276 """ 277 def __init__(self): 278 pass # Nothing for now 279 280 def define(self): 281 """Produce code for a Rust file.""" 282 raise NotImplementedError # Override me! 283 284 285class CGMethodCall(CGThing): 286 """ 287 A class to generate selection of a method signature from a set of 288 signatures and generation of a call to that signature. 289 """ 290 def __init__(self, argsPre, nativeMethodName, static, descriptor, method): 291 CGThing.__init__(self) 292 293 methodName = '\\"%s.%s\\"' % (descriptor.interface.identifier.name, method.identifier.name) 294 295 def requiredArgCount(signature): 296 arguments = signature[1] 297 if len(arguments) == 0: 298 return 0 299 requiredArgs = len(arguments) 300 while requiredArgs and arguments[requiredArgs - 1].optional: 301 requiredArgs -= 1 302 return requiredArgs 303 304 signatures = method.signatures() 305 306 def getPerSignatureCall(signature, argConversionStartsAt=0): 307 signatureIndex = signatures.index(signature) 308 return CGPerSignatureCall(signature[0], argsPre, signature[1], 309 nativeMethodName + '_' * signatureIndex, 310 static, descriptor, 311 method, argConversionStartsAt) 312 313 if len(signatures) == 1: 314 # Special case: we can just do a per-signature method call 315 # here for our one signature and not worry about switching 316 # on anything. 317 signature = signatures[0] 318 self.cgRoot = CGList([getPerSignatureCall(signature)]) 319 requiredArgs = requiredArgCount(signature) 320 321 if requiredArgs > 0: 322 code = ( 323 "if argc < %d {\n" 324 " throw_type_error(cx, \"Not enough arguments to %s.\");\n" 325 " return false;\n" 326 "}" % (requiredArgs, methodName)) 327 self.cgRoot.prepend( 328 CGWrapper(CGGeneric(code), pre="\n", post="\n")) 329 330 return 331 332 # Need to find the right overload 333 maxArgCount = method.maxArgCount 334 allowedArgCounts = method.allowedArgCounts 335 336 argCountCases = [] 337 for argCount in allowedArgCounts: 338 possibleSignatures = method.signaturesForArgCount(argCount) 339 if len(possibleSignatures) == 1: 340 # easy case! 341 signature = possibleSignatures[0] 342 argCountCases.append(CGCase(str(argCount), getPerSignatureCall(signature))) 343 continue 344 345 distinguishingIndex = method.distinguishingIndexForArgCount(argCount) 346 347 # We can't handle unions at the distinguishing index. 348 for (returnType, args) in possibleSignatures: 349 if args[distinguishingIndex].type.isUnion(): 350 raise TypeError("No support for unions as distinguishing " 351 "arguments yet: %s", 352 args[distinguishingIndex].location) 353 354 # Convert all our arguments up to the distinguishing index. 355 # Doesn't matter which of the possible signatures we use, since 356 # they all have the same types up to that point; just use 357 # possibleSignatures[0] 358 caseBody = [ 359 CGArgumentConverter(possibleSignatures[0][1][i], 360 i, "args", "argc", descriptor) 361 for i in range(0, distinguishingIndex)] 362 363 # Select the right overload from our set. 364 distinguishingArg = "args.get(%d)" % distinguishingIndex 365 366 def pickFirstSignature(condition, filterLambda): 367 sigs = filter(filterLambda, possibleSignatures) 368 assert len(sigs) < 2 369 if len(sigs) > 0: 370 call = getPerSignatureCall(sigs[0], distinguishingIndex) 371 if condition is None: 372 caseBody.append(call) 373 else: 374 caseBody.append(CGGeneric("if " + condition + " {")) 375 caseBody.append(CGIndenter(call)) 376 caseBody.append(CGGeneric("}")) 377 return True 378 return False 379 380 # First check for null or undefined 381 pickFirstSignature("%s.get().is_null_or_undefined()" % distinguishingArg, 382 lambda s: (s[1][distinguishingIndex].type.nullable() or 383 s[1][distinguishingIndex].type.isDictionary())) 384 385 # Now check for distinguishingArg being an object that implements a 386 # non-callback interface. That includes typed arrays and 387 # arraybuffers. 388 interfacesSigs = [ 389 s for s in possibleSignatures 390 if (s[1][distinguishingIndex].type.isObject() or 391 s[1][distinguishingIndex].type.isNonCallbackInterface())] 392 # There might be more than one of these; we need to check 393 # which ones we unwrap to. 394 395 if len(interfacesSigs) > 0: 396 # The spec says that we should check for "platform objects 397 # implementing an interface", but it's enough to guard on these 398 # being an object. The code for unwrapping non-callback 399 # interfaces and typed arrays will just bail out and move on to 400 # the next overload if the object fails to unwrap correctly. We 401 # could even not do the isObject() check up front here, but in 402 # cases where we have multiple object overloads it makes sense 403 # to do it only once instead of for each overload. That will 404 # also allow the unwrapping test to skip having to do codegen 405 # for the null-or-undefined case, which we already handled 406 # above. 407 caseBody.append(CGGeneric("if %s.get().is_object() {" % 408 (distinguishingArg))) 409 for idx, sig in enumerate(interfacesSigs): 410 caseBody.append(CGIndenter(CGGeneric("loop {"))) 411 type = sig[1][distinguishingIndex].type 412 413 # The argument at index distinguishingIndex can't possibly 414 # be unset here, because we've already checked that argc is 415 # large enough that we can examine this argument. 416 info = getJSToNativeConversionInfo( 417 type, descriptor, failureCode="break;", isDefinitelyObject=True) 418 template = info.template 419 declType = info.declType 420 421 testCode = instantiateJSToNativeConversionTemplate( 422 template, 423 {"val": distinguishingArg}, 424 declType, 425 "arg%d" % distinguishingIndex) 426 427 # Indent by 4, since we need to indent further than our "do" statement 428 caseBody.append(CGIndenter(testCode, 4)) 429 # If we got this far, we know we unwrapped to the right 430 # interface, so just do the call. Start conversion with 431 # distinguishingIndex + 1, since we already converted 432 # distinguishingIndex. 433 caseBody.append(CGIndenter( 434 getPerSignatureCall(sig, distinguishingIndex + 1), 4)) 435 caseBody.append(CGIndenter(CGGeneric("}"))) 436 437 caseBody.append(CGGeneric("}")) 438 439 # XXXbz Now we're supposed to check for distinguishingArg being 440 # an array or a platform object that supports indexed 441 # properties... skip that last for now. It's a bit of a pain. 442 pickFirstSignature("%s.get().is_object() && is_array_like(cx, %s)" % 443 (distinguishingArg, distinguishingArg), 444 lambda s: 445 (s[1][distinguishingIndex].type.isSequence() or 446 s[1][distinguishingIndex].type.isObject())) 447 448 # Check for Date objects 449 # XXXbz Do we need to worry about security wrappers around the Date? 450 pickFirstSignature("%s.get().is_object() && " 451 "{ rooted!(in(cx) let obj = %s.get().to_object()); " 452 "let mut is_date = false; " 453 "assert!(JS_ObjectIsDate(cx, obj.handle(), &mut is_date)); " 454 "is_date }" % 455 (distinguishingArg, distinguishingArg), 456 lambda s: (s[1][distinguishingIndex].type.isDate() or 457 s[1][distinguishingIndex].type.isObject())) 458 459 # Check for vanilla JS objects 460 # XXXbz Do we need to worry about security wrappers? 461 pickFirstSignature("%s.get().is_object() && !is_platform_object(%s.get().to_object())" % 462 (distinguishingArg, distinguishingArg), 463 lambda s: (s[1][distinguishingIndex].type.isCallback() or 464 s[1][distinguishingIndex].type.isCallbackInterface() or 465 s[1][distinguishingIndex].type.isDictionary() or 466 s[1][distinguishingIndex].type.isObject())) 467 468 # The remaining cases are mutually exclusive. The 469 # pickFirstSignature calls are what change caseBody 470 # Check for strings or enums 471 if pickFirstSignature(None, 472 lambda s: (s[1][distinguishingIndex].type.isString() or 473 s[1][distinguishingIndex].type.isEnum())): 474 pass 475 # Check for primitives 476 elif pickFirstSignature(None, 477 lambda s: s[1][distinguishingIndex].type.isPrimitive()): 478 pass 479 # Check for "any" 480 elif pickFirstSignature(None, 481 lambda s: s[1][distinguishingIndex].type.isAny()): 482 pass 483 else: 484 # Just throw; we have no idea what we're supposed to 485 # do with this. 486 caseBody.append(CGGeneric("throw_internal_error(cx, \"Could not convert JavaScript argument\");\n" 487 "return false;")) 488 489 argCountCases.append(CGCase(str(argCount), 490 CGList(caseBody, "\n"))) 491 492 overloadCGThings = [] 493 overloadCGThings.append( 494 CGGeneric("let argcount = cmp::min(argc, %d);" % 495 maxArgCount)) 496 overloadCGThings.append( 497 CGSwitch("argcount", 498 argCountCases, 499 CGGeneric("throw_type_error(cx, \"Not enough arguments to %s.\");\n" 500 "return false;" % methodName))) 501 # XXXjdm Avoid unreachable statement warnings 502 # overloadCGThings.append( 503 # CGGeneric('panic!("We have an always-returning default case");\n' 504 # 'return false;')) 505 self.cgRoot = CGWrapper(CGList(overloadCGThings, "\n"), 506 pre="\n") 507 508 def define(self): 509 return self.cgRoot.define() 510 511 512def dictionaryHasSequenceMember(dictionary): 513 return (any(typeIsSequenceOrHasSequenceMember(m.type) for m in 514 dictionary.members) or 515 (dictionary.parent and 516 dictionaryHasSequenceMember(dictionary.parent))) 517 518 519def typeIsSequenceOrHasSequenceMember(type): 520 if type.nullable(): 521 type = type.inner 522 if type.isSequence(): 523 return True 524 if type.isDictionary(): 525 return dictionaryHasSequenceMember(type.inner) 526 if type.isUnion(): 527 return any(typeIsSequenceOrHasSequenceMember(m.type) for m in 528 type.flatMemberTypes) 529 return False 530 531 532def union_native_type(t): 533 name = t.unroll().name 534 return 'UnionTypes::%s' % name 535 536 537class JSToNativeConversionInfo(): 538 """ 539 An object representing information about a JS-to-native conversion. 540 """ 541 def __init__(self, template, default=None, declType=None): 542 """ 543 template: A string representing the conversion code. This will have 544 template substitution performed on it as follows: 545 546 ${val} is a handle to the JS::Value in question 547 548 default: A string or None representing rust code for default value(if any). 549 550 declType: A CGThing representing the native C++ type we're converting 551 to. This is allowed to be None if the conversion code is 552 supposed to be used as-is. 553 """ 554 assert isinstance(template, str) 555 assert declType is None or isinstance(declType, CGThing) 556 self.template = template 557 self.default = default 558 self.declType = declType 559 560 561def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, 562 isDefinitelyObject=False, 563 isMember=False, 564 isArgument=False, 565 isAutoRooted=False, 566 invalidEnumValueFatal=True, 567 defaultValue=None, 568 treatNullAs="Default", 569 isEnforceRange=False, 570 isClamp=False, 571 exceptionCode=None, 572 allowTreatNonObjectAsNull=False, 573 isCallbackReturnValue=False, 574 sourceDescription="value"): 575 """ 576 Get a template for converting a JS value to a native object based on the 577 given type and descriptor. If failureCode is given, then we're actually 578 testing whether we can convert the argument to the desired type. That 579 means that failures to convert due to the JS value being the wrong type of 580 value need to use failureCode instead of throwing exceptions. Failures to 581 convert that are due to JS exceptions (from toString or valueOf methods) or 582 out of memory conditions need to throw exceptions no matter what 583 failureCode is. 584 585 If isDefinitelyObject is True, that means we know the value 586 isObject() and we have no need to recheck that. 587 588 isMember is `False`, "Dictionary", "Union" or "Variadic", and affects 589 whether this function returns code suitable for an on-stack rooted binding 590 or suitable for storing in an appropriate larger structure. 591 592 invalidEnumValueFatal controls whether an invalid enum value conversion 593 attempt will throw (if true) or simply return without doing anything (if 594 false). 595 596 If defaultValue is not None, it's the IDL default value for this conversion 597 598 If isEnforceRange is true, we're converting an integer and throwing if the 599 value is out of range. 600 601 If isClamp is true, we're converting an integer and clamping if the 602 value is out of range. 603 604 If allowTreatNonObjectAsNull is true, then [TreatNonObjectAsNull] 605 extended attributes on nullable callback functions will be honored. 606 607 The return value from this function is an object of JSToNativeConversionInfo consisting of four things: 608 609 1) A string representing the conversion code. This will have template 610 substitution performed on it as follows: 611 612 ${val} replaced by an expression for the JS::Value in question 613 614 2) A string or None representing Rust code for the default value (if any). 615 616 3) A CGThing representing the native C++ type we're converting to 617 (declType). This is allowed to be None if the conversion code is 618 supposed to be used as-is. 619 620 4) A boolean indicating whether the caller has to root the result. 621 622 """ 623 # We should not have a defaultValue if we know we're an object 624 assert not isDefinitelyObject or defaultValue is None 625 626 # If exceptionCode is not set, we'll just rethrow the exception we got. 627 # Note that we can't just set failureCode to exceptionCode, because setting 628 # failureCode will prevent pending exceptions from being set in cases when 629 # they really should be! 630 if exceptionCode is None: 631 exceptionCode = "return false;\n" 632 633 if failureCode is None: 634 failOrPropagate = "throw_type_error(cx, &error);\n%s" % exceptionCode 635 else: 636 failOrPropagate = failureCode 637 638 def handleOptional(template, declType, default): 639 assert (defaultValue is None) == (default is None) 640 return JSToNativeConversionInfo(template, default, declType) 641 642 # Unfortunately, .capitalize() on a string will lowercase things inside the 643 # string, which we do not want. 644 def firstCap(string): 645 return string[0].upper() + string[1:] 646 647 # Helper functions for dealing with failures due to the JS value being the 648 # wrong type of value. 649 def onFailureNotAnObject(failureCode): 650 return CGWrapper( 651 CGGeneric( 652 failureCode or 653 ('throw_type_error(cx, "%s is not an object.");\n' 654 '%s' % (firstCap(sourceDescription), exceptionCode))), 655 post="\n") 656 657 def onFailureInvalidEnumValue(failureCode, passedVarName): 658 return CGGeneric( 659 failureCode or 660 ('throw_type_error(cx, &format!("\'{}\' is not a valid enum value for enumeration \'%s\'.", %s)); %s' 661 % (type.name, passedVarName, exceptionCode))) 662 663 def onFailureNotCallable(failureCode): 664 return CGGeneric( 665 failureCode or 666 ('throw_type_error(cx, \"%s is not callable.\");\n' 667 '%s' % (firstCap(sourceDescription), exceptionCode))) 668 669 # A helper function for handling null default values. Checks that the 670 # default value, if it exists, is null. 671 def handleDefaultNull(nullValue): 672 if defaultValue is None: 673 return None 674 675 if not isinstance(defaultValue, IDLNullValue): 676 raise TypeError("Can't handle non-null default value here") 677 678 assert type.nullable() or type.isDictionary() 679 return nullValue 680 681 # A helper function for wrapping up the template body for 682 # possibly-nullable objecty stuff 683 def wrapObjectTemplate(templateBody, nullValue, isDefinitelyObject, type, 684 failureCode=None): 685 if not isDefinitelyObject: 686 # Handle the non-object cases by wrapping up the whole 687 # thing in an if cascade. 688 templateBody = ( 689 "if ${val}.get().is_object() {\n" + 690 CGIndenter(CGGeneric(templateBody)).define() + "\n") 691 if type.nullable(): 692 templateBody += ( 693 "} else if ${val}.get().is_null_or_undefined() {\n" 694 " %s\n") % nullValue 695 templateBody += ( 696 "} else {\n" + 697 CGIndenter(onFailureNotAnObject(failureCode)).define() + 698 "}") 699 return templateBody 700 701 assert not (isEnforceRange and isClamp) # These are mutually exclusive 702 703 if type.isSequence() or type.isRecord(): 704 innerInfo = getJSToNativeConversionInfo(innerContainerType(type), 705 descriptorProvider, 706 isMember=isMember, 707 isAutoRooted=isAutoRooted) 708 declType = wrapInNativeContainerType(type, innerInfo.declType) 709 config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs) 710 711 if type.nullable(): 712 declType = CGWrapper(declType, pre="Option<", post=" >") 713 714 templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" 715 " Ok(ConversionResult::Success(value)) => value,\n" 716 " Ok(ConversionResult::Failure(error)) => {\n" 717 "%s\n" 718 " }\n" 719 " _ => { %s },\n" 720 "}" % (config, indent(failOrPropagate, 8), exceptionCode)) 721 722 return handleOptional(templateBody, declType, handleDefaultNull("None")) 723 724 if type.isUnion(): 725 declType = CGGeneric(union_native_type(type)) 726 if type.nullable(): 727 declType = CGWrapper(declType, pre="Option<", post=" >") 728 729 templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" 730 " Ok(ConversionResult::Success(value)) => value,\n" 731 " Ok(ConversionResult::Failure(error)) => {\n" 732 "%s\n" 733 " }\n" 734 " _ => { %s },\n" 735 "}" % (indent(failOrPropagate, 8), exceptionCode)) 736 737 dictionaries = [ 738 memberType 739 for memberType in type.unroll().flatMemberTypes 740 if memberType.isDictionary() 741 ] 742 if dictionaries: 743 if defaultValue: 744 assert isinstance(defaultValue, IDLNullValue) 745 dictionary, = dictionaries 746 default = "%s::%s(%s::%s::empty(cx))" % ( 747 union_native_type(type), 748 dictionary.name, 749 CGDictionary.makeModuleName(dictionary.inner), 750 CGDictionary.makeDictionaryName(dictionary.inner)) 751 else: 752 default = None 753 else: 754 default = handleDefaultNull("None") 755 756 return handleOptional(templateBody, declType, default) 757 758 if type.isPromise(): 759 assert not type.nullable() 760 # Per spec, what we're supposed to do is take the original 761 # Promise.resolve and call it with the original Promise as this 762 # value to make a Promise out of whatever value we actually have 763 # here. The question is which global we should use. There are 764 # a couple cases to consider: 765 # 766 # 1) Normal call to API with a Promise argument. This is a case the 767 # spec covers, and we should be using the current Realm's 768 # Promise. That means the current compartment. 769 # 2) Promise return value from a callback or callback interface. 770 # This is in theory a case the spec covers but in practice it 771 # really doesn't define behavior here because it doesn't define 772 # what Realm we're in after the callback returns, which is when 773 # the argument conversion happens. We will use the current 774 # compartment, which is the compartment of the callable (which 775 # may itself be a cross-compartment wrapper itself), which makes 776 # as much sense as anything else. In practice, such an API would 777 # once again be providing a Promise to signal completion of an 778 # operation, which would then not be exposed to anyone other than 779 # our own implementation code. 780 templateBody = fill( 781 """ 782 { // Scope for our JSAutoCompartment. 783 784 rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx)); 785 let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get()); 786 787 rooted!(in(cx) let mut valueToResolve = $${val}.get()); 788 if !JS_WrapValue(cx, valueToResolve.handle_mut()) { 789 $*{exceptionCode} 790 } 791 match Promise::new_resolved(&promiseGlobal, cx, valueToResolve.handle()) { 792 Ok(value) => value, 793 Err(error) => { 794 throw_dom_exception(cx, &promiseGlobal, error); 795 $*{exceptionCode} 796 } 797 } 798 } 799 """, 800 exceptionCode=exceptionCode) 801 802 if isArgument: 803 declType = CGGeneric("&Promise") 804 else: 805 declType = CGGeneric("Rc<Promise>") 806 return handleOptional(templateBody, declType, handleDefaultNull("None")) 807 808 if type.isGeckoInterface(): 809 assert not isEnforceRange and not isClamp 810 811 descriptor = descriptorProvider.getDescriptor( 812 type.unroll().inner.identifier.name) 813 814 if descriptor.interface.isCallback(): 815 name = descriptor.nativeType 816 declType = CGWrapper(CGGeneric(name), pre="Rc<", post=">") 817 template = "%s::new(cx, ${val}.get().to_object())" % name 818 if type.nullable(): 819 declType = CGWrapper(declType, pre="Option<", post=">") 820 template = wrapObjectTemplate("Some(%s)" % template, "None", 821 isDefinitelyObject, type, 822 failureCode) 823 824 return handleOptional(template, declType, handleDefaultNull("None")) 825 826 conversionFunction = "root_from_handlevalue" 827 descriptorType = descriptor.returnType 828 if isMember == "Variadic": 829 conversionFunction = "native_from_handlevalue" 830 descriptorType = descriptor.nativeType 831 elif isArgument: 832 descriptorType = descriptor.argumentType 833 834 if descriptor.interface.isConsequential(): 835 raise TypeError("Consequential interface %s being used as an " 836 "argument" % descriptor.interface.identifier.name) 837 838 if failureCode is None: 839 substitutions = { 840 "sourceDescription": sourceDescription, 841 "interface": descriptor.interface.identifier.name, 842 "exceptionCode": exceptionCode, 843 } 844 unwrapFailureCode = string.Template( 845 'throw_type_error(cx, "${sourceDescription} does not ' 846 'implement interface ${interface}.");\n' 847 '${exceptionCode}').substitute(substitutions) 848 else: 849 unwrapFailureCode = failureCode 850 851 templateBody = fill( 852 """ 853 match ${function}($${val}) { 854 Ok(val) => val, 855 Err(()) => { 856 $*{failureCode} 857 } 858 } 859 """, 860 failureCode=unwrapFailureCode + "\n", 861 function=conversionFunction) 862 863 declType = CGGeneric(descriptorType) 864 if type.nullable(): 865 templateBody = "Some(%s)" % templateBody 866 declType = CGWrapper(declType, pre="Option<", post=">") 867 868 templateBody = wrapObjectTemplate(templateBody, "None", 869 isDefinitelyObject, type, failureCode) 870 871 return handleOptional(templateBody, declType, handleDefaultNull("None")) 872 873 if type.isSpiderMonkeyInterface(): 874 raise TypeError("Can't handle SpiderMonkey interface arguments yet") 875 876 if type.isDOMString(): 877 nullBehavior = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs) 878 879 conversionCode = ( 880 "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" 881 " Ok(ConversionResult::Success(strval)) => strval,\n" 882 " Ok(ConversionResult::Failure(error)) => {\n" 883 "%s\n" 884 " }\n" 885 " _ => { %s },\n" 886 "}" % (nullBehavior, indent(failOrPropagate, 8), exceptionCode)) 887 888 if defaultValue is None: 889 default = None 890 elif isinstance(defaultValue, IDLNullValue): 891 assert type.nullable() 892 default = "None" 893 else: 894 assert defaultValue.type.tag() == IDLType.Tags.domstring 895 default = 'DOMString::from("%s")' % defaultValue.value 896 if type.nullable(): 897 default = "Some(%s)" % default 898 899 declType = "DOMString" 900 if type.nullable(): 901 declType = "Option<%s>" % declType 902 903 return handleOptional(conversionCode, CGGeneric(declType), default) 904 905 if type.isUSVString(): 906 assert not isEnforceRange and not isClamp 907 908 conversionCode = ( 909 "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" 910 " Ok(ConversionResult::Success(strval)) => strval,\n" 911 " Ok(ConversionResult::Failure(error)) => {\n" 912 "%s\n" 913 " }\n" 914 " _ => { %s },\n" 915 "}" % (indent(failOrPropagate, 8), exceptionCode)) 916 917 if defaultValue is None: 918 default = None 919 elif isinstance(defaultValue, IDLNullValue): 920 assert type.nullable() 921 default = "None" 922 else: 923 assert defaultValue.type.tag() in (IDLType.Tags.domstring, IDLType.Tags.usvstring) 924 default = 'USVString("%s".to_owned())' % defaultValue.value 925 if type.nullable(): 926 default = "Some(%s)" % default 927 928 declType = "USVString" 929 if type.nullable(): 930 declType = "Option<%s>" % declType 931 932 return handleOptional(conversionCode, CGGeneric(declType), default) 933 934 if type.isByteString(): 935 assert not isEnforceRange and not isClamp 936 937 conversionCode = ( 938 "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" 939 " Ok(ConversionResult::Success(strval)) => strval,\n" 940 " Ok(ConversionResult::Failure(error)) => {\n" 941 "%s\n" 942 " }\n" 943 " _ => { %s },\n" 944 "}" % (indent(failOrPropagate, 8), exceptionCode)) 945 946 if defaultValue is None: 947 default = None 948 elif isinstance(defaultValue, IDLNullValue): 949 assert type.nullable() 950 default = "None" 951 else: 952 assert defaultValue.type.tag() in (IDLType.Tags.domstring, IDLType.Tags.bytestring) 953 default = 'ByteString::new(b"%s".to_vec())' % defaultValue.value 954 if type.nullable(): 955 default = "Some(%s)" % default 956 957 declType = "ByteString" 958 if type.nullable(): 959 declType = "Option<%s>" % declType 960 961 return handleOptional(conversionCode, CGGeneric(declType), default) 962 963 if type.isEnum(): 964 assert not isEnforceRange and not isClamp 965 966 if type.nullable(): 967 raise TypeError("We don't support nullable enumerated arguments " 968 "yet") 969 enum = type.inner.identifier.name 970 if invalidEnumValueFatal: 971 handleInvalidEnumValueCode = onFailureInvalidEnumValue(failureCode, 'search').define() 972 else: 973 handleInvalidEnumValueCode = "return true;" 974 975 template = ( 976 "match find_enum_value(cx, ${val}, %(pairs)s) {\n" 977 " Err(_) => { %(exceptionCode)s },\n" 978 " Ok((None, search)) => { %(handleInvalidEnumValueCode)s },\n" 979 " Ok((Some(&value), _)) => value,\n" 980 "}" % {"pairs": enum + "Values::pairs", 981 "exceptionCode": exceptionCode, 982 "handleInvalidEnumValueCode": handleInvalidEnumValueCode}) 983 984 if defaultValue is not None: 985 assert defaultValue.type.tag() == IDLType.Tags.domstring 986 default = "%s::%s" % (enum, getEnumValueName(defaultValue.value)) 987 else: 988 default = None 989 990 return handleOptional(template, CGGeneric(enum), default) 991 992 if type.isCallback(): 993 assert not isEnforceRange and not isClamp 994 assert not type.treatNonCallableAsNull() 995 assert not type.treatNonObjectAsNull() or type.nullable() 996 assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull() 997 998 callback = type.unroll().callback 999 declType = CGGeneric(callback.identifier.name) 1000 finalDeclType = CGTemplatedType("Rc", declType) 1001 1002 conversion = CGCallbackTempRoot(declType.define()) 1003 1004 if type.nullable(): 1005 declType = CGTemplatedType("Option", declType) 1006 finalDeclType = CGTemplatedType("Option", finalDeclType) 1007 conversion = CGWrapper(conversion, pre="Some(", post=")") 1008 1009 if allowTreatNonObjectAsNull and type.treatNonObjectAsNull(): 1010 if not isDefinitelyObject: 1011 haveObject = "${val}.get().is_object()" 1012 template = CGIfElseWrapper(haveObject, 1013 conversion, 1014 CGGeneric("None")).define() 1015 else: 1016 template = conversion 1017 else: 1018 template = CGIfElseWrapper("IsCallable(${val}.get().to_object())", 1019 conversion, 1020 onFailureNotCallable(failureCode)).define() 1021 template = wrapObjectTemplate( 1022 template, 1023 "None", 1024 isDefinitelyObject, 1025 type, 1026 failureCode) 1027 1028 if defaultValue is not None: 1029 assert allowTreatNonObjectAsNull 1030 assert type.treatNonObjectAsNull() 1031 assert type.nullable() 1032 assert isinstance(defaultValue, IDLNullValue) 1033 default = "None" 1034 else: 1035 default = None 1036 1037 return JSToNativeConversionInfo(template, default, finalDeclType) 1038 1039 if type.isAny(): 1040 assert not isEnforceRange and not isClamp 1041 assert isMember != "Union" 1042 1043 if isMember == "Dictionary" or isAutoRooted: 1044 # TODO: Need to properly root dictionaries 1045 # https://github.com/servo/servo/issues/6381 1046 if isMember == "Dictionary": 1047 declType = CGGeneric("Heap<JSVal>") 1048 # AutoRooter can trace properly inner raw GC thing pointers 1049 else: 1050 declType = CGGeneric("JSVal") 1051 1052 if defaultValue is None: 1053 default = None 1054 elif isinstance(defaultValue, IDLNullValue): 1055 default = "NullValue()" 1056 elif isinstance(defaultValue, IDLUndefinedValue): 1057 default = "UndefinedValue()" 1058 else: 1059 raise TypeError("Can't handle non-null, non-undefined default value here") 1060 return handleOptional("${val}.get()", declType, default) 1061 1062 declType = CGGeneric("HandleValue") 1063 1064 if defaultValue is None: 1065 default = None 1066 elif isinstance(defaultValue, IDLNullValue): 1067 default = "HandleValue::null()" 1068 elif isinstance(defaultValue, IDLUndefinedValue): 1069 default = "HandleValue::undefined()" 1070 else: 1071 raise TypeError("Can't handle non-null, non-undefined default value here") 1072 1073 return handleOptional("${val}", declType, default) 1074 1075 if type.isObject(): 1076 assert not isEnforceRange and not isClamp 1077 1078 # TODO: Need to root somehow 1079 # https://github.com/servo/servo/issues/6382 1080 default = "ptr::null_mut()" 1081 templateBody = wrapObjectTemplate("${val}.get().to_object()", 1082 default, 1083 isDefinitelyObject, type, failureCode) 1084 1085 if isMember in ("Dictionary", "Union"): 1086 declType = CGGeneric("Heap<*mut JSObject>") 1087 else: 1088 # TODO: Need to root somehow 1089 # https://github.com/servo/servo/issues/6382 1090 declType = CGGeneric("*mut JSObject") 1091 1092 return handleOptional(templateBody, declType, 1093 handleDefaultNull(default)) 1094 1095 if type.isDictionary(): 1096 # There are no nullable dictionaries 1097 assert not type.nullable() 1098 1099 typeName = "%s::%s" % (CGDictionary.makeModuleName(type.inner), 1100 CGDictionary.makeDictionaryName(type.inner)) 1101 declType = CGGeneric(typeName) 1102 empty = "%s::empty(cx)" % typeName 1103 1104 if type_needs_tracing(type): 1105 declType = CGTemplatedType("RootedTraceableBox", declType) 1106 1107 template = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" 1108 " Ok(ConversionResult::Success(dictionary)) => dictionary,\n" 1109 " Ok(ConversionResult::Failure(error)) => {\n" 1110 "%s\n" 1111 " }\n" 1112 " _ => { %s },\n" 1113 "}" % (indent(failOrPropagate, 8), exceptionCode)) 1114 1115 return handleOptional(template, declType, handleDefaultNull(empty)) 1116 1117 if type.isVoid(): 1118 # This one only happens for return values, and its easy: Just 1119 # ignore the jsval. 1120 return JSToNativeConversionInfo("", None, None) 1121 1122 if not type.isPrimitive(): 1123 raise TypeError("Need conversion for argument type '%s'" % str(type)) 1124 1125 conversionBehavior = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs) 1126 1127 if failureCode is None: 1128 failureCode = 'return false' 1129 1130 declType = CGGeneric(builtinNames[type.tag()]) 1131 if type.nullable(): 1132 declType = CGWrapper(declType, pre="Option<", post=">") 1133 1134 template = ( 1135 "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" 1136 " Ok(ConversionResult::Success(v)) => v,\n" 1137 " Ok(ConversionResult::Failure(error)) => {\n" 1138 "%s\n" 1139 " }\n" 1140 " _ => { %s }\n" 1141 "}" % (conversionBehavior, indent(failOrPropagate, 8), exceptionCode)) 1142 1143 if defaultValue is not None: 1144 if isinstance(defaultValue, IDLNullValue): 1145 assert type.nullable() 1146 defaultStr = "None" 1147 else: 1148 tag = defaultValue.type.tag() 1149 if tag in [IDLType.Tags.float, IDLType.Tags.double]: 1150 defaultStr = "Finite::wrap(%s)" % defaultValue.value 1151 elif tag in numericTags: 1152 defaultStr = str(defaultValue.value) 1153 else: 1154 assert tag == IDLType.Tags.bool 1155 defaultStr = toStringBool(defaultValue.value) 1156 1157 if type.nullable(): 1158 defaultStr = "Some(%s)" % defaultStr 1159 else: 1160 defaultStr = None 1161 1162 return handleOptional(template, declType, defaultStr) 1163 1164 1165def instantiateJSToNativeConversionTemplate(templateBody, replacements, 1166 declType, declName): 1167 """ 1168 Take the templateBody and declType as returned by 1169 getJSToNativeConversionInfo, a set of replacements as required by the 1170 strings in such a templateBody, and a declName, and generate code to 1171 convert into a stack Rust binding with that name. 1172 """ 1173 result = CGList([], "\n") 1174 1175 conversion = CGGeneric(string.Template(templateBody).substitute(replacements)) 1176 1177 if declType is not None: 1178 newDecl = [ 1179 CGGeneric("let "), 1180 CGGeneric(declName), 1181 CGGeneric(": "), 1182 declType, 1183 CGGeneric(" = "), 1184 conversion, 1185 CGGeneric(";"), 1186 ] 1187 result.append(CGList(newDecl)) 1188 else: 1189 result.append(conversion) 1190 1191 # Add an empty CGGeneric to get an extra newline after the argument 1192 # conversion. 1193 result.append(CGGeneric("")) 1194 1195 return result 1196 1197 1198def convertConstIDLValueToJSVal(value): 1199 if isinstance(value, IDLNullValue): 1200 return "ConstantVal::NullVal" 1201 tag = value.type.tag() 1202 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, 1203 IDLType.Tags.uint16, IDLType.Tags.int32]: 1204 return "ConstantVal::IntVal(%s)" % (value.value) 1205 if tag == IDLType.Tags.uint32: 1206 return "ConstantVal::UintVal(%s)" % (value.value) 1207 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]: 1208 return "ConstantVal::DoubleVal(%s as f64)" % (value.value) 1209 if tag == IDLType.Tags.bool: 1210 return "ConstantVal::BoolVal(true)" if value.value else "ConstantVal::BoolVal(false)" 1211 if tag in [IDLType.Tags.unrestricted_float, IDLType.Tags.float, 1212 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: 1213 return "ConstantVal::DoubleVal(%s as f64)" % (value.value) 1214 raise TypeError("Const value of unhandled type: " + value.type) 1215 1216 1217class CGArgumentConverter(CGThing): 1218 """ 1219 A class that takes an IDL argument object, its index in the 1220 argument list, and the argv and argc strings and generates code to 1221 unwrap the argument to the right native type. 1222 """ 1223 def __init__(self, argument, index, args, argc, descriptorProvider, 1224 invalidEnumValueFatal=True): 1225 CGThing.__init__(self) 1226 assert not argument.defaultValue or argument.optional 1227 1228 replacer = { 1229 "index": index, 1230 "argc": argc, 1231 "args": args 1232 } 1233 1234 replacementVariables = { 1235 "val": string.Template("${args}.get(${index})").substitute(replacer), 1236 } 1237 1238 info = getJSToNativeConversionInfo( 1239 argument.type, 1240 descriptorProvider, 1241 invalidEnumValueFatal=invalidEnumValueFatal, 1242 defaultValue=argument.defaultValue, 1243 treatNullAs=argument.treatNullAs, 1244 isEnforceRange=argument.enforceRange, 1245 isClamp=argument.clamp, 1246 isMember="Variadic" if argument.variadic else False, 1247 isAutoRooted=type_needs_auto_root(argument.type), 1248 allowTreatNonObjectAsNull=argument.allowTreatNonCallableAsNull()) 1249 template = info.template 1250 default = info.default 1251 declType = info.declType 1252 1253 if not argument.variadic: 1254 if argument.optional: 1255 condition = "{args}.get({index}).is_undefined()".format(**replacer) 1256 if argument.defaultValue: 1257 assert default 1258 template = CGIfElseWrapper(condition, 1259 CGGeneric(default), 1260 CGGeneric(template)).define() 1261 else: 1262 assert not default 1263 declType = CGWrapper(declType, pre="Option<", post=">") 1264 template = CGIfElseWrapper(condition, 1265 CGGeneric("None"), 1266 CGGeneric("Some(%s)" % template)).define() 1267 else: 1268 assert not default 1269 1270 arg = "arg%d" % index 1271 1272 self.converter = instantiateJSToNativeConversionTemplate( 1273 template, replacementVariables, declType, arg) 1274 1275 # The auto rooting is done only after the conversion is performed 1276 if type_needs_auto_root(argument.type): 1277 self.converter.append(CGGeneric("auto_root!(in(cx) let %s = %s);" % (arg, arg))) 1278 1279 else: 1280 assert argument.optional 1281 variadicConversion = { 1282 "val": string.Template("${args}.get(variadicArg)").substitute(replacer), 1283 } 1284 innerConverter = [instantiateJSToNativeConversionTemplate( 1285 template, variadicConversion, declType, "slot")] 1286 1287 arg = "arg%d" % index 1288 if argument.type.isGeckoInterface(): 1289 init = "rooted_vec!(let mut %s)" % arg 1290 innerConverter.append(CGGeneric("%s.push(Dom::from_ref(&*slot));" % arg)) 1291 else: 1292 init = "let mut %s = vec![]" % arg 1293 innerConverter.append(CGGeneric("%s.push(slot);" % arg)) 1294 inner = CGIndenter(CGList(innerConverter, "\n"), 8).define() 1295 1296 self.converter = CGGeneric("""\ 1297%(init)s; 1298if %(argc)s > %(index)s { 1299 %(arg)s.reserve(%(argc)s as usize - %(index)s); 1300 for variadicArg in %(index)s..%(argc)s { 1301%(inner)s 1302 } 1303}""" % {'arg': arg, 'argc': argc, 'index': index, 'inner': inner, 'init': init}) 1304 1305 def define(self): 1306 return self.converter.define() 1307 1308 1309def wrapForType(jsvalRef, result='result', successCode='return true;', pre=''): 1310 """ 1311 Reflect a Rust value into JS. 1312 1313 * 'jsvalRef': a MutableHandleValue in which to store the result 1314 of the conversion; 1315 * 'result': the name of the variable in which the Rust value is stored; 1316 * 'successCode': the code to run once we have done the conversion. 1317 * 'pre': code to run before the conversion if rooting is necessary 1318 """ 1319 wrap = "%s\n(%s).to_jsval(cx, %s);" % (pre, result, jsvalRef) 1320 if successCode: 1321 wrap += "\n%s" % successCode 1322 return wrap 1323 1324 1325def typeNeedsCx(type, retVal=False): 1326 if type is None: 1327 return False 1328 if type.nullable(): 1329 type = type.inner 1330 if type.isSequence(): 1331 type = type.inner 1332 if type.isUnion(): 1333 return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes) 1334 if retVal and type.isSpiderMonkeyInterface(): 1335 return True 1336 return type.isAny() or type.isObject() 1337 1338 1339# Returns a conversion behavior suitable for a type 1340def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs): 1341 if type.isSequence() or type.isRecord(): 1342 return getConversionConfigForType(innerContainerType(type), isEnforceRange, isClamp, treatNullAs) 1343 if type.isDOMString(): 1344 assert not isEnforceRange and not isClamp 1345 1346 treatAs = { 1347 "Default": "StringificationBehavior::Default", 1348 "EmptyString": "StringificationBehavior::Empty", 1349 } 1350 if treatNullAs not in treatAs: 1351 raise TypeError("We don't support [TreatNullAs=%s]" % treatNullAs) 1352 if type.nullable(): 1353 # Note: the actual behavior passed here doesn't matter for nullable 1354 # strings. 1355 return "StringificationBehavior::Default" 1356 else: 1357 return treatAs[treatNullAs] 1358 if type.isPrimitive() and type.isInteger(): 1359 if isEnforceRange: 1360 return "ConversionBehavior::EnforceRange" 1361 elif isClamp: 1362 return "ConversionBehavior::Clamp" 1363 else: 1364 return "ConversionBehavior::Default" 1365 assert not isEnforceRange and not isClamp 1366 return "()" 1367 1368 1369# Returns a CGThing containing the type of the return value. 1370def getRetvalDeclarationForType(returnType, descriptorProvider): 1371 if returnType is None or returnType.isVoid(): 1372 # Nothing to declare 1373 return CGGeneric("()") 1374 if returnType.isPrimitive() and returnType.tag() in builtinNames: 1375 result = CGGeneric(builtinNames[returnType.tag()]) 1376 if returnType.nullable(): 1377 result = CGWrapper(result, pre="Option<", post=">") 1378 return result 1379 if returnType.isDOMString(): 1380 result = CGGeneric("DOMString") 1381 if returnType.nullable(): 1382 result = CGWrapper(result, pre="Option<", post=">") 1383 return result 1384 if returnType.isUSVString(): 1385 result = CGGeneric("USVString") 1386 if returnType.nullable(): 1387 result = CGWrapper(result, pre="Option<", post=">") 1388 return result 1389 if returnType.isByteString(): 1390 result = CGGeneric("ByteString") 1391 if returnType.nullable(): 1392 result = CGWrapper(result, pre="Option<", post=">") 1393 return result 1394 if returnType.isEnum(): 1395 result = CGGeneric(returnType.unroll().inner.identifier.name) 1396 if returnType.nullable(): 1397 result = CGWrapper(result, pre="Option<", post=">") 1398 return result 1399 if returnType.isPromise(): 1400 assert not returnType.nullable() 1401 return CGGeneric("Rc<Promise>") 1402 if returnType.isGeckoInterface(): 1403 descriptor = descriptorProvider.getDescriptor( 1404 returnType.unroll().inner.identifier.name) 1405 result = CGGeneric(descriptor.returnType) 1406 if returnType.nullable(): 1407 result = CGWrapper(result, pre="Option<", post=">") 1408 return result 1409 if returnType.isCallback(): 1410 callback = returnType.unroll().callback 1411 result = CGGeneric('Rc<%s::%s>' % (getModuleFromObject(callback), callback.identifier.name)) 1412 if returnType.nullable(): 1413 result = CGWrapper(result, pre="Option<", post=">") 1414 return result 1415 if returnType.isUnion(): 1416 result = CGGeneric(union_native_type(returnType)) 1417 if returnType.nullable(): 1418 result = CGWrapper(result, pre="Option<", post=">") 1419 return result 1420 # TODO: Return the value through a MutableHandleValue outparam 1421 # https://github.com/servo/servo/issues/6307 1422 if returnType.isAny(): 1423 return CGGeneric("JSVal") 1424 if returnType.isObject() or returnType.isSpiderMonkeyInterface(): 1425 result = CGGeneric("NonNull<JSObject>") 1426 if returnType.nullable(): 1427 result = CGWrapper(result, pre="Option<", post=">") 1428 return result 1429 if returnType.isSequence() or returnType.isRecord(): 1430 result = getRetvalDeclarationForType(innerContainerType(returnType), descriptorProvider) 1431 result = wrapInNativeContainerType(returnType, result) 1432 if returnType.nullable(): 1433 result = CGWrapper(result, pre="Option<", post=">") 1434 return result 1435 if returnType.isDictionary(): 1436 nullable = returnType.nullable() 1437 dictName = returnType.inner.name if nullable else returnType.name 1438 result = CGGeneric(dictName) 1439 if type_needs_tracing(returnType): 1440 result = CGWrapper(result, pre="RootedTraceableBox<", post=">") 1441 if nullable: 1442 result = CGWrapper(result, pre="Option<", post=">") 1443 return result 1444 1445 raise TypeError("Don't know how to declare return value for %s" % 1446 returnType) 1447 1448 1449def MemberCondition(pref, func): 1450 """ 1451 A string representing the condition for a member to actually be exposed. 1452 Any of the arguments can be None. If not None, they should have the 1453 following types: 1454 1455 pref: The name of the preference. 1456 func: The name of the function. 1457 """ 1458 assert pref is None or isinstance(pref, str) 1459 assert func is None or isinstance(func, str) 1460 assert func is None or pref is None 1461 if pref: 1462 return 'Condition::Pref("%s")' % pref 1463 if func: 1464 return 'Condition::Func(%s)' % func 1465 return "Condition::Satisfied" 1466 1467 1468class PropertyDefiner: 1469 """ 1470 A common superclass for defining things on prototype objects. 1471 1472 Subclasses should implement generateArray to generate the actual arrays of 1473 things we're defining. They should also set self.regular to the list of 1474 things exposed to web pages. 1475 """ 1476 def __init__(self, descriptor, name): 1477 self.descriptor = descriptor 1478 self.name = name 1479 1480 def variableName(self): 1481 return "s" + self.name 1482 1483 def length(self): 1484 return len(self.regular) 1485 1486 def __str__(self): 1487 # We only need to generate id arrays for things that will end 1488 # up used via ResolveProperty or EnumerateProperties. 1489 return self.generateArray(self.regular, self.variableName()) 1490 1491 @staticmethod 1492 def getStringAttr(member, name): 1493 attr = member.getExtendedAttribute(name) 1494 if attr is None: 1495 return None 1496 # It's a list of strings 1497 assert len(attr) == 1 1498 assert attr[0] is not None 1499 return attr[0] 1500 1501 @staticmethod 1502 def getControllingCondition(interfaceMember, descriptor): 1503 return MemberCondition( 1504 PropertyDefiner.getStringAttr(interfaceMember, 1505 "Pref"), 1506 PropertyDefiner.getStringAttr(interfaceMember, 1507 "Func")) 1508 1509 def generateGuardedArray(self, array, name, specTemplate, specTerminator, 1510 specType, getCondition, getDataTuple): 1511 """ 1512 This method generates our various arrays. 1513 1514 array is an array of interface members as passed to generateArray 1515 1516 name is the name as passed to generateArray 1517 1518 specTemplate is a template for each entry of the spec array 1519 1520 specTerminator is a terminator for the spec array (inserted at the end 1521 of the array), or None 1522 1523 specType is the actual typename of our spec 1524 1525 getDataTuple is a callback function that takes an array entry and 1526 returns a tuple suitable for substitution into specTemplate. 1527 """ 1528 1529 # We generate an all-encompassing list of lists of specs, with each sublist 1530 # representing a group of members that share a common pref name. That will 1531 # make sure the order of the properties as exposed on the interface and 1532 # interface prototype objects does not change when pref control is added to 1533 # members while still allowing us to define all the members in the smallest 1534 # number of JSAPI calls. 1535 assert len(array) != 0 1536 specs = [] 1537 prefableSpecs = [] 1538 prefableTemplate = ' Guard::new(%s, %s[%d])' 1539 1540 for cond, members in groupby(array, lambda m: getCondition(m, self.descriptor)): 1541 currentSpecs = [specTemplate % getDataTuple(m) for m in members] 1542 if specTerminator: 1543 currentSpecs.append(specTerminator) 1544 specs.append("&[\n" + ",\n".join(currentSpecs) + "]\n") 1545 prefableSpecs.append( 1546 prefableTemplate % (cond, name + "_specs", len(specs) - 1)) 1547 1548 specsArray = ("const %s_specs: &'static [&'static[%s]] = &[\n" + 1549 ",\n".join(specs) + "\n" + 1550 "];\n") % (name, specType) 1551 1552 prefArray = ("const %s: &'static [Guard<&'static [%s]>] = &[\n" + 1553 ",\n".join(prefableSpecs) + "\n" + 1554 "];\n") % (name, specType) 1555 return specsArray + prefArray 1556 1557 1558# The length of a method is the minimum of the lengths of the 1559# argument lists of all its overloads. 1560def methodLength(method): 1561 signatures = method.signatures() 1562 return min( 1563 len([arg for arg in arguments if not arg.optional and not arg.variadic]) 1564 for (_, arguments) in signatures) 1565 1566 1567class MethodDefiner(PropertyDefiner): 1568 """ 1569 A class for defining methods on a prototype object. 1570 """ 1571 def __init__(self, descriptor, name, static, unforgeable): 1572 assert not (static and unforgeable) 1573 PropertyDefiner.__init__(self, descriptor, name) 1574 1575 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822 1576 # We should be able to check for special operations without an 1577 # identifier. For now we check if the name starts with __ 1578 1579 # Ignore non-static methods for callback interfaces 1580 if not descriptor.interface.isCallback() or static: 1581 methods = [m for m in descriptor.interface.members if 1582 m.isMethod() and m.isStatic() == static and 1583 not m.isIdentifierLess() and 1584 MemberIsUnforgeable(m, descriptor) == unforgeable] 1585 else: 1586 methods = [] 1587 self.regular = [{"name": m.identifier.name, 1588 "methodInfo": not m.isStatic(), 1589 "length": methodLength(m), 1590 "condition": PropertyDefiner.getControllingCondition(m, descriptor)} 1591 for m in methods] 1592 1593 # FIXME Check for an existing iterator on the interface first. 1594 if any(m.isGetter() and m.isIndexed() for m in methods): 1595 self.regular.append({"name": '@@iterator', 1596 "methodInfo": False, 1597 "selfHostedName": "ArrayValues", 1598 "length": 0, 1599 "condition": "Condition::Satisfied"}) 1600 1601 # Generate the keys/values/entries aliases for value iterables. 1602 maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable 1603 if (not static and not unforgeable and 1604 (maplikeOrSetlikeOrIterable and 1605 maplikeOrSetlikeOrIterable.isIterable() and 1606 maplikeOrSetlikeOrIterable.isValueIterator())): 1607 # Add our keys/values/entries/forEach 1608 self.regular.append({ 1609 "name": "keys", 1610 "methodInfo": False, 1611 "selfHostedName": "ArrayKeys", 1612 "length": 0, 1613 "condition": PropertyDefiner.getControllingCondition(m, 1614 descriptor) 1615 }) 1616 self.regular.append({ 1617 "name": "values", 1618 "methodInfo": False, 1619 "selfHostedName": "ArrayValues", 1620 "length": 0, 1621 "condition": PropertyDefiner.getControllingCondition(m, 1622 descriptor) 1623 }) 1624 self.regular.append({ 1625 "name": "entries", 1626 "methodInfo": False, 1627 "selfHostedName": "ArrayEntries", 1628 "length": 0, 1629 "condition": PropertyDefiner.getControllingCondition(m, 1630 descriptor) 1631 }) 1632 self.regular.append({ 1633 "name": "forEach", 1634 "methodInfo": False, 1635 "selfHostedName": "ArrayForEach", 1636 "length": 1, 1637 "condition": PropertyDefiner.getControllingCondition(m, 1638 descriptor) 1639 }) 1640 1641 isUnforgeableInterface = bool(descriptor.interface.getExtendedAttribute("Unforgeable")) 1642 if not static and unforgeable == isUnforgeableInterface: 1643 stringifier = descriptor.operations['Stringifier'] 1644 if stringifier: 1645 self.regular.append({ 1646 "name": "toString", 1647 "nativeName": stringifier.identifier.name, 1648 "length": 0, 1649 "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor) 1650 }) 1651 self.unforgeable = unforgeable 1652 1653 def generateArray(self, array, name): 1654 if len(array) == 0: 1655 return "" 1656 1657 def condition(m, d): 1658 return m["condition"] 1659 1660 flags = "JSPROP_ENUMERATE" 1661 if self.unforgeable: 1662 flags += " | JSPROP_PERMANENT | JSPROP_READONLY" 1663 1664 def specData(m): 1665 # TODO: Use something like JS_FNSPEC 1666 # https://github.com/servo/servo/issues/6391 1667 if "selfHostedName" in m: 1668 selfHostedName = '%s as *const u8 as *const libc::c_char' % str_to_const_array(m["selfHostedName"]) 1669 assert not m.get("methodInfo", True) 1670 accessor = "None" 1671 jitinfo = "0 as *const JSJitInfo" 1672 else: 1673 selfHostedName = "0 as *const libc::c_char" 1674 if m.get("methodInfo", True): 1675 identifier = m.get("nativeName", m["name"]) 1676 # Go through an intermediate type here, because it's not 1677 # easy to tell whether the methodinfo is a JSJitInfo or 1678 # a JSTypedMethodJitInfo here. The compiler knows, though, 1679 # so let it do the work. 1680 jitinfo = "&%s_methodinfo as *const _ as *const JSJitInfo" % identifier 1681 accessor = "Some(generic_method)" 1682 else: 1683 jitinfo = "0 as *const JSJitInfo" 1684 accessor = 'Some(%s)' % m.get("nativeName", m["name"]) 1685 if m["name"].startswith("@@"): 1686 return ('(SymbolCode::%s as i32 + 1)' 1687 % m["name"][2:], accessor, jitinfo, m["length"], flags, selfHostedName) 1688 return (str_to_const_array(m["name"]), accessor, jitinfo, m["length"], flags, selfHostedName) 1689 1690 return self.generateGuardedArray( 1691 array, name, 1692 ' JSFunctionSpec {\n' 1693 ' name: %s as *const u8 as *const libc::c_char,\n' 1694 ' call: JSNativeWrapper { op: %s, info: %s },\n' 1695 ' nargs: %s,\n' 1696 ' flags: (%s) as u16,\n' 1697 ' selfHostedName: %s\n' 1698 ' }', 1699 ' JSFunctionSpec {\n' 1700 ' name: 0 as *const libc::c_char,\n' 1701 ' call: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo },\n' 1702 ' nargs: 0,\n' 1703 ' flags: 0,\n' 1704 ' selfHostedName: 0 as *const libc::c_char\n' 1705 ' }', 1706 'JSFunctionSpec', 1707 condition, specData) 1708 1709 1710class AttrDefiner(PropertyDefiner): 1711 def __init__(self, descriptor, name, static, unforgeable): 1712 assert not (static and unforgeable) 1713 PropertyDefiner.__init__(self, descriptor, name) 1714 self.name = name 1715 self.descriptor = descriptor 1716 self.regular = [ 1717 m 1718 for m in descriptor.interface.members if 1719 m.isAttr() and m.isStatic() == static and 1720 MemberIsUnforgeable(m, descriptor) == unforgeable 1721 ] 1722 self.static = static 1723 self.unforgeable = unforgeable 1724 1725 def generateArray(self, array, name): 1726 if len(array) == 0: 1727 return "" 1728 1729 flags = "JSPROP_ENUMERATE | JSPROP_SHARED" 1730 if self.unforgeable: 1731 flags += " | JSPROP_PERMANENT" 1732 1733 def getter(attr): 1734 if self.static: 1735 accessor = 'get_' + self.descriptor.internalNameFor(attr.identifier.name) 1736 jitinfo = "0 as *const JSJitInfo" 1737 else: 1738 if attr.hasLenientThis(): 1739 accessor = "generic_lenient_getter" 1740 else: 1741 accessor = "generic_getter" 1742 jitinfo = "&%s_getterinfo" % self.descriptor.internalNameFor(attr.identifier.name) 1743 1744 return ("JSNativeWrapper { op: Some(%(native)s), info: %(info)s }" 1745 % {"info": jitinfo, 1746 "native": accessor}) 1747 1748 def setter(attr): 1749 if (attr.readonly and not attr.getExtendedAttribute("PutForwards") 1750 and not attr.getExtendedAttribute("Replaceable")): 1751 return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }" 1752 1753 if self.static: 1754 accessor = 'set_' + self.descriptor.internalNameFor(attr.identifier.name) 1755 jitinfo = "0 as *const JSJitInfo" 1756 else: 1757 if attr.hasLenientThis(): 1758 accessor = "generic_lenient_setter" 1759 else: 1760 accessor = "generic_setter" 1761 jitinfo = "&%s_setterinfo" % self.descriptor.internalNameFor(attr.identifier.name) 1762 1763 return ("JSNativeWrapper { op: Some(%(native)s), info: %(info)s }" 1764 % {"info": jitinfo, 1765 "native": accessor}) 1766 1767 def specData(attr): 1768 return (str_to_const_array(attr.identifier.name), flags, getter(attr), 1769 setter(attr)) 1770 1771 return self.generateGuardedArray( 1772 array, name, 1773 ' JSPropertySpec {\n' 1774 ' name: %s as *const u8 as *const libc::c_char,\n' 1775 ' flags: (%s) as u8,\n' 1776 ' getter: %s,\n' 1777 ' setter: %s\n' 1778 ' }', 1779 ' JSPropertySpec {\n' 1780 ' name: 0 as *const libc::c_char,\n' 1781 ' flags: 0,\n' 1782 ' getter: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo },\n' 1783 ' setter: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }\n' 1784 ' }', 1785 'JSPropertySpec', 1786 PropertyDefiner.getControllingCondition, specData) 1787 1788 1789class ConstDefiner(PropertyDefiner): 1790 """ 1791 A class for definining constants on the interface object 1792 """ 1793 def __init__(self, descriptor, name): 1794 PropertyDefiner.__init__(self, descriptor, name) 1795 self.name = name 1796 self.regular = [m for m in descriptor.interface.members if m.isConst()] 1797 1798 def generateArray(self, array, name): 1799 if len(array) == 0: 1800 return "" 1801 1802 def specData(const): 1803 return (str_to_const_array(const.identifier.name), 1804 convertConstIDLValueToJSVal(const.value)) 1805 1806 return self.generateGuardedArray( 1807 array, name, 1808 ' ConstantSpec { name: %s, value: %s }', 1809 None, 1810 'ConstantSpec', 1811 PropertyDefiner.getControllingCondition, specData) 1812 1813# We'll want to insert the indent at the beginnings of lines, but we 1814# don't want to indent empty lines. So only indent lines that have a 1815# non-newline character on them. 1816lineStartDetector = re.compile("^(?=[^\n])", re.MULTILINE) 1817 1818 1819class CGIndenter(CGThing): 1820 """ 1821 A class that takes another CGThing and generates code that indents that 1822 CGThing by some number of spaces. The default indent is two spaces. 1823 """ 1824 def __init__(self, child, indentLevel=4): 1825 CGThing.__init__(self) 1826 self.child = child 1827 self.indent = " " * indentLevel 1828 1829 def define(self): 1830 defn = self.child.define() 1831 if defn != "": 1832 return re.sub(lineStartDetector, self.indent, defn) 1833 else: 1834 return defn 1835 1836 1837class CGWrapper(CGThing): 1838 """ 1839 Generic CGThing that wraps other CGThings with pre and post text. 1840 """ 1841 def __init__(self, child, pre="", post="", reindent=False): 1842 CGThing.__init__(self) 1843 self.child = child 1844 self.pre = pre 1845 self.post = post 1846 self.reindent = reindent 1847 1848 def define(self): 1849 defn = self.child.define() 1850 if self.reindent: 1851 # We don't use lineStartDetector because we don't want to 1852 # insert whitespace at the beginning of our _first_ line. 1853 defn = stripTrailingWhitespace( 1854 defn.replace("\n", "\n" + (" " * len(self.pre)))) 1855 return self.pre + defn + self.post 1856 1857 1858class CGImports(CGWrapper): 1859 """ 1860 Generates the appropriate import/use statements. 1861 """ 1862 def __init__(self, child, descriptors, callbacks, dictionaries, enums, typedefs, imports, config, 1863 ignored_warnings=None): 1864 """ 1865 Adds a set of imports. 1866 """ 1867 if ignored_warnings is None: 1868 ignored_warnings = [ 1869 'non_camel_case_types', 1870 'non_upper_case_globals', 1871 'unused_imports', 1872 'unused_variables', 1873 'unused_assignments', 1874 'unused_mut', 1875 ] 1876 1877 def componentTypes(type): 1878 if type.isType() and type.nullable(): 1879 type = type.unroll() 1880 if type.isUnion(): 1881 return type.flatMemberTypes 1882 if type.isDictionary(): 1883 return [type] + getTypesFromDictionary(type) 1884 if type.isSequence(): 1885 return componentTypes(type.inner) 1886 return [type] 1887 1888 def isImportable(type): 1889 if not type.isType(): 1890 assert (type.isInterface() or type.isDictionary() or 1891 type.isEnum() or type.isNamespace()) 1892 return True 1893 return not (type.builtin or type.isSequence() or type.isUnion()) 1894 1895 def relatedTypesForSignatures(method): 1896 types = [] 1897 for (returnType, arguments) in method.signatures(): 1898 types += componentTypes(returnType) 1899 for arg in arguments: 1900 types += componentTypes(arg.type) 1901 return types 1902 1903 def getIdentifier(t): 1904 if t.isType(): 1905 if t.nullable(): 1906 t = t.inner 1907 if t.isCallback(): 1908 return t.callback.identifier 1909 return t.identifier 1910 assert t.isInterface() or t.isDictionary() or t.isEnum() or t.isNamespace() 1911 return t.identifier 1912 1913 def removeWrapperAndNullableTypes(types): 1914 normalized = [] 1915 for t in types: 1916 while (t.isType() and t.nullable()) or isinstance(t, IDLWrapperType): 1917 t = t.inner 1918 if isImportable(t): 1919 normalized += [t] 1920 return normalized 1921 1922 types = [] 1923 for d in descriptors: 1924 if not d.interface.isCallback(): 1925 types += [d.interface] 1926 1927 if d.interface.isIteratorInterface(): 1928 types += [d.interface.iterableInterface] 1929 1930 members = d.interface.members + d.interface.namedConstructors 1931 constructor = d.interface.ctor() 1932 if constructor: 1933 members += [constructor] 1934 1935 if d.proxy: 1936 members += [o for o in d.operations.values() if o] 1937 1938 for m in members: 1939 if m.isMethod(): 1940 types += relatedTypesForSignatures(m) 1941 elif m.isAttr(): 1942 types += componentTypes(m.type) 1943 1944 # Import the type names used in the callbacks that are being defined. 1945 for c in callbacks: 1946 types += relatedTypesForSignatures(c) 1947 1948 # Import the type names used in the dictionaries that are being defined. 1949 for d in dictionaries: 1950 types += componentTypes(d) 1951 1952 # Import the type names used in the typedefs that are being defined. 1953 for t in typedefs: 1954 if not t.innerType.isCallback(): 1955 types += componentTypes(t.innerType) 1956 1957 # Normalize the types we've collected and remove any ones which can't be imported. 1958 types = removeWrapperAndNullableTypes(types) 1959 1960 descriptorProvider = config.getDescriptorProvider() 1961 extras = [] 1962 for t in types: 1963 # Importing these types in the same module that defines them is an error. 1964 if t in dictionaries or t in enums: 1965 continue 1966 if t.isInterface() or t.isNamespace(): 1967 name = getIdentifier(t).name 1968 descriptor = descriptorProvider.getDescriptor(name) 1969 if name != 'GlobalScope': 1970 extras += [descriptor.path] 1971 parentName = descriptor.getParentName() 1972 if parentName: 1973 descriptor = descriptorProvider.getDescriptor(parentName) 1974 extras += [descriptor.path, descriptor.bindingPath] 1975 elif t.isType() and t.isRecord(): 1976 extras += ['dom::bindings::mozmap::MozMap'] 1977 elif isinstance(t, IDLPromiseType): 1978 extras += ["dom::promise::Promise"] 1979 else: 1980 if t.isEnum(): 1981 extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values'] 1982 extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name] 1983 1984 statements = [] 1985 if len(ignored_warnings) > 0: 1986 statements.append('#![allow(%s)]' % ','.join(ignored_warnings)) 1987 statements.extend('use %s;' % i for i in sorted(set(imports + extras))) 1988 1989 CGWrapper.__init__(self, child, 1990 pre='\n'.join(statements) + '\n\n') 1991 1992 1993class CGIfWrapper(CGWrapper): 1994 def __init__(self, condition, child): 1995 pre = CGWrapper(CGGeneric(condition), pre="if ", post=" {\n", 1996 reindent=True) 1997 CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(), 1998 post="\n}") 1999 2000 2001class CGTemplatedType(CGWrapper): 2002 def __init__(self, templateName, child): 2003 CGWrapper.__init__(self, child, pre=templateName + "<", post=">") 2004 2005 2006class CGNamespace(CGWrapper): 2007 def __init__(self, namespace, child, public=False): 2008 pre = "%smod %s {\n" % ("pub " if public else "", namespace) 2009 post = "} // mod %s" % namespace 2010 CGWrapper.__init__(self, child, pre=pre, post=post) 2011 2012 @staticmethod 2013 def build(namespaces, child, public=False): 2014 """ 2015 Static helper method to build multiple wrapped namespaces. 2016 """ 2017 if not namespaces: 2018 return child 2019 inner = CGNamespace.build(namespaces[1:], child, public=public) 2020 return CGNamespace(namespaces[0], inner, public=public) 2021 2022 2023def DOMClassTypeId(desc): 2024 protochain = desc.prototypeChain 2025 inner = "" 2026 if desc.hasDescendants(): 2027 if desc.interface.getExtendedAttribute("Abstract"): 2028 return "::dom::bindings::codegen::InheritTypes::TopTypeId { abstract_: () }" 2029 name = desc.interface.identifier.name 2030 inner = "(::dom::bindings::codegen::InheritTypes::%sTypeId::%s)" % (name, name) 2031 elif len(protochain) == 1: 2032 return "::dom::bindings::codegen::InheritTypes::TopTypeId { alone: () }" 2033 reversed_protochain = list(reversed(protochain)) 2034 for (child, parent) in zip(reversed_protochain, reversed_protochain[1:]): 2035 inner = "(::dom::bindings::codegen::InheritTypes::%sTypeId::%s%s)" % (parent, child, inner) 2036 return "::dom::bindings::codegen::InheritTypes::TopTypeId { %s: %s }" % (protochain[0].lower(), inner) 2037 2038 2039def DOMClass(descriptor): 2040 protoList = ['PrototypeList::ID::' + proto for proto in descriptor.prototypeChain] 2041 # Pad out the list to the right length with ID::Last so we 2042 # guarantee that all the lists are the same length. ID::Last 2043 # is never the ID of any prototype, so it's safe to use as 2044 # padding. 2045 protoList.extend(['PrototypeList::ID::Last'] * (descriptor.config.maxProtoChainLength - len(protoList))) 2046 prototypeChainString = ', '.join(protoList) 2047 mallocSizeOf = 'malloc_size_of_including_raw_self::<%s>' % descriptor.concreteType 2048 if descriptor.isGlobal(): 2049 globals_ = camel_to_upper_snake(descriptor.name) 2050 else: 2051 globals_ = 'EMPTY' 2052 return """\ 2053DOMClass { 2054 interface_chain: [ %s ], 2055 type_id: %s, 2056 malloc_size_of: %s as unsafe fn(&mut _, _) -> _, 2057 global: InterfaceObjectMap::Globals::%s, 2058}""" % (prototypeChainString, DOMClassTypeId(descriptor), mallocSizeOf, globals_) 2059 2060 2061class CGDOMJSClass(CGThing): 2062 """ 2063 Generate a DOMJSClass for a given descriptor 2064 """ 2065 def __init__(self, descriptor): 2066 CGThing.__init__(self) 2067 self.descriptor = descriptor 2068 2069 def define(self): 2070 args = { 2071 "domClass": DOMClass(self.descriptor), 2072 "enumerateHook": "None", 2073 "finalizeHook": FINALIZE_HOOK_NAME, 2074 "flags": "0", 2075 "name": str_to_const_array(self.descriptor.interface.identifier.name), 2076 "resolveHook": "None", 2077 "slots": "1", 2078 "traceHook": TRACE_HOOK_NAME, 2079 } 2080 if self.descriptor.isGlobal(): 2081 assert not self.descriptor.weakReferenceable 2082 args["enumerateHook"] = "Some(enumerate_global)" 2083 args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL" 2084 args["slots"] = "JSCLASS_GLOBAL_SLOT_COUNT + 1" 2085 args["resolveHook"] = "Some(resolve_global)" 2086 args["traceHook"] = "js::jsapi::JS_GlobalObjectTraceHook" 2087 elif self.descriptor.weakReferenceable: 2088 args["slots"] = "2" 2089 return """\ 2090static CLASS_OPS: js::jsapi::JSClassOps = js::jsapi::JSClassOps { 2091 addProperty: None, 2092 delProperty: None, 2093 getProperty: None, 2094 setProperty: None, 2095 enumerate: %(enumerateHook)s, 2096 resolve: %(resolveHook)s, 2097 mayResolve: None, 2098 finalize: Some(%(finalizeHook)s), 2099 call: None, 2100 hasInstance: None, 2101 construct: None, 2102 trace: Some(%(traceHook)s), 2103}; 2104 2105static Class: DOMJSClass = DOMJSClass { 2106 base: js::jsapi::JSClass { 2107 name: %(name)s as *const u8 as *const libc::c_char, 2108 flags: JSCLASS_IS_DOMJSCLASS | %(flags)s | 2109 (((%(slots)s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT) 2110 /* JSCLASS_HAS_RESERVED_SLOTS(%(slots)s) */, 2111 cOps: &CLASS_OPS, 2112 reserved: [0 as *mut _; 3], 2113 }, 2114 dom_class: %(domClass)s 2115};""" % args 2116 2117 2118def str_to_const_array(s): 2119 return "b\"%s\\0\"" % s 2120 2121 2122class CGPrototypeJSClass(CGThing): 2123 def __init__(self, descriptor): 2124 CGThing.__init__(self) 2125 self.descriptor = descriptor 2126 2127 def define(self): 2128 name = str_to_const_array(self.descriptor.interface.identifier.name + "Prototype") 2129 slotCount = 0 2130 if self.descriptor.hasUnforgeableMembers: 2131 slotCount += 1 2132 return """\ 2133static PrototypeClass: JSClass = JSClass { 2134 name: %(name)s as *const u8 as *const libc::c_char, 2135 flags: 2136 // JSCLASS_HAS_RESERVED_SLOTS(%(slotCount)s) 2137 (%(slotCount)s & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT, 2138 cOps: 0 as *const _, 2139 reserved: [0 as *mut os::raw::c_void; 3] 2140}; 2141""" % {'name': name, 'slotCount': slotCount} 2142 2143 2144class CGInterfaceObjectJSClass(CGThing): 2145 def __init__(self, descriptor): 2146 assert descriptor.interface.hasInterfaceObject() and not descriptor.interface.isCallback() 2147 CGThing.__init__(self) 2148 self.descriptor = descriptor 2149 2150 def define(self): 2151 if self.descriptor.interface.isNamespace(): 2152 classString = self.descriptor.interface.getExtendedAttribute("ClassString") 2153 if classString: 2154 classString = classString[0] 2155 else: 2156 classString = "Object" 2157 return """\ 2158static NAMESPACE_OBJECT_CLASS: NamespaceObjectClass = unsafe { 2159 NamespaceObjectClass::new(%s) 2160}; 2161""" % str_to_const_array(classString) 2162 if self.descriptor.interface.ctor(): 2163 constructorBehavior = "InterfaceConstructorBehavior::call(%s)" % CONSTRUCT_HOOK_NAME 2164 else: 2165 constructorBehavior = "InterfaceConstructorBehavior::throw()" 2166 name = self.descriptor.interface.identifier.name 2167 args = { 2168 "constructorBehavior": constructorBehavior, 2169 "id": name, 2170 "representation": 'b"function %s() {\\n [native code]\\n}"' % name, 2171 "depth": self.descriptor.prototypeDepth 2172 } 2173 return """\ 2174static INTERFACE_OBJECT_CLASS: NonCallbackInterfaceObjectClass = 2175 NonCallbackInterfaceObjectClass::new( 2176 &%(constructorBehavior)s, 2177 %(representation)s, 2178 PrototypeList::ID::%(id)s, 2179 %(depth)s); 2180""" % args 2181 2182 2183class CGList(CGThing): 2184 """ 2185 Generate code for a list of GCThings. Just concatenates them together, with 2186 an optional joiner string. "\n" is a common joiner. 2187 """ 2188 def __init__(self, children, joiner=""): 2189 CGThing.__init__(self) 2190 # Make a copy of the kids into a list, because if someone passes in a 2191 # generator we won't be able to both declare and define ourselves, or 2192 # define ourselves more than once! 2193 self.children = list(children) 2194 self.joiner = joiner 2195 2196 def append(self, child): 2197 self.children.append(child) 2198 2199 def prepend(self, child): 2200 self.children.insert(0, child) 2201 2202 def join(self, iterable): 2203 return self.joiner.join(s for s in iterable if len(s) > 0) 2204 2205 def define(self): 2206 return self.join(child.define() for child in self.children if child is not None) 2207 2208 def __len__(self): 2209 return len(self.children) 2210 2211 2212class CGIfElseWrapper(CGList): 2213 def __init__(self, condition, ifTrue, ifFalse): 2214 kids = [CGIfWrapper(condition, ifTrue), 2215 CGWrapper(CGIndenter(ifFalse), pre=" else {\n", post="\n}")] 2216 CGList.__init__(self, kids) 2217 2218 2219class CGGeneric(CGThing): 2220 """ 2221 A class that spits out a fixed string into the codegen. Can spit out a 2222 separate string for the declaration too. 2223 """ 2224 def __init__(self, text): 2225 self.text = text 2226 2227 def define(self): 2228 return self.text 2229 2230 2231class CGCallbackTempRoot(CGGeneric): 2232 def __init__(self, name): 2233 CGGeneric.__init__(self, "%s::new(cx, ${val}.get().to_object())" % name) 2234 2235 2236def getAllTypes(descriptors, dictionaries, callbacks, typedefs): 2237 """ 2238 Generate all the types we're dealing with. For each type, a tuple 2239 containing type, descriptor, dictionary is yielded. The 2240 descriptor and dictionary can be None if the type does not come 2241 from a descriptor or dictionary; they will never both be non-None. 2242 """ 2243 for d in descriptors: 2244 for t in getTypesFromDescriptor(d): 2245 yield (t, d, None) 2246 for dictionary in dictionaries: 2247 for t in getTypesFromDictionary(dictionary): 2248 yield (t, None, dictionary) 2249 for callback in callbacks: 2250 for t in getTypesFromCallback(callback): 2251 yield (t, None, None) 2252 for typedef in typedefs: 2253 yield (typedef.innerType, None, None) 2254 2255 2256def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): 2257 """ 2258 Returns a CGList containing CGUnionStructs for every union. 2259 """ 2260 2261 imports = [ 2262 'dom', 2263 'dom::bindings::codegen::PrototypeList', 2264 'dom::bindings::conversions::ConversionResult', 2265 'dom::bindings::conversions::FromJSValConvertible', 2266 'dom::bindings::conversions::ToJSValConvertible', 2267 'dom::bindings::conversions::ConversionBehavior', 2268 'dom::bindings::conversions::StringificationBehavior', 2269 'dom::bindings::conversions::root_from_handlevalue', 2270 'dom::bindings::error::throw_not_in_union', 2271 'std::ptr::NonNull', 2272 'dom::bindings::mozmap::MozMap', 2273 'dom::bindings::root::DomRoot', 2274 'dom::bindings::str::ByteString', 2275 'dom::bindings::str::DOMString', 2276 'dom::bindings::str::USVString', 2277 'dom::bindings::trace::RootedTraceableBox', 2278 'dom::types::*', 2279 'js::error::throw_type_error', 2280 'js::jsapi::HandleValue', 2281 'js::jsapi::Heap', 2282 'js::jsapi::JSContext', 2283 'js::jsapi::JSObject', 2284 'js::jsapi::MutableHandleValue', 2285 'js::jsval::JSVal', 2286 ] 2287 2288 # Now find all the things we'll need as arguments and return values because 2289 # we need to wrap or unwrap them. 2290 unionStructs = dict() 2291 for (t, descriptor, dictionary) in getAllTypes(descriptors, dictionaries, callbacks, typedefs): 2292 if dictionary: 2293 imports.append("%s::%s" % (CGDictionary.makeModuleName(dictionary), 2294 CGDictionary.makeDictionaryName(dictionary))) 2295 t = t.unroll() 2296 if not t.isUnion(): 2297 continue 2298 name = str(t) 2299 if name not in unionStructs: 2300 provider = descriptor or config.getDescriptorProvider() 2301 unionStructs[name] = CGList([ 2302 CGUnionStruct(t, provider), 2303 CGUnionConversionStruct(t, provider) 2304 ]) 2305 2306 # Sort unionStructs by key, retrieve value 2307 unionStructs = (i[1] for i in sorted(unionStructs.items(), key=operator.itemgetter(0))) 2308 2309 return CGImports(CGList(unionStructs, "\n\n"), 2310 descriptors=[], 2311 callbacks=[], 2312 dictionaries=[], 2313 enums=[], 2314 typedefs=[], 2315 imports=imports, 2316 config=config, 2317 ignored_warnings=[]) 2318 2319 2320class Argument(): 2321 """ 2322 A class for outputting the type and name of an argument 2323 """ 2324 def __init__(self, argType, name, default=None, mutable=False): 2325 self.argType = argType 2326 self.name = name 2327 self.default = default 2328 self.mutable = mutable 2329 2330 def declare(self): 2331 string = ('mut ' if self.mutable else '') + self.name + ((': ' + self.argType) if self.argType else '') 2332 # XXXjdm Support default arguments somehow :/ 2333 # if self.default is not None: 2334 # string += " = " + self.default 2335 return string 2336 2337 def define(self): 2338 return self.argType + ' ' + self.name 2339 2340 2341class CGAbstractMethod(CGThing): 2342 """ 2343 An abstract class for generating code for a method. Subclasses 2344 should override definition_body to create the actual code. 2345 2346 descriptor is the descriptor for the interface the method is associated with 2347 2348 name is the name of the method as a string 2349 2350 returnType is the IDLType of the return value 2351 2352 args is a list of Argument objects 2353 2354 inline should be True to generate an inline method, whose body is 2355 part of the declaration. 2356 2357 alwaysInline should be True to generate an inline method annotated with 2358 MOZ_ALWAYS_INLINE. 2359 2360 If templateArgs is not None it should be a list of strings containing 2361 template arguments, and the function will be templatized using those 2362 arguments. 2363 2364 docs is None or documentation for the method in a string. 2365 2366 unsafe is used to add the decorator 'unsafe' to a function, giving as a result 2367 an 'unsafe fn()' declaration. 2368 """ 2369 def __init__(self, descriptor, name, returnType, args, inline=False, 2370 alwaysInline=False, extern=False, unsafe=False, pub=False, 2371 templateArgs=None, docs=None, doesNotPanic=False): 2372 CGThing.__init__(self) 2373 self.descriptor = descriptor 2374 self.name = name 2375 self.returnType = returnType 2376 self.args = args 2377 self.alwaysInline = alwaysInline 2378 self.extern = extern 2379 self.unsafe = extern or unsafe 2380 self.templateArgs = templateArgs 2381 self.pub = pub 2382 self.docs = docs 2383 self.catchPanic = self.extern and not doesNotPanic 2384 2385 def _argstring(self): 2386 return ', '.join([a.declare() for a in self.args]) 2387 2388 def _template(self): 2389 if self.templateArgs is None: 2390 return '' 2391 return '<%s>\n' % ', '.join(self.templateArgs) 2392 2393 def _docs(self): 2394 if self.docs is None: 2395 return '' 2396 2397 lines = self.docs.splitlines() 2398 return ''.join('/// %s\n' % line for line in lines) 2399 2400 def _decorators(self): 2401 decorators = [] 2402 if self.alwaysInline: 2403 decorators.append('#[inline]') 2404 2405 if self.pub: 2406 decorators.append('pub') 2407 2408 if self.unsafe: 2409 decorators.append('unsafe') 2410 2411 if self.extern: 2412 decorators.append('extern') 2413 2414 if not decorators: 2415 return '' 2416 return ' '.join(decorators) + ' ' 2417 2418 def _returnType(self): 2419 return (" -> %s" % self.returnType) if self.returnType != "void" else "" 2420 2421 def define(self): 2422 body = self.definition_body() 2423 2424 if self.catchPanic: 2425 body = CGWrapper(CGIndenter(body), 2426 pre="return wrap_panic(panic::AssertUnwindSafe(|| {\n", 2427 post=("""\n}), %s);""" % ("()" if self.returnType == "void" else "false"))) 2428 2429 return CGWrapper(CGIndenter(body), 2430 pre=self.definition_prologue(), 2431 post=self.definition_epilogue()).define() 2432 2433 def definition_prologue(self): 2434 return "%s%sfn %s%s(%s)%s {\n" % (self._docs(), self._decorators(), 2435 self.name, self._template(), 2436 self._argstring(), self._returnType()) 2437 2438 def definition_epilogue(self): 2439 return "\n}\n" 2440 2441 def definition_body(self): 2442 raise NotImplementedError # Override me! 2443 2444 2445class CGConstructorEnabled(CGAbstractMethod): 2446 """ 2447 A method for testing whether we should be exposing this interface object. 2448 This can perform various tests depending on what conditions are specified 2449 on the interface. 2450 """ 2451 def __init__(self, descriptor): 2452 CGAbstractMethod.__init__(self, descriptor, 2453 'ConstructorEnabled', 'bool', 2454 [Argument("*mut JSContext", "aCx"), 2455 Argument("HandleObject", "aObj")], 2456 unsafe=True) 2457 2458 def definition_body(self): 2459 conditions = [] 2460 iface = self.descriptor.interface 2461 2462 bits = " | ".join(sorted( 2463 "InterfaceObjectMap::Globals::" + camel_to_upper_snake(i) for i in iface.exposureSet 2464 )) 2465 conditions.append("is_exposed_in(aObj, %s)" % bits) 2466 2467 pref = iface.getExtendedAttribute("Pref") 2468 if pref: 2469 assert isinstance(pref, list) and len(pref) == 1 2470 conditions.append('PREFS.get("%s").as_boolean().unwrap_or(false)' % pref[0]) 2471 2472 func = iface.getExtendedAttribute("Func") 2473 if func: 2474 assert isinstance(func, list) and len(func) == 1 2475 conditions.append("%s(aCx, aObj)" % func[0]) 2476 2477 return CGList((CGGeneric(cond) for cond in conditions), " &&\n") 2478 2479 2480def CreateBindingJSObject(descriptor, parent=None): 2481 assert not descriptor.isGlobal() 2482 create = "let raw = Box::into_raw(object);\nlet _rt = RootedTraceable::new(&*raw);\n" 2483 if descriptor.proxy: 2484 create += """ 2485let handler = RegisterBindings::PROXY_HANDLERS[PrototypeList::Proxies::%s as usize]; 2486rooted!(in(cx) let private = PrivateValue(raw as *const libc::c_void)); 2487let obj = NewProxyObject(cx, handler, 2488 private.handle(), 2489 proto.get(), %s.get(), 2490 ptr::null_mut(), ptr::null_mut()); 2491assert!(!obj.is_null()); 2492rooted!(in(cx) let obj = obj);\ 2493""" % (descriptor.name, parent) 2494 else: 2495 create += ("rooted!(in(cx) let obj = JS_NewObjectWithGivenProto(\n" 2496 " cx, &Class.base as *const JSClass, proto.handle()));\n" 2497 "assert!(!obj.is_null());\n" 2498 "\n" 2499 "JS_SetReservedSlot(obj.get(), DOM_OBJECT_SLOT,\n" 2500 " PrivateValue(raw as *const libc::c_void));") 2501 if descriptor.weakReferenceable: 2502 create += """ 2503JS_SetReservedSlot(obj.get(), DOM_WEAK_SLOT, PrivateValue(ptr::null()));""" 2504 return create 2505 2506 2507def InitUnforgeablePropertiesOnHolder(descriptor, properties): 2508 """ 2509 Define the unforgeable properties on the unforgeable holder for 2510 the interface represented by descriptor. 2511 2512 properties is a PropertyArrays instance. 2513 """ 2514 unforgeables = [] 2515 2516 defineUnforgeableAttrs = "define_guarded_properties(cx, unforgeable_holder.handle(), %s);" 2517 defineUnforgeableMethods = "define_guarded_methods(cx, unforgeable_holder.handle(), %s);" 2518 2519 unforgeableMembers = [ 2520 (defineUnforgeableAttrs, properties.unforgeable_attrs), 2521 (defineUnforgeableMethods, properties.unforgeable_methods), 2522 ] 2523 for template, array in unforgeableMembers: 2524 if array.length() > 0: 2525 unforgeables.append(CGGeneric(template % array.variableName())) 2526 return CGList(unforgeables, "\n") 2527 2528 2529def CopyUnforgeablePropertiesToInstance(descriptor): 2530 """ 2531 Copy the unforgeable properties from the unforgeable holder for 2532 this interface to the instance object we have. 2533 """ 2534 if not descriptor.hasUnforgeableMembers: 2535 return "" 2536 copyCode = "" 2537 2538 # For proxies, we want to define on the expando object, not directly on the 2539 # reflector, so we can make sure we don't get confused by named getters. 2540 if descriptor.proxy: 2541 copyCode += """\ 2542rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>()); 2543ensure_expando_object(cx, obj.handle(), expando.handle_mut()); 2544""" 2545 obj = "expando" 2546 else: 2547 obj = "obj" 2548 2549 # We can't do the fast copy for globals, because we can't allocate the 2550 # unforgeable holder for those with the right JSClass. Luckily, there 2551 # aren't too many globals being created. 2552 if descriptor.isGlobal(): 2553 copyFunc = "JS_CopyPropertiesFrom" 2554 else: 2555 copyFunc = "JS_InitializePropertiesFromCompatibleNativeObject" 2556 copyCode += """\ 2557rooted!(in(cx) let mut unforgeable_holder = ptr::null_mut::<JSObject>()); 2558unforgeable_holder.handle_mut().set( 2559 JS_GetReservedSlot(proto.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT).to_object()); 2560assert!(%(copyFunc)s(cx, %(obj)s.handle(), unforgeable_holder.handle())); 2561""" % {'copyFunc': copyFunc, 'obj': obj} 2562 2563 return copyCode 2564 2565 2566class CGWrapMethod(CGAbstractMethod): 2567 """ 2568 Class that generates the FooBinding::Wrap function for non-callback 2569 interfaces. 2570 """ 2571 def __init__(self, descriptor): 2572 assert not descriptor.interface.isCallback() 2573 assert not descriptor.isGlobal() 2574 args = [Argument('*mut JSContext', 'cx'), 2575 Argument('&GlobalScope', 'scope'), 2576 Argument("Box<%s>" % descriptor.concreteType, 'object')] 2577 retval = 'DomRoot<%s>' % descriptor.concreteType 2578 CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, 2579 pub=True, unsafe=True) 2580 2581 def definition_body(self): 2582 unforgeable = CopyUnforgeablePropertiesToInstance(self.descriptor) 2583 create = CreateBindingJSObject(self.descriptor, "scope") 2584 return CGGeneric("""\ 2585let scope = scope.reflector().get_jsobject(); 2586assert!(!scope.get().is_null()); 2587assert!(((*get_object_class(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0); 2588 2589rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>()); 2590let _ac = JSAutoCompartment::new(cx, scope.get()); 2591GetProtoObject(cx, scope, proto.handle_mut()); 2592assert!(!proto.is_null()); 2593 2594%(createObject)s 2595 2596%(copyUnforgeable)s 2597(*raw).init_reflector(obj.get()); 2598 2599DomRoot::from_ref(&*raw)""" % {'copyUnforgeable': unforgeable, 'createObject': create}) 2600 2601 2602class CGWrapGlobalMethod(CGAbstractMethod): 2603 """ 2604 Class that generates the FooBinding::Wrap function for global interfaces. 2605 """ 2606 def __init__(self, descriptor, properties): 2607 assert not descriptor.interface.isCallback() 2608 assert descriptor.isGlobal() 2609 args = [Argument('*mut JSContext', 'cx'), 2610 Argument("Box<%s>" % descriptor.concreteType, 'object')] 2611 retval = 'DomRoot<%s>' % descriptor.concreteType 2612 CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, 2613 pub=True, unsafe=True) 2614 self.properties = properties 2615 2616 def definition_body(self): 2617 values = { 2618 "unforgeable": CopyUnforgeablePropertiesToInstance(self.descriptor) 2619 } 2620 2621 pairs = [ 2622 ("define_guarded_properties", self.properties.attrs), 2623 ("define_guarded_methods", self.properties.methods), 2624 ("define_guarded_constants", self.properties.consts) 2625 ] 2626 members = ["%s(cx, obj.handle(), %s);" % (function, array.variableName()) 2627 for (function, array) in pairs if array.length() > 0] 2628 values["members"] = "\n".join(members) 2629 2630 return CGGeneric("""\ 2631let raw = Box::into_raw(object); 2632let _rt = RootedTraceable::new(&*raw); 2633 2634rooted!(in(cx) let mut obj = ptr::null_mut::<JSObject>()); 2635create_global_object( 2636 cx, 2637 &Class.base, 2638 raw as *const libc::c_void, 2639 _trace, 2640 obj.handle_mut()); 2641assert!(!obj.is_null()); 2642 2643(*raw).init_reflector(obj.get()); 2644 2645let _ac = JSAutoCompartment::new(cx, obj.get()); 2646rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>()); 2647GetProtoObject(cx, obj.handle(), proto.handle_mut()); 2648assert!(JS_SplicePrototype(cx, obj.handle(), proto.handle())); 2649let mut immutable = false; 2650assert!(JS_SetImmutablePrototype(cx, obj.handle(), &mut immutable)); 2651assert!(immutable); 2652 2653%(members)s 2654 2655%(unforgeable)s 2656 2657DomRoot::from_ref(&*raw)\ 2658""" % values) 2659 2660 2661class CGIDLInterface(CGThing): 2662 """ 2663 Class for codegen of an implementation of the IDLInterface trait. 2664 """ 2665 def __init__(self, descriptor): 2666 CGThing.__init__(self) 2667 self.descriptor = descriptor 2668 2669 def define(self): 2670 interface = self.descriptor.interface 2671 name = self.descriptor.concreteType 2672 if (interface.getUserData("hasConcreteDescendant", False) or 2673 interface.getUserData("hasProxyDescendant", False)): 2674 depth = self.descriptor.prototypeDepth 2675 check = "class.interface_chain[%s] == PrototypeList::ID::%s" % (depth, name) 2676 elif self.descriptor.proxy: 2677 check = "class as *const _ == &Class as *const _" 2678 else: 2679 check = "class as *const _ == &Class.dom_class as *const _" 2680 return """\ 2681impl IDLInterface for %(name)s { 2682 #[inline] 2683 fn derives(class: &'static DOMClass) -> bool { 2684 %(check)s 2685 } 2686} 2687 2688impl PartialEq for %(name)s { 2689 fn eq(&self, other: &%(name)s) -> bool { 2690 self as *const %(name)s == &*other 2691 } 2692} 2693""" % {'check': check, 'name': name} 2694 2695 2696class CGAbstractExternMethod(CGAbstractMethod): 2697 """ 2698 Abstract base class for codegen of implementation-only (no 2699 declaration) static methods. 2700 """ 2701 def __init__(self, descriptor, name, returnType, args, doesNotPanic=False): 2702 CGAbstractMethod.__init__(self, descriptor, name, returnType, args, 2703 inline=False, extern=True, doesNotPanic=doesNotPanic) 2704 2705 2706class PropertyArrays(): 2707 def __init__(self, descriptor): 2708 self.static_methods = MethodDefiner(descriptor, "StaticMethods", 2709 static=True, unforgeable=False) 2710 self.static_attrs = AttrDefiner(descriptor, "StaticAttributes", 2711 static=True, unforgeable=False) 2712 self.methods = MethodDefiner(descriptor, "Methods", static=False, unforgeable=False) 2713 self.unforgeable_methods = MethodDefiner(descriptor, "UnforgeableMethods", 2714 static=False, unforgeable=True) 2715 self.attrs = AttrDefiner(descriptor, "Attributes", static=False, unforgeable=False) 2716 self.unforgeable_attrs = AttrDefiner(descriptor, "UnforgeableAttributes", 2717 static=False, unforgeable=True) 2718 self.consts = ConstDefiner(descriptor, "Constants") 2719 pass 2720 2721 @staticmethod 2722 def arrayNames(): 2723 return [ 2724 "static_methods", 2725 "static_attrs", 2726 "methods", 2727 "unforgeable_methods", 2728 "attrs", 2729 "unforgeable_attrs", 2730 "consts", 2731 ] 2732 2733 def variableNames(self): 2734 names = {} 2735 for array in self.arrayNames(): 2736 names[array] = getattr(self, array).variableName() 2737 return names 2738 2739 def __str__(self): 2740 define = "" 2741 for array in self.arrayNames(): 2742 define += str(getattr(self, array)) 2743 return define 2744 2745 2746class CGCreateInterfaceObjectsMethod(CGAbstractMethod): 2747 """ 2748 Generate the CreateInterfaceObjects method for an interface descriptor. 2749 2750 properties should be a PropertyArrays instance. 2751 """ 2752 def __init__(self, descriptor, properties, haveUnscopables): 2753 args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'global'), 2754 Argument('*mut ProtoOrIfaceArray', 'cache')] 2755 CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args, 2756 unsafe=True) 2757 self.properties = properties 2758 self.haveUnscopables = haveUnscopables 2759 2760 def definition_body(self): 2761 name = self.descriptor.interface.identifier.name 2762 if self.descriptor.interface.isNamespace(): 2763 if self.descriptor.interface.getExtendedAttribute("ProtoObjectHack"): 2764 proto = "JS_GetObjectPrototype(cx, global)" 2765 else: 2766 proto = "JS_NewPlainObject(cx)" 2767 if self.properties.static_methods.length(): 2768 methods = self.properties.static_methods.variableName() 2769 else: 2770 methods = "&[]" 2771 return CGGeneric("""\ 2772rooted!(in(cx) let proto = %(proto)s); 2773assert!(!proto.is_null()); 2774rooted!(in(cx) let mut namespace = ptr::null_mut::<JSObject>()); 2775create_namespace_object(cx, global, proto.handle(), &NAMESPACE_OBJECT_CLASS, 2776 %(methods)s, %(name)s, namespace.handle_mut()); 2777assert!(!namespace.is_null()); 2778assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); 2779(*cache)[PrototypeList::Constructor::%(id)s as usize] = namespace.get(); 2780<*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize), 2781 ptr::null_mut(), 2782 namespace.get()); 2783""" % {"id": MakeNativeName(name), "methods": methods, "name": str_to_const_array(name), "proto": proto}) 2784 if self.descriptor.interface.isCallback(): 2785 assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants() 2786 return CGGeneric("""\ 2787rooted!(in(cx) let mut interface = ptr::null_mut::<JSObject>()); 2788create_callback_interface_object(cx, global, sConstants, %(name)s, interface.handle_mut()); 2789assert!(!interface.is_null()); 2790assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); 2791(*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.get(); 2792<*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize), 2793 ptr::null_mut(), 2794 interface.get()); 2795""" % {"id": name, "name": str_to_const_array(name)}) 2796 2797 parentName = self.descriptor.getParentName() 2798 if not parentName: 2799 if self.descriptor.interface.getExtendedAttribute("ExceptionClass"): 2800 getPrototypeProto = "prototype_proto.set(JS_GetErrorPrototype(cx))" 2801 elif self.descriptor.interface.isIteratorInterface(): 2802 getPrototypeProto = "prototype_proto.set(JS_GetIteratorPrototype(cx))" 2803 else: 2804 getPrototypeProto = "prototype_proto.set(JS_GetObjectPrototype(cx, global))" 2805 else: 2806 getPrototypeProto = ("%s::GetProtoObject(cx, global, prototype_proto.handle_mut())" % 2807 toBindingNamespace(parentName)) 2808 2809 code = [CGGeneric("""\ 2810rooted!(in(cx) let mut prototype_proto = ptr::null_mut::<JSObject>()); 2811%s; 2812assert!(!prototype_proto.is_null());""" % getPrototypeProto)] 2813 2814 properties = { 2815 "id": name, 2816 "unscopables": "unscopable_names" if self.haveUnscopables else "&[]" 2817 } 2818 for arrayName in self.properties.arrayNames(): 2819 array = getattr(self.properties, arrayName) 2820 if array.length(): 2821 properties[arrayName] = array.variableName() 2822 else: 2823 properties[arrayName] = "&[]" 2824 2825 if self.descriptor.isGlobal(): 2826 assert not self.haveUnscopables 2827 proto_properties = { 2828 "attrs": "&[]", 2829 "consts": "&[]", 2830 "id": name, 2831 "methods": "&[]", 2832 "unscopables": "&[]", 2833 } 2834 else: 2835 proto_properties = properties 2836 2837 code.append(CGGeneric(""" 2838rooted!(in(cx) let mut prototype = ptr::null_mut::<JSObject>()); 2839create_interface_prototype_object(cx, 2840 prototype_proto.handle(), 2841 &PrototypeClass, 2842 %(methods)s, 2843 %(attrs)s, 2844 %(consts)s, 2845 %(unscopables)s, 2846 prototype.handle_mut()); 2847assert!(!prototype.is_null()); 2848assert!((*cache)[PrototypeList::ID::%(id)s as usize].is_null()); 2849(*cache)[PrototypeList::ID::%(id)s as usize] = prototype.get(); 2850<*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::ID::%(id)s as isize), 2851 ptr::null_mut(), 2852 prototype.get()); 2853""" % proto_properties)) 2854 2855 if self.descriptor.interface.hasInterfaceObject(): 2856 properties["name"] = str_to_const_array(name) 2857 if self.descriptor.interface.ctor(): 2858 properties["length"] = methodLength(self.descriptor.interface.ctor()) 2859 else: 2860 properties["length"] = 0 2861 parentName = self.descriptor.getParentName() 2862 if parentName: 2863 parentName = toBindingNamespace(parentName) 2864 code.append(CGGeneric(""" 2865rooted!(in(cx) let mut interface_proto = ptr::null_mut::<JSObject>()); 2866%s::GetConstructorObject(cx, global, interface_proto.handle_mut());""" % parentName)) 2867 else: 2868 code.append(CGGeneric(""" 2869rooted!(in(cx) let interface_proto = JS_GetFunctionPrototype(cx, global));""")) 2870 code.append(CGGeneric("""\ 2871assert!(!interface_proto.is_null()); 2872 2873rooted!(in(cx) let mut interface = ptr::null_mut::<JSObject>()); 2874create_noncallback_interface_object(cx, 2875 global, 2876 interface_proto.handle(), 2877 &INTERFACE_OBJECT_CLASS, 2878 %(static_methods)s, 2879 %(static_attrs)s, 2880 %(consts)s, 2881 prototype.handle(), 2882 %(name)s, 2883 %(length)s, 2884 interface.handle_mut()); 2885assert!(!interface.is_null());""" % properties)) 2886 if self.descriptor.shouldCacheConstructor(): 2887 code.append(CGGeneric("""\ 2888assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); 2889(*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.get(); 2890<*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize), 2891 ptr::null_mut(), 2892 interface.get()); 2893""" % properties)) 2894 2895 aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases] 2896 if aliasedMembers: 2897 def defineAlias(alias): 2898 if alias == "@@iterator": 2899 symbolJSID = "RUST_SYMBOL_TO_JSID(GetWellKnownSymbol(cx, SymbolCode::iterator))" 2900 getSymbolJSID = CGGeneric(fill("rooted!(in(cx) let iteratorId = ${symbolJSID});", 2901 symbolJSID=symbolJSID)) 2902 defineFn = "JS_DefinePropertyById2" 2903 prop = "iteratorId.handle()" 2904 elif alias.startswith("@@"): 2905 raise TypeError("Can't handle any well-known Symbol other than @@iterator") 2906 else: 2907 getSymbolJSID = None 2908 defineFn = "JS_DefineProperty" 2909 prop = '"%s"' % alias 2910 return CGList([ 2911 getSymbolJSID, 2912 # XXX If we ever create non-enumerable properties that can 2913 # be aliased, we should consider making the aliases 2914 # match the enumerability of the property being aliased. 2915 CGGeneric(fill( 2916 """ 2917 assert!(${defineFn}(cx, prototype.handle(), ${prop}, aliasedVal.handle(), 2918 JSPROP_ENUMERATE, None, None)); 2919 """, 2920 defineFn=defineFn, 2921 prop=prop)) 2922 ], "\n") 2923 2924 def defineAliasesFor(m): 2925 return CGList([ 2926 CGGeneric(fill( 2927 """ 2928 assert!(JS_GetProperty(cx, prototype.handle(), 2929 ${prop} as *const u8 as *const _, 2930 aliasedVal.handle_mut())); 2931 """, 2932 prop=str_to_const_array(m.identifier.name))) 2933 ] + [defineAlias(alias) for alias in sorted(m.aliases)]) 2934 2935 defineAliases = CGList([ 2936 CGGeneric(fill(""" 2937 // Set up aliases on the interface prototype object we just created. 2938 2939 """)), 2940 CGGeneric("rooted!(in(cx) let mut aliasedVal = UndefinedValue());\n\n") 2941 ] + [defineAliasesFor(m) for m in sorted(aliasedMembers)]) 2942 code.append(defineAliases) 2943 2944 constructors = self.descriptor.interface.namedConstructors 2945 if constructors: 2946 decl = "let named_constructors: [(ConstructorClassHook, &'static [u8], u32); %d]" % len(constructors) 2947 specs = [] 2948 for constructor in constructors: 2949 hook = CONSTRUCT_HOOK_NAME + "_" + constructor.identifier.name 2950 name = str_to_const_array(constructor.identifier.name) 2951 length = methodLength(constructor) 2952 specs.append(CGGeneric("(%s as ConstructorClassHook, %s, %d)" % (hook, name, length))) 2953 values = CGIndenter(CGList(specs, "\n"), 4) 2954 code.append(CGWrapper(values, pre="%s = [\n" % decl, post="\n];")) 2955 code.append(CGGeneric("create_named_constructors(cx, global, &named_constructors, prototype.handle());")) 2956 2957 if self.descriptor.hasUnforgeableMembers: 2958 # We want to use the same JSClass and prototype as the object we'll 2959 # end up defining the unforgeable properties on in the end, so that 2960 # we can use JS_InitializePropertiesFromCompatibleNativeObject to do 2961 # a fast copy. In the case of proxies that's null, because the 2962 # expando object is a vanilla object, but in the case of other DOM 2963 # objects it's whatever our class is. 2964 # 2965 # Also, for a global we can't use the global's class; just use 2966 # nullpr and when we do the copy off the holder we'll take a slower 2967 # path. This also means that we don't need to worry about matching 2968 # the prototype. 2969 if self.descriptor.proxy or self.descriptor.isGlobal(): 2970 holderClass = "ptr::null()" 2971 holderProto = "HandleObject::null()" 2972 else: 2973 holderClass = "&Class.base as *const JSClass" 2974 holderProto = "prototype.handle()" 2975 code.append(CGGeneric(""" 2976rooted!(in(cx) let mut unforgeable_holder = ptr::null_mut::<JSObject>()); 2977unforgeable_holder.handle_mut().set( 2978 JS_NewObjectWithoutMetadata(cx, %(holderClass)s, %(holderProto)s)); 2979assert!(!unforgeable_holder.is_null()); 2980""" % {'holderClass': holderClass, 'holderProto': holderProto})) 2981 code.append(InitUnforgeablePropertiesOnHolder(self.descriptor, self.properties)) 2982 code.append(CGGeneric("""\ 2983JS_SetReservedSlot(prototype.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, 2984 ObjectValue(unforgeable_holder.get()))""")) 2985 2986 return CGList(code, "\n") 2987 2988 2989class CGGetPerInterfaceObject(CGAbstractMethod): 2990 """ 2991 A method for getting a per-interface object (a prototype object or interface 2992 constructor object). 2993 """ 2994 def __init__(self, descriptor, name, idPrefix="", pub=False): 2995 args = [Argument('*mut JSContext', 'cx'), 2996 Argument('HandleObject', 'global'), 2997 Argument('MutableHandleObject', 'rval')] 2998 CGAbstractMethod.__init__(self, descriptor, name, 2999 'void', args, pub=pub, unsafe=True) 3000 self.id = idPrefix + "::" + MakeNativeName(self.descriptor.name) 3001 3002 def definition_body(self): 3003 return CGGeneric(""" 3004assert!(((*get_object_class(global.get())).flags & JSCLASS_DOM_GLOBAL) != 0); 3005 3006/* Check to see whether the interface objects are already installed */ 3007let proto_or_iface_array = get_proto_or_iface_array(global.get()); 3008rval.set((*proto_or_iface_array)[%(id)s as usize]); 3009if !rval.get().is_null() { 3010 return; 3011} 3012 3013CreateInterfaceObjects(cx, global, proto_or_iface_array); 3014rval.set((*proto_or_iface_array)[%(id)s as usize]); 3015assert!(!rval.get().is_null()); 3016""" % {"id": self.id}) 3017 3018 3019class CGGetProtoObjectMethod(CGGetPerInterfaceObject): 3020 """ 3021 A method for getting the interface prototype object. 3022 """ 3023 def __init__(self, descriptor): 3024 CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject", 3025 "PrototypeList::ID", pub=True) 3026 3027 def definition_body(self): 3028 return CGList([ 3029 CGGeneric("""\ 3030/* Get the interface prototype object for this class. This will create the 3031 object as needed. */"""), 3032 CGGetPerInterfaceObject.definition_body(self), 3033 ]) 3034 3035 3036class CGGetConstructorObjectMethod(CGGetPerInterfaceObject): 3037 """ 3038 A method for getting the interface constructor object. 3039 """ 3040 def __init__(self, descriptor): 3041 CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject", 3042 "PrototypeList::Constructor", 3043 pub=True) 3044 3045 def definition_body(self): 3046 return CGList([ 3047 CGGeneric("""\ 3048/* Get the interface object for this class. This will create the object as 3049 needed. */"""), 3050 CGGetPerInterfaceObject.definition_body(self), 3051 ]) 3052 3053 3054class CGDefineProxyHandler(CGAbstractMethod): 3055 """ 3056 A method to create and cache the proxy trap for a given interface. 3057 """ 3058 def __init__(self, descriptor): 3059 assert descriptor.proxy 3060 CGAbstractMethod.__init__(self, descriptor, 'DefineProxyHandler', 3061 '*const libc::c_void', [], 3062 pub=True, unsafe=True) 3063 3064 def define(self): 3065 return CGAbstractMethod.define(self) 3066 3067 def definition_body(self): 3068 customDefineProperty = 'proxyhandler::define_property' 3069 if self.descriptor.operations['IndexedSetter'] or self.descriptor.operations['NamedSetter']: 3070 customDefineProperty = 'defineProperty' 3071 3072 customDelete = 'proxyhandler::delete' 3073 if self.descriptor.operations['NamedDeleter']: 3074 customDelete = 'delete' 3075 3076 getOwnEnumerablePropertyKeys = "own_property_keys" 3077 if self.descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"): 3078 getOwnEnumerablePropertyKeys = "getOwnEnumerablePropertyKeys" 3079 3080 args = { 3081 "defineProperty": customDefineProperty, 3082 "delete": customDelete, 3083 "getOwnEnumerablePropertyKeys": getOwnEnumerablePropertyKeys, 3084 "trace": TRACE_HOOK_NAME, 3085 "finalize": FINALIZE_HOOK_NAME, 3086 } 3087 3088 return CGGeneric("""\ 3089let traps = ProxyTraps { 3090 enter: None, 3091 getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor), 3092 defineProperty: Some(%(defineProperty)s), 3093 ownPropertyKeys: Some(own_property_keys), 3094 delete_: Some(%(delete)s), 3095 enumerate: None, 3096 getPrototypeIfOrdinary: Some(proxyhandler::get_prototype_if_ordinary), 3097 preventExtensions: Some(proxyhandler::prevent_extensions), 3098 isExtensible: Some(proxyhandler::is_extensible), 3099 has: None, 3100 get: Some(get), 3101 set: None, 3102 call: None, 3103 construct: None, 3104 getPropertyDescriptor: Some(get_property_descriptor), 3105 hasOwn: Some(hasOwn), 3106 getOwnEnumerablePropertyKeys: Some(%(getOwnEnumerablePropertyKeys)s), 3107 nativeCall: None, 3108 hasInstance: None, 3109 objectClassIs: None, 3110 className: Some(className), 3111 fun_toString: None, 3112 boxedValue_unbox: None, 3113 defaultValue: None, 3114 trace: Some(%(trace)s), 3115 finalize: Some(%(finalize)s), 3116 objectMoved: None, 3117 isCallable: None, 3118 isConstructor: None, 3119}; 3120 3121CreateProxyHandler(&traps, Class.as_void_ptr())\ 3122""" % args) 3123 3124 3125class CGDefineDOMInterfaceMethod(CGAbstractMethod): 3126 """ 3127 A method for resolve hooks to try to lazily define the interface object for 3128 a given interface. 3129 """ 3130 def __init__(self, descriptor): 3131 assert descriptor.interface.hasInterfaceObject() 3132 args = [ 3133 Argument('*mut JSContext', 'cx'), 3134 Argument('HandleObject', 'global'), 3135 ] 3136 CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 3137 'void', args, pub=True, unsafe=True) 3138 3139 def define(self): 3140 return CGAbstractMethod.define(self) 3141 3142 def definition_body(self): 3143 if self.descriptor.interface.isCallback() or self.descriptor.interface.isNamespace(): 3144 function = "GetConstructorObject" 3145 else: 3146 function = "GetProtoObject" 3147 return CGGeneric("""\ 3148assert!(!global.get().is_null()); 3149 3150if !ConstructorEnabled(cx, global) { 3151 return; 3152} 3153 3154rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>()); 3155%s(cx, global, proto.handle_mut()); 3156assert!(!proto.is_null());""" % (function,)) 3157 3158 3159def needCx(returnType, arguments, considerTypes): 3160 return (considerTypes and 3161 (typeNeedsCx(returnType, True) or 3162 any(typeNeedsCx(a.type) for a in arguments))) 3163 3164 3165class CGCallGenerator(CGThing): 3166 """ 3167 A class to generate an actual call to a C++ object. Assumes that the C++ 3168 object is stored in a variable whose name is given by the |object| argument. 3169 3170 errorResult should be a string for the value to return in case of an 3171 exception from the native code, or None if no error reporting is needed. 3172 """ 3173 def __init__(self, errorResult, arguments, argsPre, returnType, 3174 extendedAttributes, descriptor, nativeMethodName, 3175 static, object="this", hasCEReactions=False): 3176 CGThing.__init__(self) 3177 3178 assert errorResult is None or isinstance(errorResult, str) 3179 3180 isFallible = errorResult is not None 3181 3182 result = getRetvalDeclarationForType(returnType, descriptor) 3183 if isFallible: 3184 result = CGWrapper(result, pre="Result<", post=", Error>") 3185 3186 args = CGList([CGGeneric(arg) for arg in argsPre], ", ") 3187 for (a, name) in arguments: 3188 # XXXjdm Perhaps we should pass all nontrivial types by borrowed pointer 3189 if a.type.isDictionary() and not type_needs_tracing(a.type): 3190 name = "&" + name 3191 args.append(CGGeneric(name)) 3192 3193 needsCx = needCx(returnType, (a for (a, _) in arguments), True) 3194 3195 if "cx" not in argsPre and needsCx: 3196 args.prepend(CGGeneric("cx")) 3197 3198 # Build up our actual call 3199 self.cgRoot = CGList([], "\n") 3200 3201 call = CGGeneric(nativeMethodName) 3202 if static: 3203 call = CGWrapper(call, pre="%s::" % MakeNativeName(descriptor.interface.identifier.name)) 3204 else: 3205 call = CGWrapper(call, pre="%s." % object) 3206 call = CGList([call, CGWrapper(args, pre="(", post=")")]) 3207 3208 if hasCEReactions: 3209 self.cgRoot.append(CGGeneric("push_new_element_queue();\n")) 3210 3211 self.cgRoot.append(CGList([ 3212 CGGeneric("let result: "), 3213 result, 3214 CGGeneric(" = "), 3215 call, 3216 CGGeneric(";"), 3217 ])) 3218 3219 if hasCEReactions: 3220 self.cgRoot.append(CGGeneric("pop_current_element_queue();\n")) 3221 3222 if isFallible: 3223 if static: 3224 glob = "global.upcast::<GlobalScope>()" 3225 else: 3226 glob = "&this.global()" 3227 3228 self.cgRoot.append(CGGeneric( 3229 "let result = match result {\n" 3230 " Ok(result) => result,\n" 3231 " Err(e) => {\n" 3232 " throw_dom_exception(cx, %s, e);\n" 3233 " return%s;\n" 3234 " },\n" 3235 "};" % (glob, errorResult))) 3236 3237 def define(self): 3238 return self.cgRoot.define() 3239 3240 3241class CGPerSignatureCall(CGThing): 3242 """ 3243 This class handles the guts of generating code for a particular 3244 call signature. A call signature consists of four things: 3245 3246 1) A return type, which can be None to indicate that there is no 3247 actual return value (e.g. this is an attribute setter) or an 3248 IDLType if there's an IDL type involved (including |void|). 3249 2) An argument list, which is allowed to be empty. 3250 3) A name of a native method to call. 3251 4) Whether or not this method is static. 3252 3253 We also need to know whether this is a method or a getter/setter 3254 to do error reporting correctly. 3255 3256 The idlNode parameter can be either a method or an attr. We can query 3257 |idlNode.identifier| in both cases, so we can be agnostic between the two. 3258 """ 3259 # XXXbz For now each entry in the argument list is either an 3260 # IDLArgument or a FakeArgument, but longer-term we may want to 3261 # have ways of flagging things like JSContext* or optional_argc in 3262 # there. 3263 3264 def __init__(self, returnType, argsPre, arguments, nativeMethodName, static, 3265 descriptor, idlNode, argConversionStartsAt=0, 3266 getter=False, setter=False): 3267 CGThing.__init__(self) 3268 self.returnType = returnType 3269 self.descriptor = descriptor 3270 self.idlNode = idlNode 3271 self.extendedAttributes = descriptor.getExtendedAttributes(idlNode, 3272 getter=getter, 3273 setter=setter) 3274 self.argsPre = argsPre 3275 self.arguments = arguments 3276 self.argCount = len(arguments) 3277 cgThings = [] 3278 cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgs(), 3279 self.getArgc(), self.descriptor, 3280 invalidEnumValueFatal=not setter) for 3281 i in range(argConversionStartsAt, self.argCount)]) 3282 3283 errorResult = None 3284 if self.isFallible(): 3285 errorResult = " false" 3286 3287 if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod(): 3288 if idlNode.maplikeOrSetlikeOrIterable.isMaplike() or \ 3289 idlNode.maplikeOrSetlikeOrIterable.isSetlike(): 3290 raise TypeError('Maplike/Setlike methods are not supported yet') 3291 else: 3292 cgThings.append(CGIterableMethodGenerator(descriptor, 3293 idlNode.maplikeOrSetlikeOrIterable, 3294 idlNode.identifier.name)) 3295 else: 3296 hasCEReactions = idlNode.getExtendedAttribute("CEReactions") 3297 cgThings.append(CGCallGenerator( 3298 errorResult, 3299 self.getArguments(), self.argsPre, returnType, 3300 self.extendedAttributes, descriptor, nativeMethodName, 3301 static, hasCEReactions=hasCEReactions)) 3302 3303 self.cgRoot = CGList(cgThings, "\n") 3304 3305 def getArgs(self): 3306 return "args" if self.argCount > 0 else "" 3307 3308 def getArgc(self): 3309 return "argc" 3310 3311 def getArguments(self): 3312 return [(a, process_arg("arg" + str(i), a)) for (i, a) in enumerate(self.arguments)] 3313 3314 def isFallible(self): 3315 return 'infallible' not in self.extendedAttributes 3316 3317 def wrap_return_value(self): 3318 return wrapForType('args.rval()') 3319 3320 def define(self): 3321 return (self.cgRoot.define() + "\n" + self.wrap_return_value()) 3322 3323 3324class CGSwitch(CGList): 3325 """ 3326 A class to generate code for a switch statement. 3327 3328 Takes three constructor arguments: an expression, a list of cases, 3329 and an optional default. 3330 3331 Each case is a CGCase. The default is a CGThing for the body of 3332 the default case, if any. 3333 """ 3334 def __init__(self, expression, cases, default=None): 3335 CGList.__init__(self, [CGIndenter(c) for c in cases], "\n") 3336 self.prepend(CGWrapper(CGGeneric(expression), 3337 pre="match ", post=" {")) 3338 if default is not None: 3339 self.append( 3340 CGIndenter( 3341 CGWrapper( 3342 CGIndenter(default), 3343 pre="_ => {\n", 3344 post="\n}" 3345 ) 3346 ) 3347 ) 3348 3349 self.append(CGGeneric("}")) 3350 3351 3352class CGCase(CGList): 3353 """ 3354 A class to generate code for a case statement. 3355 3356 Takes three constructor arguments: an expression, a CGThing for 3357 the body (allowed to be None if there is no body), and an optional 3358 argument (defaulting to False) for whether to fall through. 3359 """ 3360 def __init__(self, expression, body, fallThrough=False): 3361 CGList.__init__(self, [], "\n") 3362 self.append(CGWrapper(CGGeneric(expression), post=" => {")) 3363 bodyList = CGList([body], "\n") 3364 if fallThrough: 3365 raise TypeError("fall through required but unsupported") 3366 # bodyList.append(CGGeneric('panic!("fall through unsupported"); /* Fall through */')) 3367 self.append(CGIndenter(bodyList)) 3368 self.append(CGGeneric("}")) 3369 3370 3371class CGGetterCall(CGPerSignatureCall): 3372 """ 3373 A class to generate a native object getter call for a particular IDL 3374 getter. 3375 """ 3376 def __init__(self, argsPre, returnType, nativeMethodName, descriptor, attr): 3377 CGPerSignatureCall.__init__(self, returnType, argsPre, [], 3378 nativeMethodName, attr.isStatic(), descriptor, 3379 attr, getter=True) 3380 3381 3382class FakeArgument(): 3383 """ 3384 A class that quacks like an IDLArgument. This is used to make 3385 setters look like method calls or for special operations. 3386 """ 3387 def __init__(self, type, interfaceMember, allowTreatNonObjectAsNull=False): 3388 self.type = type 3389 self.optional = False 3390 self.variadic = False 3391 self.defaultValue = None 3392 self._allowTreatNonObjectAsNull = allowTreatNonObjectAsNull 3393 self.treatNullAs = interfaceMember.treatNullAs 3394 self.enforceRange = False 3395 self.clamp = False 3396 3397 def allowTreatNonCallableAsNull(self): 3398 return self._allowTreatNonObjectAsNull 3399 3400 3401class CGSetterCall(CGPerSignatureCall): 3402 """ 3403 A class to generate a native object setter call for a particular IDL 3404 setter. 3405 """ 3406 def __init__(self, argsPre, argType, nativeMethodName, descriptor, attr): 3407 CGPerSignatureCall.__init__(self, None, argsPre, 3408 [FakeArgument(argType, attr, allowTreatNonObjectAsNull=True)], 3409 nativeMethodName, attr.isStatic(), descriptor, attr, 3410 setter=True) 3411 3412 def wrap_return_value(self): 3413 # We have no return value 3414 return "\nreturn true;" 3415 3416 def getArgc(self): 3417 return "1" 3418 3419 3420class CGAbstractStaticBindingMethod(CGAbstractMethod): 3421 """ 3422 Common class to generate the JSNatives for all our static methods, getters 3423 and setters. This will generate the function declaration and unwrap the 3424 global object. Subclasses are expected to override the generate_code 3425 function to do the rest of the work. This function should return a 3426 CGThing which is already properly indented. 3427 """ 3428 def __init__(self, descriptor, name): 3429 args = [ 3430 Argument('*mut JSContext', 'cx'), 3431 Argument('libc::c_uint', 'argc'), 3432 Argument('*mut JSVal', 'vp'), 3433 ] 3434 CGAbstractMethod.__init__(self, descriptor, name, "bool", args, extern=True) 3435 self.exposureSet = descriptor.interface.exposureSet 3436 3437 def definition_body(self): 3438 preamble = "let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object());\n" 3439 if len(self.exposureSet) == 1: 3440 preamble += """ 3441let global = DomRoot::downcast::<dom::types::%s>(global).unwrap(); 3442""" % list(self.exposureSet)[0] 3443 return CGList([CGGeneric(preamble), self.generate_code()]) 3444 3445 def generate_code(self): 3446 raise NotImplementedError # Override me! 3447 3448 3449class CGSpecializedMethod(CGAbstractExternMethod): 3450 """ 3451 A class for generating the C++ code for a specialized method that the JIT 3452 can call with lower overhead. 3453 """ 3454 def __init__(self, descriptor, method): 3455 self.method = method 3456 name = method.identifier.name 3457 args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', '_obj'), 3458 Argument('*const %s' % descriptor.concreteType, 'this'), 3459 Argument('*const JSJitMethodCallArgs', 'args')] 3460 CGAbstractExternMethod.__init__(self, descriptor, name, 'bool', args) 3461 3462 def definition_body(self): 3463 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, 3464 self.method) 3465 return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(), 3466 self.descriptor, self.method), 3467 pre="let this = &*this;\n" 3468 "let args = &*args;\n" 3469 "let argc = args._base.argc_;\n") 3470 3471 @staticmethod 3472 def makeNativeName(descriptor, method): 3473 name = method.identifier.name 3474 nativeName = descriptor.binaryNameFor(name) 3475 if nativeName == name: 3476 nativeName = descriptor.internalNameFor(name) 3477 return MakeNativeName(nativeName) 3478 3479 3480class CGStaticMethod(CGAbstractStaticBindingMethod): 3481 """ 3482 A class for generating the Rust code for an IDL static method. 3483 """ 3484 def __init__(self, descriptor, method): 3485 self.method = method 3486 name = method.identifier.name 3487 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) 3488 3489 def generate_code(self): 3490 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, 3491 self.method) 3492 setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n") 3493 call = CGMethodCall(["&global"], nativeName, True, self.descriptor, self.method) 3494 return CGList([setupArgs, call]) 3495 3496 3497class CGSpecializedGetter(CGAbstractExternMethod): 3498 """ 3499 A class for generating the code for a specialized attribute getter 3500 that the JIT can call with lower overhead. 3501 """ 3502 def __init__(self, descriptor, attr): 3503 self.attr = attr 3504 name = 'get_' + descriptor.internalNameFor(attr.identifier.name) 3505 args = [Argument('*mut JSContext', 'cx'), 3506 Argument('HandleObject', '_obj'), 3507 Argument('*const %s' % descriptor.concreteType, 'this'), 3508 Argument('JSJitGetterCallArgs', 'args')] 3509 CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) 3510 3511 def definition_body(self): 3512 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, 3513 self.attr) 3514 3515 return CGWrapper(CGGetterCall([], self.attr.type, nativeName, 3516 self.descriptor, self.attr), 3517 pre="let this = &*this;\n") 3518 3519 @staticmethod 3520 def makeNativeName(descriptor, attr): 3521 name = attr.identifier.name 3522 nativeName = descriptor.binaryNameFor(name) 3523 if nativeName == name: 3524 nativeName = descriptor.internalNameFor(name) 3525 nativeName = MakeNativeName(nativeName) 3526 infallible = ('infallible' in 3527 descriptor.getExtendedAttributes(attr, getter=True)) 3528 if attr.type.nullable() or not infallible: 3529 return "Get" + nativeName 3530 3531 return nativeName 3532 3533 3534class CGStaticGetter(CGAbstractStaticBindingMethod): 3535 """ 3536 A class for generating the C++ code for an IDL static attribute getter. 3537 """ 3538 def __init__(self, descriptor, attr): 3539 self.attr = attr 3540 name = 'get_' + attr.identifier.name 3541 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) 3542 3543 def generate_code(self): 3544 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, 3545 self.attr) 3546 setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n") 3547 call = CGGetterCall(["&global"], self.attr.type, nativeName, self.descriptor, 3548 self.attr) 3549 return CGList([setupArgs, call]) 3550 3551 3552class CGSpecializedSetter(CGAbstractExternMethod): 3553 """ 3554 A class for generating the code for a specialized attribute setter 3555 that the JIT can call with lower overhead. 3556 """ 3557 def __init__(self, descriptor, attr): 3558 self.attr = attr 3559 name = 'set_' + descriptor.internalNameFor(attr.identifier.name) 3560 args = [Argument('*mut JSContext', 'cx'), 3561 Argument('HandleObject', 'obj'), 3562 Argument('*const %s' % descriptor.concreteType, 'this'), 3563 Argument('JSJitSetterCallArgs', 'args')] 3564 CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) 3565 3566 def definition_body(self): 3567 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, 3568 self.attr) 3569 return CGWrapper(CGSetterCall([], self.attr.type, nativeName, 3570 self.descriptor, self.attr), 3571 pre="let this = &*this;\n") 3572 3573 @staticmethod 3574 def makeNativeName(descriptor, attr): 3575 name = attr.identifier.name 3576 nativeName = descriptor.binaryNameFor(name) 3577 if nativeName == name: 3578 nativeName = descriptor.internalNameFor(name) 3579 return "Set" + MakeNativeName(nativeName) 3580 3581 3582class CGStaticSetter(CGAbstractStaticBindingMethod): 3583 """ 3584 A class for generating the C++ code for an IDL static attribute setter. 3585 """ 3586 def __init__(self, descriptor, attr): 3587 self.attr = attr 3588 name = 'set_' + attr.identifier.name 3589 CGAbstractStaticBindingMethod.__init__(self, descriptor, name) 3590 3591 def generate_code(self): 3592 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, 3593 self.attr) 3594 checkForArg = CGGeneric( 3595 "let args = CallArgs::from_vp(vp, argc);\n" 3596 "if argc == 0 {\n" 3597 " throw_type_error(cx, \"Not enough arguments to %s setter.\");\n" 3598 " return false;\n" 3599 "}" % self.attr.identifier.name) 3600 call = CGSetterCall(["&global"], self.attr.type, nativeName, self.descriptor, 3601 self.attr) 3602 return CGList([checkForArg, call]) 3603 3604 3605class CGSpecializedForwardingSetter(CGSpecializedSetter): 3606 """ 3607 A class for generating the code for an IDL attribute forwarding setter. 3608 """ 3609 def __init__(self, descriptor, attr): 3610 CGSpecializedSetter.__init__(self, descriptor, attr) 3611 3612 def definition_body(self): 3613 attrName = self.attr.identifier.name 3614 forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0] 3615 # JS_GetProperty and JS_SetProperty can only deal with ASCII 3616 assert all(ord(c) < 128 for c in attrName) 3617 assert all(ord(c) < 128 for c in forwardToAttrName) 3618 return CGGeneric("""\ 3619rooted!(in(cx) let mut v = UndefinedValue()); 3620if !JS_GetProperty(cx, obj, %s as *const u8 as *const libc::c_char, v.handle_mut()) { 3621 return false; 3622} 3623if !v.is_object() { 3624 throw_type_error(cx, "Value.%s is not an object."); 3625 return false; 3626} 3627rooted!(in(cx) let target_obj = v.to_object()); 3628JS_SetProperty(cx, target_obj.handle(), %s as *const u8 as *const libc::c_char, args.get(0)) 3629""" % (str_to_const_array(attrName), attrName, str_to_const_array(forwardToAttrName))) 3630 3631 3632class CGSpecializedReplaceableSetter(CGSpecializedSetter): 3633 """ 3634 A class for generating the code for an IDL replaceable attribute setter. 3635 """ 3636 def __init__(self, descriptor, attr): 3637 CGSpecializedSetter.__init__(self, descriptor, attr) 3638 3639 def definition_body(self): 3640 assert self.attr.readonly 3641 name = str_to_const_array(self.attr.identifier.name) 3642 # JS_DefineProperty can only deal with ASCII. 3643 assert all(ord(c) < 128 for c in name) 3644 return CGGeneric("""\ 3645JS_DefineProperty(cx, obj, %s as *const u8 as *const libc::c_char, 3646 args.get(0), JSPROP_ENUMERATE, None, None)""" % name) 3647 3648 3649class CGMemberJITInfo(CGThing): 3650 """ 3651 A class for generating the JITInfo for a property that points to 3652 our specialized getter and setter. 3653 """ 3654 def __init__(self, descriptor, member): 3655 self.member = member 3656 self.descriptor = descriptor 3657 3658 def defineJitInfo(self, infoName, opName, opType, infallible, movable, 3659 aliasSet, alwaysInSlot, lazilyInSlot, slotIndex, 3660 returnTypes, args): 3661 """ 3662 aliasSet is a JSJitInfo_AliasSet value, without the "JSJitInfo_AliasSet::" bit. 3663 3664 args is None if we don't want to output argTypes for some 3665 reason (e.g. we have overloads or we're not a method) and 3666 otherwise an iterable of the arguments for this method. 3667 """ 3668 assert not movable or aliasSet != "AliasEverything" # Can't move write-aliasing things 3669 assert not alwaysInSlot or movable # Things always in slots had better be movable 3670 3671 def jitInfoInitializer(isTypedMethod): 3672 initializer = fill( 3673 """ 3674 JSJitInfo { 3675 call: ${opName} as *const os::raw::c_void, 3676 protoID: PrototypeList::ID::${name} as u16, 3677 depth: ${depth}, 3678 _bitfield_1: new_jsjitinfo_bitfield_1!( 3679 JSJitInfo_OpType::${opType} as u8, 3680 JSJitInfo_AliasSet::${aliasSet} as u8, 3681 JSValueType::${returnType} as u8, 3682 ${isInfallible}, 3683 ${isMovable}, 3684 ${isEliminatable}, 3685 ${isAlwaysInSlot}, 3686 ${isLazilyCachedInSlot}, 3687 ${isTypedMethod}, 3688 ${slotIndex}, 3689 ), 3690 } 3691 """, 3692 opName=opName, 3693 name=self.descriptor.name, 3694 depth=self.descriptor.interface.inheritanceDepth(), 3695 opType=opType, 3696 aliasSet=aliasSet, 3697 returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes, 3698 ""), 3699 isInfallible=toStringBool(infallible), 3700 isMovable=toStringBool(movable), 3701 # FIXME(nox): https://github.com/servo/servo/issues/10991 3702 isEliminatable=toStringBool(False), 3703 isAlwaysInSlot=toStringBool(alwaysInSlot), 3704 isLazilyCachedInSlot=toStringBool(lazilyInSlot), 3705 isTypedMethod=toStringBool(isTypedMethod), 3706 slotIndex=slotIndex) 3707 return initializer.rstrip() 3708 3709 if args is not None: 3710 argTypes = "%s_argTypes" % infoName 3711 args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args] 3712 args.append("JSJitInfo_ArgType::ArgTypeListEnd as i32") 3713 argTypesDecl = ( 3714 "const %s: [i32; %d] = [ %s ];\n" % 3715 (argTypes, len(args), ", ".join(args))) 3716 return fill( 3717 """ 3718 $*{argTypesDecl} 3719 const ${infoName}: JSTypedMethodJitInfo = JSTypedMethodJitInfo { 3720 base: ${jitInfo}, 3721 argTypes: &${argTypes} as *const _ as *const JSJitInfo_ArgType, 3722 }; 3723 """, 3724 argTypesDecl=argTypesDecl, 3725 infoName=infoName, 3726 jitInfo=indent(jitInfoInitializer(True)), 3727 argTypes=argTypes) 3728 3729 return ("\n" 3730 "const %s: JSJitInfo = %s;\n" 3731 % (infoName, jitInfoInitializer(False))) 3732 3733 def define(self): 3734 if self.member.isAttr(): 3735 internalMemberName = self.descriptor.internalNameFor(self.member.identifier.name) 3736 getterinfo = ("%s_getterinfo" % internalMemberName) 3737 getter = ("get_%s" % internalMemberName) 3738 getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True) 3739 3740 movable = self.mayBeMovable() and getterinfal 3741 aliasSet = self.aliasSet() 3742 3743 isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot") 3744 if self.member.slotIndices is not None: 3745 assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached") 3746 isLazilyCachedInSlot = not isAlwaysInSlot 3747 slotIndex = memberReservedSlot(self.member) # noqa:FIXME: memberReservedSlot is not defined 3748 # We'll statically assert that this is not too big in 3749 # CGUpdateMemberSlotsMethod, in the case when 3750 # isAlwaysInSlot is true. 3751 else: 3752 isLazilyCachedInSlot = False 3753 slotIndex = "0" 3754 3755 result = self.defineJitInfo(getterinfo, getter, "Getter", 3756 getterinfal, movable, aliasSet, 3757 isAlwaysInSlot, isLazilyCachedInSlot, 3758 slotIndex, 3759 [self.member.type], None) 3760 if (not self.member.readonly or self.member.getExtendedAttribute("PutForwards") 3761 or self.member.getExtendedAttribute("Replaceable")): 3762 setterinfo = ("%s_setterinfo" % internalMemberName) 3763 setter = ("set_%s" % internalMemberName) 3764 # Setters are always fallible, since they have to do a typed unwrap. 3765 result += self.defineJitInfo(setterinfo, setter, "Setter", 3766 False, False, "AliasEverything", 3767 False, False, "0", 3768 [BuiltinTypes[IDLBuiltinType.Types.void]], 3769 None) 3770 return result 3771 if self.member.isMethod(): 3772 methodinfo = ("%s_methodinfo" % self.member.identifier.name) 3773 method = ("%s" % self.member.identifier.name) 3774 3775 # Methods are infallible if they are infallible, have no arguments 3776 # to unwrap, and have a return type that's infallible to wrap up for 3777 # return. 3778 sigs = self.member.signatures() 3779 if len(sigs) != 1: 3780 # Don't handle overloading. If there's more than one signature, 3781 # one of them must take arguments. 3782 methodInfal = False 3783 args = None 3784 movable = False 3785 else: 3786 sig = sigs[0] 3787 # For methods that affect nothing, it's OK to set movable to our 3788 # notion of infallible on the C++ side, without considering 3789 # argument conversions, since argument conversions that can 3790 # reliably throw would be effectful anyway and the jit doesn't 3791 # move effectful things. 3792 hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member) 3793 movable = self.mayBeMovable() and hasInfallibleImpl 3794 # XXXbz can we move the smarts about fallibility due to arg 3795 # conversions into the JIT, using our new args stuff? 3796 if (len(sig[1]) != 0): 3797 # We have arguments or our return-value boxing can fail 3798 methodInfal = False 3799 else: 3800 methodInfal = hasInfallibleImpl 3801 # For now, only bother to output args if we're side-effect-free. 3802 if self.member.affects == "Nothing": 3803 args = sig[1] 3804 else: 3805 args = None 3806 3807 aliasSet = self.aliasSet() 3808 result = self.defineJitInfo(methodinfo, method, "Method", 3809 methodInfal, movable, aliasSet, 3810 False, False, "0", 3811 [s[0] for s in sigs], args) 3812 return result 3813 raise TypeError("Illegal member type to CGPropertyJITInfo") 3814 3815 def mayBeMovable(self): 3816 """ 3817 Returns whether this attribute or method may be movable, just 3818 based on Affects/DependsOn annotations. 3819 """ 3820 affects = self.member.affects 3821 dependsOn = self.member.dependsOn 3822 assert affects in IDLInterfaceMember.AffectsValues 3823 assert dependsOn in IDLInterfaceMember.DependsOnValues 3824 # Things that are DependsOn=DeviceState are not movable, because we 3825 # don't want them coalesced with each other or loop-hoisted, since 3826 # their return value can change even if nothing is going on from our 3827 # point of view. 3828 return (affects == "Nothing" and 3829 (dependsOn != "Everything" and dependsOn != "DeviceState")) 3830 3831 def aliasSet(self): 3832 """Returns the alias set to store in the jitinfo. This may not be the 3833 effective alias set the JIT uses, depending on whether we have enough 3834 information about our args to allow the JIT to prove that effectful 3835 argument conversions won't happen. 3836 3837 """ 3838 dependsOn = self.member.dependsOn 3839 assert dependsOn in IDLInterfaceMember.DependsOnValues 3840 3841 if dependsOn == "Nothing" or dependsOn == "DeviceState": 3842 assert self.member.affects == "Nothing" 3843 return "AliasNone" 3844 3845 if dependsOn == "DOMState": 3846 assert self.member.affects == "Nothing" 3847 return "AliasDOMSets" 3848 3849 return "AliasEverything" 3850 3851 @staticmethod 3852 def getJSReturnTypeTag(t): 3853 if t.nullable(): 3854 # Sometimes it might return null, sometimes not 3855 return "JSVAL_TYPE_UNKNOWN" 3856 if t.isVoid(): 3857 # No return, every time 3858 return "JSVAL_TYPE_UNDEFINED" 3859 if t.isSequence(): 3860 return "JSVAL_TYPE_OBJECT" 3861 if t.isRecord(): 3862 return "JSVAL_TYPE_OBJECT" 3863 if t.isPromise(): 3864 return "JSVAL_TYPE_OBJECT" 3865 if t.isGeckoInterface(): 3866 return "JSVAL_TYPE_OBJECT" 3867 if t.isString(): 3868 return "JSVAL_TYPE_STRING" 3869 if t.isEnum(): 3870 return "JSVAL_TYPE_STRING" 3871 if t.isCallback(): 3872 return "JSVAL_TYPE_OBJECT" 3873 if t.isAny(): 3874 # The whole point is to return various stuff 3875 return "JSVAL_TYPE_UNKNOWN" 3876 if t.isObject(): 3877 return "JSVAL_TYPE_OBJECT" 3878 if t.isSpiderMonkeyInterface(): 3879 return "JSVAL_TYPE_OBJECT" 3880 if t.isUnion(): 3881 u = t.unroll() 3882 if u.hasNullableType: 3883 # Might be null or not 3884 return "JSVAL_TYPE_UNKNOWN" 3885 return reduce(CGMemberJITInfo.getSingleReturnType, 3886 u.flatMemberTypes, "") 3887 if t.isDictionary(): 3888 return "JSVAL_TYPE_OBJECT" 3889 if t.isDate(): 3890 return "JSVAL_TYPE_OBJECT" 3891 if not t.isPrimitive(): 3892 raise TypeError("No idea what type " + str(t) + " is.") 3893 tag = t.tag() 3894 if tag == IDLType.Tags.bool: 3895 return "JSVAL_TYPE_BOOLEAN" 3896 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, 3897 IDLType.Tags.int16, IDLType.Tags.uint16, 3898 IDLType.Tags.int32]: 3899 return "JSVAL_TYPE_INT32" 3900 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64, 3901 IDLType.Tags.unrestricted_float, IDLType.Tags.float, 3902 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: 3903 # These all use JS_NumberValue, which can return int or double. 3904 # But TI treats "double" as meaning "int or double", so we're 3905 # good to return JSVAL_TYPE_DOUBLE here. 3906 return "JSVAL_TYPE_DOUBLE" 3907 if tag != IDLType.Tags.uint32: 3908 raise TypeError("No idea what type " + str(t) + " is.") 3909 # uint32 is sometimes int and sometimes double. 3910 return "JSVAL_TYPE_DOUBLE" 3911 3912 @staticmethod 3913 def getSingleReturnType(existingType, t): 3914 type = CGMemberJITInfo.getJSReturnTypeTag(t) 3915 if existingType == "": 3916 # First element of the list; just return its type 3917 return type 3918 3919 if type == existingType: 3920 return existingType 3921 if ((type == "JSVAL_TYPE_DOUBLE" and 3922 existingType == "JSVAL_TYPE_INT32") or 3923 (existingType == "JSVAL_TYPE_DOUBLE" and 3924 type == "JSVAL_TYPE_INT32")): 3925 # Promote INT32 to DOUBLE as needed 3926 return "JSVAL_TYPE_DOUBLE" 3927 # Different types 3928 return "JSVAL_TYPE_UNKNOWN" 3929 3930 @staticmethod 3931 def getJSArgType(t): 3932 assert not t.isVoid() 3933 if t.nullable(): 3934 # Sometimes it might return null, sometimes not 3935 return "JSJitInfo_ArgType::Null as i32 | %s" % CGMemberJITInfo.getJSArgType(t.inner) 3936 if t.isSequence(): 3937 return "JSJitInfo_ArgType::Object as i32" 3938 if t.isGeckoInterface(): 3939 return "JSJitInfo_ArgType::Object as i32" 3940 if t.isString(): 3941 return "JSJitInfo_ArgType::String as i32" 3942 if t.isEnum(): 3943 return "JSJitInfo_ArgType::String as i32" 3944 if t.isCallback(): 3945 return "JSJitInfo_ArgType::Object as i32" 3946 if t.isAny(): 3947 # The whole point is to return various stuff 3948 return "JSJitInfo_ArgType::Any as i32" 3949 if t.isObject(): 3950 return "JSJitInfo_ArgType::Object as i32" 3951 if t.isSpiderMonkeyInterface(): 3952 return "JSJitInfo_ArgType::Object as i32" 3953 if t.isUnion(): 3954 u = t.unroll() 3955 type = "JSJitInfo::Null as i32" if u.hasNullableType else "" 3956 return reduce(CGMemberJITInfo.getSingleArgType, 3957 u.flatMemberTypes, type) 3958 if t.isDictionary(): 3959 return "JSJitInfo_ArgType::Object as i32" 3960 if t.isDate(): 3961 return "JSJitInfo_ArgType::Object as i32" 3962 if not t.isPrimitive(): 3963 raise TypeError("No idea what type " + str(t) + " is.") 3964 tag = t.tag() 3965 if tag == IDLType.Tags.bool: 3966 return "JSJitInfo_ArgType::Boolean as i32" 3967 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, 3968 IDLType.Tags.int16, IDLType.Tags.uint16, 3969 IDLType.Tags.int32]: 3970 return "JSJitInfo_ArgType::Integer as i32" 3971 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64, 3972 IDLType.Tags.unrestricted_float, IDLType.Tags.float, 3973 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: 3974 # These all use JS_NumberValue, which can return int or double. 3975 # But TI treats "double" as meaning "int or double", so we're 3976 # good to return JSVAL_TYPE_DOUBLE here. 3977 return "JSJitInfo_ArgType::Double as i32" 3978 if tag != IDLType.Tags.uint32: 3979 raise TypeError("No idea what type " + str(t) + " is.") 3980 # uint32 is sometimes int and sometimes double. 3981 return "JSJitInfo_ArgType::Double as i32" 3982 3983 @staticmethod 3984 def getSingleArgType(existingType, t): 3985 type = CGMemberJITInfo.getJSArgType(t) 3986 if existingType == "": 3987 # First element of the list; just return its type 3988 return type 3989 3990 if type == existingType: 3991 return existingType 3992 return "%s | %s" % (existingType, type) 3993 3994 3995def getEnumValueName(value): 3996 # Some enum values can be empty strings. Others might have weird 3997 # characters in them. Deal with the former by returning "_empty", 3998 # deal with possible name collisions from that by throwing if the 3999 # enum value is actually "_empty", and throw on any value 4000 # containing non-ASCII chars for now. Replace all chars other than 4001 # [0-9A-Za-z_] with '_'. 4002 if re.match("[^\x20-\x7E]", value): 4003 raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters') 4004 if re.match("^[0-9]", value): 4005 raise SyntaxError('Enum value "' + value + '" starts with a digit') 4006 value = re.sub(r'[^0-9A-Za-z_]', '_', value) 4007 if re.match("^_[A-Z]|__", value): 4008 raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec') 4009 if value == "_empty": 4010 raise SyntaxError('"_empty" is not an IDL enum value we support yet') 4011 if value == "": 4012 return "_empty" 4013 return MakeNativeName(value) 4014 4015 4016class CGEnum(CGThing): 4017 def __init__(self, enum): 4018 CGThing.__init__(self) 4019 4020 ident = enum.identifier.name 4021 decl = """\ 4022#[repr(usize)] 4023#[derive(Copy, Clone, Debug, JSTraceable, MallocSizeOf, PartialEq)] 4024pub enum %s { 4025 %s 4026} 4027""" % (ident, ",\n ".join(map(getEnumValueName, enum.values()))) 4028 4029 pairs = ",\n ".join(['("%s", super::%s::%s)' % (val, ident, getEnumValueName(val)) for val in enum.values()]) 4030 4031 inner = string.Template("""\ 4032use dom::bindings::conversions::ToJSValConvertible; 4033use js::jsapi::{JSContext, MutableHandleValue}; 4034use js::jsval::JSVal; 4035 4036pub const pairs: &'static [(&'static str, super::${ident})] = &[ 4037 ${pairs}, 4038]; 4039 4040impl super::${ident} { 4041 pub fn as_str(&self) -> &'static str { 4042 pairs[*self as usize].0 4043 } 4044} 4045 4046impl Default for super::${ident} { 4047 fn default() -> super::${ident} { 4048 pairs[0].1 4049 } 4050} 4051 4052impl ToJSValConvertible for super::${ident} { 4053 unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { 4054 pairs[*self as usize].0.to_jsval(cx, rval); 4055 } 4056} 4057 """).substitute({ 4058 'ident': ident, 4059 'pairs': pairs 4060 }) 4061 self.cgRoot = CGList([ 4062 CGGeneric(decl), 4063 CGNamespace.build([ident + "Values"], 4064 CGIndenter(CGGeneric(inner)), public=True), 4065 ]) 4066 4067 def define(self): 4068 return self.cgRoot.define() 4069 4070 4071def convertConstIDLValueToRust(value): 4072 tag = value.type.tag() 4073 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, 4074 IDLType.Tags.int16, IDLType.Tags.uint16, 4075 IDLType.Tags.int32, IDLType.Tags.uint32, 4076 IDLType.Tags.int64, IDLType.Tags.uint64, 4077 IDLType.Tags.unrestricted_float, IDLType.Tags.float, 4078 IDLType.Tags.unrestricted_double, IDLType.Tags.double]: 4079 return str(value.value) 4080 4081 if tag == IDLType.Tags.bool: 4082 return toStringBool(value.value) 4083 4084 raise TypeError("Const value of unhandled type: " + value.type) 4085 4086 4087class CGConstant(CGThing): 4088 def __init__(self, constant): 4089 CGThing.__init__(self) 4090 self.constant = constant 4091 4092 def define(self): 4093 name = self.constant.identifier.name 4094 value = convertConstIDLValueToRust(self.constant.value) 4095 4096 tag = self.constant.value.type.tag() 4097 const_type = builtinNames[self.constant.value.type.tag()] 4098 # Finite<f32> or Finite<f64> cannot be used un a constant declaration. 4099 # Remote the Finite type from restricted float and double tag declarations. 4100 if tag == IDLType.Tags.float: 4101 const_type = "f32" 4102 elif tag == IDLType.Tags.double: 4103 const_type = "f64" 4104 4105 return "pub const %s: %s = %s;\n" % (name, const_type, value) 4106 4107 4108def getUnionTypeTemplateVars(type, descriptorProvider): 4109 if type.isGeckoInterface(): 4110 name = type.inner.identifier.name 4111 typeName = descriptorProvider.getDescriptor(name).returnType 4112 elif type.isEnum(): 4113 name = type.inner.identifier.name 4114 typeName = name 4115 elif type.isDictionary(): 4116 name = type.name 4117 typeName = name 4118 elif type.isSequence() or type.isRecord(): 4119 name = type.name 4120 inner = getUnionTypeTemplateVars(innerContainerType(type), descriptorProvider) 4121 typeName = wrapInNativeContainerType(type, CGGeneric(inner["typeName"])).define() 4122 elif type.isByteString(): 4123 name = type.name 4124 typeName = "ByteString" 4125 elif type.isDOMString(): 4126 name = type.name 4127 typeName = "DOMString" 4128 elif type.isUSVString(): 4129 name = type.name 4130 typeName = "USVString" 4131 elif type.isPrimitive(): 4132 name = type.name 4133 typeName = builtinNames[type.tag()] 4134 elif type.isObject(): 4135 name = type.name 4136 typeName = "Heap<*mut JSObject>" 4137 else: 4138 raise TypeError("Can't handle %s in unions yet" % type) 4139 4140 info = getJSToNativeConversionInfo( 4141 type, descriptorProvider, failureCode="return Ok(None);", 4142 exceptionCode='return Err(());', 4143 isDefinitelyObject=True, 4144 isMember="Union") 4145 template = info.template 4146 4147 jsConversion = string.Template(template).substitute({ 4148 "val": "value", 4149 }) 4150 jsConversion = CGWrapper(CGGeneric(jsConversion), pre="Ok(Some(", post="))") 4151 4152 return { 4153 "name": name, 4154 "typeName": typeName, 4155 "jsConversion": jsConversion, 4156 } 4157 4158 4159class CGUnionStruct(CGThing): 4160 def __init__(self, type, descriptorProvider): 4161 assert not type.nullable() 4162 assert not type.hasNullableType 4163 4164 CGThing.__init__(self) 4165 self.type = type 4166 self.descriptorProvider = descriptorProvider 4167 4168 def membersNeedTracing(self): 4169 for t in self.type.flatMemberTypes: 4170 if type_needs_tracing(t): 4171 return True 4172 return False 4173 4174 def define(self): 4175 templateVars = map(lambda t: (getUnionTypeTemplateVars(t, self.descriptorProvider), 4176 type_needs_tracing(t)), 4177 self.type.flatMemberTypes) 4178 enumValues = [ 4179 " %s(%s)," % (v["name"], "RootedTraceableBox<%s>" % v["typeName"] if trace else v["typeName"]) 4180 for (v, trace) in templateVars 4181 ] 4182 enumConversions = [ 4183 " %s::%s(ref inner) => inner.to_jsval(cx, rval)," 4184 % (self.type, v["name"]) for (v, _) in templateVars 4185 ] 4186 return ("""\ 4187#[derive(JSTraceable)] 4188pub enum %s { 4189%s 4190} 4191 4192impl ToJSValConvertible for %s { 4193 unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { 4194 match *self { 4195%s 4196 } 4197 } 4198} 4199""") % (self.type, "\n".join(enumValues), self.type, "\n".join(enumConversions)) 4200 4201 4202class CGUnionConversionStruct(CGThing): 4203 def __init__(self, type, descriptorProvider): 4204 assert not type.nullable() 4205 assert not type.hasNullableType 4206 4207 CGThing.__init__(self) 4208 self.type = type 4209 self.descriptorProvider = descriptorProvider 4210 4211 def membersNeedTracing(self): 4212 for t in self.type.flatMemberTypes: 4213 if type_needs_tracing(t): 4214 return True 4215 return False 4216 4217 def from_jsval(self): 4218 memberTypes = self.type.flatMemberTypes 4219 names = [] 4220 conversions = [] 4221 4222 def get_name(memberType): 4223 if self.type.isGeckoInterface(): 4224 return memberType.inner.identifier.name 4225 4226 return memberType.name 4227 4228 def get_match(name): 4229 return ( 4230 "match %s::TryConvertTo%s(cx, value) {\n" 4231 " Err(_) => return Err(()),\n" 4232 " Ok(Some(value)) => return Ok(ConversionResult::Success(%s::%s(value))),\n" 4233 " Ok(None) => (),\n" 4234 "}\n") % (self.type, name, self.type, name) 4235 4236 interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes) 4237 if len(interfaceMemberTypes) > 0: 4238 typeNames = [get_name(memberType) for memberType in interfaceMemberTypes] 4239 interfaceObject = CGList(CGGeneric(get_match(typeName)) for typeName in typeNames) 4240 names.extend(typeNames) 4241 else: 4242 interfaceObject = None 4243 4244 arrayObjectMemberTypes = filter(lambda t: t.isSequence(), memberTypes) 4245 if len(arrayObjectMemberTypes) > 0: 4246 assert len(arrayObjectMemberTypes) == 1 4247 typeName = arrayObjectMemberTypes[0].name 4248 arrayObject = CGGeneric(get_match(typeName)) 4249 names.append(typeName) 4250 else: 4251 arrayObject = None 4252 4253 dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes) 4254 if len(dateObjectMemberTypes) > 0: 4255 assert len(dateObjectMemberTypes) == 1 4256 raise TypeError("Can't handle dates in unions.") 4257 else: 4258 dateObject = None 4259 4260 callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes) 4261 if len(callbackMemberTypes) > 0: 4262 assert len(callbackMemberTypes) == 1 4263 raise TypeError("Can't handle callbacks in unions.") 4264 else: 4265 callbackObject = None 4266 4267 dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes) 4268 if len(dictionaryMemberTypes) > 0: 4269 assert len(dictionaryMemberTypes) == 1 4270 typeName = dictionaryMemberTypes[0].name 4271 dictionaryObject = CGGeneric(get_match(typeName)) 4272 names.append(typeName) 4273 else: 4274 dictionaryObject = None 4275 4276 objectMemberTypes = filter(lambda t: t.isObject(), memberTypes) 4277 if len(objectMemberTypes) > 0: 4278 assert len(objectMemberTypes) == 1 4279 typeName = objectMemberTypes[0].name 4280 object = CGGeneric(get_match(typeName)) 4281 names.append(typeName) 4282 else: 4283 object = None 4284 4285 mozMapMemberTypes = filter(lambda t: t.isRecord(), memberTypes) 4286 if len(mozMapMemberTypes) > 0: 4287 assert len(mozMapMemberTypes) == 1 4288 typeName = mozMapMemberTypes[0].name 4289 mozMapObject = CGGeneric(get_match(typeName)) 4290 names.append(typeName) 4291 else: 4292 mozMapObject = None 4293 4294 hasObjectTypes = interfaceObject or arrayObject or dateObject or object or mozMapObject 4295 if hasObjectTypes: 4296 # "object" is not distinguishable from other types 4297 assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject or mozMapObject) 4298 templateBody = CGList([], "\n") 4299 if interfaceObject: 4300 templateBody.append(interfaceObject) 4301 if arrayObject: 4302 templateBody.append(arrayObject) 4303 if mozMapObject: 4304 templateBody.append(mozMapObject) 4305 conversions.append(CGIfWrapper("value.get().is_object()", templateBody)) 4306 4307 if dictionaryObject: 4308 assert not hasObjectTypes 4309 conversions.append(dictionaryObject) 4310 4311 stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()] 4312 numericTypes = [t for t in memberTypes if t.isNumeric()] 4313 booleanTypes = [t for t in memberTypes if t.isBoolean()] 4314 if stringTypes or numericTypes or booleanTypes: 4315 assert len(stringTypes) <= 1 4316 assert len(numericTypes) <= 1 4317 assert len(booleanTypes) <= 1 4318 4319 def getStringOrPrimitiveConversion(memberType): 4320 typename = get_name(memberType) 4321 return CGGeneric(get_match(typename)) 4322 other = [] 4323 stringConversion = map(getStringOrPrimitiveConversion, stringTypes) 4324 numericConversion = map(getStringOrPrimitiveConversion, numericTypes) 4325 booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes) 4326 if stringConversion: 4327 if booleanConversion: 4328 other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0])) 4329 if numericConversion: 4330 other.append(CGIfWrapper("value.get().is_number()", numericConversion[0])) 4331 other.append(stringConversion[0]) 4332 elif numericConversion: 4333 if booleanConversion: 4334 other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0])) 4335 other.append(numericConversion[0]) 4336 else: 4337 assert booleanConversion 4338 other.append(booleanConversion[0]) 4339 conversions.append(CGList(other, "\n\n")) 4340 conversions.append(CGGeneric( 4341 "throw_not_in_union(cx, \"%s\");\n" 4342 "Err(())" % ", ".join(names))) 4343 method = CGWrapper( 4344 CGIndenter(CGList(conversions, "\n\n")), 4345 pre="unsafe fn from_jsval(cx: *mut JSContext,\n" 4346 " value: HandleValue,\n" 4347 " _option: ())\n" 4348 " -> Result<ConversionResult<%s>, ()> {\n" % self.type, 4349 post="\n}") 4350 return CGWrapper( 4351 CGIndenter(CGList([ 4352 CGGeneric("type Config = ();"), 4353 method, 4354 ], "\n")), 4355 pre="impl FromJSValConvertible for %s {\n" % self.type, 4356 post="\n}") 4357 4358 def try_method(self, t): 4359 templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider) 4360 actualType = templateVars["typeName"] 4361 if type_needs_tracing(t): 4362 actualType = "RootedTraceableBox<%s>" % actualType 4363 returnType = "Result<Option<%s>, ()>" % actualType 4364 jsConversion = templateVars["jsConversion"] 4365 4366 # Any code to convert to Object is unused, since we're already converting 4367 # from an Object value. 4368 if t.name == 'Object': 4369 return CGGeneric('') 4370 4371 return CGWrapper( 4372 CGIndenter(jsConversion, 4), 4373 pre="unsafe fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %s {\n" 4374 % (t.name, returnType), 4375 post="\n}") 4376 4377 def define(self): 4378 from_jsval = self.from_jsval() 4379 methods = CGIndenter(CGList([ 4380 self.try_method(t) for t in self.type.flatMemberTypes 4381 ], "\n\n")) 4382 return """ 4383%s 4384 4385impl %s { 4386%s 4387} 4388""" % (from_jsval.define(), self.type, methods.define()) 4389 4390 4391class ClassItem: 4392 """ Use with CGClass """ 4393 def __init__(self, name, visibility): 4394 self.name = name 4395 self.visibility = visibility 4396 4397 def declare(self, cgClass): 4398 assert False 4399 4400 def define(self, cgClass): 4401 assert False 4402 4403 4404class ClassBase(ClassItem): 4405 def __init__(self, name, visibility='pub'): 4406 ClassItem.__init__(self, name, visibility) 4407 4408 def declare(self, cgClass): 4409 return '%s %s' % (self.visibility, self.name) 4410 4411 def define(self, cgClass): 4412 # Only in the header 4413 return '' 4414 4415 4416class ClassMethod(ClassItem): 4417 def __init__(self, name, returnType, args, inline=False, static=False, 4418 virtual=False, const=False, bodyInHeader=False, 4419 templateArgs=None, visibility='public', body=None, 4420 breakAfterReturnDecl="\n", unsafe=False, 4421 breakAfterSelf="\n", override=False): 4422 """ 4423 override indicates whether to flag the method as MOZ_OVERRIDE 4424 """ 4425 assert not override or virtual 4426 assert not (override and static) 4427 self.returnType = returnType 4428 self.args = args 4429 self.inline = False 4430 self.static = static 4431 self.virtual = virtual 4432 self.const = const 4433 self.bodyInHeader = True 4434 self.templateArgs = templateArgs 4435 self.body = body 4436 self.breakAfterReturnDecl = breakAfterReturnDecl 4437 self.breakAfterSelf = breakAfterSelf 4438 self.override = override 4439 self.unsafe = unsafe 4440 ClassItem.__init__(self, name, visibility) 4441 4442 def getDecorators(self, declaring): 4443 decorators = [] 4444 if self.inline: 4445 decorators.append('inline') 4446 if declaring: 4447 if self.static: 4448 decorators.append('static') 4449 if self.virtual: 4450 decorators.append('virtual') 4451 if decorators: 4452 return ' '.join(decorators) + ' ' 4453 return '' 4454 4455 def getBody(self): 4456 # Override me or pass a string to constructor 4457 assert self.body is not None 4458 return self.body 4459 4460 def declare(self, cgClass): 4461 templateClause = '<%s>' % ', '.join(self.templateArgs) \ 4462 if self.bodyInHeader and self.templateArgs else '' 4463 args = ', '.join([a.declare() for a in self.args]) 4464 if self.bodyInHeader: 4465 body = CGIndenter(CGGeneric(self.getBody())).define() 4466 body = ' {\n' + body + '\n}' 4467 else: 4468 body = ';' 4469 4470 return string.Template( 4471 "${decorators}%s" 4472 "${visibility}${unsafe}fn ${name}${templateClause}(${args})${returnType}${const}${override}${body}%s" % 4473 (self.breakAfterReturnDecl, self.breakAfterSelf) 4474 ).substitute({ 4475 'templateClause': templateClause, 4476 'decorators': self.getDecorators(True), 4477 'returnType': (" -> %s" % self.returnType) if self.returnType else "", 4478 'name': self.name, 4479 'const': ' const' if self.const else '', 4480 'override': ' MOZ_OVERRIDE' if self.override else '', 4481 'args': args, 4482 'body': body, 4483 'visibility': self.visibility + ' ' if self.visibility != 'priv' else '', 4484 'unsafe': "unsafe " if self.unsafe else "", 4485 }) 4486 4487 def define(self, cgClass): 4488 pass 4489 4490 4491class ClassConstructor(ClassItem): 4492 """ 4493 Used for adding a constructor to a CGClass. 4494 4495 args is a list of Argument objects that are the arguments taken by the 4496 constructor. 4497 4498 inline should be True if the constructor should be marked inline. 4499 4500 bodyInHeader should be True if the body should be placed in the class 4501 declaration in the header. 4502 4503 visibility determines the visibility of the constructor (public, 4504 protected, private), defaults to private. 4505 4506 explicit should be True if the constructor should be marked explicit. 4507 4508 baseConstructors is a list of strings containing calls to base constructors, 4509 defaults to None. 4510 4511 body contains a string with the code for the constructor, defaults to empty. 4512 """ 4513 def __init__(self, args, inline=False, bodyInHeader=False, 4514 visibility="priv", explicit=False, baseConstructors=None, 4515 body=""): 4516 self.args = args 4517 self.inline = False 4518 self.bodyInHeader = bodyInHeader 4519 self.explicit = explicit 4520 self.baseConstructors = baseConstructors or [] 4521 self.body = body 4522 ClassItem.__init__(self, None, visibility) 4523 4524 def getDecorators(self, declaring): 4525 decorators = [] 4526 if self.explicit: 4527 decorators.append('explicit') 4528 if self.inline and declaring: 4529 decorators.append('inline') 4530 if decorators: 4531 return ' '.join(decorators) + ' ' 4532 return '' 4533 4534 def getInitializationList(self, cgClass): 4535 items = [str(c) for c in self.baseConstructors] 4536 for m in cgClass.members: 4537 if not m.static: 4538 initialize = m.body 4539 if initialize: 4540 items.append(m.name + "(" + initialize + ")") 4541 4542 if len(items) > 0: 4543 return '\n : ' + ',\n '.join(items) 4544 return '' 4545 4546 def getBody(self, cgClass): 4547 initializers = [" parent: %s" % str(self.baseConstructors[0])] 4548 return (self.body + ( 4549 "let mut ret = Rc::new(%s {\n" 4550 "%s\n" 4551 "});\n" 4552 "// Note: callback cannot be moved after calling init.\n" 4553 "match Rc::get_mut(&mut ret) {\n" 4554 " Some(ref mut callback) => callback.parent.init(%s, %s),\n" 4555 " None => unreachable!(),\n" 4556 "};\n" 4557 "ret") % (cgClass.name, '\n'.join(initializers), 4558 self.args[0].name, self.args[1].name)) 4559 4560 def declare(self, cgClass): 4561 args = ', '.join([a.declare() for a in self.args]) 4562 body = ' ' + self.getBody(cgClass) 4563 body = stripTrailingWhitespace(body.replace('\n', '\n ')) 4564 if len(body) > 0: 4565 body += '\n' 4566 body = ' {\n' + body + '}' 4567 4568 return string.Template("""\ 4569pub unsafe fn ${decorators}new(${args}) -> Rc<${className}>${body} 4570""").substitute({'decorators': self.getDecorators(True), 4571 'className': cgClass.getNameString(), 4572 'args': args, 4573 'body': body}) 4574 4575 def define(self, cgClass): 4576 if self.bodyInHeader: 4577 return '' 4578 4579 args = ', '.join([a.define() for a in self.args]) 4580 4581 body = ' ' + self.getBody() 4582 body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n ')) 4583 if len(body) > 0: 4584 body += '\n' 4585 4586 return string.Template("""\ 4587${decorators} 4588${className}::${className}(${args})${initializationList} 4589{${body}} 4590""").substitute({'decorators': self.getDecorators(False), 4591 'className': cgClass.getNameString(), 4592 'args': args, 4593 'initializationList': self.getInitializationList(cgClass), 4594 'body': body}) 4595 4596 4597class ClassMember(ClassItem): 4598 def __init__(self, name, type, visibility="priv", static=False, 4599 body=None): 4600 self.type = type 4601 self.static = static 4602 self.body = body 4603 ClassItem.__init__(self, name, visibility) 4604 4605 def declare(self, cgClass): 4606 return '%s %s: %s,\n' % (self.visibility, self.name, self.type) 4607 4608 def define(self, cgClass): 4609 if not self.static: 4610 return '' 4611 if self.body: 4612 body = " = " + self.body 4613 else: 4614 body = "" 4615 return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(), 4616 self.name, body) 4617 4618 4619class CGClass(CGThing): 4620 def __init__(self, name, bases=[], members=[], constructors=[], 4621 destructor=None, methods=[], 4622 typedefs=[], enums=[], unions=[], templateArgs=[], 4623 templateSpecialization=[], 4624 disallowCopyConstruction=False, indent='', 4625 decorators='', 4626 extradeclarations=''): 4627 CGThing.__init__(self) 4628 self.name = name 4629 self.bases = bases 4630 self.members = members 4631 self.constructors = constructors 4632 # We store our single destructor in a list, since all of our 4633 # code wants lists of members. 4634 self.destructors = [destructor] if destructor else [] 4635 self.methods = methods 4636 self.typedefs = typedefs 4637 self.enums = enums 4638 self.unions = unions 4639 self.templateArgs = templateArgs 4640 self.templateSpecialization = templateSpecialization 4641 self.disallowCopyConstruction = disallowCopyConstruction 4642 self.indent = indent 4643 self.decorators = decorators 4644 self.extradeclarations = extradeclarations 4645 4646 def getNameString(self): 4647 className = self.name 4648 if self.templateSpecialization: 4649 className = className + \ 4650 '<%s>' % ', '.join([str(a) for a 4651 in self.templateSpecialization]) 4652 return className 4653 4654 def define(self): 4655 result = '' 4656 if self.templateArgs: 4657 templateArgs = [a.declare() for a in self.templateArgs] 4658 templateArgs = templateArgs[len(self.templateSpecialization):] 4659 result = result + self.indent + 'template <%s>\n' % ','.join([str(a) for a in templateArgs]) 4660 4661 if self.templateSpecialization: 4662 specialization = \ 4663 '<%s>' % ', '.join([str(a) for a in self.templateSpecialization]) 4664 else: 4665 specialization = '' 4666 4667 myself = '' 4668 if self.decorators != '': 4669 myself += self.decorators + '\n' 4670 myself += '%spub struct %s%s' % (self.indent, self.name, specialization) 4671 result += myself 4672 4673 assert len(self.bases) == 1 # XXjdm Can we support multiple inheritance? 4674 4675 result += ' {\n' 4676 4677 if self.bases: 4678 self.members = [ClassMember("parent", self.bases[0].name, "pub")] + self.members 4679 4680 result += CGIndenter(CGGeneric(self.extradeclarations), 4681 len(self.indent)).define() 4682 4683 def declareMembers(cgClass, memberList): 4684 result = '' 4685 4686 for member in memberList: 4687 declaration = member.declare(cgClass) 4688 declaration = CGIndenter(CGGeneric(declaration)).define() 4689 result = result + declaration 4690 return result 4691 4692 if self.disallowCopyConstruction: 4693 class DisallowedCopyConstructor(object): 4694 def __init__(self): 4695 self.visibility = "private" 4696 4697 def declare(self, cgClass): 4698 name = cgClass.getNameString() 4699 return ("%s(const %s&) MOZ_DELETE;\n" 4700 "void operator=(const %s) MOZ_DELETE;\n" % (name, name, name)) 4701 disallowedCopyConstructors = [DisallowedCopyConstructor()] 4702 else: 4703 disallowedCopyConstructors = [] 4704 4705 order = [(self.enums, ''), (self.unions, ''), 4706 (self.typedefs, ''), (self.members, '')] 4707 4708 for (memberList, separator) in order: 4709 memberString = declareMembers(self, memberList) 4710 if self.indent: 4711 memberString = CGIndenter(CGGeneric(memberString), 4712 len(self.indent)).define() 4713 result = result + memberString 4714 4715 result += self.indent + '}\n\n' 4716 result += 'impl %s {\n' % self.name 4717 4718 order = [(self.constructors + disallowedCopyConstructors, '\n'), 4719 (self.destructors, '\n'), (self.methods, '\n)')] 4720 for (memberList, separator) in order: 4721 memberString = declareMembers(self, memberList) 4722 if self.indent: 4723 memberString = CGIndenter(CGGeneric(memberString), 4724 len(self.indent)).define() 4725 result = result + memberString 4726 4727 result += "}" 4728 return result 4729 4730 4731class CGProxySpecialOperation(CGPerSignatureCall): 4732 """ 4733 Base class for classes for calling an indexed or named special operation 4734 (don't use this directly, use the derived classes below). 4735 """ 4736 def __init__(self, descriptor, operation): 4737 nativeName = MakeNativeName(descriptor.binaryNameFor(operation)) 4738 operation = descriptor.operations[operation] 4739 assert len(operation.signatures()) == 1 4740 signature = operation.signatures()[0] 4741 4742 (returnType, arguments) = signature 4743 if operation.isGetter() and not returnType.nullable(): 4744 returnType = IDLNullableType(returnType.location, returnType) 4745 4746 # We pass len(arguments) as the final argument so that the 4747 # CGPerSignatureCall won't do any argument conversion of its own. 4748 CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName, 4749 False, descriptor, operation, 4750 len(arguments)) 4751 4752 if operation.isSetter() or operation.isCreator(): 4753 # arguments[0] is the index or name of the item that we're setting. 4754 argument = arguments[1] 4755 info = getJSToNativeConversionInfo( 4756 argument.type, descriptor, treatNullAs=argument.treatNullAs, 4757 exceptionCode="return false;") 4758 template = info.template 4759 declType = info.declType 4760 4761 templateValues = { 4762 "val": "value.handle()", 4763 } 4764 self.cgRoot.prepend(instantiateJSToNativeConversionTemplate( 4765 template, templateValues, declType, argument.identifier.name)) 4766 self.cgRoot.prepend(CGGeneric("rooted!(in(cx) let value = desc.value);")) 4767 4768 def getArguments(self): 4769 args = [(a, process_arg(a.identifier.name, a)) for a in self.arguments] 4770 return args 4771 4772 def wrap_return_value(self): 4773 if not self.idlNode.isGetter() or self.templateValues is None: 4774 return "" 4775 4776 wrap = CGGeneric(wrapForType(**self.templateValues)) 4777 wrap = CGIfWrapper("let Some(result) = result", wrap) 4778 return "\n" + wrap.define() 4779 4780 4781class CGProxyIndexedGetter(CGProxySpecialOperation): 4782 """ 4783 Class to generate a call to an indexed getter. If templateValues is not None 4784 the returned value will be wrapped with wrapForType using templateValues. 4785 """ 4786 def __init__(self, descriptor, templateValues=None): 4787 self.templateValues = templateValues 4788 CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter') 4789 4790 4791class CGProxyIndexedSetter(CGProxySpecialOperation): 4792 """ 4793 Class to generate a call to an indexed setter. 4794 """ 4795 def __init__(self, descriptor): 4796 CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter') 4797 4798 4799class CGProxyNamedOperation(CGProxySpecialOperation): 4800 """ 4801 Class to generate a call to a named operation. 4802 """ 4803 def __init__(self, descriptor, name): 4804 CGProxySpecialOperation.__init__(self, descriptor, name) 4805 4806 def define(self): 4807 # Our first argument is the id we're getting. 4808 argName = self.arguments[0].identifier.name 4809 return ("let %s = jsid_to_string(cx, id).expect(\"Not a string-convertible JSID?\");\n" 4810 "let this = UnwrapProxy(proxy);\n" 4811 "let this = &*this;\n" % argName + 4812 CGProxySpecialOperation.define(self)) 4813 4814 4815class CGProxyNamedGetter(CGProxyNamedOperation): 4816 """ 4817 Class to generate a call to an named getter. If templateValues is not None 4818 the returned value will be wrapped with wrapForType using templateValues. 4819 """ 4820 def __init__(self, descriptor, templateValues=None): 4821 self.templateValues = templateValues 4822 CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter') 4823 4824 4825class CGProxyNamedPresenceChecker(CGProxyNamedGetter): 4826 """ 4827 Class to generate a call that checks whether a named property exists. 4828 For now, we just delegate to CGProxyNamedGetter 4829 """ 4830 def __init__(self, descriptor): 4831 CGProxyNamedGetter.__init__(self, descriptor) 4832 4833 4834class CGProxyNamedSetter(CGProxyNamedOperation): 4835 """ 4836 Class to generate a call to a named setter. 4837 """ 4838 def __init__(self, descriptor): 4839 CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter') 4840 4841 4842class CGProxyNamedDeleter(CGProxyNamedOperation): 4843 """ 4844 Class to generate a call to a named deleter. 4845 """ 4846 def __init__(self, descriptor): 4847 CGProxySpecialOperation.__init__(self, descriptor, 'NamedDeleter') 4848 4849 4850class CGProxyUnwrap(CGAbstractMethod): 4851 def __init__(self, descriptor): 4852 args = [Argument('HandleObject', 'obj')] 4853 CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", 4854 '*const ' + descriptor.concreteType, args, 4855 alwaysInline=True, unsafe=True) 4856 4857 def definition_body(self): 4858 return CGGeneric("""\ 4859/*if (xpc::WrapperFactory::IsXrayWrapper(obj)) { 4860 obj = js::UnwrapObject(obj); 4861}*/ 4862//MOZ_ASSERT(IsProxy(obj)); 4863let box_ = GetProxyPrivate(obj.get()).to_private() as *const %s; 4864return box_;""" % self.descriptor.concreteType) 4865 4866 4867class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod): 4868 def __init__(self, descriptor): 4869 args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), 4870 Argument('HandleId', 'id'), 4871 Argument('MutableHandle<PropertyDescriptor>', 'desc')] 4872 CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor", 4873 "bool", args) 4874 self.descriptor = descriptor 4875 4876 # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty 4877 def getBody(self): 4878 indexedGetter = self.descriptor.operations['IndexedGetter'] 4879 4880 get = "" 4881 if indexedGetter: 4882 get = "let index = get_array_index_from_id(cx, id);\n" 4883 4884 attrs = "JSPROP_ENUMERATE" 4885 if self.descriptor.operations['IndexedSetter'] is None: 4886 attrs += " | JSPROP_READONLY" 4887 # FIXME(#11868) Should assign to desc.value, desc.get() is a copy. 4888 fillDescriptor = ("desc.get().value = result_root.get();\n" 4889 "fill_property_descriptor(desc, proxy.get(), %s);\n" 4890 "return true;" % attrs) 4891 templateValues = { 4892 'jsvalRef': 'result_root.handle_mut()', 4893 'successCode': fillDescriptor, 4894 'pre': 'rooted!(in(cx) let mut result_root = UndefinedValue());' 4895 } 4896 get += ("if let Some(index) = index {\n" + 4897 " let this = UnwrapProxy(proxy);\n" + 4898 " let this = &*this;\n" + 4899 CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" + 4900 "}\n") 4901 4902 namedGetter = self.descriptor.operations['NamedGetter'] 4903 if namedGetter: 4904 attrs = [] 4905 if not self.descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"): 4906 attrs.append("JSPROP_ENUMERATE") 4907 if self.descriptor.operations['NamedSetter'] is None: 4908 attrs.append("JSPROP_READONLY") 4909 if attrs: 4910 attrs = " | ".join(attrs) 4911 else: 4912 attrs = "0" 4913 # FIXME(#11868) Should assign to desc.value, desc.get() is a copy. 4914 fillDescriptor = ("desc.get().value = result_root.get();\n" 4915 "fill_property_descriptor(desc, proxy.get(), %s);\n" 4916 "return true;" % attrs) 4917 templateValues = { 4918 'jsvalRef': 'result_root.handle_mut()', 4919 'successCode': fillDescriptor, 4920 'pre': 'rooted!(in(cx) let mut result_root = UndefinedValue());' 4921 } 4922 4923 # See the similar-looking in CGDOMJSProxyHandler_get for the spec quote. 4924 condition = "RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id)" 4925 if indexedGetter: 4926 condition = "index.is_none() && (%s)" % condition 4927 # Once we start supporting OverrideBuiltins we need to make 4928 # ResolveOwnProperty or EnumerateOwnProperties filter out named 4929 # properties that shadow prototype properties. 4930 namedGet = """ 4931if %s { 4932 let mut has_on_proto = false; 4933 if !has_property_on_prototype(cx, proxy, id, &mut has_on_proto) { 4934 return false; 4935 } 4936 if !has_on_proto { 4937 %s 4938 } 4939} 4940""" % (condition, CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues), 8).define()) 4941 else: 4942 namedGet = "" 4943 4944 # FIXME(#11868) Should assign to desc.obj, desc.get() is a copy. 4945 return get + """\ 4946rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>()); 4947get_expando_object(proxy, expando.handle_mut()); 4948//if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) { 4949if !expando.is_null() { 4950 if !JS_GetPropertyDescriptorById(cx, expando.handle(), id, desc) { 4951 return false; 4952 } 4953 if !desc.obj.is_null() { 4954 // Pretend the property lives on the wrapper. 4955 desc.get().obj = proxy.get(); 4956 return true; 4957 } 4958} 4959""" + namedGet + """\ 4960desc.get().obj = ptr::null_mut(); 4961return true;""" 4962 4963 def definition_body(self): 4964 return CGGeneric(self.getBody()) 4965 4966 4967class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod): 4968 def __init__(self, descriptor): 4969 args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), 4970 Argument('HandleId', 'id'), 4971 Argument('Handle<PropertyDescriptor>', 'desc'), 4972 Argument('*mut ObjectOpResult', 'opresult')] 4973 CGAbstractExternMethod.__init__(self, descriptor, "defineProperty", "bool", args) 4974 self.descriptor = descriptor 4975 4976 def getBody(self): 4977 set = "" 4978 4979 indexedSetter = self.descriptor.operations['IndexedSetter'] 4980 if indexedSetter: 4981 set += ("let index = get_array_index_from_id(cx, id);\n" + 4982 "if let Some(index) = index {\n" + 4983 " let this = UnwrapProxy(proxy);\n" + 4984 " let this = &*this;\n" + 4985 CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() + 4986 " return (*opresult).succeed();\n" + 4987 "}\n") 4988 elif self.descriptor.operations['IndexedGetter']: 4989 set += ("if get_array_index_from_id(cx, id).is_some() {\n" + 4990 " return (*opresult).failNoIndexedSetter();\n" + 4991 "}\n") 4992 4993 namedSetter = self.descriptor.operations['NamedSetter'] 4994 if namedSetter: 4995 if self.descriptor.hasUnforgeableMembers: 4996 raise TypeError("Can't handle a named setter on an interface that has " 4997 "unforgeables. Figure out how that should work!") 4998 set += ("if RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id) {\n" + 4999 CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + 5000 " return (*opresult).succeed();\n" + 5001 "}\n") 5002 else: 5003 set += ("if RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id) {\n" + 5004 CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + 5005 " if result.is_some() {\n" 5006 " return (*opresult).failNoNamedSetter();\n" 5007 " }\n" 5008 "}\n") 5009 set += "return proxyhandler::define_property(%s);" % ", ".join(a.name for a in self.args) 5010 return set 5011 5012 def definition_body(self): 5013 return CGGeneric(self.getBody()) 5014 5015 5016class CGDOMJSProxyHandler_delete(CGAbstractExternMethod): 5017 def __init__(self, descriptor): 5018 args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), 5019 Argument('HandleId', 'id'), 5020 Argument('*mut ObjectOpResult', 'res')] 5021 CGAbstractExternMethod.__init__(self, descriptor, "delete", "bool", args) 5022 self.descriptor = descriptor 5023 5024 def getBody(self): 5025 set = "" 5026 if self.descriptor.operations['NamedDeleter']: 5027 if self.descriptor.hasUnforgeableMembers: 5028 raise TypeError("Can't handle a deleter on an interface that has " 5029 "unforgeables. Figure out how that should work!") 5030 set += CGProxyNamedDeleter(self.descriptor).define() 5031 set += "return proxyhandler::delete(%s);" % ", ".join(a.name for a in self.args) 5032 return set 5033 5034 def definition_body(self): 5035 return CGGeneric(self.getBody()) 5036 5037 5038class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod): 5039 def __init__(self, descriptor): 5040 args = [Argument('*mut JSContext', 'cx'), 5041 Argument('HandleObject', 'proxy'), 5042 Argument('*mut AutoIdVector', 'props')] 5043 CGAbstractExternMethod.__init__(self, descriptor, "own_property_keys", "bool", args) 5044 self.descriptor = descriptor 5045 5046 def getBody(self): 5047 body = dedent( 5048 """ 5049 let unwrapped_proxy = UnwrapProxy(proxy); 5050 """) 5051 5052 if self.descriptor.operations['IndexedGetter']: 5053 body += dedent( 5054 """ 5055 for i in 0..(*unwrapped_proxy).Length() { 5056 rooted!(in(cx) let rooted_jsid = int_to_jsid(i as i32)); 5057 AppendToAutoIdVector(props, rooted_jsid.handle().get()); 5058 } 5059 """) 5060 5061 if self.descriptor.operations['NamedGetter']: 5062 body += dedent( 5063 """ 5064 for name in (*unwrapped_proxy).SupportedPropertyNames() { 5065 let cstring = CString::new(name).unwrap(); 5066 let jsstring = JS_AtomizeAndPinString(cx, cstring.as_ptr()); 5067 rooted!(in(cx) let rooted = jsstring); 5068 let jsid = INTERNED_STRING_TO_JSID(cx, rooted.handle().get()); 5069 rooted!(in(cx) let rooted_jsid = jsid); 5070 AppendToAutoIdVector(props, rooted_jsid.handle().get()); 5071 } 5072 """) 5073 5074 body += dedent( 5075 """ 5076 rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>()); 5077 get_expando_object(proxy, expando.handle_mut()); 5078 if !expando.is_null() { 5079 GetPropertyKeys(cx, expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); 5080 } 5081 5082 return true; 5083 """) 5084 5085 return body 5086 5087 def definition_body(self): 5088 return CGGeneric(self.getBody()) 5089 5090 5091class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod): 5092 def __init__(self, descriptor): 5093 assert (descriptor.operations["IndexedGetter"] and 5094 descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties")) 5095 args = [Argument('*mut JSContext', 'cx'), 5096 Argument('HandleObject', 'proxy'), 5097 Argument('*mut AutoIdVector', 'props')] 5098 CGAbstractExternMethod.__init__(self, descriptor, 5099 "getOwnEnumerablePropertyKeys", "bool", args) 5100 self.descriptor = descriptor 5101 5102 def getBody(self): 5103 body = dedent( 5104 """ 5105 let unwrapped_proxy = UnwrapProxy(proxy); 5106 """) 5107 5108 if self.descriptor.operations['IndexedGetter']: 5109 body += dedent( 5110 """ 5111 for i in 0..(*unwrapped_proxy).Length() { 5112 rooted!(in(cx) let rooted_jsid = int_to_jsid(i as i32)); 5113 AppendToAutoIdVector(props, rooted_jsid.handle().get()); 5114 } 5115 """) 5116 5117 body += dedent( 5118 """ 5119 rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>()); 5120 get_expando_object(proxy, expando.handle_mut()); 5121 if !expando.is_null() { 5122 GetPropertyKeys(cx, expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); 5123 } 5124 5125 return true; 5126 """) 5127 5128 return body 5129 5130 def definition_body(self): 5131 return CGGeneric(self.getBody()) 5132 5133 5134class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod): 5135 def __init__(self, descriptor): 5136 args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), 5137 Argument('HandleId', 'id'), Argument('*mut bool', 'bp')] 5138 CGAbstractExternMethod.__init__(self, descriptor, "hasOwn", "bool", args) 5139 self.descriptor = descriptor 5140 5141 def getBody(self): 5142 indexedGetter = self.descriptor.operations['IndexedGetter'] 5143 if indexedGetter: 5144 indexed = ("let index = get_array_index_from_id(cx, id);\n" + 5145 "if let Some(index) = index {\n" + 5146 " let this = UnwrapProxy(proxy);\n" + 5147 " let this = &*this;\n" + 5148 CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" + 5149 " *bp = result.is_some();\n" + 5150 " return true;\n" + 5151 "}\n\n") 5152 else: 5153 indexed = "" 5154 5155 namedGetter = self.descriptor.operations['NamedGetter'] 5156 condition = "RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id)" 5157 if indexedGetter: 5158 condition = "index.is_none() && (%s)" % condition 5159 if namedGetter: 5160 named = """\ 5161if %s { 5162 let mut has_on_proto = false; 5163 if !has_property_on_prototype(cx, proxy, id, &mut has_on_proto) { 5164 return false; 5165 } 5166 if !has_on_proto { 5167 %s 5168 *bp = result.is_some(); 5169 return true; 5170 } 5171} 5172 5173""" % (condition, CGIndenter(CGProxyNamedGetter(self.descriptor), 8).define()) 5174 else: 5175 named = "" 5176 5177 return indexed + """\ 5178rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>()); 5179get_expando_object(proxy, expando.handle_mut()); 5180if !expando.is_null() { 5181 let ok = JS_HasPropertyById(cx, expando.handle(), id, bp); 5182 if !ok || *bp { 5183 return ok; 5184 } 5185} 5186""" + named + """\ 5187*bp = false; 5188return true;""" 5189 5190 def definition_body(self): 5191 return CGGeneric(self.getBody()) 5192 5193 5194class CGDOMJSProxyHandler_get(CGAbstractExternMethod): 5195 def __init__(self, descriptor): 5196 args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), 5197 Argument('HandleValue', 'receiver'), Argument('HandleId', 'id'), 5198 Argument('MutableHandleValue', 'vp')] 5199 CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args) 5200 self.descriptor = descriptor 5201 5202 # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty 5203 def getBody(self): 5204 getFromExpando = """\ 5205rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>()); 5206get_expando_object(proxy, expando.handle_mut()); 5207if !expando.is_null() { 5208 let mut hasProp = false; 5209 if !JS_HasPropertyById(cx, expando.handle(), id, &mut hasProp) { 5210 return false; 5211 } 5212 5213 if hasProp { 5214 return JS_ForwardGetPropertyTo(cx, expando.handle(), id, receiver, vp); 5215 } 5216}""" 5217 5218 templateValues = { 5219 'jsvalRef': 'vp', 5220 'successCode': 'return true;', 5221 } 5222 5223 indexedGetter = self.descriptor.operations['IndexedGetter'] 5224 if indexedGetter: 5225 getIndexedOrExpando = ("let index = get_array_index_from_id(cx, id);\n" + 5226 "if let Some(index) = index {\n" + 5227 " let this = UnwrapProxy(proxy);\n" + 5228 " let this = &*this;\n" + 5229 CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) 5230 getIndexedOrExpando += """\ 5231 // Even if we don't have this index, we don't forward the 5232 // get on to our expando object. 5233} else { 5234 %s 5235} 5236""" % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n '))) 5237 else: 5238 getIndexedOrExpando = getFromExpando + "\n" 5239 5240 namedGetter = self.descriptor.operations['NamedGetter'] 5241 if namedGetter: 5242 condition = "RUST_JSID_IS_STRING(id) || RUST_JSID_IS_INT(id)" 5243 # From step 1: 5244 # If O supports indexed properties and P is an array index, then: 5245 # 5246 # 3. Set ignoreNamedProps to true. 5247 if indexedGetter: 5248 condition = "index.is_none() && (%s)" % condition 5249 getNamed = ("if %s {\n" + 5250 CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + 5251 "}\n") % condition 5252 else: 5253 getNamed = "" 5254 5255 return """\ 5256//MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 5257//"Should not have a XrayWrapper here"); 5258 5259%s 5260let mut found = false; 5261if !get_property_on_prototype(cx, proxy, receiver, id, &mut found, vp) { 5262 return false; 5263} 5264 5265if found { 5266 return true; 5267} 5268%s 5269vp.set(UndefinedValue()); 5270return true;""" % (getIndexedOrExpando, getNamed) 5271 5272 def definition_body(self): 5273 return CGGeneric(self.getBody()) 5274 5275 5276class CGDOMJSProxyHandler_className(CGAbstractExternMethod): 5277 def __init__(self, descriptor): 5278 args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', '_proxy')] 5279 CGAbstractExternMethod.__init__(self, descriptor, "className", "*const i8", args, doesNotPanic=True) 5280 self.descriptor = descriptor 5281 5282 def getBody(self): 5283 return '%s as *const u8 as *const i8' % str_to_const_array(self.descriptor.name) 5284 5285 def definition_body(self): 5286 return CGGeneric(self.getBody()) 5287 5288 5289class CGAbstractClassHook(CGAbstractExternMethod): 5290 """ 5291 Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw 5292 'this' unwrapping as it assumes that the unwrapped type is always known. 5293 """ 5294 def __init__(self, descriptor, name, returnType, args, doesNotPanic=False): 5295 CGAbstractExternMethod.__init__(self, descriptor, name, returnType, 5296 args) 5297 5298 def definition_body_prologue(self): 5299 return CGGeneric(""" 5300let this = native_from_object::<%s>(obj).unwrap(); 5301""" % self.descriptor.concreteType) 5302 5303 def definition_body(self): 5304 return CGList([ 5305 self.definition_body_prologue(), 5306 self.generate_code(), 5307 ]) 5308 5309 def generate_code(self): 5310 raise NotImplementedError # Override me! 5311 5312 5313def finalizeHook(descriptor, hookName, context): 5314 release = "" 5315 if descriptor.isGlobal(): 5316 release += """\ 5317finalize_global(obj); 5318""" 5319 elif descriptor.weakReferenceable: 5320 release += """\ 5321let weak_box_ptr = JS_GetReservedSlot(obj, DOM_WEAK_SLOT).to_private() as *mut WeakBox<%s>; 5322if !weak_box_ptr.is_null() { 5323 let count = { 5324 let weak_box = &*weak_box_ptr; 5325 assert!(weak_box.value.get().is_some()); 5326 assert!(weak_box.count.get() > 0); 5327 weak_box.value.set(None); 5328 let count = weak_box.count.get() - 1; 5329 weak_box.count.set(count); 5330 count 5331 }; 5332 if count == 0 { 5333 mem::drop(Box::from_raw(weak_box_ptr)); 5334 } 5335} 5336""" % descriptor.concreteType 5337 release += """\ 5338if !this.is_null() { 5339 // The pointer can be null if the object is the unforgeable holder of that interface. 5340 let _ = Box::from_raw(this as *mut %s); 5341} 5342debug!("%s finalize: {:p}", this);\ 5343""" % (descriptor.concreteType, descriptor.concreteType) 5344 return release 5345 5346 5347class CGClassTraceHook(CGAbstractClassHook): 5348 """ 5349 A hook to trace through our native object; used for GC and CC 5350 """ 5351 def __init__(self, descriptor): 5352 args = [Argument('*mut JSTracer', 'trc'), Argument('*mut JSObject', 'obj')] 5353 CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void', 5354 args, doesNotPanic=True) 5355 self.traceGlobal = descriptor.isGlobal() 5356 5357 def generate_code(self): 5358 body = [CGGeneric("if this.is_null() { return; } // GC during obj creation\n" 5359 "(*this).trace(%s);" % self.args[0].name)] 5360 if self.traceGlobal: 5361 body += [CGGeneric("trace_global(trc, obj);")] 5362 return CGList(body, "\n") 5363 5364 5365class CGClassConstructHook(CGAbstractExternMethod): 5366 """ 5367 JS-visible constructor for our objects 5368 """ 5369 def __init__(self, descriptor, constructor=None): 5370 args = [Argument('*mut JSContext', 'cx'), Argument('u32', 'argc'), Argument('*mut JSVal', 'vp')] 5371 name = CONSTRUCT_HOOK_NAME 5372 if constructor: 5373 name += "_" + constructor.identifier.name 5374 else: 5375 constructor = descriptor.interface.ctor() 5376 assert constructor 5377 CGAbstractExternMethod.__init__(self, descriptor, name, 'bool', args) 5378 self.constructor = constructor 5379 self.exposureSet = descriptor.interface.exposureSet 5380 5381 def definition_body(self): 5382 preamble = """let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object());\n""" 5383 if len(self.exposureSet) == 1: 5384 preamble += """\ 5385let global = DomRoot::downcast::<dom::types::%s>(global).unwrap(); 5386""" % list(self.exposureSet)[0] 5387 preamble += """let args = CallArgs::from_vp(vp, argc);\n""" 5388 preamble = CGGeneric(preamble) 5389 if self.constructor.isHTMLConstructor(): 5390 signatures = self.constructor.signatures() 5391 assert len(signatures) == 1 5392 constructorCall = CGGeneric("""\ 5393// Step 2 https://html.spec.whatwg.org/multipage/#htmlconstructor 5394// The custom element definition cannot use an element interface as its constructor 5395 5396// The new_target might be a cross-compartment wrapper. Get the underlying object 5397// so we can do the spec's object-identity checks. 5398rooted!(in(cx) let new_target = UnwrapObject(args.new_target().to_object(), 1)); 5399if new_target.is_null() { 5400 throw_dom_exception(cx, global.upcast::<GlobalScope>(), Error::Type("new.target is null".to_owned())); 5401 return false; 5402} 5403 5404if args.callee() == new_target.get() { 5405 throw_dom_exception(cx, global.upcast::<GlobalScope>(), 5406 Error::Type("new.target must not be the active function object".to_owned())); 5407 return false; 5408} 5409 5410// Step 6 5411rooted!(in(cx) let mut prototype = ptr::null_mut::<JSObject>()); 5412{ 5413 rooted!(in(cx) let mut proto_val = UndefinedValue()); 5414 let _ac = JSAutoCompartment::new(cx, new_target.get()); 5415 if !JS_GetProperty(cx, new_target.handle(), b"prototype\\0".as_ptr() as *const _, proto_val.handle_mut()) { 5416 return false; 5417 } 5418 5419 if !proto_val.is_object() { 5420 // Step 7 of https://html.spec.whatwg.org/multipage/#htmlconstructor. 5421 // This fallback behavior is designed to match analogous behavior for the 5422 // JavaScript built-ins. So we enter the compartment of our underlying 5423 // newTarget object and fall back to the prototype object from that global. 5424 // XXX The spec says to use GetFunctionRealm(), which is not actually 5425 // the same thing as what we have here (e.g. in the case of scripted callable proxies 5426 // whose target is not same-compartment with the proxy, or bound functions, etc). 5427 // https://bugzilla.mozilla.org/show_bug.cgi?id=1317658 5428 5429 rooted!(in(cx) let global_object = CurrentGlobalOrNull(cx)); 5430 GetProtoObject(cx, global_object.handle(), prototype.handle_mut()); 5431 } else { 5432 // Step 6 5433 prototype.set(proto_val.to_object()); 5434 }; 5435} 5436 5437// Wrap prototype in this context since it is from the newTarget compartment 5438if !JS_WrapObject(cx, prototype.handle_mut()) { 5439 return false; 5440} 5441 5442let result: Result<DomRoot<%s>, Error> = html_constructor(&global, &args); 5443let result = match result { 5444 Ok(result) => result, 5445 Err(e) => { 5446 throw_dom_exception(cx, global.upcast::<GlobalScope>(), e); 5447 return false; 5448 }, 5449}; 5450 5451rooted!(in(cx) let mut element = result.reflector().get_jsobject().get()); 5452if !JS_WrapObject(cx, element.handle_mut()) { 5453 return false; 5454} 5455 5456JS_SetPrototype(cx, element.handle(), prototype.handle()); 5457 5458(result).to_jsval(cx, args.rval()); 5459return true; 5460""" % self.descriptor.name) 5461 else: 5462 name = self.constructor.identifier.name 5463 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name)) 5464 constructorCall = CGMethodCall(["&global"], nativeName, True, 5465 self.descriptor, self.constructor) 5466 return CGList([preamble, constructorCall]) 5467 5468 5469class CGClassFinalizeHook(CGAbstractClassHook): 5470 """ 5471 A hook for finalize, used to release our native object. 5472 """ 5473 def __init__(self, descriptor): 5474 args = [Argument('*mut JSFreeOp', '_fop'), Argument('*mut JSObject', 'obj')] 5475 CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, 5476 'void', args) 5477 5478 def generate_code(self): 5479 return CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name)) 5480 5481 5482class CGDOMJSProxyHandlerDOMClass(CGThing): 5483 def __init__(self, descriptor): 5484 CGThing.__init__(self) 5485 self.descriptor = descriptor 5486 5487 def define(self): 5488 return "static Class: DOMClass = " + DOMClass(self.descriptor) + ";\n" 5489 5490 5491class CGInterfaceTrait(CGThing): 5492 def __init__(self, descriptor): 5493 CGThing.__init__(self) 5494 5495 def attribute_arguments(needCx, argument=None): 5496 if needCx: 5497 yield "cx", "*mut JSContext" 5498 5499 if argument: 5500 yield "value", argument_type(descriptor, argument) 5501 5502 def members(): 5503 for m in descriptor.interface.members: 5504 if (m.isMethod() and not m.isStatic() and 5505 not m.isMaplikeOrSetlikeOrIterableMethod() and 5506 (not m.isIdentifierLess() or m.isStringifier())): 5507 name = CGSpecializedMethod.makeNativeName(descriptor, m) 5508 infallible = 'infallible' in descriptor.getExtendedAttributes(m) 5509 for idx, (rettype, arguments) in enumerate(m.signatures()): 5510 arguments = method_arguments(descriptor, rettype, arguments) 5511 rettype = return_type(descriptor, rettype, infallible) 5512 yield name + ('_' * idx), arguments, rettype 5513 elif m.isAttr() and not m.isStatic(): 5514 name = CGSpecializedGetter.makeNativeName(descriptor, m) 5515 infallible = 'infallible' in descriptor.getExtendedAttributes(m, getter=True) 5516 yield (name, 5517 attribute_arguments(typeNeedsCx(m.type, True)), 5518 return_type(descriptor, m.type, infallible)) 5519 5520 if not m.readonly: 5521 name = CGSpecializedSetter.makeNativeName(descriptor, m) 5522 infallible = 'infallible' in descriptor.getExtendedAttributes(m, setter=True) 5523 if infallible: 5524 rettype = "()" 5525 else: 5526 rettype = "ErrorResult" 5527 yield name, attribute_arguments(typeNeedsCx(m.type, False), m.type), rettype 5528 5529 if descriptor.proxy: 5530 for name, operation in descriptor.operations.iteritems(): 5531 if not operation or operation.isStringifier(): 5532 continue 5533 5534 assert len(operation.signatures()) == 1 5535 rettype, arguments = operation.signatures()[0] 5536 5537 infallible = 'infallible' in descriptor.getExtendedAttributes(operation) 5538 if operation.isGetter(): 5539 if not rettype.nullable(): 5540 rettype = IDLNullableType(rettype.location, rettype) 5541 arguments = method_arguments(descriptor, rettype, arguments) 5542 5543 # If this interface 'supports named properties', then we 5544 # should be able to access 'supported property names' 5545 # 5546 # WebIDL, Second Draft, section 3.2.4.5 5547 # https://heycam.github.io/webidl/#idl-named-properties 5548 if operation.isNamed(): 5549 yield "SupportedPropertyNames", [], "Vec<DOMString>" 5550 else: 5551 arguments = method_arguments(descriptor, rettype, arguments) 5552 rettype = return_type(descriptor, rettype, infallible) 5553 yield name, arguments, rettype 5554 5555 def fmt(arguments): 5556 return "".join(", %s: %s" % argument for argument in arguments) 5557 5558 def contains_unsafe_arg(arguments): 5559 if not arguments or len(arguments) == 0: 5560 return False 5561 return reduce((lambda x, y: x or y[1] == '*mut JSContext'), arguments, False) 5562 5563 methods = [] 5564 for name, arguments, rettype in members(): 5565 arguments = list(arguments) 5566 methods.append(CGGeneric("%sfn %s(&self%s) -> %s;\n" % ( 5567 'unsafe ' if contains_unsafe_arg(arguments) else '', 5568 name, fmt(arguments), rettype)) 5569 ) 5570 5571 if methods: 5572 self.cgRoot = CGWrapper(CGIndenter(CGList(methods, "")), 5573 pre="pub trait %sMethods {\n" % descriptor.interface.identifier.name, 5574 post="}") 5575 else: 5576 self.cgRoot = CGGeneric("") 5577 self.empty = not methods 5578 5579 def define(self): 5580 return self.cgRoot.define() 5581 5582 5583class CGWeakReferenceableTrait(CGThing): 5584 def __init__(self, descriptor): 5585 CGThing.__init__(self) 5586 assert descriptor.weakReferenceable 5587 self.code = "impl WeakReferenceable for %s {}" % descriptor.interface.identifier.name 5588 5589 def define(self): 5590 return self.code 5591 5592 5593def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries=None, enums=None, typedefs=None): 5594 if not callbacks: 5595 callbacks = [] 5596 if not dictionaries: 5597 dictionaries = [] 5598 if not enums: 5599 enums = [] 5600 if not typedefs: 5601 typedefs = [] 5602 5603 return CGImports(cgthings, descriptors, callbacks, dictionaries, enums, typedefs, [ 5604 'js', 5605 'js::JSCLASS_GLOBAL_SLOT_COUNT', 5606 'js::JSCLASS_IS_DOMJSCLASS', 5607 'js::JSCLASS_IS_GLOBAL', 5608 'js::JSCLASS_RESERVED_SLOTS_MASK', 5609 'js::JS_CALLEE', 5610 'js::error::throw_type_error', 5611 'js::error::throw_internal_error', 5612 'js::jsapi::AutoIdVector', 5613 'js::jsapi::Call', 5614 'js::jsapi::CallArgs', 5615 'js::jsapi::CurrentGlobalOrNull', 5616 'js::jsapi::FreeOp', 5617 'js::jsapi::GetPropertyKeys', 5618 'js::jsapi::GetWellKnownSymbol', 5619 'js::jsapi::Handle', 5620 'js::jsapi::HandleId', 5621 'js::jsapi::HandleObject', 5622 'js::jsapi::HandleValue', 5623 'js::jsapi::HandleValueArray', 5624 'js::jsapi::Heap', 5625 'js::jsapi::INTERNED_STRING_TO_JSID', 5626 'js::jsapi::IsCallable', 5627 'js::jsapi::JSAutoCompartment', 5628 'js::jsapi::JSCLASS_RESERVED_SLOTS_SHIFT', 5629 'js::jsapi::JSClass', 5630 'js::jsapi::JSContext', 5631 'js::jsapi::JSFreeOp', 5632 'js::jsapi::JSFunctionSpec', 5633 'js::jsapi::JSITER_HIDDEN', 5634 'js::jsapi::JSITER_OWNONLY', 5635 'js::jsapi::JSITER_SYMBOLS', 5636 'js::jsapi::JSJitGetterCallArgs', 5637 'js::jsapi::JSJitInfo', 5638 'js::jsapi::JSJitInfo_AliasSet', 5639 'js::jsapi::JSJitInfo_ArgType', 5640 'js::jsapi::JSJitInfo_OpType', 5641 'js::jsapi::JSJitMethodCallArgs', 5642 'js::jsapi::JSJitSetterCallArgs', 5643 'js::jsapi::JSNative', 5644 'js::jsapi::JSNativeWrapper', 5645 'js::jsapi::JSObject', 5646 'js::jsapi::JSPROP_ENUMERATE', 5647 'js::jsapi::JSPROP_PERMANENT', 5648 'js::jsapi::JSPROP_READONLY', 5649 'js::jsapi::JSPROP_SHARED', 5650 'js::jsapi::JSPropertySpec', 5651 'js::jsapi::JSString', 5652 'js::jsapi::JSTracer', 5653 'js::jsapi::JSType', 5654 'js::jsapi::JSTypedMethodJitInfo', 5655 'js::jsapi::JSValueType', 5656 'js::jsapi::JS_AtomizeAndPinString', 5657 'js::jsapi::JS_CallFunctionValue', 5658 'js::jsapi::JS_CopyPropertiesFrom', 5659 'js::jsapi::JS_DefineProperty', 5660 'js::jsapi::JS_DefinePropertyById2', 5661 'js::jsapi::JS_ForwardGetPropertyTo', 5662 'js::jsapi::JS_GetErrorPrototype', 5663 'js::jsapi::JS_GetFunctionPrototype', 5664 'js::jsapi::JS_GetGlobalForObject', 5665 'js::jsapi::JS_GetIteratorPrototype', 5666 'js::jsapi::JS_GetObjectPrototype', 5667 'js::jsapi::JS_GetProperty', 5668 'js::jsapi::JS_GetPropertyById', 5669 'js::jsapi::JS_GetPropertyDescriptorById', 5670 'js::jsapi::JS_GetReservedSlot', 5671 'js::jsapi::JS_HasProperty', 5672 'js::jsapi::JS_HasPropertyById', 5673 'js::jsapi::JS_InitializePropertiesFromCompatibleNativeObject', 5674 'js::jsapi::JS_NewObject', 5675 'js::jsapi::JS_NewObjectWithGivenProto', 5676 'js::jsapi::JS_NewObjectWithoutMetadata', 5677 'js::jsapi::JS_ObjectIsDate', 5678 'js::jsapi::JS_SetImmutablePrototype', 5679 'js::jsapi::JS_SetProperty', 5680 'js::jsapi::JS_SetPrototype', 5681 'js::jsapi::JS_SetReservedSlot', 5682 'js::jsapi::JS_SplicePrototype', 5683 'js::jsapi::JS_WrapValue', 5684 'js::jsapi::JS_WrapObject', 5685 'js::jsapi::MutableHandle', 5686 'js::jsapi::MutableHandleObject', 5687 'js::jsapi::MutableHandleValue', 5688 'js::jsapi::ObjectOpResult', 5689 'js::jsapi::PropertyDescriptor', 5690 'js::jsapi::RootedId', 5691 'js::jsapi::RootedObject', 5692 'js::jsapi::RootedString', 5693 'js::jsapi::SymbolCode', 5694 'js::jsapi::jsid', 5695 'js::jsval::JSVal', 5696 'js::jsval::NullValue', 5697 'js::jsval::ObjectValue', 5698 'js::jsval::ObjectOrNullValue', 5699 'js::jsval::PrivateValue', 5700 'js::jsval::UndefinedValue', 5701 'js::glue::AppendToAutoIdVector', 5702 'js::glue::CallJitGetterOp', 5703 'js::glue::CallJitMethodOp', 5704 'js::glue::CallJitSetterOp', 5705 'js::glue::CreateProxyHandler', 5706 'js::glue::GetProxyPrivate', 5707 'js::glue::NewProxyObject', 5708 'js::glue::ProxyTraps', 5709 'js::glue::RUST_JSID_IS_INT', 5710 'js::glue::RUST_JSID_IS_STRING', 5711 'js::glue::RUST_SYMBOL_TO_JSID', 5712 'js::glue::int_to_jsid', 5713 'js::glue::UnwrapObject', 5714 'js::panic::maybe_resume_unwind', 5715 'js::panic::wrap_panic', 5716 'js::rust::GCMethods', 5717 'js::rust::CustomAutoRooterGuard', 5718 'js::rust::define_methods', 5719 'js::rust::define_properties', 5720 'js::rust::get_object_class', 5721 'dom', 5722 'dom::bindings', 5723 'dom::bindings::codegen::InterfaceObjectMap', 5724 'dom::bindings::constant::ConstantSpec', 5725 'dom::bindings::constant::ConstantVal', 5726 'dom::bindings::interface::ConstructorClassHook', 5727 'dom::bindings::interface::InterfaceConstructorBehavior', 5728 'dom::bindings::interface::NonCallbackInterfaceObjectClass', 5729 'dom::bindings::interface::create_global_object', 5730 'dom::bindings::interface::create_callback_interface_object', 5731 'dom::bindings::interface::create_interface_prototype_object', 5732 'dom::bindings::interface::create_named_constructors', 5733 'dom::bindings::interface::create_noncallback_interface_object', 5734 'dom::bindings::interface::define_guarded_constants', 5735 'dom::bindings::interface::define_guarded_methods', 5736 'dom::bindings::interface::define_guarded_properties', 5737 'dom::bindings::htmlconstructor::html_constructor', 5738 'dom::bindings::interface::is_exposed_in', 5739 'dom::bindings::htmlconstructor::pop_current_element_queue', 5740 'dom::bindings::htmlconstructor::push_new_element_queue', 5741 'dom::bindings::iterable::Iterable', 5742 'dom::bindings::iterable::IteratorType', 5743 'dom::bindings::namespace::NamespaceObjectClass', 5744 'dom::bindings::namespace::create_namespace_object', 5745 'dom::bindings::reflector::MutDomObject', 5746 'dom::bindings::reflector::DomObject', 5747 'dom::bindings::root::Dom', 5748 'dom::bindings::root::DomRoot', 5749 'dom::bindings::root::OptionalHeapSetter', 5750 'dom::bindings::root::RootedReference', 5751 'dom::bindings::utils::AsVoidPtr', 5752 'dom::bindings::utils::DOMClass', 5753 'dom::bindings::utils::DOMJSClass', 5754 'dom::bindings::utils::DOM_PROTO_UNFORGEABLE_HOLDER_SLOT', 5755 'dom::bindings::utils::JSCLASS_DOM_GLOBAL', 5756 'dom::bindings::utils::ProtoOrIfaceArray', 5757 'dom::bindings::utils::enumerate_global', 5758 'dom::bindings::utils::finalize_global', 5759 'dom::bindings::utils::find_enum_value', 5760 'dom::bindings::utils::generic_getter', 5761 'dom::bindings::utils::generic_lenient_getter', 5762 'dom::bindings::utils::generic_lenient_setter', 5763 'dom::bindings::utils::generic_method', 5764 'dom::bindings::utils::generic_setter', 5765 'dom::bindings::utils::get_array_index_from_id', 5766 'dom::bindings::utils::get_dictionary_property', 5767 'dom::bindings::utils::get_property_on_prototype', 5768 'dom::bindings::utils::get_proto_or_iface_array', 5769 'dom::bindings::utils::has_property_on_prototype', 5770 'dom::bindings::utils::is_platform_object', 5771 'dom::bindings::utils::resolve_global', 5772 'dom::bindings::utils::set_dictionary_property', 5773 'dom::bindings::utils::trace_global', 5774 'dom::bindings::trace::JSTraceable', 5775 'dom::bindings::trace::RootedTraceable', 5776 'dom::bindings::trace::RootedTraceableBox', 5777 'dom::bindings::callback::CallSetup', 5778 'dom::bindings::callback::CallbackContainer', 5779 'dom::bindings::callback::CallbackInterface', 5780 'dom::bindings::callback::CallbackFunction', 5781 'dom::bindings::callback::CallbackObject', 5782 'dom::bindings::callback::ExceptionHandling', 5783 'dom::bindings::callback::wrap_call_this_object', 5784 'dom::bindings::conversions::ConversionBehavior', 5785 'dom::bindings::conversions::ConversionResult', 5786 'dom::bindings::conversions::DOM_OBJECT_SLOT', 5787 'dom::bindings::conversions::FromJSValConvertible', 5788 'dom::bindings::conversions::IDLInterface', 5789 'dom::bindings::conversions::StringificationBehavior', 5790 'dom::bindings::conversions::ToJSValConvertible', 5791 'dom::bindings::conversions::is_array_like', 5792 'dom::bindings::conversions::native_from_handlevalue', 5793 'dom::bindings::conversions::native_from_object', 5794 'dom::bindings::conversions::private_from_object', 5795 'dom::bindings::conversions::root_from_handleobject', 5796 'dom::bindings::conversions::root_from_handlevalue', 5797 'dom::bindings::conversions::root_from_object', 5798 'dom::bindings::conversions::jsid_to_string', 5799 'dom::bindings::codegen::PrototypeList', 5800 'dom::bindings::codegen::RegisterBindings', 5801 'dom::bindings::codegen::UnionTypes', 5802 'dom::bindings::error::Error', 5803 'dom::bindings::error::ErrorResult', 5804 'dom::bindings::error::Fallible', 5805 'dom::bindings::error::Error::JSFailed', 5806 'dom::bindings::error::throw_dom_exception', 5807 'dom::bindings::guard::Condition', 5808 'dom::bindings::guard::Guard', 5809 'dom::bindings::inheritance::Castable', 5810 'dom::bindings::proxyhandler', 5811 'dom::bindings::proxyhandler::ensure_expando_object', 5812 'dom::bindings::proxyhandler::fill_property_descriptor', 5813 'dom::bindings::proxyhandler::get_expando_object', 5814 'dom::bindings::proxyhandler::get_property_descriptor', 5815 'dom::bindings::mozmap::MozMap', 5816 'std::ptr::NonNull', 5817 'dom::bindings::num::Finite', 5818 'dom::bindings::str::ByteString', 5819 'dom::bindings::str::DOMString', 5820 'dom::bindings::str::USVString', 5821 'dom::bindings::weakref::DOM_WEAK_SLOT', 5822 'dom::bindings::weakref::WeakBox', 5823 'dom::bindings::weakref::WeakReferenceable', 5824 'dom::windowproxy::WindowProxy', 5825 'dom::globalscope::GlobalScope', 5826 'mem::malloc_size_of_including_raw_self', 5827 'libc', 5828 'servo_config::prefs::PREFS', 5829 'std::borrow::ToOwned', 5830 'std::cmp', 5831 'std::mem', 5832 'std::num', 5833 'std::os', 5834 'std::panic', 5835 'std::ptr', 5836 'std::str', 5837 'std::rc', 5838 'std::rc::Rc', 5839 'std::default::Default', 5840 'std::ffi::CString', 5841 ], config) 5842 5843 5844class CGDescriptor(CGThing): 5845 def __init__(self, descriptor, config, soleDescriptor): 5846 CGThing.__init__(self) 5847 5848 assert not descriptor.concrete or not descriptor.interface.isCallback() 5849 5850 reexports = [] 5851 5852 def reexportedName(name): 5853 if name.startswith(descriptor.name): 5854 return name 5855 if not soleDescriptor: 5856 return '%s as %s%s' % (name, descriptor.name, name) 5857 return name 5858 5859 cgThings = [] 5860 5861 unscopableNames = [] 5862 for m in descriptor.interface.members: 5863 if (m.isMethod() and 5864 (not m.isIdentifierLess() or m == descriptor.operations["Stringifier"])): 5865 if m.getExtendedAttribute("Unscopable"): 5866 assert not m.isStatic() 5867 unscopableNames.append(m.identifier.name) 5868 if m.isStatic(): 5869 assert descriptor.interface.hasInterfaceObject() 5870 cgThings.append(CGStaticMethod(descriptor, m)) 5871 elif not descriptor.interface.isCallback(): 5872 cgThings.append(CGSpecializedMethod(descriptor, m)) 5873 cgThings.append(CGMemberJITInfo(descriptor, m)) 5874 elif m.isAttr(): 5875 if m.stringifier: 5876 raise TypeError("Stringifier attributes not supported yet. " 5877 "See https://github.com/servo/servo/issues/7590\n" 5878 "%s" % m.location) 5879 if m.getExtendedAttribute("Unscopable"): 5880 assert not m.isStatic() 5881 unscopableNames.append(m.identifier.name) 5882 if m.isStatic(): 5883 assert descriptor.interface.hasInterfaceObject() 5884 cgThings.append(CGStaticGetter(descriptor, m)) 5885 elif not descriptor.interface.isCallback(): 5886 cgThings.append(CGSpecializedGetter(descriptor, m)) 5887 5888 if not m.readonly: 5889 if m.isStatic(): 5890 assert descriptor.interface.hasInterfaceObject() 5891 cgThings.append(CGStaticSetter(descriptor, m)) 5892 elif not descriptor.interface.isCallback(): 5893 cgThings.append(CGSpecializedSetter(descriptor, m)) 5894 elif m.getExtendedAttribute("PutForwards"): 5895 cgThings.append(CGSpecializedForwardingSetter(descriptor, m)) 5896 elif m.getExtendedAttribute("Replaceable"): 5897 cgThings.append(CGSpecializedReplaceableSetter(descriptor, m)) 5898 5899 if (not m.isStatic() and not descriptor.interface.isCallback()): 5900 cgThings.append(CGMemberJITInfo(descriptor, m)) 5901 5902 if descriptor.concrete: 5903 cgThings.append(CGClassFinalizeHook(descriptor)) 5904 cgThings.append(CGClassTraceHook(descriptor)) 5905 5906 # If there are no constant members, don't make a module for constants 5907 constMembers = [CGConstant(m) for m in descriptor.interface.members if m.isConst()] 5908 if constMembers: 5909 cgThings.append(CGNamespace.build([descriptor.name + "Constants"], 5910 CGIndenter(CGList(constMembers)), 5911 public=True)) 5912 reexports.append(descriptor.name + 'Constants') 5913 5914 if descriptor.proxy: 5915 cgThings.append(CGDefineProxyHandler(descriptor)) 5916 5917 properties = PropertyArrays(descriptor) 5918 5919 if descriptor.concrete: 5920 if descriptor.proxy: 5921 # cgThings.append(CGProxyIsProxy(descriptor)) 5922 cgThings.append(CGProxyUnwrap(descriptor)) 5923 cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor)) 5924 cgThings.append(CGDOMJSProxyHandler_ownPropertyKeys(descriptor)) 5925 if descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"): 5926 cgThings.append(CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(descriptor)) 5927 cgThings.append(CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)) 5928 cgThings.append(CGDOMJSProxyHandler_className(descriptor)) 5929 cgThings.append(CGDOMJSProxyHandler_get(descriptor)) 5930 cgThings.append(CGDOMJSProxyHandler_hasOwn(descriptor)) 5931 5932 if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']: 5933 cgThings.append(CGDOMJSProxyHandler_defineProperty(descriptor)) 5934 5935 # We want to prevent indexed deleters from compiling at all. 5936 assert not descriptor.operations['IndexedDeleter'] 5937 5938 if descriptor.operations['NamedDeleter']: 5939 cgThings.append(CGDOMJSProxyHandler_delete(descriptor)) 5940 5941 # cgThings.append(CGDOMJSProxyHandler(descriptor)) 5942 # cgThings.append(CGIsMethod(descriptor)) 5943 pass 5944 else: 5945 cgThings.append(CGDOMJSClass(descriptor)) 5946 pass 5947 5948 if descriptor.isGlobal(): 5949 cgThings.append(CGWrapGlobalMethod(descriptor, properties)) 5950 else: 5951 cgThings.append(CGWrapMethod(descriptor)) 5952 reexports.append('Wrap') 5953 5954 haveUnscopables = False 5955 if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace(): 5956 if unscopableNames: 5957 haveUnscopables = True 5958 cgThings.append( 5959 CGList([CGGeneric("const unscopable_names: &'static [&'static [u8]] = &["), 5960 CGIndenter(CGList([CGGeneric(str_to_const_array(name)) for 5961 name in unscopableNames], ",\n")), 5962 CGGeneric("];\n")], "\n")) 5963 if descriptor.concrete or descriptor.hasDescendants(): 5964 cgThings.append(CGIDLInterface(descriptor)) 5965 5966 interfaceTrait = CGInterfaceTrait(descriptor) 5967 cgThings.append(interfaceTrait) 5968 if not interfaceTrait.empty: 5969 reexports.append('%sMethods' % descriptor.name) 5970 5971 if descriptor.weakReferenceable: 5972 cgThings.append(CGWeakReferenceableTrait(descriptor)) 5973 5974 cgThings.append(CGGeneric(str(properties))) 5975 5976 if not descriptor.interface.getExtendedAttribute("Inline"): 5977 if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace(): 5978 cgThings.append(CGGetProtoObjectMethod(descriptor)) 5979 reexports.append('GetProtoObject') 5980 cgThings.append(CGPrototypeJSClass(descriptor)) 5981 if descriptor.interface.hasInterfaceObject(): 5982 if descriptor.interface.ctor(): 5983 cgThings.append(CGClassConstructHook(descriptor)) 5984 for ctor in descriptor.interface.namedConstructors: 5985 cgThings.append(CGClassConstructHook(descriptor, ctor)) 5986 if not descriptor.interface.isCallback(): 5987 cgThings.append(CGInterfaceObjectJSClass(descriptor)) 5988 if descriptor.shouldHaveGetConstructorObjectMethod(): 5989 cgThings.append(CGGetConstructorObjectMethod(descriptor)) 5990 reexports.append('GetConstructorObject') 5991 if descriptor.register: 5992 cgThings.append(CGDefineDOMInterfaceMethod(descriptor)) 5993 reexports.append('DefineDOMInterface') 5994 cgThings.append(CGConstructorEnabled(descriptor)) 5995 cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, haveUnscopables)) 5996 5997 cgThings = generate_imports(config, CGList(cgThings, '\n'), [descriptor]) 5998 cgThings = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name), 5999 cgThings, public=True), 6000 post='\n') 6001 6002 if reexports: 6003 reexports = ', '.join(map(lambda name: reexportedName(name), reexports)) 6004 cgThings = CGList([CGGeneric('pub use self::%s::{%s};' % (toBindingNamespace(descriptor.name), reexports)), 6005 cgThings], '\n') 6006 6007 self.cgRoot = cgThings 6008 6009 def define(self): 6010 return self.cgRoot.define() 6011 6012 6013class CGNonNamespacedEnum(CGThing): 6014 def __init__(self, enumName, names, first, comment="", deriving="", repr=""): 6015 # Account for first value 6016 entries = ["%s = %s" % (names[0], first)] + names[1:] 6017 6018 # Append a Last. 6019 entries.append('#[allow(dead_code)] Last = ' + str(first + len(entries))) 6020 6021 # Indent. 6022 entries = [' ' + e for e in entries] 6023 6024 # Build the enum body. 6025 enumstr = comment + 'pub enum %s {\n%s\n}\n' % (enumName, ',\n'.join(entries)) 6026 if repr: 6027 enumstr = ('#[repr(%s)]\n' % repr) + enumstr 6028 if deriving: 6029 enumstr = ('#[derive(%s)]\n' % deriving) + enumstr 6030 curr = CGGeneric(enumstr) 6031 6032 # Add some whitespace padding. 6033 curr = CGWrapper(curr, pre='\n', post='\n') 6034 6035 # Add the typedef 6036 # typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName) 6037 # curr = CGList([curr, CGGeneric(typedef)]) 6038 6039 # Save the result. 6040 self.node = curr 6041 6042 def define(self): 6043 return self.node.define() 6044 6045 6046class CGDictionary(CGThing): 6047 def __init__(self, dictionary, descriptorProvider): 6048 self.dictionary = dictionary 6049 if all(CGDictionary(d, descriptorProvider).generatable for 6050 d in CGDictionary.getDictionaryDependencies(dictionary)): 6051 self.generatable = True 6052 else: 6053 self.generatable = False 6054 # Nothing else to do here 6055 return 6056 6057 self.memberInfo = [ 6058 (member, 6059 getJSToNativeConversionInfo(member.type, 6060 descriptorProvider, 6061 isMember="Dictionary", 6062 defaultValue=member.defaultValue, 6063 exceptionCode="return Err(());")) 6064 for member in dictionary.members] 6065 6066 def define(self): 6067 if not self.generatable: 6068 return "" 6069 return self.struct() + "\n" + self.impl() 6070 6071 def struct(self): 6072 d = self.dictionary 6073 if d.parent: 6074 inheritance = " pub parent: %s::%s,\n" % (self.makeModuleName(d.parent), 6075 self.makeClassName(d.parent)) 6076 else: 6077 inheritance = "" 6078 memberDecls = [" pub %s: %s," % 6079 (self.makeMemberName(m[0].identifier.name), self.getMemberType(m)) 6080 for m in self.memberInfo] 6081 6082 derive = ["JSTraceable"] 6083 mustRoot = "" 6084 if self.membersNeedTracing(): 6085 mustRoot = "#[must_root]\n" 6086 derive += ["Default"] 6087 6088 return (string.Template( 6089 "#[derive(${derive})]\n" 6090 "${mustRoot}" + 6091 "pub struct ${selfName} {\n" + 6092 "${inheritance}" + 6093 "\n".join(memberDecls) + "\n" + 6094 "}").substitute({"selfName": self.makeClassName(d), 6095 "inheritance": inheritance, 6096 "mustRoot": mustRoot, 6097 "derive": ', '.join(derive)})) 6098 6099 def impl(self): 6100 d = self.dictionary 6101 if d.parent: 6102 initParent = ("{\n" 6103 " match try!(%s::%s::new(cx, val)) {\n" 6104 " ConversionResult::Success(v) => v,\n" 6105 " ConversionResult::Failure(error) => {\n" 6106 " throw_type_error(cx, &error);\n" 6107 " return Err(());\n" 6108 " }\n" 6109 " }\n" 6110 "}" % (self.makeModuleName(d.parent), 6111 self.makeClassName(d.parent))) 6112 else: 6113 initParent = "" 6114 6115 def memberInit(memberInfo, isInitial): 6116 member, _ = memberInfo 6117 name = self.makeMemberName(member.identifier.name) 6118 conversion = self.getMemberConversion(memberInfo, member.type) 6119 if isInitial: 6120 return CGGeneric("%s: %s,\n" % (name, conversion.define())) 6121 if member.type.isAny() or member.type.isObject(): 6122 return CGGeneric("dictionary.%s.set(%s);\n" % (name, conversion.define())) 6123 return CGGeneric("dictionary.%s = %s;\n" % (name, conversion.define())) 6124 6125 def varInsert(varName, dictionaryName): 6126 insertion = ("rooted!(in(cx) let mut %s_js = UndefinedValue());\n" 6127 "%s.to_jsval(cx, %s_js.handle_mut());\n" 6128 "set_dictionary_property(cx, obj.handle(), \"%s\", %s_js.handle()).unwrap();" 6129 % (varName, varName, varName, dictionaryName, varName)) 6130 return CGGeneric(insertion) 6131 6132 def memberInsert(memberInfo): 6133 member, _ = memberInfo 6134 name = self.makeMemberName(member.identifier.name) 6135 if member.optional and not member.defaultValue: 6136 insertion = CGIfWrapper("let Some(ref %s) = self.%s" % (name, name), 6137 varInsert(name, member.identifier.name)) 6138 else: 6139 insertion = CGGeneric("let %s = &self.%s;\n%s" % 6140 (name, name, varInsert(name, member.identifier.name).define())) 6141 return CGGeneric("%s\n" % insertion.define()) 6142 6143 memberInserts = CGList([memberInsert(m) for m in self.memberInfo]) 6144 6145 selfName = self.makeClassName(d) 6146 if self.membersNeedTracing(): 6147 actualType = "RootedTraceableBox<%s>" % selfName 6148 preInitial = "let mut dictionary = RootedTraceableBox::new(%s::default());\n" % selfName 6149 initParent = initParent = ("dictionary.parent = %s;\n" % initParent) if initParent else "" 6150 memberInits = CGList([memberInit(m, False) for m in self.memberInfo]) 6151 postInitial = "" 6152 else: 6153 actualType = selfName 6154 preInitial = "let dictionary = %s {\n" % selfName 6155 postInitial = "};\n" 6156 initParent = ("parent: %s,\n" % initParent) if initParent else "" 6157 memberInits = CGList([memberInit(m, True) for m in self.memberInfo]) 6158 6159 return string.Template( 6160 "impl ${selfName} {\n" 6161 " pub unsafe fn empty(cx: *mut JSContext) -> ${actualType} {\n" 6162 " match ${selfName}::new(cx, HandleValue::null()) {\n" 6163 " Ok(ConversionResult::Success(v)) => v,\n" 6164 " _ => unreachable!(),\n" 6165 " }\n" 6166 " }\n" 6167 " pub unsafe fn new(cx: *mut JSContext, val: HandleValue) \n" 6168 " -> Result<ConversionResult<${actualType}>, ()> {\n" 6169 " let object = if val.get().is_null_or_undefined() {\n" 6170 " ptr::null_mut()\n" 6171 " } else if val.get().is_object() {\n" 6172 " val.get().to_object()\n" 6173 " } else {\n" 6174 " return Ok(ConversionResult::Failure(\"Value is not an object.\".into()));\n" 6175 " };\n" 6176 " rooted!(in(cx) let object = object);\n" 6177 "${preInitial}" 6178 "${initParent}" 6179 "${initMembers}" 6180 "${postInitial}" 6181 " Ok(ConversionResult::Success(dictionary))\n" 6182 " }\n" 6183 "}\n" 6184 "\n" 6185 "impl FromJSValConvertible for ${actualType} {\n" 6186 " type Config = ();\n" 6187 " unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ())\n" 6188 " -> Result<ConversionResult<${actualType}>, ()> {\n" 6189 " ${selfName}::new(cx, value)\n" 6190 " }\n" 6191 "}\n" 6192 "\n" 6193 "impl ToJSValConvertible for ${selfName} {\n" 6194 " unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {\n" 6195 " rooted!(in(cx) let obj = JS_NewObject(cx, ptr::null()));\n" 6196 "${insertMembers}" 6197 " rval.set(ObjectOrNullValue(obj.get()))\n" 6198 " }\n" 6199 "}\n").substitute({ 6200 "selfName": selfName, 6201 "actualType": actualType, 6202 "initParent": CGIndenter(CGGeneric(initParent), indentLevel=12).define(), 6203 "initMembers": CGIndenter(memberInits, indentLevel=12).define(), 6204 "insertMembers": CGIndenter(memberInserts, indentLevel=8).define(), 6205 "preInitial": CGIndenter(CGGeneric(preInitial), indentLevel=12).define(), 6206 "postInitial": CGIndenter(CGGeneric(postInitial), indentLevel=12).define(), 6207 }) 6208 6209 def membersNeedTracing(self): 6210 for member, _ in self.memberInfo: 6211 if type_needs_tracing(member.type): 6212 return True 6213 return False 6214 6215 @staticmethod 6216 def makeDictionaryName(dictionary): 6217 return dictionary.identifier.name 6218 6219 def makeClassName(self, dictionary): 6220 return self.makeDictionaryName(dictionary) 6221 6222 @staticmethod 6223 def makeModuleName(dictionary): 6224 return getModuleFromObject(dictionary) 6225 6226 def getMemberType(self, memberInfo): 6227 member, info = memberInfo 6228 declType = info.declType 6229 if member.optional and not member.defaultValue: 6230 declType = CGWrapper(info.declType, pre="Option<", post=">") 6231 return declType.define() 6232 6233 def getMemberConversion(self, memberInfo, memberType): 6234 def indent(s): 6235 return CGIndenter(CGGeneric(s), 12).define() 6236 6237 member, info = memberInfo 6238 templateBody = info.template 6239 default = info.default 6240 replacements = {"val": "rval.handle()"} 6241 conversion = string.Template(templateBody).substitute(replacements) 6242 6243 assert (member.defaultValue is None) == (default is None) 6244 if not member.optional: 6245 assert default is None 6246 default = ("throw_type_error(cx, \"Missing required member \\\"%s\\\".\");\n" 6247 "return Err(());") % member.identifier.name 6248 elif not default: 6249 default = "None" 6250 conversion = "Some(%s)" % conversion 6251 6252 conversion = ( 6253 "{\n" 6254 " rooted!(in(cx) let mut rval = UndefinedValue());\n" 6255 " match try!(get_dictionary_property(cx, object.handle(), \"%s\", rval.handle_mut())) {\n" 6256 " true => {\n" 6257 "%s\n" 6258 " },\n" 6259 " false => {\n" 6260 "%s\n" 6261 " },\n" 6262 " }\n" 6263 "}") % (member.identifier.name, indent(conversion), indent(default)) 6264 6265 return CGGeneric(conversion) 6266 6267 @staticmethod 6268 def makeMemberName(name): 6269 # Can't use Rust keywords as member names. 6270 if name in RUST_KEYWORDS: 6271 return name + "_" 6272 return name 6273 6274 @staticmethod 6275 def getDictionaryDependencies(dictionary): 6276 deps = set() 6277 if dictionary.parent: 6278 deps.add(dictionary.parent) 6279 for member in dictionary.members: 6280 if member.type.isDictionary(): 6281 deps.add(member.type.unroll().inner) 6282 return deps 6283 6284 6285class CGRegisterProxyHandlersMethod(CGAbstractMethod): 6286 def __init__(self, descriptors): 6287 docs = "Create the global vtables used by the generated DOM bindings to implement JS proxies." 6288 CGAbstractMethod.__init__(self, None, 'RegisterProxyHandlers', 'void', [], 6289 unsafe=True, pub=True, docs=docs) 6290 self.descriptors = descriptors 6291 6292 def definition_body(self): 6293 return CGList([ 6294 CGGeneric("PROXY_HANDLERS[Proxies::%s as usize] = Bindings::%s::DefineProxyHandler();" 6295 % (desc.name, '::'.join([desc.name + 'Binding'] * 2))) 6296 for desc in self.descriptors 6297 ], "\n") 6298 6299 6300class CGRegisterProxyHandlers(CGThing): 6301 def __init__(self, config): 6302 descriptors = config.getDescriptors(proxy=True) 6303 length = len(descriptors) 6304 self.root = CGList([ 6305 CGGeneric("pub static mut PROXY_HANDLERS: [*const libc::c_void; %d] = [0 as *const libc::c_void; %d];" 6306 % (length, length)), 6307 CGRegisterProxyHandlersMethod(descriptors), 6308 ], "\n") 6309 6310 def define(self): 6311 return self.root.define() 6312 6313 6314class CGBindingRoot(CGThing): 6315 """ 6316 DomRoot codegen class for binding generation. Instantiate the class, and call 6317 declare or define to generate header or cpp code (respectively). 6318 """ 6319 def __init__(self, config, prefix, webIDLFile): 6320 descriptors = config.getDescriptors(webIDLFile=webIDLFile, 6321 hasInterfaceObject=True) 6322 # We also want descriptors that have an interface prototype object 6323 # (isCallback=False), but we don't want to include a second copy 6324 # of descriptors that we also matched in the previous line 6325 # (hence hasInterfaceObject=False). 6326 descriptors.extend(config.getDescriptors(webIDLFile=webIDLFile, 6327 hasInterfaceObject=False, 6328 isCallback=False, 6329 register=True)) 6330 6331 dictionaries = config.getDictionaries(webIDLFile=webIDLFile) 6332 6333 mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile) 6334 callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile, 6335 isCallback=True) 6336 6337 enums = config.getEnums(webIDLFile) 6338 typedefs = config.getTypedefs(webIDLFile) 6339 6340 if not (descriptors or dictionaries or mainCallbacks or callbackDescriptors or enums): 6341 self.root = None 6342 return 6343 6344 # Do codegen for all the enums. 6345 cgthings = [CGEnum(e) for e in enums] 6346 6347 # Do codegen for all the typedefs 6348 for t in typedefs: 6349 typeName = getRetvalDeclarationForType(t.innerType, config.getDescriptorProvider()) 6350 substs = { 6351 "name": t.identifier.name, 6352 "type": typeName.define(), 6353 } 6354 6355 if t.innerType.isUnion() and not t.innerType.nullable(): 6356 # Allow using the typedef's name for accessing variants. 6357 template = "pub use self::%(type)s as %(name)s;" 6358 else: 6359 template = "pub type %(name)s = %(type)s;" 6360 6361 cgthings.append(CGGeneric(template % substs)) 6362 6363 # Do codegen for all the dictionaries. 6364 cgthings.extend([CGDictionary(d, config.getDescriptorProvider()) 6365 for d in dictionaries]) 6366 6367 # Do codegen for all the callbacks. 6368 cgthings.extend(CGList([CGCallbackFunction(c, config.getDescriptorProvider()), 6369 CGCallbackFunctionImpl(c)], "\n") 6370 for c in mainCallbacks) 6371 6372 # Do codegen for all the descriptors 6373 cgthings.extend([CGDescriptor(x, config, len(descriptors) == 1) for x in descriptors]) 6374 6375 # Do codegen for all the callback interfaces. 6376 cgthings.extend(CGList([CGCallbackInterface(x), 6377 CGCallbackFunctionImpl(x.interface)], "\n") 6378 for x in callbackDescriptors) 6379 6380 # And make sure we have the right number of newlines at the end 6381 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") 6382 6383 # Add imports 6384 curr = generate_imports(config, curr, callbackDescriptors, mainCallbacks, 6385 dictionaries, enums, typedefs) 6386 6387 # Add the auto-generated comment. 6388 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 6389 6390 # Store the final result. 6391 self.root = curr 6392 6393 def define(self): 6394 if not self.root: 6395 return None 6396 return stripTrailingWhitespace(self.root.define()) 6397 6398 6399def type_needs_tracing(t): 6400 assert isinstance(t, IDLObject), (t, type(t)) 6401 6402 if t.isType(): 6403 if isinstance(t, IDLWrapperType): 6404 return type_needs_tracing(t.inner) 6405 6406 if t.nullable(): 6407 return type_needs_tracing(t.inner) 6408 6409 if t.isAny(): 6410 return True 6411 6412 if t.isObject(): 6413 return True 6414 6415 if t.isSequence(): 6416 return type_needs_tracing(t.inner) 6417 6418 if t.isUnion(): 6419 return any(type_needs_tracing(member) for member in t.flatMemberTypes) 6420 6421 return False 6422 6423 if t.isDictionary(): 6424 if t.parent and type_needs_tracing(t.parent): 6425 return True 6426 6427 if any(type_needs_tracing(member.type) for member in t.members): 6428 return True 6429 6430 return False 6431 6432 if t.isInterface(): 6433 return False 6434 6435 if t.isEnum(): 6436 return False 6437 6438 assert False, (t, type(t)) 6439 6440 6441def type_needs_auto_root(t): 6442 """ 6443 Certain IDL types, such as `sequence<any>` or `sequence<object>` need to be 6444 traced and wrapped via (Custom)AutoRooter 6445 """ 6446 assert isinstance(t, IDLObject), (t, type(t)) 6447 6448 if t.isType(): 6449 if t.isSequence() and (t.inner.isAny() or t.inner.isObject()): 6450 return True 6451 6452 return False 6453 6454 6455def argument_type(descriptorProvider, ty, optional=False, defaultValue=None, variadic=False): 6456 info = getJSToNativeConversionInfo( 6457 ty, descriptorProvider, isArgument=True, 6458 isAutoRooted=type_needs_auto_root(ty)) 6459 declType = info.declType 6460 6461 if variadic: 6462 if ty.isGeckoInterface(): 6463 declType = CGWrapper(declType, pre="&[", post="]") 6464 else: 6465 declType = CGWrapper(declType, pre="Vec<", post=">") 6466 elif optional and not defaultValue: 6467 declType = CGWrapper(declType, pre="Option<", post=">") 6468 6469 if ty.isDictionary() and not type_needs_tracing(ty): 6470 declType = CGWrapper(declType, pre="&") 6471 6472 if type_needs_auto_root(ty): 6473 declType = CGTemplatedType("CustomAutoRooterGuard", declType) 6474 6475 return declType.define() 6476 6477 6478def method_arguments(descriptorProvider, returnType, arguments, passJSBits=True, trailing=None): 6479 if needCx(returnType, arguments, passJSBits): 6480 yield "cx", "*mut JSContext" 6481 6482 for argument in arguments: 6483 ty = argument_type(descriptorProvider, argument.type, argument.optional, 6484 argument.defaultValue, argument.variadic) 6485 yield CGDictionary.makeMemberName(argument.identifier.name), ty 6486 6487 if trailing: 6488 yield trailing 6489 6490 6491def return_type(descriptorProvider, rettype, infallible): 6492 result = getRetvalDeclarationForType(rettype, descriptorProvider) 6493 if not infallible: 6494 result = CGWrapper(result, pre="Fallible<", post=">") 6495 return result.define() 6496 6497 6498class CGNativeMember(ClassMethod): 6499 def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, 6500 breakAfter=True, passJSBitsAsNeeded=True, visibility="public", 6501 unsafe=False): 6502 """ 6503 If passJSBitsAsNeeded is false, we don't automatically pass in a 6504 JSContext* or a JSObject* based on the return and argument types. 6505 """ 6506 self.descriptorProvider = descriptorProvider 6507 self.member = member 6508 self.extendedAttrs = extendedAttrs 6509 self.passJSBitsAsNeeded = passJSBitsAsNeeded 6510 breakAfterSelf = "\n" if breakAfter else "" 6511 ClassMethod.__init__(self, name, 6512 self.getReturnType(signature[0]), 6513 self.getArgs(signature[0], signature[1]), 6514 static=member.isStatic(), 6515 # Mark our getters, which are attrs that 6516 # have a non-void return type, as const. 6517 const=(not member.isStatic() and member.isAttr() and 6518 not signature[0].isVoid()), 6519 breakAfterSelf=breakAfterSelf, 6520 unsafe=unsafe, 6521 visibility=visibility) 6522 6523 def getReturnType(self, type): 6524 infallible = 'infallible' in self.extendedAttrs 6525 typeDecl = return_type(self.descriptorProvider, type, infallible) 6526 return typeDecl 6527 6528 def getArgs(self, returnType, argList): 6529 return [Argument(arg[1], arg[0]) for arg in method_arguments(self.descriptorProvider, 6530 returnType, 6531 argList, 6532 self.passJSBitsAsNeeded)] 6533 6534 6535class CGCallback(CGClass): 6536 def __init__(self, idlObject, descriptorProvider, baseName, methods): 6537 self.baseName = baseName 6538 self._deps = idlObject.getDeps() 6539 name = idlObject.identifier.name 6540 # For our public methods that needThisHandling we want most of the 6541 # same args and the same return type as what CallbackMember 6542 # generates. So we want to take advantage of all its 6543 # CGNativeMember infrastructure, but that infrastructure can't deal 6544 # with templates and most especially template arguments. So just 6545 # cheat and have CallbackMember compute all those things for us. 6546 realMethods = [] 6547 for method in methods: 6548 if not method.needThisHandling: 6549 realMethods.append(method) 6550 else: 6551 realMethods.extend(self.getMethodImpls(method)) 6552 CGClass.__init__(self, name, 6553 bases=[ClassBase(baseName)], 6554 constructors=self.getConstructors(), 6555 methods=realMethods, 6556 decorators="#[derive(JSTraceable, PartialEq)]\n#[allow_unrooted_interior]") 6557 6558 def getConstructors(self): 6559 return [ClassConstructor( 6560 [Argument("*mut JSContext", "aCx"), Argument("*mut JSObject", "aCallback")], 6561 bodyInHeader=True, 6562 visibility="pub", 6563 explicit=False, 6564 baseConstructors=[ 6565 "%s::new()" % self.baseName 6566 ])] 6567 6568 def getMethodImpls(self, method): 6569 assert method.needThisHandling 6570 args = list(method.args) 6571 # Strip out the JSContext*/JSObject* args 6572 # that got added. 6573 assert args[0].name == "cx" and args[0].argType == "*mut JSContext" 6574 assert args[1].name == "aThisObj" and args[1].argType == "HandleObject" 6575 args = args[2:] 6576 # Record the names of all the arguments, so we can use them when we call 6577 # the private method. 6578 argnames = [arg.name for arg in args] 6579 argnamesWithThis = ["s.get_context()", "thisObjJS.handle()"] + argnames 6580 argnamesWithoutThis = ["s.get_context()", "thisObjJS.handle()"] + argnames 6581 # Now that we've recorded the argnames for our call to our private 6582 # method, insert our optional argument for deciding whether the 6583 # CallSetup should re-throw exceptions on aRv. 6584 args.append(Argument("ExceptionHandling", "aExceptionHandling", 6585 "ReportExceptions")) 6586 6587 # And now insert our template argument. 6588 argsWithoutThis = list(args) 6589 args.insert(0, Argument("&T", "thisObj")) 6590 6591 # And the self argument 6592 method.args.insert(0, Argument(None, "&self")) 6593 args.insert(0, Argument(None, "&self")) 6594 argsWithoutThis.insert(0, Argument(None, "&self")) 6595 6596 setupCall = "let s = CallSetup::new(self, aExceptionHandling);\n" 6597 6598 bodyWithThis = string.Template( 6599 setupCall + 6600 "rooted!(in(s.get_context()) let mut thisObjJS = ptr::null_mut::<JSObject>());\n" 6601 "wrap_call_this_object(s.get_context(), thisObj, thisObjJS.handle_mut());\n" 6602 "if thisObjJS.is_null() {\n" 6603 " return Err(JSFailed);\n" 6604 "}\n" 6605 "unsafe { ${methodName}(${callArgs}) }").substitute({ 6606 "callArgs": ", ".join(argnamesWithThis), 6607 "methodName": 'self.' + method.name, 6608 }) 6609 bodyWithoutThis = string.Template( 6610 setupCall + 6611 "rooted!(in(s.get_context()) let thisObjJS = ptr::null_mut::<JSObject>());\n" 6612 "unsafe { ${methodName}(${callArgs}) }").substitute({ 6613 "callArgs": ", ".join(argnamesWithoutThis), 6614 "methodName": 'self.' + method.name, 6615 }) 6616 return [ClassMethod(method.name + '_', method.returnType, args, 6617 bodyInHeader=True, 6618 templateArgs=["T: DomObject"], 6619 body=bodyWithThis, 6620 visibility='pub'), 6621 ClassMethod(method.name + '__', method.returnType, argsWithoutThis, 6622 bodyInHeader=True, 6623 body=bodyWithoutThis, 6624 visibility='pub'), 6625 method] 6626 6627 def deps(self): 6628 return self._deps 6629 6630 6631# We're always fallible 6632def callbackGetterName(attr, descriptor): 6633 return "Get" + MakeNativeName( 6634 descriptor.binaryNameFor(attr.identifier.name)) 6635 6636 6637def callbackSetterName(attr, descriptor): 6638 return "Set" + MakeNativeName( 6639 descriptor.binaryNameFor(attr.identifier.name)) 6640 6641 6642class CGCallbackFunction(CGCallback): 6643 def __init__(self, callback, descriptorProvider): 6644 CGCallback.__init__(self, callback, descriptorProvider, 6645 "CallbackFunction", 6646 methods=[CallCallback(callback, descriptorProvider)]) 6647 6648 def getConstructors(self): 6649 return CGCallback.getConstructors(self) 6650 6651 6652class CGCallbackFunctionImpl(CGGeneric): 6653 def __init__(self, callback): 6654 impl = string.Template("""\ 6655impl CallbackContainer for ${type} { 6656 unsafe fn new(cx: *mut JSContext, callback: *mut JSObject) -> Rc<${type}> { 6657 ${type}::new(cx, callback) 6658 } 6659 6660 fn callback_holder(&self) -> &CallbackObject { 6661 self.parent.callback_holder() 6662 } 6663} 6664 6665impl ToJSValConvertible for ${type} { 6666 unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { 6667 self.callback().to_jsval(cx, rval); 6668 } 6669}\ 6670""").substitute({"type": callback.identifier.name}) 6671 CGGeneric.__init__(self, impl) 6672 6673 6674class CGCallbackInterface(CGCallback): 6675 def __init__(self, descriptor): 6676 iface = descriptor.interface 6677 attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()] 6678 assert not attrs 6679 methods = [m for m in iface.members 6680 if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()] 6681 methods = [CallbackOperation(m, sig, descriptor) for m in methods 6682 for sig in m.signatures()] 6683 assert not iface.isJSImplemented() or not iface.ctor() 6684 CGCallback.__init__(self, iface, descriptor, "CallbackInterface", methods) 6685 6686 6687class FakeMember(): 6688 def __init__(self): 6689 self.treatNullAs = "Default" 6690 6691 def isStatic(self): 6692 return False 6693 6694 def isAttr(self): 6695 return False 6696 6697 def isMethod(self): 6698 return False 6699 6700 def getExtendedAttribute(self, name): 6701 return None 6702 6703 6704class CallbackMember(CGNativeMember): 6705 def __init__(self, sig, name, descriptorProvider, needThisHandling): 6706 """ 6707 needThisHandling is True if we need to be able to accept a specified 6708 thisObj, False otherwise. 6709 """ 6710 6711 self.retvalType = sig[0] 6712 self.originalSig = sig 6713 args = sig[1] 6714 self.argCount = len(args) 6715 if self.argCount > 0: 6716 # Check for variadic arguments 6717 lastArg = args[self.argCount - 1] 6718 if lastArg.variadic: 6719 self.argCountStr = ( 6720 "(%d - 1) + %s.len()" % (self.argCount, 6721 lastArg.identifier.name)) 6722 else: 6723 self.argCountStr = "%d" % self.argCount 6724 self.needThisHandling = needThisHandling 6725 # If needThisHandling, we generate ourselves as private and the caller 6726 # will handle generating public versions that handle the "this" stuff. 6727 visibility = "priv" if needThisHandling else "pub" 6728 # We don't care, for callback codegen, whether our original member was 6729 # a method or attribute or whatnot. Just always pass FakeMember() 6730 # here. 6731 CGNativeMember.__init__(self, descriptorProvider, FakeMember(), 6732 name, (self.retvalType, args), 6733 extendedAttrs={}, 6734 passJSBitsAsNeeded=False, 6735 unsafe=needThisHandling, 6736 visibility=visibility) 6737 # We have to do all the generation of our body now, because 6738 # the caller relies on us throwing if we can't manage it. 6739 self.exceptionCode = "return Err(JSFailed);" 6740 self.body = self.getImpl() 6741 6742 def getImpl(self): 6743 replacements = { 6744 "declRval": self.getRvalDecl(), 6745 "returnResult": self.getResultConversion(), 6746 "convertArgs": self.getArgConversions(), 6747 "doCall": self.getCall(), 6748 "setupCall": self.getCallSetup(), 6749 } 6750 if self.argCount > 0: 6751 replacements["argCount"] = self.argCountStr 6752 replacements["argvDecl"] = string.Template( 6753 "rooted_vec!(let mut argv);\n" 6754 "argv.extend((0..${argCount}).map(|_| Heap::default()));\n" 6755 ).substitute(replacements) 6756 else: 6757 # Avoid weird 0-sized arrays 6758 replacements["argvDecl"] = "" 6759 6760 # Newlines and semicolons are in the values 6761 pre = string.Template( 6762 "${setupCall}" 6763 "${declRval}" 6764 "${argvDecl}").substitute(replacements) 6765 body = string.Template( 6766 "${convertArgs}" 6767 "${doCall}" 6768 "${returnResult}").substitute(replacements) 6769 return pre + "\n" + body 6770 6771 def getResultConversion(self): 6772 replacements = { 6773 "val": "rval.handle()", 6774 } 6775 6776 info = getJSToNativeConversionInfo( 6777 self.retvalType, 6778 self.descriptorProvider, 6779 exceptionCode=self.exceptionCode, 6780 isCallbackReturnValue="Callback", 6781 # XXXbz we should try to do better here 6782 sourceDescription="return value") 6783 template = info.template 6784 declType = info.declType 6785 6786 convertType = instantiateJSToNativeConversionTemplate( 6787 template, replacements, declType, "rvalDecl") 6788 6789 if self.retvalType is None or self.retvalType.isVoid(): 6790 retval = "()" 6791 elif self.retvalType.isAny(): 6792 retval = "rvalDecl.get()" 6793 else: 6794 retval = "rvalDecl" 6795 6796 return "%s\nOk(%s)\n" % (convertType.define(), retval) 6797 6798 def getArgConversions(self): 6799 # Just reget the arglist from self.originalSig, because our superclasses 6800 # just have way to many members they like to clobber, so I can't find a 6801 # safe member name to store it in. 6802 argConversions = [self.getArgConversion(i, arg) for (i, arg) 6803 in enumerate(self.originalSig[1])] 6804 # Do them back to front, so our argc modifications will work 6805 # correctly, because we examine trailing arguments first. 6806 argConversions.reverse() 6807 argConversions = [CGGeneric(c) for c in argConversions] 6808 if self.argCount > 0: 6809 argConversions.insert(0, self.getArgcDecl()) 6810 # And slap them together. 6811 return CGList(argConversions, "\n\n").define() + "\n\n" 6812 6813 def getArgConversion(self, i, arg): 6814 argval = arg.identifier.name 6815 6816 if arg.variadic: 6817 argval = argval + "[idx].get()" 6818 jsvalIndex = "%d + idx" % i 6819 else: 6820 jsvalIndex = "%d" % i 6821 if arg.optional and not arg.defaultValue: 6822 argval += ".clone().unwrap()" 6823 6824 conversion = wrapForType( 6825 "argv_root.handle_mut()", result=argval, 6826 successCode=("{\n" + 6827 "let arg = &mut argv[%s];\n" + 6828 "*arg = Heap::default();\n" + 6829 "arg.set(argv_root.get());\n" + 6830 "}") % jsvalIndex, 6831 pre="rooted!(in(cx) let mut argv_root = UndefinedValue());") 6832 if arg.variadic: 6833 conversion = string.Template( 6834 "for idx in 0..${arg}.len() {\n" + 6835 CGIndenter(CGGeneric(conversion)).define() + "\n" 6836 "}" 6837 ).substitute({"arg": arg.identifier.name}) 6838 elif arg.optional and not arg.defaultValue: 6839 conversion = ( 6840 CGIfWrapper("%s.is_some()" % arg.identifier.name, 6841 CGGeneric(conversion)).define() + 6842 " else if argc == %d {\n" 6843 " // This is our current trailing argument; reduce argc\n" 6844 " argc -= 1;\n" 6845 "} else {\n" 6846 " argv[%d] = Heap::default();\n" 6847 "}" % (i + 1, i)) 6848 return conversion 6849 6850 def getArgs(self, returnType, argList): 6851 args = CGNativeMember.getArgs(self, returnType, argList) 6852 if not self.needThisHandling: 6853 # Since we don't need this handling, we're the actual method that 6854 # will be called, so we need an aRethrowExceptions argument. 6855 args.append(Argument("ExceptionHandling", "aExceptionHandling", 6856 "ReportExceptions")) 6857 return args 6858 # We want to allow the caller to pass in a "this" object, as 6859 # well as a JSContext. 6860 return [Argument("*mut JSContext", "cx"), 6861 Argument("HandleObject", "aThisObj")] + args 6862 6863 def getCallSetup(self): 6864 if self.needThisHandling: 6865 # It's been done for us already 6866 return "" 6867 return ( 6868 "CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n" 6869 "JSContext* cx = s.get_context();\n" 6870 "if (!cx) {\n" 6871 " return Err(JSFailed);\n" 6872 "}\n") 6873 6874 def getArgcDecl(self): 6875 if self.argCount <= 1: 6876 return CGGeneric("let argc = %s;" % self.argCountStr) 6877 return CGGeneric("let mut argc = %s;" % self.argCountStr) 6878 6879 @staticmethod 6880 def ensureASCIIName(idlObject): 6881 type = "attribute" if idlObject.isAttr() else "operation" 6882 if re.match("[^\x20-\x7E]", idlObject.identifier.name): 6883 raise SyntaxError('Callback %s name "%s" contains non-ASCII ' 6884 "characters. We can't handle that. %s" % 6885 (type, idlObject.identifier.name, 6886 idlObject.location)) 6887 if re.match('"', idlObject.identifier.name): 6888 raise SyntaxError("Callback %s name '%s' contains " 6889 "double-quote character. We can't handle " 6890 "that. %s" % 6891 (type, idlObject.identifier.name, 6892 idlObject.location)) 6893 6894 6895class CallbackMethod(CallbackMember): 6896 def __init__(self, sig, name, descriptorProvider, needThisHandling): 6897 CallbackMember.__init__(self, sig, name, descriptorProvider, 6898 needThisHandling) 6899 6900 def getRvalDecl(self): 6901 return "rooted!(in(cx) let mut rval = UndefinedValue());\n" 6902 6903 def getCall(self): 6904 replacements = { 6905 "thisObj": self.getThisObj(), 6906 "getCallable": self.getCallableDecl(), 6907 "callGuard": self.getCallGuard(), 6908 } 6909 if self.argCount > 0: 6910 replacements["argv"] = "argv.as_ptr() as *const JSVal" 6911 replacements["argc"] = "argc" 6912 else: 6913 replacements["argv"] = "ptr::null_mut()" 6914 replacements["argc"] = "0" 6915 return string.Template( 6916 "${getCallable}" 6917 "rooted!(in(cx) let rootedThis = ${thisObj});\n" 6918 "let ok = ${callGuard}JS_CallFunctionValue(\n" 6919 " cx, rootedThis.handle(), callable.handle(),\n" 6920 " &HandleValueArray {\n" 6921 " length_: ${argc} as ::libc::size_t,\n" 6922 " elements_: ${argv}\n" 6923 " }, rval.handle_mut());\n" 6924 "maybe_resume_unwind();\n" 6925 "if !ok {\n" 6926 " return Err(JSFailed);\n" 6927 "}\n").substitute(replacements) 6928 6929 6930class CallCallback(CallbackMethod): 6931 def __init__(self, callback, descriptorProvider): 6932 self.callback = callback 6933 CallbackMethod.__init__(self, callback.signatures()[0], "Call", 6934 descriptorProvider, needThisHandling=True) 6935 6936 def getThisObj(self): 6937 return "aThisObj.get()" 6938 6939 def getCallableDecl(self): 6940 return "rooted!(in(cx) let callable = ObjectValue(self.callback()));\n" 6941 6942 def getCallGuard(self): 6943 if self.callback._treatNonObjectAsNull: 6944 return "!IsCallable(self.callback()) || " 6945 return "" 6946 6947 6948class CallbackOperationBase(CallbackMethod): 6949 """ 6950 Common class for implementing various callback operations. 6951 """ 6952 def __init__(self, signature, jsName, nativeName, descriptor, singleOperation): 6953 self.singleOperation = singleOperation 6954 self.methodName = jsName 6955 CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation) 6956 6957 def getThisObj(self): 6958 if not self.singleOperation: 6959 return "self.callback()" 6960 # This relies on getCallableDecl declaring a boolean 6961 # isCallable in the case when we're a single-operation 6962 # interface. 6963 return "if isCallable { aThisObj.get() } else { self.callback() }" 6964 6965 def getCallableDecl(self): 6966 replacements = { 6967 "methodName": self.methodName 6968 } 6969 getCallableFromProp = string.Template( 6970 'try!(self.parent.get_callable_property(cx, "${methodName}"))' 6971 ).substitute(replacements) 6972 if not self.singleOperation: 6973 return 'rooted!(in(cx) let callable =\n' + getCallableFromProp + ');\n' 6974 return ( 6975 'let isCallable = IsCallable(self.callback());\n' 6976 'rooted!(in(cx) let callable =\n' + 6977 CGIndenter( 6978 CGIfElseWrapper('isCallable', 6979 CGGeneric('ObjectValue(self.callback())'), 6980 CGGeneric(getCallableFromProp))).define() + ');\n') 6981 6982 def getCallGuard(self): 6983 return "" 6984 6985 6986class CallbackOperation(CallbackOperationBase): 6987 """ 6988 Codegen actual WebIDL operations on callback interfaces. 6989 """ 6990 def __init__(self, method, signature, descriptor): 6991 self.ensureASCIIName(method) 6992 jsName = method.identifier.name 6993 CallbackOperationBase.__init__(self, signature, 6994 jsName, 6995 MakeNativeName(descriptor.binaryNameFor(jsName)), 6996 descriptor, descriptor.interface.isSingleOperationInterface()) 6997 6998 6999class CGIterableMethodGenerator(CGGeneric): 7000 """ 7001 Creates methods for iterable interfaces. Unwrapping/wrapping 7002 will be taken care of by the usual method generation machinery in 7003 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of 7004 using CGCallGenerator. 7005 """ 7006 def __init__(self, descriptor, iterable, methodName): 7007 if methodName == "forEach": 7008 CGGeneric.__init__(self, fill( 7009 """ 7010 if !IsCallable(arg0) { 7011 throw_type_error(cx, "Argument 1 of ${ifaceName}.forEach is not callable."); 7012 return false; 7013 } 7014 rooted!(in(cx) let arg0 = ObjectValue(arg0)); 7015 rooted!(in(cx) let mut call_arg1 = UndefinedValue()); 7016 rooted!(in(cx) let mut call_arg2 = UndefinedValue()); 7017 let mut call_args = vec![UndefinedValue(), UndefinedValue(), ObjectValue(*_obj)]; 7018 rooted!(in(cx) let mut ignoredReturnVal = UndefinedValue()); 7019 for i in 0..(*this).get_iterable_length() { 7020 (*this).get_value_at_index(i).to_jsval(cx, call_arg1.handle_mut()); 7021 (*this).get_key_at_index(i).to_jsval(cx, call_arg2.handle_mut()); 7022 call_args[0] = call_arg1.handle().get(); 7023 call_args[1] = call_arg2.handle().get(); 7024 let call_args = HandleValueArray { length_: 3, elements_: call_args.as_ptr() }; 7025 if !Call(cx, arg1, arg0.handle(), &call_args, 7026 ignoredReturnVal.handle_mut()) { 7027 return false; 7028 } 7029 } 7030 7031 let result = (); 7032 """, 7033 ifaceName=descriptor.interface.identifier.name)) 7034 return 7035 CGGeneric.__init__(self, fill( 7036 """ 7037 let result = ${iterClass}::new(&*this, 7038 IteratorType::${itrMethod}, 7039 super::${ifaceName}IteratorBinding::Wrap); 7040 """, 7041 iterClass=iteratorNativeType(descriptor, True), 7042 ifaceName=descriptor.interface.identifier.name, 7043 itrMethod=methodName.title())) 7044 7045 7046def camel_to_upper_snake(s): 7047 return "_".join(m.group(0).upper() for m in re.finditer("[A-Z][a-z]*", s)) 7048 7049 7050def process_arg(expr, arg): 7051 if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback(): 7052 if arg.type.nullable() or arg.type.isSequence() or arg.optional: 7053 expr += ".r()" 7054 else: 7055 expr = "&" + expr 7056 elif isinstance(arg.type, IDLPromiseType): 7057 expr = "&" + expr 7058 return expr 7059 7060 7061class GlobalGenRoots(): 7062 """ 7063 Roots for global codegen. 7064 7065 To generate code, call the method associated with the target, and then 7066 call the appropriate define/declare method. 7067 """ 7068 7069 @staticmethod 7070 def InterfaceObjectMap(config): 7071 mods = [ 7072 "dom::bindings::codegen", 7073 "js::jsapi::{HandleObject, JSContext}", 7074 "phf", 7075 ] 7076 imports = CGList([CGGeneric("use %s;" % mod) for mod in mods], "\n") 7077 7078 global_descriptors = config.getDescriptors(isGlobal=True) 7079 flags = [("EMPTY", 0)] 7080 flags.extend( 7081 (camel_to_upper_snake(d.name), 2 ** idx) 7082 for (idx, d) in enumerate(global_descriptors) 7083 ) 7084 global_flags = CGWrapper(CGIndenter(CGList([ 7085 CGGeneric("const %s = %#x;" % args) 7086 for args in flags 7087 ], "\n")), pre="pub struct Globals: u8 {\n", post="\n}") 7088 globals_ = CGWrapper(CGIndenter(global_flags), pre="bitflags! {\n", post="\n}") 7089 7090 phf = CGGeneric("include!(concat!(env!(\"OUT_DIR\"), \"/InterfaceObjectMapPhf.rs\"));") 7091 7092 return CGList([ 7093 CGGeneric(AUTOGENERATED_WARNING_COMMENT), 7094 CGList([imports, globals_, phf], "\n\n") 7095 ]) 7096 7097 @staticmethod 7098 def InterfaceObjectMapData(config): 7099 pairs = [] 7100 for d in config.getDescriptors(hasInterfaceObject=True, isInline=False): 7101 binding = toBindingNamespace(d.name) 7102 pairs.append((d.name, binding, binding)) 7103 for ctor in d.interface.namedConstructors: 7104 pairs.append((ctor.identifier.name, binding, binding)) 7105 pairs.sort(key=operator.itemgetter(0)) 7106 mappings = [ 7107 CGGeneric('"%s": "codegen::Bindings::%s::%s::DefineDOMInterface as unsafe fn(_, _)"' % pair) 7108 for pair in pairs 7109 ] 7110 return CGWrapper( 7111 CGList(mappings, ",\n"), 7112 pre="{\n", 7113 post="\n}\n") 7114 7115 @staticmethod 7116 def PrototypeList(config): 7117 # Prototype ID enum. 7118 interfaces = config.getDescriptors(isCallback=False, isNamespace=False) 7119 protos = [d.name for d in interfaces] 7120 constructors = sorted([MakeNativeName(d.name) 7121 for d in config.getDescriptors(hasInterfaceObject=True) 7122 if d.shouldHaveGetConstructorObjectMethod()]) 7123 7124 proxies = [d.name for d in config.getDescriptors(proxy=True)] 7125 7126 return CGList([ 7127 CGGeneric(AUTOGENERATED_WARNING_COMMENT), 7128 CGGeneric("pub const PROTO_OR_IFACE_LENGTH: usize = %d;\n" % (len(protos) + len(constructors))), 7129 CGGeneric("pub const MAX_PROTO_CHAIN_LENGTH: usize = %d;\n\n" % config.maxProtoChainLength), 7130 CGNonNamespacedEnum('ID', protos, 0, deriving="PartialEq, Copy, Clone", repr="u16"), 7131 CGNonNamespacedEnum('Constructor', constructors, len(protos), 7132 deriving="PartialEq, Copy, Clone", repr="u16"), 7133 CGWrapper(CGIndenter(CGList([CGGeneric('"' + name + '"') for name in protos], 7134 ",\n"), 7135 indentLevel=4), 7136 pre="static INTERFACES: [&'static str; %d] = [\n" % len(protos), 7137 post="\n];\n\n"), 7138 CGGeneric("pub fn proto_id_to_name(proto_id: u16) -> &'static str {\n" 7139 " debug_assert!(proto_id < ID::Last as u16);\n" 7140 " INTERFACES[proto_id as usize]\n" 7141 "}\n\n"), 7142 CGNonNamespacedEnum('Proxies', proxies, 0, deriving="PartialEq, Copy, Clone"), 7143 ]) 7144 7145 @staticmethod 7146 def RegisterBindings(config): 7147 # TODO - Generate the methods we want 7148 code = CGList([ 7149 CGRegisterProxyHandlers(config), 7150 ], "\n") 7151 7152 return CGImports(code, descriptors=[], callbacks=[], dictionaries=[], enums=[], typedefs=[], imports=[ 7153 'dom::bindings::codegen::Bindings', 7154 'dom::bindings::codegen::PrototypeList::Proxies', 7155 'libc', 7156 ], config=config, ignored_warnings=[]) 7157 7158 @staticmethod 7159 def InterfaceTypes(config): 7160 descriptors = sorted([MakeNativeName(d.name) 7161 for d in config.getDescriptors(register=True, 7162 isCallback=False, 7163 isIteratorInterface=False)]) 7164 curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), 7165 MakeNativeName(name))) 7166 for name in descriptors]) 7167 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 7168 return curr 7169 7170 @staticmethod 7171 def Bindings(config): 7172 7173 def leafModule(d): 7174 return getModuleFromObject(d).split('::')[-1] 7175 7176 descriptors = config.getDescriptors(register=True, isIteratorInterface=False) 7177 descriptors = (set(toBindingNamespace(d.name) for d in descriptors) | 7178 set(leafModule(d) for d in config.callbacks) | 7179 set(leafModule(d) for d in config.getDictionaries())) 7180 curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)]) 7181 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 7182 return curr 7183 7184 @staticmethod 7185 def InheritTypes(config): 7186 7187 descriptors = config.getDescriptors(register=True, isCallback=False) 7188 imports = [CGGeneric("use dom::types::*;\n"), 7189 CGGeneric("use dom::bindings::conversions::{DerivedFrom, get_dom_class};\n"), 7190 CGGeneric("use dom::bindings::inheritance::Castable;\n"), 7191 CGGeneric("use dom::bindings::root::{Dom, DomRoot, LayoutDom};\n"), 7192 CGGeneric("use dom::bindings::trace::JSTraceable;\n"), 7193 CGGeneric("use dom::bindings::reflector::DomObject;\n"), 7194 CGGeneric("use js::jsapi::JSTracer;\n\n"), 7195 CGGeneric("use std::mem;\n\n")] 7196 allprotos = [] 7197 topTypes = [] 7198 hierarchy = defaultdict(list) 7199 for descriptor in descriptors: 7200 name = descriptor.name 7201 chain = descriptor.prototypeChain 7202 upcast = descriptor.hasDescendants() 7203 downcast = len(chain) != 1 7204 7205 if upcast and not downcast: 7206 topTypes.append(name) 7207 7208 if not upcast: 7209 # No other interface will implement DeriveFrom<Foo> for this Foo, so avoid 7210 # implementing it for itself. 7211 chain = chain[:-1] 7212 7213 # Implement `DerivedFrom<Bar>` for `Foo`, for all `Bar` that `Foo` inherits from. 7214 if chain: 7215 allprotos.append(CGGeneric("impl Castable for %s {}\n" % name)) 7216 for baseName in chain: 7217 allprotos.append(CGGeneric("impl DerivedFrom<%s> for %s {}\n" % (baseName, name))) 7218 if chain: 7219 allprotos.append(CGGeneric("\n")) 7220 7221 if downcast: 7222 hierarchy[descriptor.interface.parent.identifier.name].append(name) 7223 7224 typeIdCode = [] 7225 topTypeVariants = [ 7226 ("ID used by abstract interfaces.", "pub abstract_: ()"), 7227 ("ID used by interfaces that are not castable.", "pub alone: ()"), 7228 ] 7229 topTypeVariants += [ 7230 ("ID used by interfaces that derive from %s." % typeName, 7231 "pub %s: %sTypeId" % (typeName.lower(), typeName)) 7232 for typeName in topTypes 7233 ] 7234 topTypeVariantsAsStrings = [CGGeneric("/// %s\n%s," % variant) for variant in topTypeVariants] 7235 typeIdCode.append(CGWrapper(CGIndenter(CGList(topTypeVariantsAsStrings, "\n"), 4), 7236 pre="#[derive(Copy)]\npub union TopTypeId {\n", 7237 post="\n}\n\n")) 7238 7239 typeIdCode.append(CGGeneric("""\ 7240impl Clone for TopTypeId { 7241 fn clone(&self) -> Self { *self } 7242} 7243 7244""")) 7245 7246 def type_id_variant(name): 7247 # If `name` is present in the hierarchy keys', that means some other interfaces 7248 # derive from it and this enum variant should have an argument with its own 7249 # TypeId enum. 7250 return "%s(%sTypeId)" % (name, name) if name in hierarchy else name 7251 7252 for base, derived in hierarchy.iteritems(): 7253 variants = [] 7254 if config.getDescriptor(base).concrete: 7255 variants.append(CGGeneric(base)) 7256 variants += [CGGeneric(type_id_variant(derivedName)) for derivedName in derived] 7257 derives = "Clone, Copy, Debug, PartialEq" 7258 typeIdCode.append(CGWrapper(CGIndenter(CGList(variants, ",\n"), 4), 7259 pre="#[derive(%s)]\npub enum %sTypeId {\n" % (derives, base), 7260 post="\n}\n\n")) 7261 if base in topTypes: 7262 typeIdCode.append(CGGeneric("""\ 7263impl %(base)s { 7264 pub fn type_id(&self) -> &'static %(base)sTypeId { 7265 unsafe { 7266 &get_dom_class(self.reflector().get_jsobject().get()) 7267 .unwrap() 7268 .type_id 7269 .%(field)s 7270 } 7271 } 7272} 7273 7274""" % {'base': base, 'field': base.lower()})) 7275 7276 curr = CGList(imports + typeIdCode + allprotos) 7277 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 7278 return curr 7279 7280 @staticmethod 7281 def UnionTypes(config): 7282 7283 curr = UnionTypes(config.getDescriptors(), 7284 config.getDictionaries(), 7285 config.getCallbacks(), 7286 config.typedefs, 7287 config) 7288 7289 # Add the auto-generated comment. 7290 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) 7291 7292 # Done. 7293 return curr 7294 7295 @staticmethod 7296 def SupportedDomApis(config): 7297 descriptors = config.getDescriptors(isExposedConditionally=False) 7298 7299 base_path = os.path.join('dom', 'bindings', 'codegen') 7300 with open(os.path.join(base_path, 'apis.html.template')) as f: 7301 base_template = f.read() 7302 with open(os.path.join(base_path, 'api.html.template')) as f: 7303 api_template = f.read() 7304 with open(os.path.join(base_path, 'property.html.template')) as f: 7305 property_template = f.read() 7306 with open(os.path.join(base_path, 'interface.html.template')) as f: 7307 interface_template = f.read() 7308 7309 apis = [] 7310 interfaces = [] 7311 for descriptor in descriptors: 7312 props = [] 7313 for m in descriptor.interface.members: 7314 if PropertyDefiner.getStringAttr(m, 'Pref') or \ 7315 PropertyDefiner.getStringAttr(m, 'Func') or \ 7316 (m.isMethod() and m.isIdentifierLess()): 7317 continue 7318 display = m.identifier.name + ('()' if m.isMethod() else '') 7319 props += [property_template.replace('${name}', display)] 7320 name = descriptor.interface.identifier.name 7321 apis += [(api_template.replace('${interface}', name) 7322 .replace('${properties}', '\n'.join(props)))] 7323 interfaces += [interface_template.replace('${interface}', name)] 7324 7325 return CGGeneric((base_template.replace('${apis}', '\n'.join(apis)) 7326 .replace('${interfaces}', '\n'.join(interfaces)))) 7327