Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/env python
      2 
      3 import os.path
      4 import re
      5 import subprocess
      6 import sys
      7 
      8 from in_file import InFile
      9 import in_generator
     10 import license
     11 
     12 
     13 HEADER_TEMPLATE = """
     14 %(license)s
     15 
     16 #ifndef %(class_name)s_h
     17 #define %(class_name)s_h
     18 
     19 #include "core/css/CSSParserMode.h"
     20 #include "wtf/HashFunctions.h"
     21 #include "wtf/HashTraits.h"
     22 #include <string.h>
     23 
     24 namespace WTF {
     25 class AtomicString;
     26 class String;
     27 }
     28 
     29 namespace WebCore {
     30 
     31 enum CSSPropertyID {
     32     CSSPropertyInvalid = 0,
     33 %(property_enums)s
     34 };
     35 
     36 const int firstCSSProperty = %(first_property_id)s;
     37 const int numCSSProperties = %(properties_count)s;
     38 const int lastCSSProperty = %(last_property_id)d;
     39 const size_t maxCSSPropertyNameLength = %(max_name_length)d;
     40 
     41 const char* getPropertyName(CSSPropertyID);
     42 const WTF::AtomicString& getPropertyNameAtomicString(CSSPropertyID);
     43 WTF::String getPropertyNameString(CSSPropertyID);
     44 WTF::String getJSPropertyName(CSSPropertyID);
     45 bool isInternalProperty(CSSPropertyID id);
     46 
     47 inline CSSPropertyID convertToCSSPropertyID(int value)
     48 {
     49     ASSERT((value >= firstCSSProperty && value <= lastCSSProperty) || value == CSSPropertyInvalid);
     50     return static_cast<CSSPropertyID>(value);
     51 }
     52 
     53 } // namespace WebCore
     54 
     55 namespace WTF {
     56 template<> struct DefaultHash<WebCore::CSSPropertyID> { typedef IntHash<unsigned> Hash; };
     57 template<> struct HashTraits<WebCore::CSSPropertyID> : GenericHashTraits<WebCore::CSSPropertyID> {
     58     static const bool emptyValueIsZero = true;
     59     static const bool needsDestruction = false;
     60     static void constructDeletedValue(WebCore::CSSPropertyID& slot) { slot = static_cast<WebCore::CSSPropertyID>(WebCore::lastCSSProperty + 1); }
     61     static bool isDeletedValue(WebCore::CSSPropertyID value) { return value == (WebCore::lastCSSProperty + 1); }
     62 };
     63 }
     64 
     65 #endif // %(class_name)s_h
     66 """
     67 
     68 GPERF_TEMPLATE = """
     69 %%{
     70 %(license)s
     71 
     72 #include "config.h"
     73 #include "%(class_name)s.h"
     74 #include "core/css/HashTools.h"
     75 #include <string.h>
     76 
     77 #include "wtf/ASCIICType.h"
     78 #include "wtf/text/AtomicString.h"
     79 #include "wtf/text/WTFString.h"
     80 
     81 namespace WebCore {
     82 static const char propertyNameStringsPool[] = {
     83 %(property_name_strings)s
     84 };
     85 
     86 static const unsigned short propertyNameStringsOffsets[] = {
     87 %(property_name_offsets)s
     88 };
     89 
     90 %%}
     91 %%struct-type
     92 struct Property;
     93 %%omit-struct-type
     94 %%language=C++
     95 %%readonly-tables
     96 %%global-table
     97 %%compare-strncmp
     98 %%define class-name %(class_name)sHash
     99 %%define lookup-function-name findPropertyImpl
    100 %%define hash-function-name property_hash_function
    101 %%define slot-name nameOffset
    102 %%define word-array-name property_word_list
    103 %%enum
    104 %%%%
    105 %(property_to_enum_map)s
    106 %%%%
    107 const Property* findProperty(register const char* str, register unsigned int len)
    108 {
    109     return %(class_name)sHash::findPropertyImpl(str, len);
    110 }
    111 
    112 const char* getPropertyName(CSSPropertyID id)
    113 {
    114     if (id < firstCSSProperty)
    115         return 0;
    116     int index = id - firstCSSProperty;
    117     if (index >= numCSSProperties)
    118         return 0;
    119     return propertyNameStringsPool + propertyNameStringsOffsets[index];
    120 }
    121 
    122 const AtomicString& getPropertyNameAtomicString(CSSPropertyID id)
    123 {
    124     if (id < firstCSSProperty)
    125         return nullAtom;
    126     int index = id - firstCSSProperty;
    127     if (index >= numCSSProperties)
    128         return nullAtom;
    129 
    130     static AtomicString* propertyStrings = new AtomicString[numCSSProperties]; // Intentionally never destroyed.
    131     AtomicString& propertyString = propertyStrings[index];
    132     if (propertyString.isNull()) {
    133         const char* propertyName = propertyNameStringsPool + propertyNameStringsOffsets[index];
    134         propertyString = AtomicString(propertyName, strlen(propertyName), AtomicString::ConstructFromLiteral);
    135     }
    136     return propertyString;
    137 }
    138 
    139 String getPropertyNameString(CSSPropertyID id)
    140 {
    141     // We share the StringImpl with the AtomicStrings.
    142     return getPropertyNameAtomicString(id).string();
    143 }
    144 
    145 String getJSPropertyName(CSSPropertyID id)
    146 {
    147     char result[maxCSSPropertyNameLength + 1];
    148     const char* cssPropertyName = getPropertyName(id);
    149     const char* propertyNamePointer = cssPropertyName;
    150     if (!propertyNamePointer)
    151         return emptyString();
    152 
    153     char* resultPointer = result;
    154     while (char character = *propertyNamePointer++) {
    155         if (character == '-') {
    156             char nextCharacter = *propertyNamePointer++;
    157             if (!nextCharacter)
    158                 break;
    159             character = (propertyNamePointer - 2 != cssPropertyName) ? toASCIIUpper(nextCharacter) : nextCharacter;
    160         }
    161         *resultPointer++ = character;
    162     }
    163     *resultPointer = '\\0';
    164     return String(result);
    165 }
    166 
    167 bool isInternalProperty(CSSPropertyID id)
    168 {
    169     switch (id) {
    170         %(internal_properties)s
    171             return true;
    172         default:
    173             return false;
    174     }
    175 }
    176 
    177 } // namespace WebCore
    178 """
    179 
    180 
    181 class CSSPropertiesWriter(in_generator.Writer):
    182     class_name = "CSSPropertyNames"
    183     defaults = {
    184         'alias_for': None,
    185         'is_internal': False,
    186     }
    187 
    188     def __init__(self, file_paths):
    189         in_generator.Writer.__init__(self, file_paths)
    190         self._outputs = {(self.class_name + ".h"): self.generate_header,
    191                          (self.class_name + ".cpp"): self.generate_implementation,
    192                         }
    193 
    194         self._aliases = filter(lambda property: property['alias_for'], self.in_file.name_dictionaries)
    195         for offset, property in enumerate(self._aliases):
    196             # Aliases use the enum_name that they are an alias for.
    197             property['enum_name'] = self._enum_name_from_property_name(property['alias_for'])
    198             # Aliases do not get an enum_value.
    199 
    200         self._properties = filter(lambda property: not property['alias_for'], self.in_file.name_dictionaries)
    201         if len(self._properties) > 1024:
    202             print "ERROR : There is more than 1024 CSS Properties, you need to update CSSProperty.h/StylePropertyMetadata m_propertyID accordingly."
    203             exit(1)
    204         self._first_property_id = 1  # We start after CSSPropertyInvalid.
    205         property_id = self._first_property_id
    206         for offset, property in enumerate(self._properties):
    207             property['enum_name'] = self._enum_name_from_property_name(property['name'])
    208             property['enum_value'] = self._first_property_id + offset
    209             if property['name'].startswith('-internal-'):
    210                 property['is_internal'] = True
    211 
    212     def _enum_name_from_property_name(self, property_name):
    213         return "CSSProperty" + re.sub(r'(^[^-])|-(.)', lambda match: (match.group(1) or match.group(2)).upper(), property_name)
    214 
    215     def _enum_declaration(self, property):
    216         return "    %(enum_name)s = %(enum_value)s," % property
    217 
    218     def generate_header(self):
    219         return HEADER_TEMPLATE % {
    220             'license': license.license_for_generated_cpp(),
    221             'class_name': self.class_name,
    222             'property_enums': "\n".join(map(self._enum_declaration, self._properties)),
    223             'first_property_id': self._first_property_id,
    224             'properties_count': len(self._properties),
    225             'last_property_id': self._first_property_id + len(self._properties) - 1,
    226             'max_name_length': reduce(max, map(len, map(lambda property: property['name'], self._properties))),
    227         }
    228 
    229     def _case_properties(self, property):
    230         return "case %(enum_name)s:" % property
    231 
    232     def generate_implementation(self):
    233         property_offsets = []
    234         current_offset = 0
    235         for property in self._properties:
    236             property_offsets.append(current_offset)
    237             current_offset += len(property["name"]) + 1
    238 
    239         gperf_input = GPERF_TEMPLATE % {
    240             'license': license.license_for_generated_cpp(),
    241             'class_name': self.class_name,
    242             'property_name_strings': '\n'.join(map(lambda property: '    "%(name)s\\0"' % property, self._properties)),
    243             'property_name_offsets': '\n'.join(map(lambda offset: '    %d,' % offset, property_offsets)),
    244             'property_to_enum_map': '\n'.join(map(lambda property: '%(name)s, %(enum_name)s' % property, self._properties + self._aliases)),
    245             'internal_properties': '\n'.join(map(self._case_properties, filter(lambda property: property['is_internal'], self._properties))),
    246         }
    247         # FIXME: If we could depend on Python 2.7, we would use subprocess.check_output
    248         gperf_args = [self.gperf_path, '--key-positions=*', '-P', '-n']
    249         gperf_args.extend(['-m', '50'])  # Pick best of 50 attempts.
    250         gperf_args.append('-D')  # Allow duplicate hashes -> More compact code.
    251         gperf = subprocess.Popen(gperf_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
    252         return gperf.communicate(gperf_input)[0]
    253 
    254 
    255 if __name__ == "__main__":
    256     in_generator.Maker(CSSPropertiesWriter).main(sys.argv)
    257