Bug 1454591 part 1 - Generate more structured data in ServoCSSPropList.py. r=heycam

This patch changes ServoCSSPropList.py to use classes for properties.
This allows extending the data in the file without needing to update all
users of this file.

Sorting in GenerateCSSPropsGenerated.py is removed because the data file
has the right order already.

MozReview-Commit-ID: D74bItCfpPH

--HG--
extra : rebase_source : e0138c255f77515f491496fcb8680686362f4e9e
This commit is contained in:
Xidorn Quan 2018-05-04 13:44:51 +10:00
parent 3b95cfc89e
commit 7db648bdec
6 changed files with 110 additions and 101 deletions

View File

@ -10,6 +10,7 @@ This information is used to generate the properties-db.js file.
import json import json
import os import os
import runpy
import sys import sys
import string import string
import subprocess import subprocess
@ -53,15 +54,12 @@ class MachCommands(MachCommandBase):
# The data takes the following form: # The data takes the following form:
# [ (name, prop, id, flags, pref, proptype), ... ] # [ (name, prop, id, flags, pref, proptype), ... ]
dataPath = resolve_path(self.topobjdir, 'layout/style/ServoCSSPropList.py') dataPath = resolve_path(self.topobjdir, 'layout/style/ServoCSSPropList.py')
with open(dataPath, "r") as f: data = runpy.run_path(dataPath)['data']
data = eval(f.read())
# Map this list # Map this list
# (name, prop, id, flags, pref, proptype) => (name, pref)
preferences = [ preferences = [
(name, pref) (p.name, p.pref) for p in data
for name, prop, id, flags, pref, proptype in data if 'CSSPropFlags::Internal' not in p.flags and p.pref]
if 'CSSPropFlags::Internal' not in flags and pref]
return preferences return preferences

View File

@ -5,6 +5,7 @@
import sys import sys
import string import string
import argparse import argparse
import runpy
# Generates a line of WebIDL with the given spelling of the property name # Generates a line of WebIDL with the given spelling of the property name
# (whether camelCase, _underscorePrefixed, etc.) and the given array of # (whether camelCase, _underscorePrefixed, etc.) and the given array of
@ -13,18 +14,19 @@ def generateLine(propName, extendedAttrs):
return " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs), return " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
propName) propName)
def generate(output, idlFilename, dataFile): def generate(output, idlFilename, dataFile):
with open(dataFile, "r") as f: propList = runpy.run_path(dataFile)["data"]
propList = eval(f.read())
props = "" props = ""
for name, prop, id, flags, pref, proptype in propList: for p in propList:
if "CSSPropFlags::Internal" in flags: if "CSSPropFlags::Internal" in p.flags:
continue continue
# Unfortunately, even some of the getters here are fallible # Unfortunately, even some of the getters here are fallible
# (e.g. on nsComputedDOMStyle). # (e.g. on nsComputedDOMStyle).
extendedAttrs = ["CEReactions", "Throws", "TreatNullAs=EmptyString", extendedAttrs = ["CEReactions", "Throws", "TreatNullAs=EmptyString",
"SetterNeedsSubjectPrincipal=NonSystem"] "SetterNeedsSubjectPrincipal=NonSystem"]
if pref is not "": if p.pref is not "":
extendedAttrs.append('Pref="%s"' % pref) extendedAttrs.append('Pref="%s"' % p.pref)
prop = p.method
# webkit properties get a camelcase "webkitFoo" accessor # webkit properties get a camelcase "webkitFoo" accessor
# as well as a capitalized "WebkitFoo" alias (added here). # as well as a capitalized "WebkitFoo" alias (added here).
@ -52,8 +54,8 @@ def generate(output, idlFilename, dataFile):
# cover (3) and all of (1) except "float". If we now add an alias # cover (3) and all of (1) except "float". If we now add an alias
# for all the cases where "name" doesn't match "prop", that will cover # for all the cases where "name" doesn't match "prop", that will cover
# "float" and (2). # "float" and (2).
if prop != name: if prop != p.name:
extendedAttrs.append('BindingAlias="%s"' % name) extendedAttrs.append('BindingAlias="%s"' % p.name)
props += generateLine(prop, extendedAttrs) props += generateLine(prop, extendedAttrs)

View File

@ -2,30 +2,30 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
import runpy
import string import string
def generate(output, template, dataFile): def generate(output, template, dataFile):
with open(template, "r") as f: with open(template, "r") as f:
template = string.Template(f.read()) template = string.Template(f.read())
with open(dataFile, "r") as f: data = runpy.run_path(dataFile)["data"]
data = eval(f.read())
longhand_count = 0 longhand_count = 0
shorthand_count = 0 shorthand_count = 0
alias_count = 0 alias_count = 0
property_ids = [] property_ids = []
for name, method, id, flags, pref, prototype in data: for prop in data:
if prototype != "alias": if prop.type() != "alias":
if prototype == "longhand": if prop.type() == "longhand":
assert shorthand_count == 0 assert shorthand_count == 0
longhand_count += 1 longhand_count += 1
else: else:
assert alias_count == 0 assert alias_count == 0
shorthand_count += 1 shorthand_count += 1
property_ids.append("eCSSProperty_{}".format(id)) property_ids.append("eCSSProperty_{}".format(prop.id))
else: else:
alias_count += 1 alias_count += 1
property_ids.append("eCSSPropertyAlias_{}".format(id[0])) property_ids.append("eCSSPropertyAlias_{}".format(prop.alias_id))
output.write("/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n") output.write("/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n")
output.write(template.substitute({ output.write(template.substitute({

View File

@ -2,75 +2,66 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file, # License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. # You can obtain one at http://mozilla.org/MPL/2.0/.
import runpy
import sys import sys
import string import string
import argparse import argparse
def get_properties(dataFile): class PropertyWrapper(object):
with open(dataFile, "r") as f: __slots__ = ["index", "prop", "idlname"]
properties = eval(f.read())
properties = [{"name":p[0], "prop":p[1], "id":p[2],
"flags":p[3], "pref":p[4], "proptype":p[5]}
for (i, p) in enumerate(properties)]
# Sort the list so that longhand properties are intermingled first, def __init__(self, index, prop):
# shorthand properties follow, then aliases appear last. self.index = index
# This matches the order of the nsCSSPropertyID enum. self.prop = prop
if "CSSPropFlags::Internal" in prop.flags:
def property_compare(x, y): self.idlname = None
property_order = {"longhand": 0, "shorthand": 1, "alias": 2}
return property_order[x["proptype"]] - property_order[y["proptype"]]
properties = sorted(properties, cmp=property_compare)
for i, p in enumerate(properties):
p["index"] = i
# Record each property's IDL name.
for p in properties:
if "CSSPropFlags::Internal" in p["flags"]:
p["idlname"] = None
else: else:
idl_name = p["prop"] idl_name = prop.method
if not idl_name.startswith("Moz"): if not idl_name.startswith("Moz"):
idl_name = idl_name[0].lower() + idl_name[1:] idl_name = idl_name[0].lower() + idl_name[1:]
p["idlname"] = idl_name self.idlname = idl_name
return properties def __getattr__(self, name):
return getattr(self.prop, name)
def get_properties(dataFile):
properties = runpy.run_path(dataFile)["data"]
return [PropertyWrapper(i, p) for i, p in enumerate(properties)]
def generate_idl_names(properties): def generate_idl_names(properties):
names = [] names = []
for p in properties: for p in properties:
if p["proptype"] is "alias": if p.type() == "alias":
continue continue
if p["idlname"] is None: if p.idlname is None:
names.append(" nullptr, // %s" % p["name"]) names.append(" nullptr, // %s" % p.name)
else: else:
names.append(' "%s",' % p["idlname"]) names.append(' "%s",' % p.idlname)
return "\n".join(names) return "\n".join(names)
def generate_assertions(properties): def generate_assertions(properties):
def enum(p): def enum(p):
if p["proptype"] is "alias": if p.type() == "alias":
return "eCSSPropertyAlias_%s" % p["id"][0] return "eCSSPropertyAlias_%s" % p.alias_id
else: else:
return "eCSSProperty_%s" % p["id"] return "eCSSProperty_%s" % p.id
msg = ('static_assert(%s == %d, "GenerateCSSPropsGenerated.py did not list ' msg = ('static_assert(%s == %d, "GenerateCSSPropsGenerated.py did not list '
'properties in nsCSSPropertyID order");') 'properties in nsCSSPropertyID order");')
return "\n".join(map(lambda p: msg % (enum(p), p["index"]), properties)) return "\n".join(map(lambda p: msg % (enum(p), p.index), properties))
def generate_idl_name_positions(properties): def generate_idl_name_positions(properties):
# Skip aliases. # Skip aliases.
ps = filter(lambda p: p["proptype"] is not "alias", properties) ps = filter(lambda p: p.type() != "alias", properties)
# Sort alphabetically by IDL name. # Sort alphabetically by IDL name.
ps = sorted(ps, key=lambda p: p["idlname"]) ps = sorted(ps, key=lambda p: p.idlname)
# Annotate entries with the sorted position. # Annotate entries with the sorted position.
ps = [(p, position) for position, p in enumerate(ps)] ps = [(p, position) for position, p in enumerate(ps)]
# Sort back to nsCSSPropertyID order. # Sort back to nsCSSPropertyID order.
ps = sorted(ps, key=lambda (p, position): p["index"]) ps = sorted(ps, key=lambda (p, position): p.index)
return ",\n".join(map(lambda (p, position): " %d" % position, ps)) return ",\n".join(map(lambda (p, position): " %d" % position, ps))

View File

@ -5,6 +5,7 @@
import buildconfig import buildconfig
import mozpack.path as mozpath import mozpack.path as mozpath
import os import os
import runpy
import subprocess import subprocess
import string import string
import sys import sys
@ -32,8 +33,7 @@ def generate_data(output, template):
def generate_header(output, data): def generate_header(output, data):
with open(data, "r") as f: data = runpy.run_path(data)["data"]
data = eval(f.read())
output.write("""/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */ output.write("""/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */
@ -68,28 +68,29 @@ def generate_header(output, data):
"shorthand": "CSS_PROP_SHORTHAND", "shorthand": "CSS_PROP_SHORTHAND",
"alias": "CSS_PROP_ALIAS", "alias": "CSS_PROP_ALIAS",
} }
for name, method, id, flags, pref, proptype in data: for prop in data:
is_internal = "CSSPropFlags::Internal" in flags is_internal = "CSSPropFlags::Internal" in prop.flags
pref = '"' + pref + '"' pref = '"' + prop.pref + '"'
if proptype == "alias": if prop.type() == "alias":
params = [name, id[0], id[1], method, pref] params = [prop.name, prop.alias_id, prop.prop_id, prop.method, pref]
else: else:
method = prop.method
if method == "CssFloat": if method == "CssFloat":
method = "CSS_PROP_PUBLIC_OR_PRIVATE(CssFloat, Float)" method = "CSS_PROP_PUBLIC_OR_PRIVATE(CssFloat, Float)"
elif method.startswith("Moz"): elif method.startswith("Moz"):
method = "CSS_PROP_DOMPROP_PREFIXED({})".format(method[3:]) method = "CSS_PROP_DOMPROP_PREFIXED({})".format(method[3:])
if flags: if prop.flags:
flags = " | ".join(flags) flags = " | ".join(prop.flags)
else: else:
flags = "CSSPropFlags(0)" flags = "CSSPropFlags(0)"
params = [name, id, method, flags, pref] params = [prop.name, prop.id, method, flags, pref]
is_component_of_all = not is_internal and name not in ["direction", "unicode-bidi"] is_component_of_all = not is_internal and prop.name not in ["direction", "unicode-bidi"]
if not is_component_of_all: if not is_component_of_all:
output.write("#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND\n") output.write("#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND\n")
if is_internal: if is_internal:
output.write("#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL\n") output.write("#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL\n")
output.write("{}({})\n".format(MACRO_NAMES[proptype], ", ".join(params))) output.write("{}({})\n".format(MACRO_NAMES[prop.type()], ", ".join(params)))
if is_internal: if is_internal:
output.write("#endif\n") output.write("#endif\n")
if not is_component_of_all: if not is_component_of_all:

View File

@ -2,6 +2,43 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/. # file, You can obtain one at https://mozilla.org/MPL/2.0/.
def _assign_slots(obj, args):
for i, attr in enumerate(obj.__slots__):
setattr(obj, attr, args[i])
class Longhand(object):
__slots__ = ["name", "method", "id", "flags", "pref"]
def __init__(self, *args):
_assign_slots(self, args)
@staticmethod
def type():
return "longhand"
class Shorthand(object):
__slots__ = ["name", "method", "id", "flags", "pref"]
def __init__(self, *args):
_assign_slots(self, args)
@staticmethod
def type():
return "shorthand"
class Alias(object):
__slots__ = ["name", "method", "alias_id", "prop_id", "flags", "pref"]
def __init__(self, *args):
_assign_slots(self, args)
@staticmethod
def type():
return "alias"
<%! <%!
# nsCSSPropertyID of longhands and shorthands is ordered alphabetically # nsCSSPropertyID of longhands and shorthands is ordered alphabetically
# with vendor prefixes removed. Note that aliases use their alias name # with vendor prefixes removed. Note that aliases use their alias name
@ -27,6 +64,13 @@ def is_internal(prop):
] ]
return prop.name in OTHER_INTERNALS return prop.name in OTHER_INTERNALS
def method(prop):
if prop.name == "float":
return "CssFloat"
if prop.name.startswith("-x-"):
return prop.camel_case[1:]
return prop.camel_case
def flags(prop): def flags(prop):
result = [] result = []
if prop.explicitly_enabled_in_chrome(): if prop.explicitly_enabled_in_chrome():
@ -49,43 +93,16 @@ def pref(prop):
return '""' return '""'
%> %>
[ data = [
% for prop in sorted(data.longhands, key=order_key): % for prop in sorted(data.longhands, key=order_key):
( Longhand("${prop.name}", "${method(prop)}", "${prop.ident}", [${flags(prop)}], ${pref(prop)}),
"${prop.name}",
% if prop.name == "float":
"CssFloat",
% elif prop.name.startswith("-x-"):
"${prop.camel_case[1:]}",
% else:
"${prop.camel_case}",
% endif
"${prop.ident}",
[${flags(prop)}],
${pref(prop)},
"longhand",
),
% endfor % endfor
% for prop in sorted(data.shorthands, key=order_key): % for prop in sorted(data.shorthands, key=order_key):
( Shorthand("${prop.name}", "${prop.camel_case}", "${prop.ident}", [${flags(prop)}], ${pref(prop)}),
"${prop.name}",
"${prop.camel_case}",
"${prop.ident}",
[${flags(prop)}],
${pref(prop)},
"shorthand",
),
% endfor % endfor
% for prop in sorted(data.all_aliases(), key=lambda x: x.name): % for prop in sorted(data.all_aliases(), key=lambda x: x.name):
( Alias("${prop.name}", "${prop.camel_case}", "${prop.ident}", "${prop.original.ident}", [], ${pref(prop)}),
"${prop.name}",
"${prop.camel_case}",
("${prop.ident}", "${prop.original.ident}"),
[],
${pref(prop)},
"alias",
),
% endfor % endfor
] ]