Changeset 102440 in webkit


Ignore:
Timestamp:
Dec 9, 2011 12:33:02 AM (12 years ago)
Author:
yurys@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-12-09
Reviewed by NOBODY.

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

    r102435 r102440  
     12011-12-09  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 NOBODY.
     7
     8        Extends python generator functionality.
     9        Makes constructor in InspectorObject public.
     10
     11        * inspector/CodeGeneratorInspector.py:
     12        * inspector/InspectorValues.h:
     13
    1142011-12-08  Viatcheslav Ostapenko  <ostapenko.viatcheslav@nokia.com>
    215
  • trunk/Source/WebCore/inspector/CodeGeneratorInspector.py

    r102005 r102440  
    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        return "_".join(output).upper()
     135
     136    @staticmethod
     137    def split_camel_case_(str):
     138        output = []
     139        pos_being = 0
     140        pos = 1
     141        has_oneletter = False
     142        while pos < len(str):
     143            if str[pos].isupper():
     144                output.append(str[pos_being:pos].upper())
     145                if pos - pos_being == 1:
     146                    has_oneletter = True
     147                pos_being = pos
     148            pos += 1
     149        output.append(str[pos_being:])
     150        if has_oneletter:
     151            array_pos = 0
     152            while array_pos < len(output) - 1:
     153                if len(output[array_pos]) == 1:
     154                    array_pos_end = array_pos + 1
     155                    while array_pos_end < len(output) and len(output[array_pos_end]) == 1:
     156                        array_pos_end += 1
     157                    if array_pos_end - array_pos > 1:
     158                        possible_abbreviation = "".join(output[array_pos:array_pos_end])
     159                        if possible_abbreviation.upper() in Capitalizer.ABBREVIATION:
     160                            output[array_pos:array_pos_end] = [possible_abbreviation]
     161                        else:
     162                            array_pos = array_pos_end - 1
     163                array_pos += 1
     164        return output
     165
    123166    ABBREVIATION = frozenset(["XHR", "DOM", "CSS"])
    124167
     
    187230        elif json_type == "number":
    188231            return RawTypes.Number
     232        elif json_type == "any":
     233            return RawTypes.Any
    189234        else:
    190235            raise Exception("Unknown type: %s" % json_type)
     
    202247        @classmethod
    203248        def get_c_param_type(cls, param_type, optional):
    204             if param_type == ParamType.EVENT:
     249            if param_type == ParamType.EVENT or param_type == ParamType.TYPE_BUILDER_OUTPUT:
    205250                return cls._ref_c_type
    206251            else:
     
    297342        @classmethod
    298343        def get_c_param_type(cls, param_type, optional):
    299             if param_type == ParamType.EVENT:
     344            if param_type == ParamType.EVENT or param_type == ParamType.TYPE_BUILDER_OUTPUT:
    300345                return cls._ref_c_type
    301346            else:
     
    322367        _plain_c_type = CParamType("RefPtr<InspectorObject>")
    323368        _ref_c_type = CParamType("PassRefPtr<InspectorObject>")
     369
     370    class Any(BaseType):
     371        @classmethod
     372        def get_c_param_type(cls, param_type, optional):
     373            if param_type == ParamType.EVENT or param_type == ParamType.TYPE_BUILDER_OUTPUT:
     374                return cls._ref_c_type
     375            else:
     376                return cls._plain_c_type
     377
     378        @staticmethod
     379        def get_getter_name():
     380            return "Value"
     381
     382        get_setter_name = get_getter_name
     383
     384        @staticmethod
     385        def get_c_initializer():
     386            return "InspectorValue::create()"
     387
     388        @staticmethod
     389        def get_js_bind_type():
     390            raise Exception("Unsupported")
     391
     392        @staticmethod
     393        def is_event_param_check_optional():
     394            return True
     395
     396        _plain_c_type = CParamType("RefPtr<InspectorValue>")
     397        _ref_c_type = CParamType("PassRefPtr<InspectorValue>")
    324398
    325399    class Array(BaseType):
     
    359433    OUTPUT = "output"
    360434    EVENT = "event"
     435    TYPE_BUILDER_OUTPUT = "typeBuilderOutput"
     436
     437# Collection of InspectorObject class methods that are likely to be overloaded in generated class.
     438# We must explicitly import all overloaded methods or they won't be available to user.
     439INSPECTOR_OBJECT_SETTER_NAMES = frozenset(["setValue", "setBoolean", "setNumber", "setString", "setValue", "setObject", "setArray"])
     440
     441
     442class TypeBindings:
     443    @staticmethod
     444    def create_for_named_type_declaration(json_type, context_domain_name):
     445        if json_type["type"] == "string":
     446            if "enum" in json_type:
     447
     448                class EnumBinding:
     449                    @staticmethod
     450                    def generate_type_builder(output, forward_listener):
     451                        enum = json_type["enum"]
     452                        # TODO: doc
     453                        output.append("namespace ")
     454                        output.append(json_type["id"])
     455                        output.append(" {\n")
     456                        for enum_item in enum:
     457                            item_c_name = enum_item.replace('-', '_')
     458                            output.append("const char* const ")
     459                            output.append(Capitalizer.upper_camel_case_to_lower(item_c_name))
     460                            output.append(" = \"")
     461                            output.append(enum_item)
     462                            output.append("\";\n")
     463                        output.append("} // namespace ")
     464                        output.append(json_type["id"])
     465                        output.append("\n\n")
     466
     467                return EnumBinding
     468            else:
     469
     470                class PlainString:
     471                    @staticmethod
     472                    def generate_type_builder(output, forward_listener):
     473                        if "description" in json_type:
     474                            output.append("/* ")
     475                            output.append(json_type["description"])
     476                            output.append(" */\n")
     477                        output.append("typedef String ")
     478                        output.append(json_type["id"])
     479                        output.append(";\n\n")
     480                return PlainString
     481
     482        elif json_type["type"] == "object":
     483            if "properties" in json_type:
     484
     485                class ClassBinding:
     486                    @staticmethod
     487                    def generate_type_builder(output, forward_listener):
     488                        # TODO: doc
     489                        output.append("class ")
     490                        class_name = json_type["id"]
     491                        output.append(class_name)
     492                        output.append(" : public InspectorObject {\n")
     493                        output.append("public:\n")
     494
     495                        properties = json_type["properties"]
     496                        main_properties = []
     497                        optional_properties = []
     498                        for p in properties:
     499                            if "optional" in p and p["optional"]:
     500                                optional_properties.append(p)
     501                            else:
     502                                main_properties.append(p)
     503
     504                        output.append(
     505"""    enum {
     506        NO_FIELDS_SET = 0,
     507""")
     508
     509                        state_enum_items = []
     510                        if len(main_properties) > 0:
     511                            pos = 0
     512                            for p in main_properties:
     513                                item_name = Capitalizer.camel_case_to_capitalized_with_underscores(p["name"]) + "_SET"
     514                                state_enum_items.append(item_name)
     515                                output.append("        %s = 1 << %s,\n" % (item_name, pos))
     516                                pos += 1
     517                            all_fields_set_value = "(" + (" | ".join(state_enum_items)) + ")"
     518                        else:
     519                            all_fields_set_value = "0"
     520
     521                        output.append(
     522"""        ALL_FIELDS_SET = %s
     523    };
     524
     525    template<int STATE>
     526    class Builder {
     527    private:
     528        RefPtr<InspectorObject> m_result;
     529
     530        template<int STEP> Builder<STATE | STEP>& castState()
     531        {
     532            return *reinterpret_cast<Builder<STATE | STEP>*>(this);
     533        }
     534
     535        Builder(PassRefPtr<%s> ptr)
     536        {
     537            COMPILE_ASSERT(STATE == NO_FIELDS_SET, builder_created_in_non_init_state);
     538            m_result = ptr;
     539        }
     540        friend class %s;
     541    public:
     542""" % (all_fields_set_value, class_name, class_name))
     543
     544                        pos = 0
     545                        for prop in main_properties:
     546                            prop_name = prop["name"]
     547                            param_raw_type = resolve_param_raw_type(prop, context_domain_name)
     548                            output.append("""
     549        Builder<STATE | %s>& set%s(%s value)
     550        {
     551            COMPILE_ASSERT(!(STATE & %s), property_%s_already_set);
     552            m_result->set%s("%s", value);
     553            return castState<%s>();
     554        }
     555"""
     556                            % (state_enum_items[pos],
     557                               Capitalizer.lower_camel_case_to_upper(prop_name),
     558                               param_raw_type.get_c_param_type(ParamType.TYPE_BUILDER_OUTPUT, False).get_text(),
     559                               state_enum_items[pos], prop_name,
     560                               param_raw_type.get_setter_name(), prop_name, state_enum_items[pos]))
     561
     562                            pos += 1
     563
     564                        output.append("""
     565        operator RefPtr<%s>& ()
     566        {
     567            COMPILE_ASSERT(STATE == ALL_FIELDS_SET, result_is_not_ready);
     568            return *reinterpret_cast<RefPtr<%s>*>(&m_result);
     569        }
     570
     571        operator PassRefPtr<%s> ()
     572        {
     573            return RefPtr<%s>(*this);
     574        }
     575    };
     576
     577"""
     578                        % (class_name, class_name, class_name, class_name))
     579
     580                        output.append("    /*\n")
     581                        output.append("     * Synthetic constructor:\n")
     582                        output.append("     * RefPtr<%s> result = %s::create()" % (class_name, class_name))
     583                        for prop in main_properties:
     584                            output.append("\n     *     .set%s(...)" % Capitalizer.lower_camel_case_to_upper(prop["name"]))
     585                        output.append(";\n     */\n")
     586
     587                        output.append(
     588"""    static Builder<NO_FIELDS_SET> create()
     589    {
     590        return Builder<NO_FIELDS_SET>(adoptRef(new %s()));
     591    }
     592""" % class_name)
     593
     594                        for prop in optional_properties:
     595                            param_raw_type = resolve_param_raw_type(prop, context_domain_name)
     596                            setter_name = "set%s" % Capitalizer.lower_camel_case_to_upper(prop["name"])
     597                            output.append("\n    void %s" % setter_name)
     598                            output.append("(%s value)\n" % param_raw_type.get_c_param_type(ParamType.TYPE_BUILDER_OUTPUT, False).get_text())
     599                            output.append("    {\n")
     600                            output.append("        this->set%s(\"%s\", value);\n" % (param_raw_type.get_setter_name(), prop["name"]))
     601                            output.append("    }\n")
     602
     603                            if setter_name in INSPECTOR_OBJECT_SETTER_NAMES:
     604                                output.append("    using InspectorObject::%s;\n\n" % setter_name)
     605
     606                        output.append("};\n\n")
     607                return ClassBinding
     608            else:
     609
     610                class PlainObjectBinding:
     611                    @staticmethod
     612                    def generate_type_builder(output, forward_listener):
     613                        # No-op
     614                        pass
     615                return PlainObjectBinding
     616        else:
     617            raw_type = RawTypes.get(json_type["type"])
     618
     619            class RawTypesBinding:
     620                @staticmethod
     621                def generate_type_builder(output, forward_listener):
     622                    # No-op
     623                    pass
     624            return RawTypesBinding
    361625
    362626
    363627class TypeData(object):
    364     def __init__(self, json_type, json_domain):
     628    def __init__(self, json_type, json_domain, domain_data):
    365629        self.json_type_ = json_type
    366630        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:
     631        self.domain_data_ = domain_data
     632
     633        if "type" not in json_type:
    372634            raise Exception("Unknown type")
     635
     636        json_type_name = json_type["type"]
     637        raw_type = RawTypes.get(json_type_name)
    373638        self.raw_type_ = raw_type
     639        self.binding_ = TypeBindings.create_for_named_type_declaration(json_type, json_domain["domain"])
    374640
    375641    def get_raw_type(self):
    376642        return self.raw_type_
     643
     644    def get_binding(self):
     645        return self.binding_
     646
     647
     648class DomainData:
     649    def __init__(self, json_domain):
     650        self.json_domain = json_domain
     651        self.types_ = []
     652
     653    def add_type(self, type_data):
     654        self.types_.append(type_data)
     655
     656    def name(self):
     657        return self.json_domain["domain"]
     658
     659    def types(self):
     660        return self.types_
    377661
    378662
     
    380664    def __init__(self, api):
    381665        self.map_ = {}
     666        self.domains_ = []
    382667        for json_domain in api["domains"]:
    383668            domain_name = json_domain["domain"]
     
    385670            domain_map = {}
    386671            self.map_[domain_name] = domain_map
     672
     673            domain_data = DomainData(json_domain)
     674            self.domains_.append(domain_data)
    387675
    388676            if "types" in json_domain:
    389677                for json_type in json_domain["types"]:
    390678                    type_name = json_type["id"]
    391                     type_data = TypeData(json_type, json_domain)
     679                    type_data = TypeData(json_type, json_domain, domain_data)
    392680                    domain_map[type_name] = type_data
     681                    domain_data.add_type(type_data)
     682
     683    def domains(self):
     684        return self.domains_
    393685
    394686    def get(self, domain_name, type_name):
     
    426718
    427719class Templates:
     720    def get_this_script_path_(absolute_path):
     721        absolute_path = os.path.abspath(absolute_path)
     722        components = []
     723
     724        def fill_recursive(path_part, depth):
     725            if depth <= 0 or path_part == '/':
     726                return
     727            fill_recursive(os.path.dirname(path_part), depth - 1)
     728            components.append(os.path.basename(path_part))
     729
     730        # Typical path is /Source/WebCore/inspector/CodeGeneratorInspector.py
     731        # Let's take 4 components from the real path then.
     732        fill_recursive(absolute_path, 4)
     733
     734        return "/".join(components)
     735
     736    file_header_ = ("// File is generated by %s\n\n" % get_this_script_path_(sys.argv[0]) +
     737"""// Copyright (c) 2011 The Chromium Authors. All rights reserved.
     738// Use of this source code is governed by a BSD-style license that can be
     739// found in the LICENSE file.
     740""")
     741
     742
     743
    428744    frontend_domain_class = string.Template(
    429745"""    class $domainClassName {
     
    467783""")
    468784
    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
     785    frontend_h = string.Template(file_header_ +
     786"""#ifndef InspectorFrontend_h
    473787#define InspectorFrontend_h
    474788
     789#include "InspectorValues.h"
    475790#include <PlatformString.h>
    476791#include <wtf/PassRefPtr.h>
     
    478793namespace WebCore {
    479794
     795class InspectorFrontendChannel;
     796
     797// Both InspectorObject and InspectorArray may or may not be declared at this point as defined by ENABLED_INSPECTOR.
     798// Double-check we have them at least as forward declaration.
    480799class InspectorArray;
    481 class InspectorFrontendChannel;
    482800class InspectorObject;
    483801
    484802typedef String ErrorString;
     803
     804#if ENABLE(INSPECTOR)
     805
     806namespace TypeBuilder {
     807${typeBuilders}
     808} // namespace TypeBuilder
     809
     810#endif // ENABLE(INSPECTOR)
    485811
    486812class InspectorFrontend {
     
    498824""")
    499825
    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
     826    backend_h = string.Template(file_header_ +
     827"""#ifndef InspectorBackendDispatcher_h
    504828#define InspectorBackendDispatcher_h
    505829
     
    571895""")
    572896
    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 
     897    backend_cpp = string.Template(file_header_ +
     898"""
    577899
    578900#include "config.h"
     
    9021224""")
    9031225
    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 
     1226    frontend_cpp = string.Template(file_header_ +
     1227"""
    9081228
    9091229#include "config.h"
     
    9321252""")
    9331253
    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.
     1254    backend_js = string.Template(file_header_ +
     1255"""
    9371256
    9381257$delegates
     
    9701289    backend_include_list = []
    9711290    frontend_constructor_init_list = []
     1291    type_builder_fragments = []
    9721292
    9731293    @staticmethod
    9741294    def go():
     1295        Generator.process_types(type_map)
     1296
    9751297        for json_domain in json_api["domains"]:
    9761298            domain_name = json_domain["domain"]
     
    11561478        Generator.backend_js_initializer_list.append("InspectorBackend.registerCommand(\"%s.%s\", [%s], %s);\n" % (domain_name, json_command_name, js_parameters_text, js_reply_list))
    11571479
     1480    @staticmethod
     1481    def process_types(type_map):
     1482        output = Generator.type_builder_fragments
     1483
     1484        class ForwardListener:
     1485            pass
     1486
     1487        for domain_data in type_map.domains():
     1488            output.append("namespace ")
     1489            output.append(domain_data.name())
     1490            output.append(" {\n")
     1491            for type_data in domain_data.types():
     1492                type_data.get_binding().generate_type_builder(output, ForwardListener)
     1493
     1494            output.append("} // ")
     1495            output.append(domain_data.name())
     1496            output.append("\n")
     1497
    11581498Generator.go()
    11591499
     
    11691509frontend_h_file.write(Templates.frontend_h.substitute(None,
    11701510         fieldDeclarations=join(Generator.frontend_class_field_lines, ""),
    1171          domainClassList=join(Generator.frontend_domain_class_lines, "")))
     1511         domainClassList=join(Generator.frontend_domain_class_lines, ""),
     1512         typeBuilders=join(Generator.type_builder_fragments, "")))
    11721513
    11731514backend_h_file.write(Templates.backend_h.substitute(None,
  • trunk/Source/WebCore/inspector/InspectorValues.h

    r102005 r102440  
    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.