Changeset 101249 in webkit


Ignore:
Timestamp:
Nov 28, 2011 4:30:54 AM (12 years ago)
Author:
loislo@chromium.org
Message:

Web Inspector: [protocol] generate C++ classes for protocol JSON named types
https://bugs.webkit.org/show_bug.cgi?id=72835

Patch by Peter Rybin <peter.rybin@gmail.com> on 2011-11-28
Reviewed by Pavel Feldman.

Extends python generator functionality.
Makes constructor in InspectorObject public.

  • inspector/CodeGeneratorInspector.py:
  • inspector/InspectorValues.h:
Location:
trunk/Source/WebCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r101247 r101249  
     12011-11-28  Peter Rybin  <peter.rybin@gmail.com>
     2
     3        Web Inspector: [protocol] generate C++ classes for protocol JSON named types
     4        https://bugs.webkit.org/show_bug.cgi?id=72835
     5
     6        Reviewed by Pavel Feldman.
     7
     8        Extends python generator functionality.
     9        Makes constructor in InspectorObject public.
     10
     11        * inspector/CodeGeneratorInspector.py:
     12        * inspector/InspectorValues.h:
     13
    1142011-11-28  Simon Hausmann  <simon.hausmann@nokia.com>
    215
  • trunk/Source/WebCore/inspector/CodeGeneratorInspector.py

    r101236 r101249  
    105105class Capitalizer:
    106106    @staticmethod
     107    def lower_camel_case_to_upper(str):
     108        if len(str) > 0 and str[0].islower():
     109            str = str[0].upper() + str[1:]
     110        return str
     111
     112    @staticmethod
    107113    def upper_camel_case_to_lower(str):
    108114        pos = 0
     
    121127        return str
    122128
     129    @staticmethod
     130    def camel_case_to_capitalized_with_underscores(str):
     131        if len(str) == 0:
     132            return str
     133        output = Capitalizer.split_camel_case_(str)
     134        pos = 0
     135        while pos < len(output):
     136            output[pos] = output[pos].upper()
     137            pos += 1
     138        return "_".join(output)
     139
     140    @staticmethod
     141    def split_camel_case_(str):
     142        output = []
     143        pos_being = 0
     144        pos = 1
     145        has_oneletter = False
     146        while pos < len(str):
     147            if str[pos].isupper():
     148                output.append(str[pos_being:pos].upper())
     149                if pos - pos_being == 1:
     150                    has_oneletter = True
     151                pos_being = pos
     152            pos += 1
     153        output.append(str[pos_being:])
     154        if has_oneletter:
     155            array_pos = 0
     156            while array_pos < len(output) - 1:
     157                if len(output[array_pos]) == 1:
     158                    array_pos_end = array_pos + 1
     159                    while array_pos_end < len(output) and len(output[array_pos_end]) == 1:
     160                        array_pos_end += 1
     161                    if array_pos_end - array_pos > 1:
     162                        possible_abbreviation = "".join(output[array_pos:array_pos_end])
     163                        if possible_abbreviation.upper() in Capitalizer.ABBREVIATION:
     164                            output[array_pos:array_pos_end] = [possible_abbreviation]
     165                        else:
     166                            array_pos = array_pos_end - 1
     167                array_pos += 1
     168        return output
     169
    123170    ABBREVIATION = frozenset(["XHR", "DOM", "CSS"])
    124171
     
    189236        elif json_type == "number":
    190237            return RawTypes.Number
     238        elif json_type == "any":
     239            return RawTypes.Any
    191240        else:
    192241            raise Exception("Unknown type: %s" % json_type)
     
    204253        @classmethod
    205254        def get_c_param_type(cls, param_type, optional):
    206             if param_type == ParamType.EVENT:
     255            if param_type == ParamType.EVENT or param_type == ParamType.TYPE_BUILDER_OUTPUT:
    207256                return cls._ref_c_type
    208257            else:
     
    299348        @classmethod
    300349        def get_c_param_type(cls, param_type, optional):
    301             if param_type == ParamType.EVENT:
     350            if param_type == ParamType.EVENT or param_type == ParamType.TYPE_BUILDER_OUTPUT:
    302351                return cls._ref_c_type
    303352            else:
     
    324373        _plain_c_type = CParamType("RefPtr<InspectorObject>")
    325374        _ref_c_type = CParamType("PassRefPtr<InspectorObject>")
     375
     376    class Any(BaseType):
     377        @classmethod
     378        def get_c_param_type(cls, param_type, optional):
     379            if param_type == ParamType.EVENT or param_type == ParamType.TYPE_BUILDER_OUTPUT:
     380                return cls._ref_c_type
     381            else:
     382                return cls._plain_c_type
     383
     384        @staticmethod
     385        def get_getter_name():
     386            return "Value"
     387
     388        get_setter_name = get_getter_name
     389
     390        @staticmethod
     391        def get_c_initializer():
     392            return "InspectorValue::create()"
     393
     394        @staticmethod
     395        def get_js_bind_type():
     396            raise Exception("Unsupported")
     397
     398        @staticmethod
     399        def is_event_param_check_optional():
     400            return True
     401
     402        _plain_c_type = CParamType("RefPtr<InspectorValue>")
     403        _ref_c_type = CParamType("PassRefPtr<InspectorValue>")
    326404
    327405    class Array(BaseType):
     
    361439    OUTPUT = "output"
    362440    EVENT = "event"
     441    TYPE_BUILDER_OUTPUT = "typeBuilderOutput"
     442
     443# Collection of InspectorObject class methods that are likely to be overloaded in generated class.
     444# We must explicitly import all overloaded methods or they won't be available to user.
     445INSPECTOR_OBJECT_SETTER_NAMES = frozenset(["setValue", "setBoolean", "setNumber", "setString", "setValue", "setObject", "setArray"])
     446
     447
     448class TypeBindings:
     449    @staticmethod
     450    def create_for_named_type_declaration(json_type, context_domain_name):
     451        if json_type["type"] == "string":
     452            if "enum" in json_type:
     453
     454                class EnumBinding:
     455                    @staticmethod
     456                    def generate_type_builder(output, forward_listener):
     457                        enum = json_type["enum"]
     458                        # TODO: doc
     459                        output.append("namespace ")
     460                        output.append(json_type["id"])
     461                        output.append(" {\n")
     462                        for enum_item in enum:
     463                            item_c_name = enum_item.replace('-', '_')
     464                            output.append("const char* const ")
     465                            output.append(Capitalizer.upper_camel_case_to_lower(item_c_name))
     466                            output.append(" = \"")
     467                            output.append(enum_item)
     468                            output.append("\";\n")
     469                        output.append("} // namespace ")
     470                        output.append(json_type["id"])
     471                        output.append("\n\n")
     472
     473                return EnumBinding
     474            else:
     475
     476                class PlainString:
     477                    @staticmethod
     478                    def generate_type_builder(output, forward_listener):
     479                        if "description" in json_type:
     480                            output.append("/* ")
     481                            output.append(json_type["description"])
     482                            output.append(" */\n")
     483                        output.append("typedef String ")
     484                        output.append(json_type["id"])
     485                        output.append(";\n\n")
     486                return PlainString
     487
     488        elif json_type["type"] == "object":
     489            if "properties" in json_type:
     490
     491                class ClassBinding:
     492                    @staticmethod
     493                    def generate_type_builder(output, forward_listener):
     494                        # TODO: doc
     495                        output.append("class ")
     496                        class_name = json_type["id"]
     497                        output.append(class_name)
     498                        output.append(" : public InspectorObject {\n")
     499                        output.append("public:\n")
     500
     501                        properties = json_type["properties"]
     502                        main_properties = []
     503                        optional_properties = []
     504                        for p in properties:
     505                            if "optional" in p and p["optional"]:
     506                                optional_properties.append(p)
     507                            else:
     508                                main_properties.append(p)
     509
     510                        output.append(
     511"""    enum {
     512        NO_FIELDS_SET = 0,
     513""")
     514
     515                        state_enum_items = []
     516                        if len(main_properties) > 0:
     517                            pos = 0
     518                            for p in main_properties:
     519                                item_name = Capitalizer.camel_case_to_capitalized_with_underscores(p["name"]) + "_SET"
     520                                state_enum_items.append(item_name)
     521                                output.append("        %s = 1 << %s,\n" % (item_name, pos))
     522                                pos += 1
     523                            all_fields_set_value = "(" + (" | ".join(state_enum_items)) + ")"
     524                        else:
     525                            all_fields_set_value = "0"
     526
     527                        output.append(
     528"""        ALL_FIELDS_SET = %s
     529    };
     530
     531    template<int STATE>
     532    class Builder {
     533    private:
     534        RefPtr<InspectorObject> m_result;
     535
     536        template<int STEP> Builder<STATE | STEP>& castState()
     537        {
     538            return *reinterpret_cast<Builder<STATE | STEP>*>(this);
     539        }
     540
     541        Builder(PassRefPtr<%s> ptr)
     542        {
     543            COMPILE_ASSERT(STATE == NO_FIELDS_SET, builder_created_in_non_init_state);
     544            m_result = ptr;
     545        }
     546        friend class %s;
     547    public:
     548""" % (all_fields_set_value, class_name, class_name))
     549
     550                        pos = 0
     551                        for prop in main_properties:
     552                            prop_name = prop["name"]
     553                            param_raw_type = resolve_param_raw_type(prop, context_domain_name)
     554                            output.append("""
     555        Builder<STATE | %s>& set%s(%s value)
     556        {
     557            COMPILE_ASSERT(!(STATE & %s), property_%s_already_set);
     558            m_result->set%s("%s", value);
     559            return castState<%s>();
     560        }
     561"""
     562                            % (state_enum_items[pos],
     563                               Capitalizer.lower_camel_case_to_upper(prop_name),
     564                               param_raw_type.get_c_param_type(ParamType.TYPE_BUILDER_OUTPUT, False).get_text(),
     565                               state_enum_items[pos], prop_name,
     566                               param_raw_type.get_setter_name(), prop_name, state_enum_items[pos]))
     567
     568                            pos += 1
     569
     570                        output.append("""
     571        operator RefPtr<%s>& ()
     572        {
     573            COMPILE_ASSERT(STATE == ALL_FIELDS_SET, result_is_not_ready);
     574            return *reinterpret_cast<RefPtr<%s>*>(&m_result);
     575        }
     576
     577        operator PassRefPtr<%s> ()
     578        {
     579            return RefPtr<%s>(*this);
     580        }
     581    };
     582
     583"""
     584                        % (class_name, class_name, class_name, class_name))
     585
     586                        output.append("    /*\n")
     587                        output.append("     * Synthetic constructor:\n")
     588                        output.append("     * RefPtr<%s> result = %s::create()" % (class_name, class_name))
     589                        for prop in main_properties:
     590                            output.append("\n     *     .set%s(...)" % Capitalizer.lower_camel_case_to_upper(prop["name"]))
     591                        output.append(";\n     */\n")
     592
     593                        output.append(
     594"""    static Builder<NO_FIELDS_SET> create()
     595    {
     596        return Builder<NO_FIELDS_SET>(adoptRef(new %s()));
     597    }
     598""" % class_name)
     599
     600                        for prop in optional_properties:
     601                            param_raw_type = resolve_param_raw_type(prop, context_domain_name)
     602                            setter_name = "set%s" % Capitalizer.lower_camel_case_to_upper(prop["name"])
     603                            output.append("\n    void %s" % setter_name)
     604                            output.append("(%s value)\n" % param_raw_type.get_c_param_type(ParamType.TYPE_BUILDER_OUTPUT, False).get_text())
     605                            output.append("    {\n")
     606                            output.append("        this->set%s(\"%s\", value);\n" % (param_raw_type.get_setter_name(), prop["name"]))
     607                            output.append("    }\n")
     608
     609                            if setter_name in INSPECTOR_OBJECT_SETTER_NAMES:
     610                                output.append("    using InspectorObject::%s;\n\n" % setter_name)
     611
     612                        output.append("};\n\n")
     613                return ClassBinding
     614            else:
     615
     616                class PlainObjectBinding:
     617                    @staticmethod
     618                    def generate_type_builder(output, forward_listener):
     619                        # No-op
     620                        pass
     621                return PlainObjectBinding
     622        else:
     623            raw_type = RawTypes.get(json_type["type"])
     624
     625            class RawTypesBinding:
     626                @staticmethod
     627                def generate_type_builder(output, forward_listener):
     628                    # No-op
     629                    pass
     630            return RawTypesBinding
    363631
    364632
    365633class TypeData(object):
    366     def __init__(self, json_type, json_domain):
     634    def __init__(self, json_type, json_domain, domain_data):
    367635        self.json_type_ = json_type
    368636        self.json_domain_ = json_domain
    369 
    370         if "type" in json_type:
    371             json_type_name = json_type["type"]
    372             raw_type = RawTypes.get(json_type_name)
    373         else:
     637        self.domain_data_ = domain_data
     638
     639        if "type" not in json_type:
    374640            raise Exception("Unknown type")
     641
     642        json_type_name = json_type["type"]
     643        raw_type = RawTypes.get(json_type_name)
    375644        self.raw_type_ = raw_type
     645        self.binding_ = TypeBindings.create_for_named_type_declaration(json_type, json_domain["domain"])
    376646
    377647    def get_raw_type(self):
    378648        return self.raw_type_
     649
     650    def get_binding(self):
     651        return self.binding_
     652
     653
     654class DomainData:
     655    def __init__(self, json_domain):
     656        self.json_domain = json_domain
     657        self.types_ = []
     658
     659    def add_type(self, type_data):
     660        self.types_.append(type_data)
     661
     662    def name(self):
     663        return self.json_domain["domain"]
     664
     665    def types(self):
     666        return self.types_
    379667
    380668
     
    382670    def __init__(self, api):
    383671        self.map_ = {}
     672        self.domains_ = []
    384673        for json_domain in api["domains"]:
    385674            domain_name = json_domain["domain"]
     
    387676            domain_map = {}
    388677            self.map_[domain_name] = domain_map
     678
     679            domain_data = DomainData(json_domain)
     680            self.domains_.append(domain_data)
    389681
    390682            if "types" in json_domain:
    391683                for json_type in json_domain["types"]:
    392684                    type_name = json_type["id"]
    393                     type_data = TypeData(json_type, json_domain)
     685                    type_data = TypeData(json_type, json_domain, domain_data)
    394686                    domain_map[type_name] = type_data
     687                    domain_data.add_type(type_data)
     688
     689    def domains(self):
     690        return self.domains_
    395691
    396692    def get(self, domain_name, type_name):
     
    428724
    429725class Templates:
     726    def get_this_script_path_(absolute_path):
     727        absolute_path = os.path.abspath(absolute_path)
     728        components = []
     729
     730        def fill_recursive(path_part, depth):
     731            if depth <= 0 or path_part == '/':
     732                return
     733            fill_recursive(os.path.dirname(path_part), depth - 1)
     734            components.append(os.path.basename(path_part))
     735
     736        # Typical path is /Source/WebCore/inspector/CodeGeneratorInspector.py
     737        # Let's take 4 components from the real path then.
     738        fill_recursive(absolute_path, 4)
     739
     740        return "/".join(components)
     741
     742    file_header_ = ("// File is generated by %s\n\n" % get_this_script_path_(sys.argv[0]) +
     743"""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
     744// Use of this source code is governed by a BSD-style license that can be
     745// found in the LICENSE file.
     746""")
     747
     748
     749
    430750    frontend_domain_class = string.Template(
    431751"""    class $domainClassName {
     
    469789""")
    470790
    471     frontend_h = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    472 // Use of this source code is governed by a BSD-style license that can be
    473 // found in the LICENSE file.
    474 #ifndef InspectorFrontend_h
     791    frontend_h = string.Template(file_header_ +
     792"""#ifndef InspectorFrontend_h
    475793#define InspectorFrontend_h
    476794
     795#include "InspectorValues.h"
    477796#include <PlatformString.h>
    478797#include <wtf/PassRefPtr.h>
     
    480799namespace WebCore {
    481800
    482 class InspectorArray;
    483801class InspectorFrontendChannel;
    484 class InspectorObject;
    485802
    486803typedef String ErrorString;
     804
     805namespace TypeBuilder {
     806${typeBuilders}
     807} // namespace TypeBuilder
    487808
    488809class InspectorFrontend {
     
    500821""")
    501822
    502     backend_h = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    503 // Use of this source code is governed by a BSD-style license that can be
    504 // found in the LICENSE file.
    505 #ifndef InspectorBackendDispatcher_h
     823    backend_h = string.Template(file_header_ +
     824"""#ifndef InspectorBackendDispatcher_h
    506825#define InspectorBackendDispatcher_h
    507826
     
    572891""")
    573892
    574     backend_cpp = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    575 // Use of this source code is governed by a BSD-style license that can be
    576 // found in the LICENSE file.
    577 
     893    backend_cpp = string.Template(file_header_ +
     894"""
    578895
    579896#include "config.h"
     
    9031220""")
    9041221
    905     frontend_cpp = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    906 // Use of this source code is governed by a BSD-style license that can be
    907 // found in the LICENSE file.
    908 
     1222    frontend_cpp = string.Template(file_header_ +
     1223"""
    9091224
    9101225#include "config.h"
     
    9351250""")
    9361251
    937     backend_js = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    938 // Use of this source code is governed by a BSD-style license that can be
    939 // found in the LICENSE file.
    940 
     1252    backend_js = string.Template(file_header_ +
     1253"""
    9411254
    9421255InspectorBackendStub = function()
     
    12131526    frontend_constructor_init_list = []
    12141527    frontend_capabilities_constants_list = []
     1528    type_builder_fragments = []
    12151529
    12161530    @staticmethod
    12171531    def go():
     1532        Generator.process_types(type_map)
     1533
    12181534        for json_domain in json_api["domains"]:
    12191535            domain_name = json_domain["domain"]
     
    14101726        Generator.backend_js_initializer_list.append("    this._registerDelegate('{\"method\": \"%s.%s\"%s, \"id\": 0}');\n" % (domain_name, json_command_name, js_parameters_text))
    14111727
     1728    @staticmethod
     1729    def process_types(type_map):
     1730        output = Generator.type_builder_fragments
     1731
     1732        class ForwardListener:
     1733            pass
     1734
     1735        for domain_data in type_map.domains():
     1736            output.append("namespace ")
     1737            output.append(domain_data.name())
     1738            output.append(" {\n")
     1739            for type_data in domain_data.types():
     1740                type_data.get_binding().generate_type_builder(output, ForwardListener)
     1741
     1742            output.append("} // ")
     1743            output.append(domain_data.name())
     1744            output.append("\n")
     1745
    14121746Generator.go()
    14131747
     
    14231757frontend_h_file.write(Templates.frontend_h.substitute(None,
    14241758         fieldDeclarations=join(Generator.frontend_class_field_lines, ""),
    1425          domainClassList=join(Generator.frontend_domain_class_lines, "")))
     1759         domainClassList=join(Generator.frontend_domain_class_lines, ""),
     1760         typeBuilders=join(Generator.type_builder_fragments, "")))
    14261761
    14271762backend_h_file.write(Templates.backend_h.substitute(None,
  • trunk/Source/WebCore/inspector/InspectorValues.h

    r99683 r101249  
    207207    const_iterator end() const { return m_data.end(); }
    208208
    209 private:
     209protected:
    210210    InspectorObject();
     211
     212private:
    211213    Dictionary m_data;
    212214    Vector<String> m_order;
Note: See TracChangeset for help on using the changeset viewer.