Changeset 102004 in webkit


Ignore:
Timestamp:
Dec 5, 2011 8:56:01 AM (12 years ago)
Author:
commit-queue@webkit.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-12-05
Reviewed by Yury Semikhatsky.

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

    r102003 r102004  
     12011-12-05  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 Yury Semikhatsky.
     7
     8        Extends python generator functionality.
     9        Makes constructor in InspectorObject public.
     10
     11        * inspector/CodeGeneratorInspector.py:
     12        * inspector/InspectorValues.h:
     13
    1142011-12-05  Pavel Podivilov  <podivilov@chromium.org>
    215
  • trunk/Source/WebCore/inspector/CodeGeneratorInspector.py

    r101756 r102004  
    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
     
    187234        elif json_type == "number":
    188235            return RawTypes.Number
     236        elif json_type == "any":
     237            return RawTypes.Any
    189238        else:
    190239            raise Exception("Unknown type: %s" % json_type)
     
    202251        @classmethod
    203252        def get_c_param_type(cls, param_type, optional):
    204             if param_type == ParamType.EVENT:
     253            if param_type == ParamType.EVENT or param_type == ParamType.TYPE_BUILDER_OUTPUT:
    205254                return cls._ref_c_type
    206255            else:
     
    297346        @classmethod
    298347        def get_c_param_type(cls, param_type, optional):
    299             if param_type == ParamType.EVENT:
     348            if param_type == ParamType.EVENT or param_type == ParamType.TYPE_BUILDER_OUTPUT:
    300349                return cls._ref_c_type
    301350            else:
     
    322371        _plain_c_type = CParamType("RefPtr<InspectorObject>")
    323372        _ref_c_type = CParamType("PassRefPtr<InspectorObject>")
     373
     374    class Any(BaseType):
     375        @classmethod
     376        def get_c_param_type(cls, param_type, optional):
     377            if param_type == ParamType.EVENT or param_type == ParamType.TYPE_BUILDER_OUTPUT:
     378                return cls._ref_c_type
     379            else:
     380                return cls._plain_c_type
     381
     382        @staticmethod
     383        def get_getter_name():
     384            return "Value"
     385
     386        get_setter_name = get_getter_name
     387
     388        @staticmethod
     389        def get_c_initializer():
     390            return "InspectorValue::create()"
     391
     392        @staticmethod
     393        def get_js_bind_type():
     394            raise Exception("Unsupported")
     395
     396        @staticmethod
     397        def is_event_param_check_optional():
     398            return True
     399
     400        _plain_c_type = CParamType("RefPtr<InspectorValue>")
     401        _ref_c_type = CParamType("PassRefPtr<InspectorValue>")
    324402
    325403    class Array(BaseType):
     
    359437    OUTPUT = "output"
    360438    EVENT = "event"
     439    TYPE_BUILDER_OUTPUT = "typeBuilderOutput"
     440
     441# Collection of InspectorObject class methods that are likely to be overloaded in generated class.
     442# We must explicitly import all overloaded methods or they won't be available to user.
     443INSPECTOR_OBJECT_SETTER_NAMES = frozenset(["setValue", "setBoolean", "setNumber", "setString", "setValue", "setObject", "setArray"])
     444
     445
     446class TypeBindings:
     447    @staticmethod
     448    def create_for_named_type_declaration(json_type, context_domain_name):
     449        if json_type["type"] == "string":
     450            if "enum" in json_type:
     451
     452                class EnumBinding:
     453                    @staticmethod
     454                    def generate_type_builder(output, forward_listener):
     455                        enum = json_type["enum"]
     456                        # TODO: doc
     457                        output.append("namespace ")
     458                        output.append(json_type["id"])
     459                        output.append(" {\n")
     460                        for enum_item in enum:
     461                            item_c_name = enum_item.replace('-', '_')
     462                            output.append("const char* const ")
     463                            output.append(Capitalizer.upper_camel_case_to_lower(item_c_name))
     464                            output.append(" = \"")
     465                            output.append(enum_item)
     466                            output.append("\";\n")
     467                        output.append("} // namespace ")
     468                        output.append(json_type["id"])
     469                        output.append("\n\n")
     470
     471                return EnumBinding
     472            else:
     473
     474                class PlainString:
     475                    @staticmethod
     476                    def generate_type_builder(output, forward_listener):
     477                        if "description" in json_type:
     478                            output.append("/* ")
     479                            output.append(json_type["description"])
     480                            output.append(" */\n")
     481                        output.append("typedef String ")
     482                        output.append(json_type["id"])
     483                        output.append(";\n\n")
     484                return PlainString
     485
     486        elif json_type["type"] == "object":
     487            if "properties" in json_type:
     488
     489                class ClassBinding:
     490                    @staticmethod
     491                    def generate_type_builder(output, forward_listener):
     492                        # TODO: doc
     493                        output.append("class ")
     494                        class_name = json_type["id"]
     495                        output.append(class_name)
     496                        output.append(" : public InspectorObject {\n")
     497                        output.append("public:\n")
     498
     499                        properties = json_type["properties"]
     500                        main_properties = []
     501                        optional_properties = []
     502                        for p in properties:
     503                            if "optional" in p and p["optional"]:
     504                                optional_properties.append(p)
     505                            else:
     506                                main_properties.append(p)
     507
     508                        output.append(
     509"""    enum {
     510        NO_FIELDS_SET = 0,
     511""")
     512
     513                        state_enum_items = []
     514                        if len(main_properties) > 0:
     515                            pos = 0
     516                            for p in main_properties:
     517                                item_name = Capitalizer.camel_case_to_capitalized_with_underscores(p["name"]) + "_SET"
     518                                state_enum_items.append(item_name)
     519                                output.append("        %s = 1 << %s,\n" % (item_name, pos))
     520                                pos += 1
     521                            all_fields_set_value = "(" + (" | ".join(state_enum_items)) + ")"
     522                        else:
     523                            all_fields_set_value = "0"
     524
     525                        output.append(
     526"""        ALL_FIELDS_SET = %s
     527    };
     528
     529    template<int STATE>
     530    class Builder {
     531    private:
     532        RefPtr<InspectorObject> m_result;
     533
     534        template<int STEP> Builder<STATE | STEP>& castState()
     535        {
     536            return *reinterpret_cast<Builder<STATE | STEP>*>(this);
     537        }
     538
     539        Builder(PassRefPtr<%s> ptr)
     540        {
     541            COMPILE_ASSERT(STATE == NO_FIELDS_SET, builder_created_in_non_init_state);
     542            m_result = ptr;
     543        }
     544        friend class %s;
     545    public:
     546""" % (all_fields_set_value, class_name, class_name))
     547
     548                        pos = 0
     549                        for prop in main_properties:
     550                            prop_name = prop["name"]
     551                            param_raw_type = resolve_param_raw_type(prop, context_domain_name)
     552                            output.append("""
     553        Builder<STATE | %s>& set%s(%s value)
     554        {
     555            COMPILE_ASSERT(!(STATE & %s), property_%s_already_set);
     556            m_result->set%s("%s", value);
     557            return castState<%s>();
     558        }
     559"""
     560                            % (state_enum_items[pos],
     561                               Capitalizer.lower_camel_case_to_upper(prop_name),
     562                               param_raw_type.get_c_param_type(ParamType.TYPE_BUILDER_OUTPUT, False).get_text(),
     563                               state_enum_items[pos], prop_name,
     564                               param_raw_type.get_setter_name(), prop_name, state_enum_items[pos]))
     565
     566                            pos += 1
     567
     568                        output.append("""
     569        operator RefPtr<%s>& ()
     570        {
     571            COMPILE_ASSERT(STATE == ALL_FIELDS_SET, result_is_not_ready);
     572            return *reinterpret_cast<RefPtr<%s>*>(&m_result);
     573        }
     574
     575        operator PassRefPtr<%s> ()
     576        {
     577            return RefPtr<%s>(*this);
     578        }
     579    };
     580
     581"""
     582                        % (class_name, class_name, class_name, class_name))
     583
     584                        output.append("    /*\n")
     585                        output.append("     * Synthetic constructor:\n")
     586                        output.append("     * RefPtr<%s> result = %s::create()" % (class_name, class_name))
     587                        for prop in main_properties:
     588                            output.append("\n     *     .set%s(...)" % Capitalizer.lower_camel_case_to_upper(prop["name"]))
     589                        output.append(";\n     */\n")
     590
     591                        output.append(
     592"""    static Builder<NO_FIELDS_SET> create()
     593    {
     594        return Builder<NO_FIELDS_SET>(adoptRef(new %s()));
     595    }
     596""" % class_name)
     597
     598                        for prop in optional_properties:
     599                            param_raw_type = resolve_param_raw_type(prop, context_domain_name)
     600                            setter_name = "set%s" % Capitalizer.lower_camel_case_to_upper(prop["name"])
     601                            output.append("\n    void %s" % setter_name)
     602                            output.append("(%s value)\n" % param_raw_type.get_c_param_type(ParamType.TYPE_BUILDER_OUTPUT, False).get_text())
     603                            output.append("    {\n")
     604                            output.append("        this->set%s(\"%s\", value);\n" % (param_raw_type.get_setter_name(), prop["name"]))
     605                            output.append("    }\n")
     606
     607                            if setter_name in INSPECTOR_OBJECT_SETTER_NAMES:
     608                                output.append("    using InspectorObject::%s;\n\n" % setter_name)
     609
     610                        output.append("};\n\n")
     611                return ClassBinding
     612            else:
     613
     614                class PlainObjectBinding:
     615                    @staticmethod
     616                    def generate_type_builder(output, forward_listener):
     617                        # No-op
     618                        pass
     619                return PlainObjectBinding
     620        else:
     621            raw_type = RawTypes.get(json_type["type"])
     622
     623            class RawTypesBinding:
     624                @staticmethod
     625                def generate_type_builder(output, forward_listener):
     626                    # No-op
     627                    pass
     628            return RawTypesBinding
    361629
    362630
    363631class TypeData(object):
    364     def __init__(self, json_type, json_domain):
     632    def __init__(self, json_type, json_domain, domain_data):
    365633        self.json_type_ = json_type
    366634        self.json_domain_ = json_domain
    367 
    368         if "type" in json_type:
    369             json_type_name = json_type["type"]
    370             raw_type = RawTypes.get(json_type_name)
    371         else:
     635        self.domain_data_ = domain_data
     636
     637        if "type" not in json_type:
    372638            raise Exception("Unknown type")
     639
     640        json_type_name = json_type["type"]
     641        raw_type = RawTypes.get(json_type_name)
    373642        self.raw_type_ = raw_type
     643        self.binding_ = TypeBindings.create_for_named_type_declaration(json_type, json_domain["domain"])
    374644
    375645    def get_raw_type(self):
    376646        return self.raw_type_
     647
     648    def get_binding(self):
     649        return self.binding_
     650
     651
     652class DomainData:
     653    def __init__(self, json_domain):
     654        self.json_domain = json_domain
     655        self.types_ = []
     656
     657    def add_type(self, type_data):
     658        self.types_.append(type_data)
     659
     660    def name(self):
     661        return self.json_domain["domain"]
     662
     663    def types(self):
     664        return self.types_
    377665
    378666
     
    380668    def __init__(self, api):
    381669        self.map_ = {}
     670        self.domains_ = []
    382671        for json_domain in api["domains"]:
    383672            domain_name = json_domain["domain"]
     
    385674            domain_map = {}
    386675            self.map_[domain_name] = domain_map
     676
     677            domain_data = DomainData(json_domain)
     678            self.domains_.append(domain_data)
    387679
    388680            if "types" in json_domain:
    389681                for json_type in json_domain["types"]:
    390682                    type_name = json_type["id"]
    391                     type_data = TypeData(json_type, json_domain)
     683                    type_data = TypeData(json_type, json_domain, domain_data)
    392684                    domain_map[type_name] = type_data
     685                    domain_data.add_type(type_data)
     686
     687    def domains(self):
     688        return self.domains_
    393689
    394690    def get(self, domain_name, type_name):
     
    426722
    427723class Templates:
     724    def get_this_script_path_(absolute_path):
     725        absolute_path = os.path.abspath(absolute_path)
     726        components = []
     727
     728        def fill_recursive(path_part, depth):
     729            if depth <= 0 or path_part == '/':
     730                return
     731            fill_recursive(os.path.dirname(path_part), depth - 1)
     732            components.append(os.path.basename(path_part))
     733
     734        # Typical path is /Source/WebCore/inspector/CodeGeneratorInspector.py
     735        # Let's take 4 components from the real path then.
     736        fill_recursive(absolute_path, 4)
     737
     738        return "/".join(components)
     739
     740    file_header_ = ("// File is generated by %s\n\n" % get_this_script_path_(sys.argv[0]) +
     741"""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
     742// Use of this source code is governed by a BSD-style license that can be
     743// found in the LICENSE file.
     744""")
     745
     746
     747
    428748    frontend_domain_class = string.Template(
    429749"""    class $domainClassName {
     
    467787""")
    468788
    469     frontend_h = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    470 // Use of this source code is governed by a BSD-style license that can be
    471 // found in the LICENSE file.
    472 #ifndef InspectorFrontend_h
     789    frontend_h = string.Template(file_header_ +
     790"""#ifndef InspectorFrontend_h
    473791#define InspectorFrontend_h
    474792
     793#include "InspectorValues.h"
    475794#include <PlatformString.h>
    476795#include <wtf/PassRefPtr.h>
     
    478797namespace WebCore {
    479798
    480 class InspectorArray;
    481799class InspectorFrontendChannel;
    482 class InspectorObject;
    483800
    484801typedef String ErrorString;
     802
     803namespace TypeBuilder {
     804${typeBuilders}
     805} // namespace TypeBuilder
    485806
    486807class InspectorFrontend {
     
    498819""")
    499820
    500     backend_h = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    501 // Use of this source code is governed by a BSD-style license that can be
    502 // found in the LICENSE file.
    503 #ifndef InspectorBackendDispatcher_h
     821    backend_h = string.Template(file_header_ +
     822"""#ifndef InspectorBackendDispatcher_h
    504823#define InspectorBackendDispatcher_h
    505824
     
    571890""")
    572891
    573     backend_cpp = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    574 // Use of this source code is governed by a BSD-style license that can be
    575 // found in the LICENSE file.
    576 
     892    backend_cpp = string.Template(file_header_ +
     893"""
    577894
    578895#include "config.h"
     
    9021219""")
    9031220
    904     frontend_cpp = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    905 // Use of this source code is governed by a BSD-style license that can be
    906 // found in the LICENSE file.
    907 
     1221    frontend_cpp = string.Template(file_header_ +
     1222"""
    9081223
    9091224#include "config.h"
     
    9321247""")
    9331248
    934     backend_js = string.Template("""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
    935 // Use of this source code is governed by a BSD-style license that can be
    936 // found in the LICENSE file.
     1249    backend_js = string.Template(file_header_ +
     1250"""
    9371251
    9381252$delegates
     
    9701284    backend_include_list = []
    9711285    frontend_constructor_init_list = []
     1286    type_builder_fragments = []
    9721287
    9731288    @staticmethod
    9741289    def go():
     1290        Generator.process_types(type_map)
     1291
    9751292        for json_domain in json_api["domains"]:
    9761293            domain_name = json_domain["domain"]
     
    11561473        Generator.backend_js_initializer_list.append("InspectorBackend.registerCommand(\"%s.%s\", [%s], %s);\n" % (domain_name, json_command_name, js_parameters_text, js_reply_list))
    11571474
     1475    @staticmethod
     1476    def process_types(type_map):
     1477        output = Generator.type_builder_fragments
     1478
     1479        class ForwardListener:
     1480            pass
     1481
     1482        for domain_data in type_map.domains():
     1483            output.append("namespace ")
     1484            output.append(domain_data.name())
     1485            output.append(" {\n")
     1486            for type_data in domain_data.types():
     1487                type_data.get_binding().generate_type_builder(output, ForwardListener)
     1488
     1489            output.append("} // ")
     1490            output.append(domain_data.name())
     1491            output.append("\n")
     1492
    11581493Generator.go()
    11591494
     
    11691504frontend_h_file.write(Templates.frontend_h.substitute(None,
    11701505         fieldDeclarations=join(Generator.frontend_class_field_lines, ""),
    1171          domainClassList=join(Generator.frontend_domain_class_lines, "")))
     1506         domainClassList=join(Generator.frontend_domain_class_lines, ""),
     1507         typeBuilders=join(Generator.type_builder_fragments, "")))
    11721508
    11731509backend_h_file.write(Templates.backend_h.substitute(None,
  • trunk/Source/WebCore/inspector/InspectorValues.h

    r101367 r102004  
    206206    const_iterator end() const { return m_data.end(); }
    207207
    208 private:
     208protected:
    209209    InspectorObject();
     210
     211private:
    210212    Dictionary m_data;
    211213    Vector<String> m_order;
Note: See TracChangeset for help on using the changeset viewer.