diff options
-rw-r--r-- | build/json_schema_compile.gypi | 41 | ||||
-rw-r--r-- | chrome/browser/extensions/api/dns/dns_api.cc | 22 | ||||
-rw-r--r-- | chrome/browser/extensions/api/dns/dns_api.h | 3 | ||||
-rw-r--r-- | chrome/browser/extensions/api/dns/dns_apitest.cc | 8 | ||||
-rw-r--r-- | chrome/common/extensions/api/api.gyp | 4 | ||||
-rw-r--r-- | chrome/common/extensions/api/experimental.dns.idl | 28 | ||||
-rw-r--r-- | tools/json_schema_compiler/compiler.py | 16 | ||||
-rw-r--r-- | tools/json_schema_compiler/idl_schema.py | 211 | ||||
-rw-r--r-- | tools/json_schema_compiler/json_schema.py | 2 | ||||
-rw-r--r-- | tools/json_schema_compiler/model.py | 3 |
10 files changed, 315 insertions, 23 deletions
diff --git a/build/json_schema_compile.gypi b/build/json_schema_compile.gypi index 3abecf3..b44fe91 100644 --- a/build/json_schema_compile.gypi +++ b/build/json_schema_compile.gypi @@ -23,9 +23,48 @@ '<(api_gen_dir)/cc_generator.py', '<(api_gen_dir)/code.py', '<(api_gen_dir)/compiler.py', + '<(api_gen_dir)/cpp_type_generator.py', '<(api_gen_dir)/cpp_util.py', + '<(api_gen_dir)/h_generator.py', + '<(api_gen_dir)/json_schema.py', + '<(api_gen_dir)/model.py', + '<(api_gen_dir)/util.cc', + '<(api_gen_dir)/util.h', + '<(api_gen_dir)/util_cc_helper.py', + # TODO(calamity): uncomment this when gyp on windows behaves like other + # platforms. List expansions of filepaths in inputs expand to different + # things. + # '<@(json_schema_files)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/<(RULE_INPUT_ROOT).cc', + '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/<(RULE_INPUT_ROOT).h', + ], + 'action': [ + 'python', + '<(api_gen)', + '<(RULE_INPUT_PATH)', + '--root=<(DEPTH)', + '--destdir=<(SHARED_INTERMEDIATE_DIR)', + '--namespace=<(root_namespace)', + ], + 'message': 'Generating C++ code from <(RULE_INPUT_PATH) json files', + 'process_outputs_as_sources': 1, + }, + { + 'rule_name': 'genapi_idl', + 'extension': 'idl', + 'inputs': [ + '<(api_gen_dir)/any.cc', + '<(api_gen_dir)/any.h', + '<(api_gen_dir)/any_helper.py', + '<(api_gen_dir)/cc_generator.py', + '<(api_gen_dir)/code.py', + '<(api_gen_dir)/compiler.py', '<(api_gen_dir)/cpp_type_generator.py', + '<(api_gen_dir)/cpp_util.py', '<(api_gen_dir)/h_generator.py', + '<(api_gen_dir)/idl_schema.py', '<(api_gen_dir)/model.py', '<(api_gen_dir)/util.cc', '<(api_gen_dir)/util.h', @@ -47,7 +86,7 @@ '--destdir=<(SHARED_INTERMEDIATE_DIR)', '--namespace=<(root_namespace)', ], - 'message': 'Generating C++ code from <(RULE_INPUT_PATH) jsons', + 'message': 'Generating C++ code from <(RULE_INPUT_PATH) IDL files', 'process_outputs_as_sources': 1, }, ], diff --git a/chrome/browser/extensions/api/dns/dns_api.cc b/chrome/browser/extensions/api/dns/dns_api.cc index 2ddbd69..bc74483 100644 --- a/chrome/browser/extensions/api/dns/dns_api.cc +++ b/chrome/browser/extensions/api/dns/dns_api.cc @@ -8,18 +8,17 @@ #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/io_thread.h" +#include "chrome/common/extensions/api/experimental.dns.h" #include "content/public/browser/browser_thread.h" #include "net/base/host_port_pair.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" using content::BrowserThread; +using namespace extensions::api::experimental; namespace extensions { -const char kAddressKey[] = "address"; -const char kResultCodeKey[] = "resultCode"; - // static net::HostResolver* DNSResolveFunction::host_resolver_for_testing; @@ -42,7 +41,11 @@ void DNSResolveFunction::set_host_resolver_for_testing( } bool DNSResolveFunction::RunImpl() { - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &hostname_)); + scoped_ptr<Resolve::Params> params(Resolve::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + hostname_ = params->hostname; + bool result = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&DNSResolveFunction::WorkOnIOThread, this)); @@ -77,14 +80,17 @@ void DNSResolveFunction::WorkOnIOThread() { } void DNSResolveFunction::OnLookupFinished(int resolve_result) { - DictionaryValue* api_result = new DictionaryValue(); - api_result->SetInteger(kResultCodeKey, resolve_result); + + scoped_ptr<ResolveCallbackResolveInfo> resolve_info( + new ResolveCallbackResolveInfo()); + resolve_info->result_code = resolve_result; if (resolve_result == net::OK) { const struct addrinfo* head = addresses_->head(); DCHECK(head); - api_result->SetString(kAddressKey, net::NetAddressToString(head)); + resolve_info->address.reset( + new std::string(net::NetAddressToString(head))); } - result_.reset(api_result); + result_.reset(Resolve::Result::Create(*resolve_info)); response_ = true; bool post_task_result = BrowserThread::PostTask( diff --git a/chrome/browser/extensions/api/dns/dns_api.h b/chrome/browser/extensions/api/dns/dns_api.h index 07c2c48..54a2002 100644 --- a/chrome/browser/extensions/api/dns/dns_api.h +++ b/chrome/browser/extensions/api/dns/dns_api.h @@ -19,9 +19,6 @@ class IOThread; namespace extensions { -extern const char kAddressKey[]; -extern const char kResultCodeKey[]; - class DNSResolveFunction : public AsyncExtensionFunction { public: DNSResolveFunction(); diff --git a/chrome/browser/extensions/api/dns/dns_apitest.cc b/chrome/browser/extensions/api/dns/dns_apitest.cc index d882aca..6614530 100644 --- a/chrome/browser/extensions/api/dns/dns_apitest.cc +++ b/chrome/browser/extensions/api/dns/dns_apitest.cc @@ -119,11 +119,11 @@ IN_PROC_BROWSER_TEST_F(DNSApiTest, DNSResolveIPLiteral) { DictionaryValue *value = static_cast<DictionaryValue*>(result.get()); int resultCode; - EXPECT_TRUE(value->GetInteger(extensions::kResultCodeKey, &resultCode)); + EXPECT_TRUE(value->GetInteger("resultCode", &resultCode)); EXPECT_EQ(net::OK, resultCode); std::string address; - EXPECT_TRUE(value->GetString(extensions::kAddressKey, &address)); + EXPECT_TRUE(value->GetString("address", &address)); EXPECT_EQ("127.0.0.1", address); } @@ -145,11 +145,11 @@ IN_PROC_BROWSER_TEST_F(DNSApiTest, DNSResolveHostname) { DictionaryValue *value = static_cast<DictionaryValue*>(result.get()); int resultCode; - EXPECT_TRUE(value->GetInteger(extensions::kResultCodeKey, &resultCode)); + EXPECT_TRUE(value->GetInteger("resultCode", &resultCode)); EXPECT_EQ(net::OK, resultCode); std::string address; - EXPECT_TRUE(value->GetString(extensions::kAddressKey, &address)); + EXPECT_TRUE(value->GetString("address", &address)); EXPECT_EQ(DNSApiTest::kAddress, address); } diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp index 896b31c..9c9d36c 100644 --- a/chrome/common/extensions/api/api.gyp +++ b/chrome/common/extensions/api/api.gyp @@ -9,6 +9,7 @@ 'type': 'static_library', 'sources': [ '<@(json_schema_files)', + '<@(idl_schema_files)', ], 'includes': ['../../../../build/json_schema_compile.gypi'], 'variables': { @@ -19,6 +20,9 @@ 'tabs.json', 'windows.json', ], + 'idl_schema_files': [ + 'experimental.dns.idl', + ], 'cc_dir': 'chrome/common/extensions/api', 'root_namespace': 'extensions::api', }, diff --git a/chrome/common/extensions/api/experimental.dns.idl b/chrome/common/extensions/api/experimental.dns.idl new file mode 100644 index 0000000..7fab5ad --- /dev/null +++ b/chrome/common/extensions/api/experimental.dns.idl @@ -0,0 +1,28 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// File-level comment to appease parser. Eventually this will not be necessary. + +[nodoc] namespace experimental.dns { + + dictionary ResolveCallbackResolveInfo { + // The result code. Zero indicates success. + long resultCode; + + // A string representing the IP address literal. Supplied only if resultCode + // indicates success. Note that we presently return only IPv4 addresses. + DOMString? address; + }; + + callback ResolveCallback = void (ResolveCallbackResolveInfo resolveInfo); + + interface Functions { + // Resolves the given hostname or IP address literal. + // |hostname| : The hostname to resolve. + // |callback| : Called when the resolution operation completes. + static void resolve(DOMString hostname, + ResolveCallback callback); + }; + +}; diff --git a/tools/json_schema_compiler/compiler.py b/tools/json_schema_compiler/compiler.py index fb84a6c..f782f72 100644 --- a/tools/json_schema_compiler/compiler.py +++ b/tools/json_schema_compiler/compiler.py @@ -19,7 +19,8 @@ Usage example: import cc_generator import cpp_type_generator import h_generator -from json_schema import LoadJSON +import idl_schema +import json_schema import model import optparse import os.path @@ -47,17 +48,24 @@ if __name__ == '__main__': api_model = model.Model() - # Actually generate for source file. - api_defs = LoadJSON(schema) + schema_filename, schema_extension = os.path.splitext(schema) + if schema_extension == '.json': + api_defs = json_schema.Load(schema) + elif schema_extension == '.idl': + api_defs = idl_schema.Load(schema) + else: + sys.exit("Did not recognize file extension %s for schema %s" % + (schema_extension, schema)) for target_namespace in api_defs: referenced_schemas = target_namespace.get('dependencies', []) # Load type dependencies into the model. + # TODO(miket): do we need this in IDL? for referenced_schema in referenced_schemas: referenced_schema_path = os.path.join( os.path.dirname(schema), referenced_schema + '.json') - referenced_api_defs = LoadJSON(referenced_schema_path) + referenced_api_defs = json_schema.Load(referenced_schema_path) for namespace in referenced_api_defs: api_model.AddNamespace(namespace, diff --git a/tools/json_schema_compiler/idl_schema.py b/tools/json_schema_compiler/idl_schema.py new file mode 100644 index 0000000..d64f111 --- /dev/null +++ b/tools/json_schema_compiler/idl_schema.py @@ -0,0 +1,211 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os.path +import sys + +# This file is a peer to json_schema.py. Each of these files understands a +# certain format describing APIs (either JSON or IDL), reads files written +# in that format into memory, and emits them as a Python array of objects +# corresponding to those APIs, where the objects are formatted in a way that +# the JSON schema compiler understands. compiler.py drives both idl_schema.py +# and json_schema.py. + +# idl_parser expects to be able to import certain files in its directory, +# so let's set things up the way it wants. +idl_generators_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, os.pardir, 'ppapi', 'generators') +if idl_generators_path not in sys.path: + sys.path.insert(0, idl_generators_path) +import idl_parser + +class Callspec(object): + ''' + Given a Callspec node representing an IDL function declaration, converts into + a name/value pair where the value is a list of function parameters. + ''' + def __init__(self, callspec_node): + self.node = callspec_node + + def process(self, refs): + parameters = [] + for node in self.node.children: + parameters.append(Param(node).process(refs)) + return self.node.GetName(), parameters + +class Param(object): + ''' + Given a Param node representing a function parameter, converts into a Python + dictionary that the JSON schema compiler expects to see. + ''' + def __init__(self, param_node): + self.node = param_node + + def process(self, refs): + return Typeref(self.node.GetProperty( 'TYPEREF'), + self.node, + { 'name': self.node.GetName() }).process(refs) + +class Dictionary(object): + ''' + Given an IDL Dictionary node, converts into a Python dictionary that the JSON + schema compiler expects to see. + ''' + def __init__(self, dictionary_node): + self.node = dictionary_node + + def process(self, refs): + properties = {} + for node in self.node.children: + if node.cls == 'Member': + k, v = Member(node).process(refs) + properties[k] = v + return { 'id': self.node.GetName(), + 'properties': properties, + 'type': 'object' } + +class Member(object): + ''' + Given an IDL dictionary or interface member, converts into a name/value pair + where the value is a Python dictionary that the JSON schema compiler expects + to see. + ''' + def __init__(self, member_node): + self.node = member_node + + def process(self, refs): + properties = {} + name = self.node.GetName() + if self.node.GetProperty('OPTIONAL'): + properties['optional'] = True + if self.node.GetProperty('nodoc'): + properties['nodoc'] = True + is_function = False + for node in self.node.children: + if node.cls == 'Callspec': + is_function = True + name, parameters = Callspec(node).process(refs) + properties['parameters'] = parameters + properties['name'] = name + if is_function: + properties['type'] = 'function' + else: + properties = Typeref(self.node.GetProperty('TYPEREF'), + self.node, properties).process(refs) + return name, properties + +class Typeref(object): + ''' + Given a TYPEREF property representing the type of dictionary member or + function parameter, converts into a Python dictionary that the JSON schema + compiler expects to see. + ''' + def __init__(self, typeref, parent, additional_properties={}): + self.typeref = typeref + self.parent = parent + self.additional_properties = additional_properties + + def process(self, refs): + properties = self.additional_properties + if self.typeref == 'DOMString': + properties['type'] = 'string' + elif self.typeref == 'boolean': + properties['type'] = 'boolean' + elif self.typeref == 'long': + properties['type'] = 'integer' + elif self.typeref is None: + properties['type'] = 'function' + else: + try: + properties = refs[self.typeref] + except KeyError, e: + properties['$ref'] = self.typeref + return properties + +class Namespace(object): + ''' + Given an IDLNode representing an IDL namespace, converts into a Python + dictionary that the JSON schema compiler expects to see. + ''' + + def __init__(self, namespace_node, nodoc=False): + self.namespace = namespace_node + self.nodoc = nodoc + self.events = [] + self.functions = [] + self.types = [] + self.refs = {} + + def process(self): + for node in self.namespace.children: + cls = node.cls + if cls == "Dictionary": + self.types.append(Dictionary(node).process(self.refs)) + elif cls == "Callback": + k, v = Member(node).process(self.refs) + self.refs[k] = v + elif cls == "Interface" and node.GetName() == "Functions": + self.functions = self.process_interface(node) + elif cls == "Interface" and node.GetName() == "Events": + self.events = self.process_interface(node) + else: + sys.exit("Did not process %s %s" % (node.cls, node)) + + return { 'events': self.events, + 'functions': self.functions, + 'types': self.types, + 'namespace': self.namespace.GetName(), + 'nodoc': self.nodoc } + + def process_interface(self, node): + members = [] + for member in node.children: + if member.cls == 'Member': + name, properties = Member(member).process(self.refs) + members.append(properties) + return members + +class IDLSchema(object): + ''' + Given a list of IDLNodes and IDLAttributes, converts into a Python list + of api_defs that the JSON schema compiler expects to see. + ''' + + def __init__(self, idl): + self.idl = idl + + def process(self): + namespaces = [] + for node in self.idl: + nodoc = False + cls = node.cls + if cls == 'Namespace': + namespace = Namespace(node, nodoc) + namespaces.append(namespace.process()) + elif cls == 'Copyright': + continue + elif cls == 'Comment': + continue + elif cls == 'ExtAttribute': + if node.name == 'nodoc': + nodoc = bool(node.value) + else: + continue + else: + sys.exit("Did not process %s %s" % (node.cls, node)) + return namespaces + +def Load(filename): + ''' + Given the filename of an IDL file, parses it and returns an equivalent + Python dictionary in a format that the JSON schema compiler expects to see. + ''' + + f = open(filename, 'r') + contents = f.read() + f.close() + + idl = idl_parser.IDLParser().ParseData(contents, filename) + idl_schema = IDLSchema(idl) + return idl_schema.process() diff --git a/tools/json_schema_compiler/json_schema.py b/tools/json_schema_compiler/json_schema.py index aa192c0..779ec87 100644 --- a/tools/json_schema_compiler/json_schema.py +++ b/tools/json_schema_compiler/json_schema.py @@ -14,6 +14,6 @@ if third_party_path not in sys.path: sys.path.insert(0, third_party_path) import json_minify as minify -def LoadJSON(filename): +def Load(filename): with open(filename, 'r') as handle: return json.loads(minify.json_minify(handle.read())) diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py index de392d2..cb569c6 100644 --- a/tools/json_schema_compiler/model.py +++ b/tools/json_schema_compiler/model.py @@ -90,7 +90,7 @@ class Type(object): props = [] for prop_name, prop_json in json.get('properties', {}).items(): # TODO(calamity): support functions (callbacks) as properties. The model - # doesn't support it yet because to h/cc generators don't -- this is + # doesn't support it yet because the h/cc generators don't -- this is # because we'd need to hook it into a base::Callback or something. # # However, pragmatically it's not necessary to support them anyway, since @@ -328,4 +328,3 @@ def GetModelHierarchy(entity): entity = entity.parent hierarchy.reverse() return hierarchy - |