def _GenerateEvent(self, event): """Generates the namespaces for an event. """ c = Code() # TODO(kalman): use event.unix_name not Classname. event_namespace = cpp_util.Classname(event.name) (c.Append('namespace %s {' % event_namespace).Append().Concat( self._GenerateEventNameConstant(event)).Concat( self._GenerateCreateCallbackArguments(event)).Append( '} // namespace %s' % event_namespace)) return c
def _GenerateEvent(self, event): # TODO(kalman): use event.unix_name not Classname. c = Code() event_namespace = cpp_util.Classname(event.name) (c.Append('namespace %s {' % event_namespace) .Append() .Cblock(self._GenerateEventNameConstant(event)) .Cblock(self._GenerateCreateCallbackArguments(None, event)) .Append('} // namespace %s' % event_namespace) ) return c
def GenerateIncludes(self): """Returns the #include lines for self._namespace. """ c = Code() for header in sorted([ '%s/%s.h' % (dependency.source_file_dir, self._cpp_namespaces[dependency]) for dependency in self._NamespaceTypeDependencies().keys() ]): c.Append('#include "%s"' % header) return c
def GenerateIncludes(self, include_soft=False): """Returns the #include lines for self._default_namespace. """ c = Code() if self._default_namespace.manifest_keys: c.Append('#include "base/strings/string_piece.h"') # Note: It's possible that there are multiple dependencies from the same # API. Make sure to only include them once. added_paths = set() for namespace, dependencies in self._NamespaceTypeDependencies().items( ): for dependency in dependencies: if dependency.hard or include_soft: path = '%s/%s.h' % (namespace.source_file_dir, namespace.unix_name) if path not in added_paths: added_paths.add(path) c.Append('#include "%s"' % cpp_util.ToPosixPath(path)) return c
def testLinePrefixes(self): c = Code() c.Sblock(line='/**', line_prefix=' * ') c.Sblock('@typedef {{') c.Append('foo: bar,') c.Sblock('baz: {') c.Append('x: y') c.Eblock('}') c.Eblock('}}') c.Eblock(line=' */') output = c.Render() self.assertMultiLineEqual( '/**\n' ' * @typedef {{\n' ' * foo: bar,\n' ' * baz: {\n' ' * x: y\n' ' * }\n' ' * }}\n' ' */', output)
def CloseNamespace(namespace): """Get closing root namespace declarations. """ c = Code() # In lieu of GYP supporting None for the namespace variable the '' namespace # implies there is no root namespace. if namespace == '': return c for component in reversed(namespace.split('::')): c.Append('} // namespace %s' % component) return c
def testCommentWithSpecialCharacters(self): c = Code() c.Comment('20% of 80%s') c.Substitute({}) self.assertEquals('// 20% of 80%s', c.Render()) d = Code() d.Append('90') d.Concat(c) self.assertEquals('90\n' '// 20% of 80%s', d.Render())
def OpenNamespace(namespace): """Get opening root namespace declarations. """ c = Code() # In lieu of GYP supporting None for the namespace variable the '' namespace # implies there is no root namespace. if namespace == '': return c for component in namespace.split('::'): c.Append('namespace %s {' % component) return c
def GenerateForwardDeclarations(self): """Returns the forward declarations for self._namespace. Use after GetRootNamespaceStart. Assumes all namespaces are relative to self._root_namespace. """ c = Code() for namespace, types in sorted( self._NamespaceTypeDependencies().items()): c.Append('namespace %s {' % namespace.name) for type_ in types: if namespace.types[type_].type_ != PropertyType.ARRAY: c.Append('struct %s;' % type_) c.Append('}') c.Concat(self.GetNamespaceStart()) for (name, type_) in self._namespace.types.items(): if not type_.functions and type_.type_ != PropertyType.ARRAY: c.Append('struct %s;' % name) c.Concat(self.GetNamespaceEnd()) return c
def GenerateIncludes(self, include_soft=False): """Returns the #include lines for self._default_namespace. """ c = Code() for namespace, dependencies in self._NamespaceTypeDependencies().items( ): for dependency in dependencies: if dependency.hard or include_soft: c.Append('#include "%s/%s.h"' % (namespace.source_file_dir, namespace.unix_name)) return c
def _GenerateCreateCallbackArguments(self, function_scope, callback): """Generate all functions to create Value parameters for a callback. E.g for function "Bar", generate Bar::Results::Create E.g for event "Baz", generate Baz::Create function_scope: the function scope path, e.g. Foo::Bar for the function Foo::Bar::Baz(). callback: the Function object we are creating callback arguments for. """ c = Code() params = callback.params expanded_params = self._cpp_type_generator.ExpandParams(params) c.Concat(self._GeneratePropertyFunctions(function_scope, expanded_params)) param_lists = self._cpp_type_generator.GetAllPossibleParameterLists(params) for param_list in param_lists: (c.Sblock('scoped_ptr<base::ListValue> %(function_scope)s::' 'Create(%(declaration_list)s) {') .Append('scoped_ptr<base::ListValue> create_results(' 'new base::ListValue());') ) declaration_list = [] for param in param_list: # We treat this argument as 'required' to avoid wrapping it in a # scoped_ptr if it's optional. param_copy = param.Copy() param_copy.optional = False c.Append('create_results->Append(%s);' % self._CreateValueFromProperty(param_copy, param_copy.unix_name)) declaration_list.append("const %s" % cpp_util.GetParameterDeclaration( param_copy, self._cpp_type_generator.GetType(param_copy))) c.Append('return create_results.Pass();') c.Eblock('}') c.Substitute({ 'function_scope': function_scope, 'declaration_list': ', '.join(declaration_list) }) return c
def _GenerateFunctionJsDoc(self, function): """Generates the documentation for a function as a Code. Returns an empty code object if the object has no documentation. """ c = Code() c.Sblock(line='/**', line_prefix=' * ') if function.description: c.Comment(function.description, comment_prefix='') def append_field(c, tag, js_type, name, optional, description): c.Append('@%s {' % tag) c.Concat(js_type, new_line=False) if optional: c.Append('=', new_line=False) c.Append('} %s' % name, new_line=False) if description: c.Comment(' %s' % description, comment_prefix='', wrap_indent=4, new_line=False) for param in function.params: append_field(c, 'param', self._TypeToJsType(param.type_), param.name, param.optional, param.description) if function.callback: append_field(c, 'param', self._FunctionToJsFunction(function.callback), function.callback.name, function.callback.optional, function.callback.description) if function.returns: append_field(c, 'return', self._TypeToJsType(function.returns), '', False, function.returns.description) if function.deprecated: c.Append('@deprecated %s' % function.deprecated) c.Append(self._GenerateSeeLink('method', function.name)) c.Eblock(' */') return c
def Generate(self): """Generates a Code object with the .cc for a single namespace. """ c = Code() (c.Append(cpp_util.CHROMIUM_LICENSE).Append().Append( cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file).Append().Append( self._util_cc_helper.GetIncludePath()).Append( '#include "base/logging.h"').Append( '#include "base/strings/string_number_conversions.h"'). Append('#include "base/strings/utf_string_conversions.h"').Append( '#include "%s/%s.h"' % (self._namespace.source_file_dir, self._namespace.unix_name)).Cblock( self._type_helper.GenerateIncludes( include_soft=True)).Append().Concat( cpp_util.OpenNamespace(self._cpp_namespace)).Cblock( self._type_helper.GetNamespaceStart())) if self._namespace.properties: (c.Append('//').Append('// Properties').Append('//').Append()) for property in self._namespace.properties.values(): property_code = self._type_helper.GeneratePropertyValues( property, 'const %(type)s %(name)s = %(value)s;', nodoc=True) if property_code: c.Cblock(property_code) if self._namespace.types: (c.Append('//').Append('// Types').Append('//').Append().Cblock( self._GenerateTypes(None, self._namespace.types.values()))) if self._namespace.functions: (c.Append('//').Append('// Functions').Append('//').Append()) for function in self._namespace.functions.values(): c.Cblock(self._GenerateFunction(function)) if self._namespace.events: (c.Append('//').Append('// Events').Append('//').Append()) for event in self._namespace.events.values(): c.Cblock(self._GenerateEvent(event)) (c.Concat(self._type_helper.GetNamespaceEnd()).Cblock( cpp_util.CloseNamespace(self._cpp_namespace))) return c
def _GenerateFunctionParamsCreate(self, function): """Generate function to create an instance of Params. The generated function takes a base::ListValue of arguments. E.g for function "Bar", generate Bar::Params::Create() """ c = Code() (c.Append('// static').Sblock( 'scoped_ptr<Params> Params::Create(%s) {' % self._GenerateParams(['const base::ListValue& args']))) if self._generate_error_messages: c.Append('DCHECK(error);') (c.Concat(self._GenerateParamsCheck( function, 'args')).Append('scoped_ptr<Params> params(new Params());')) for param in function.params: c.Concat(self._InitializePropertyToDefault(param, 'params')) for i, param in enumerate(function.params): # Any failure will cause this function to return. If any argument is # incorrect or missing, those following it are not processed. Note that # for optional arguments, we allow missing arguments and proceed because # there may be other arguments following it. failure_value = 'scoped_ptr<Params>()' c.Append() value_var = param.unix_name + '_value' (c.Append('const base::Value* %(value_var)s = NULL;').Append( 'if (args.Get(%(i)s, &%(value_var)s) &&').Sblock( ' !%(value_var)s->IsType(base::Value::TYPE_NULL)) {'). Concat( self._GeneratePopulatePropertyFromValue( param, value_var, 'params', failure_value)).Eblock('}')) if not param.optional: (c.Sblock('else {').Concat( self._GenerateError('"\'%%(key)s\' is required"')).Append( 'return %s;' % failure_value).Eblock('}')) c.Substitute({'value_var': value_var, 'i': i, 'key': param.name}) (c.Append().Append('return params.Pass();').Eblock('}').Append()) return c
def _GenerateTypePopulateProperty(self, prop, src, dst): """Generate the code to populate a single property in a type. src: base::DictionaryValue* dst: Type* """ c = Code() value_var = prop.unix_name + '_value' c.Append('const base::Value* %(value_var)s = NULL;') if prop.optional: (c.Sblock( 'if (%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {' ).Concat( self._GeneratePopulatePropertyFromValue( prop, value_var, dst, 'false'))) underlying_type = self._type_helper.FollowRef(prop.type_) if underlying_type.property_type == PropertyType.ENUM: namespace_prefix = ( '%s::' % underlying_type.namespace.unix_name if underlying_type.namespace != self._namespace else '') (c.Append('} else {').Append( '%%(dst)s->%%(name)s = %s%s;' % (namespace_prefix, self._type_helper.GetEnumNoneValue(prop.type_)))) c.Eblock('}') else: (c.Sblock( 'if (!%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {' ).Concat(self._GenerateError('"\'%%(key)s\' is required"')).Append( 'return false;').Eblock('}').Concat( self._GeneratePopulatePropertyFromValue( prop, value_var, dst, 'false'))) c.Append() c.Substitute({ 'value_var': value_var, 'key': prop.name, 'src': src, 'dst': dst, 'name': prop.unix_name }) return c
def _TypeToJsType(self, namespace_name, js_type): """Converts a model.Type to a JS type (number, Array, etc.)""" if js_type.property_type in (PropertyType.INTEGER, PropertyType.DOUBLE): return Code().Append('number') if js_type.property_type is PropertyType.OBJECT: if js_type.properties: c = Code() self.AppendObjectDefinition(c, namespace_name, js_type.properties) return c if js_type.instance_of: return Code().Append(js_type.instance_of) return Code().Append('Object') if js_type.property_type is PropertyType.ARRAY: return (Code().Append('!Array<').Concat( self._TypeToJsType(namespace_name, js_type.item_type), new_line=False).Append('>', new_line=False)) if js_type.property_type is PropertyType.REF: ref_type = '!chrome.%s.%s' % (namespace_name, js_type.ref_type) return Code().Append(ref_type) if js_type.property_type is PropertyType.CHOICES: c = Code() c.Append('(') for i, choice in enumerate(js_type.choices): c.Concat(self._TypeToJsType(namespace_name, choice), new_line=False) if i is not len(js_type.choices) - 1: c.Append('|', new_line=False) c.Append(')', new_line=False) return c if js_type.property_type is PropertyType.FUNCTION: return self._FunctionToJsFunction(namespace_name, js_type.function) if js_type.property_type is PropertyType.BINARY: return Code().Append('ArrayBuffer') if js_type.property_type is PropertyType.ANY: return Code().Append('*') if js_type.property_type.is_fundamental: return Code().Append(js_type.property_type.name) return Code().Append( '?') # TODO(tbreisacher): Make this more specific.
def _GenerateChoiceTypeToValue(self, cpp_namespace, type_): """Generates a function that serializes a choice-representing type into a base::Value. """ c = Code() c.Sblock('std::unique_ptr<base::Value> %s::ToValue() const {' % cpp_namespace) c.Append('std::unique_ptr<base::Value> result;') for choice in type_.choices: choice_var = 'as_%s' % choice.unix_name # Enums cannot be wrapped with scoped_ptr, but the XXX_NONE enum value # is equal to 0. (c.Sblock('if (%s) {' % choice_var).Append( 'DCHECK(!result) << "Cannot set multiple choices for %s";' % type_.unix_name).Cblock( self._CreateValueFromType('result = %s;', choice.name, choice, choice_var, True)).Eblock('}')) (c.Append('DCHECK(result) << "Must set at least one choice for %s";' % type_.unix_name).Append('return result;').Eblock('}')) return c
def _GenerateManifestKeyConstants(self, properties): # type: (list[Property]) -> Code """Generates string constants for manifest keys for the given |properties|. """ c = Code() for prop in properties: c.Append( 'static constexpr char %s[] = "%s";' % (cpp_util.UnixNameToConstantName(prop.unix_name), prop.name)) return c
def _GenerateTypeFromValue(self, cpp_namespace, type_): classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() (c.Append('// static').Append( 'scoped_ptr<%s> %s::FromValue(const base::Value& value) {' % (classname, cpp_namespace)).Append( ' scoped_ptr<%s> out(new %s());' % (classname, classname)).Append( ' if (!Populate(value, out.get()))').Append( ' return scoped_ptr<%s>();' % classname).Append(' return out.Pass();').Append('}')) return c
def _GeneratePropertyFunctions(self, param_namespace, params): """Generate the functions for structures generated by a property such as CreateEnumValue for ENUMs and Populate/ToValue for Params/Results objects. """ c = Code() for param in params: if param.type_ == PropertyType.OBJECT: c.Concat(self._GenerateType( param_namespace + '::' + cpp_util.Classname(param.name), param)) c.Append() elif param.type_ == PropertyType.ARRAY: c.Concat(self._GeneratePropertyFunctions( param_namespace, [param.item_type])) elif param.type_ == PropertyType.CHOICES: c.Concat(self._GeneratePropertyFunctions( param_namespace, param.choices.values())) elif param.type_ == PropertyType.ENUM: c.Concat(self._GenerateCreateEnumValue(param_namespace, param)) c.Append() return c
def _GenerateError(self, body): """Generates an error message pertaining to population failure. E.g 'expected bool, got int' """ c = Code() if not self._generate_error_messages: return c (c.Append('if (error->length())') .Append(' error->append(UTF8ToUTF16("; "));') .Append('error->append(UTF8ToUTF16(%s));' % body)) return c
def _GeneratePropertyStructures(self, props): """Generate the structures required by a property such as OBJECT classes and enums. """ c = Code() for prop in props: if prop.type_ == PropertyType.OBJECT: c.Concat(self._GenerateType(prop)) c.Append() elif prop.type_ == PropertyType.ARRAY: c.Concat(self._GeneratePropertyStructures([prop.item_type])) c.Append() elif prop.type_ == PropertyType.CHOICES: c.Concat(self._GenerateEnumDeclaration( self._cpp_type_generator.GetChoicesEnumType(prop), prop, [choice.type_.name for choice in prop.choices.values()])) c.Concat(self._GeneratePropertyStructures(prop.choices.values())) elif prop.type_ == PropertyType.ENUM: enum_name = self._cpp_type_generator.GetCompiledType(prop) c.Concat(self._GenerateEnumDeclaration( enum_name, prop, prop.enum_values)) create_enum_value = ('scoped_ptr<base::Value> CreateEnumValue(%s %s);' % (enum_name, prop.unix_name)) enum_to_string = 'std::string ToString(%s enum_param);' % enum_name enum_from_string = ('%s From%sString(const std::string& enum_string);' % (enum_name, enum_name)) # If the property is from the UI then we're in a struct so this function # should be static. If it's from the client, then we're just in a # namespace so we can't have the static keyword. if prop.from_json: create_enum_value = 'static %s' % create_enum_value enum_to_string = 'static %s' % enum_to_string enum_from_string = 'static %s' % enum_from_string (c.Append(create_enum_value) .Append(enum_to_string) .Append(enum_from_string)) return c
def GenerateForwardDeclarations(self): """Returns the forward declarations for self._namespace. Use after GetRootNamespaceStart. Assumes all namespaces are relative to self._root_namespace. """ c = Code() for namespace, types in sorted( self._NamespaceTypeDependencies().items()): c.Append('namespace %s {' % namespace.name) for type_ in types: if namespace.types[type_].type_ == PropertyType.STRING: c.Append('typedef std::string %s;' % type_) elif namespace.types[type_].type_ == PropertyType.ARRAY: c.Append('typedef std::vector<%(item_type)s> %(name)s;') c.Substitute({ 'name': type_, 'item_type': self.GetType(namespace.types[type_].item_type, wrap_optional=True) }) else: c.Append('struct %s;' % type_) c.Append('}') c.Concat(self.GetNamespaceStart()) for (name, type_) in self._namespace.types.items(): if not type_.functions and type_.type_ == PropertyType.OBJECT: c.Append('struct %s;' % name) c.Concat(self.GetNamespaceEnd()) return c
def Render(self): """Returns the Code object for the body of the .cc file, which handles the initialization of all features.""" c = Code() c.Append('%s::%s() {' % (self._provider_class, self._provider_class)) c.Sblock() for k in sorted(self._features.keys()): c.Sblock('{') feature = self._features[k] if type(feature) is list: c.Append('std::vector<Feature*> features;') for f in feature: c.Sblock('{') c.Concat(f.GetCode(self._feature_class)) c.Append('features.push_back(feature);') c.Eblock('}') c.Append( 'ComplexFeature* feature(new ComplexFeature(&features));') c.Append('feature->set_name("%s");' % k) else: c.Concat(feature.GetCode(self._feature_class)) c.Append('AddFeature("%s", feature);' % k) c.Eblock('}') c.Eblock('}') return c
def GenerateForwardDeclarations(self): """Returns the forward declarations for self._namespace. Use after GetRootNamespaceStart. Assumes all namespaces are relative to self._root_namespace. """ c = Code() namespace_type_dependencies = self._NamespaceTypeDependencies() for namespace in sorted(namespace_type_dependencies.keys(), key=operator.attrgetter('name')): c.Append('namespace %s {' % namespace.name) for type_ in sorted(namespace_type_dependencies[namespace], key=schema_util.StripSchemaNamespace): type_name = schema_util.StripSchemaNamespace(type_) if namespace.types[type_].type_ == PropertyType.STRING: c.Append('typedef std::string %s;' % type_name) elif namespace.types[type_].type_ == PropertyType.ARRAY: c.Append('typedef std::vector<%(item_type)s> %(name)s;') c.Substitute({ 'name': type_name, 'item_type': self.GetType(namespace.types[type_].item_type, wrap_optional=True) }) # Enums cannot be forward declared. elif namespace.types[type_].type_ != PropertyType.ENUM: c.Append('struct %s;' % type_name) c.Append('}') c.Concat(self.GetNamespaceStart()) for (name, type_) in self._namespace.types.items(): if not type_.functions and type_.type_ == PropertyType.OBJECT: c.Append('struct %s;' % schema_util.StripSchemaNamespace(name)) c.Concat(self.GetNamespaceEnd()) return c
def Render(self): """Returns the Code object for the body of the .cc file, which handles the initialization of all features.""" c = Code() c.Sblock() for k in sorted(self._features.keys()): c.Sblock('{') feature = self._features[k] c.Concat(feature.GetCode(self._feature_type)) c.Append('provider->AddFeature("%s", feature);' % k) c.Eblock('}') c.Eblock() return c
def Generate(self): """Generates a Code object with the .dart for the entire namespace. """ c = Code() (c.Append(LICENSE) .Append() .Append('// Generated from namespace: %s' % self._namespace.name) .Append() .Append('part of chrome;')) if self._types: (c.Append() .Append('/**') .Append(' * Types') .Append(' */') .Append() ) for type_ in self._types.values(): # Check for custom dart for this whole type. override = self._GetOverride([type_.name], document_with=type_) c.Cblock(override if override is not None else self._GenerateType(type_)) if self._namespace.events: (c.Append('/**') .Append(' * Events') .Append(' */') .Append() ) for event_name in self._namespace.events: c.Cblock(self._GenerateEvent(self._namespace.events[event_name])) (c.Append('/**') .Append(' * Functions') .Append(' */') .Append() ) c.Cblock(self._GenerateMainClass()) return c
def _GenerateMainClass(self): """Generates the main class for this file, which links to all functions and events. Returns a code object. """ c = Code() (c.Sblock( 'class API_%s {' % self._namespace.unix_name).Append('/*').Append( ' * API connection').Append(' */').Append('Object _jsObject;')) # Add events. if self._namespace.events: (c.Append().Append('/*').Append(' * Events').Append(' */')) for event_name in self._namespace.events: c.Append('Event_%s_%s %s;' % (self._namespace.unix_name, event_name, event_name)) # Add functions. if self._namespace.functions: (c.Append().Append('/*').Append(' * Functions').Append(' */')) for function in self._namespace.functions.values(): # Check for custom dart for this whole property. override = self._GetOverride([function.name], document_with=function) c.Cblock(override if override is not None else self. _GenerateFunction(function)) # Add the constructor. c.Sblock('API_%s(this._jsObject) {' % self._namespace.unix_name) # Add events to constructor. for event_name in self._namespace.events: c.Append("%s = new Event_%s_%s(JS('', '#.%s', this._jsObject));" % (event_name, self._namespace.unix_name, event_name, event_name)) (c.Eblock('}').Eblock('}')) return c
def Write(self): """Writes the output.""" header_file = self._out_base_filename + '.h' cc_file = self._out_base_filename + '.cc' include_file_root = self._out_root[len(self._gen_dir_relpath) + 1:] header_file_path = '%s/%s' % (include_file_root, header_file) cc_file_path = '%s/%s' % (include_file_root, cc_file) substitutions = ({ 'header_file_path': header_file_path, 'header_guard': (header_file_path.replace('/', '_').replace('.', '_').upper()), 'method_name': self._method_name, 'source_files': str(self._source_files), 'year': str(datetime.now().year) }) if not os.path.exists(self._out_root): os.makedirs(self._out_root) # Write the .h file. with open(os.path.join(self._out_root, header_file), 'w') as f: header_file = Code() header_file.Append(HEADER_FILE_TEMPLATE) header_file.Substitute(substitutions) f.write(header_file.Render().strip()) # Write the .cc file. with open(os.path.join(self._out_root, cc_file), 'w') as f: cc_file = Code() cc_file.Append(CC_FILE_BEGIN) cc_file.Substitute(substitutions) cc_file.Concat(self.Render()) cc_end = Code() cc_end.Append(CC_FILE_END) cc_end.Substitute(substitutions) cc_file.Concat(cc_end) f.write(cc_file.Render().strip())
def _GenerateChoiceTypeToValue(self, cpp_namespace, type_): """Generates a function that serializes a choice-representing type into a base::Value. """ c = Code() c.Sblock('scoped_ptr<base::Value> %s::ToValue() const {' % cpp_namespace) c.Append('scoped_ptr<base::Value> result;') for choice in type_.choices: choice_var = 'as_%s' % choice.unix_name (c.Sblock('if (%s) {' % choice_var) .Append('DCHECK(!result) << "Cannot set multiple choices for %s";' % type_.unix_name) .Append('result.reset(%s);' % self._CreateValueFromType(choice, '*%s' % choice_var)) .Eblock('}') ) (c.Append('DCHECK(result) << "Must set at least one choice for %s";' % type_.unix_name) .Append('return result.Pass();') .Eblock('}') ) return c