Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions codegen/apipatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ def get_method_def(self, classname, methodname) -> str:
# Get arg names and types
idl_line = functions[name_idl]
args = idl_line.split("(", 1)[1].split(")", 1)[0].split(",")
args = [Attribute(arg) for arg in args if arg.strip()]
args = [Attribute(arg, "param") for arg in args if arg.strip()]
return_type = idl_line.split()[0]
if return_type.startswith("[NewObject]"):
# [NewObject] can be skipped: https://webidl.spec.whatwg.org/#NewObject
Expand All @@ -472,11 +472,9 @@ def get_method_def(self, classname, methodname) -> str:
("Options", "Descriptor", "Configuration")
):
assert args[0].typename.startswith("GPU")
des_is_optional = bool(args[0].default)
attributes = self.idl.structs[args[0].typename[3:]].values()
py_args = [
self._arg_from_attribute(methodname, attr, des_is_optional)
for attr in attributes
self._arg_from_attribute(methodname, attr) for attr in attributes
]
if py_args[0].startswith("label: str"):
py_args[0] = 'label: str=""'
Expand Down Expand Up @@ -505,15 +503,15 @@ def get_method_def(self, classname, methodname) -> str:
)
return " " + line

def _arg_from_attribute(self, methodname, attribute, force_optional=False):
def _arg_from_attribute(self, methodname, attribute):
name = to_snake_case(attribute.name)
optional_in_py = (methodname, name) in ARGS_TO_MAKE_OPTIONAL
d = attribute.default
t = self.idl.resolve_type(attribute.typename)
result = name
if not d and not attribute.required:
d = "None"
elif (force_optional or optional_in_py) and not d:
elif optional_in_py and not d:
d = "optional"
elif d == "{}":
d = "None"
Expand Down Expand Up @@ -583,7 +581,7 @@ def get_method_comment(self, classname, methodname):
idl_line = functions[name_idl]

args = idl_line.split("(", 1)[1].split(")", 1)[0].split(",")
args = [Attribute(arg) for arg in args if arg.strip()]
args = [Attribute(arg, "param") for arg in args if arg.strip()]

# If one arg that is a dict, flatten dict to kwargs
if len(args) == 1 and args[0].typename.endswith(
Expand Down
2 changes: 1 addition & 1 deletion codegen/apiwriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def write_structs():
elif default == "{}":
default = "None" # no mutable defaults
code_type += " | None"
elif not default and not field.required:
if not default and not field.required:
default = "None"
if "None" not in code_type:
code_type += " | None"
Expand Down
53 changes: 35 additions & 18 deletions codegen/idlparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
identify and remove code paths that are no longer used.
"""

from typing import Literal

from codegen.utils import print
from codegen.files import read_file

Expand Down Expand Up @@ -35,38 +37,53 @@ def get_idl_parser(*, allow_cache=True):
class Attribute:
"""A little object to hold a function argument or struct field."""

def __init__(self, line):
def __init__(self, line: str, source: Literal["struct", "param"]):
self.line = line.strip().strip(",;").strip()
self.source = source

default = None # None means 'no default' and "None" kinda means "auto".
required = False
# Detect default value
default = None
arg = self.line
if "=" in arg:
arg, default = arg.rsplit("=", 1)
arg, default = arg.strip(), default.strip()
if default and default in ["false", "true"]:
default = default.capitalize()
arg_type, arg_name = arg.strip().rsplit(" ", 1)
if arg_type.startswith("required "):
required = True
arg_type = arg_type.split(" ", 1)[1]
# required args should not have a default
assert default is None
elif arg_type.startswith("optional "):
arg_type = arg_type.split(" ", 1)[1]
default = default or "None"

if default:
if default in ["false", "true"]:
default = default.capitalize()

# Handling required vs optional differs between structs fields and function params.
# Struct fields are optional unless marked as required, see https://webidl.spec.whatwg.org/#required-dictionary-member
# Function params are required unless marked as optional, see https://webidl.spec.whatwg.org/#dfn-optional-argument
if source == "struct":
if arg_type.startswith("required "):
required = True
arg_type = arg_type.split(" ", 1)[1]
# required args should not have a default
assert default is None
else:
required = False
# default could still be None, i.e. struct field can be omitted
elif source == "param":
if arg_type.startswith("optional "):
required = False
arg_type = arg_type.split(" ", 1)[1]
default = default or "None"
else:
# but otherwise they are required
required = True
assert default is None
else:
raise RuntimeError(f"Invalid Attribute source: {source!r}")

self.name = arg_name
self.typename = arg_type
self.default = default
self.required = required

def __repr__(self):
return f"<Attribute '{self.typename} {self.name}'>"
return f"<Attribute from {self.source} '{self.typename} {self.name}'>"

def to_str(self):
def to_str(self) -> str:
return self.line


Expand Down Expand Up @@ -434,7 +451,7 @@ def _parse(self):
continue
assert line.endswith(";")

attribute = Attribute(line)
attribute = Attribute(line, "struct")
d[attribute.name] = attribute
self.structs[name] = d
elif line.startswith(("[Exposed=", "[Serializable]")):
Expand Down
Loading
Loading