def _GenerateEnumToString(self, cpp_namespace, type_): """Generates ToString() which gets the string representation of an enum. """ c = Code() classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) if cpp_namespace is not None: c.Append('// static') maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace c.Sblock('std::string %sToString(%s enum_param) {' % (maybe_namespace, classname)) c.Sblock('switch (enum_param) {') for enum_value in self._type_helper.FollowRef(type_).enum_values: name = enum_value.name if 'camel_case_enum_to_string' in self._namespace.compiler_options: name = enum_value.CamelName() (c.Append('case %s: ' % self._type_helper.GetEnumValue(type_, enum_value)) .Append(' return "%s";' % name)) (c.Append('case %s:' % self._type_helper.GetEnumNoneValue(type_)) .Append(' return "";') .Eblock('}') .Append('NOTREACHED();') .Append('return "";') .Eblock('}') ) return c
def _GenerateEnumFromString(self, cpp_namespace, type_): """Generates FromClassNameString() which gets an enum from its string representation. """ c = Code() classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) if cpp_namespace is not None: c.Append('// static') maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace c.Sblock('%s%s %sParse%s(const std::string& enum_string) {' % (maybe_namespace, classname, maybe_namespace, classname)) for _, enum_value in enumerate( self._type_helper.FollowRef(type_).enum_values): # This is broken up into all ifs with no else ifs because we get # "fatal error C1061: compiler limit : blocks nested too deeply" # on Windows. (c.Append('if (enum_string == "%s")' % enum_value.name) .Append(' return %s;' % self._type_helper.GetEnumValue(type_, enum_value))) (c.Append('return %s;' % self._type_helper.GetEnumNoneValue(type_)) .Eblock('}') ) return c
def _FindType(self, full_name): """Finds the model.Type with name |qualified_name|. If it's not from |self._default_namespace| then it needs to be qualified. """ namespace = self._schema_loader.ResolveType(full_name, self._default_namespace) if namespace is None: raise KeyError('Cannot resolve type %s. Maybe it needs a prefix ' 'if it comes from another namespace?' % full_name) return namespace.types[schema_util.StripNamespace(full_name)]
def _GenerateType(self, cpp_namespace, type_): """Generates the function definitions for a type. """ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() if type_.functions: # Wrap functions within types in the type's namespace. (c.Append('namespace %s {' % classname).Append()) for function in type_.functions.values(): c.Cblock(self._GenerateFunction(function)) c.Append('} // namespace %s' % classname) elif type_.property_type == PropertyType.ARRAY: c.Cblock(self._GenerateType(cpp_namespace, type_.item_type)) elif type_.property_type in (PropertyType.CHOICES, PropertyType.OBJECT): if cpp_namespace is None: classname_in_namespace = classname else: classname_in_namespace = '%s::%s' % (cpp_namespace, classname) if type_.property_type == PropertyType.OBJECT: c.Cblock( self._GeneratePropertyFunctions(classname_in_namespace, type_.properties.values())) else: c.Cblock( self._GenerateTypes(classname_in_namespace, type_.choices)) (c.Append('%s::%s()' % (classname_in_namespace, classname)).Cblock( self._GenerateInitializersAndBody(type_)).Append( '%s::~%s() {}' % (classname_in_namespace, classname))) # Note: we use 'rhs' because some API objects have a member 'other'. (c.Append('%s::%s(%s&& rhs)' % (classname_in_namespace, classname, classname)).Cblock( self._GenerateMoveCtor(type_)).Append( '%s& %s::operator=(%s&& rhs)' % (classname_in_namespace, classname_in_namespace, classname)).Cblock( self._GenerateMoveAssignOperator(type_))) if type_.origin.from_json: c.Cblock( self._GenerateTypePopulate(classname_in_namespace, type_)) if cpp_namespace is None: # only generate for top-level types c.Cblock( self._GenerateTypeFromValue(classname_in_namespace, type_)) if type_.origin.from_client: c.Cblock( self._GenerateTypeToValue(classname_in_namespace, type_)) elif type_.property_type == PropertyType.ENUM: (c.Cblock(self._GenerateEnumToString(cpp_namespace, type_)).Cblock( self._GenerateEnumFromString(cpp_namespace, type_))) 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 _GenerateTypePopulate(self, cpp_namespace, type_): """Generates the function for populating a type given a pointer to it. E.g for type "Foo", generates Foo::Populate() """ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() (c.Append('// static').Append('bool %(namespace)s::Populate(').Sblock( ' const base::Value& value, %(name)s* out) {')) if type_.property_type == PropertyType.CHOICES: for choice in type_.choices: value_type = cpp_util.GetValueType( self._type_helper.FollowRef(choice)) (c.Sblock('if (value.IsType(%s)) {' % value_type).Concat( self._GeneratePopulateVariableFromValue( choice, '(&value)', 'out->as_%s' % choice.unix_name, 'false', is_ptr=True)).Append('return true;').Eblock('}')) c.Append('return false;') elif type_.property_type == PropertyType.OBJECT: (c.Append('if (!value.IsType(base::Value::TYPE_DICTIONARY))'). Append(' return false;')) if type_.properties or type_.additional_properties is not None: c.Append('const base::DictionaryValue* dict = ' 'static_cast<const base::DictionaryValue*>(&value);') for prop in type_.properties.values(): c.Concat(self._InitializePropertyToDefault(prop, 'out')) for prop in type_.properties.values(): c.Concat( self._GenerateTypePopulateProperty(prop, 'dict', 'out')) if type_.additional_properties is not None: if type_.additional_properties.property_type == PropertyType.ANY: c.Append( 'out->additional_properties.MergeDictionary(dict);') else: cpp_type = self._type_helper.GetCppType( type_.additional_properties, is_in_container=True) (c.Append('for (base::DictionaryValue::Iterator it(*dict);' ).Sblock(' !it.IsAtEnd(); it.Advance()) {'). Append('%s tmp;' % cpp_type).Concat( self._GeneratePopulateVariableFromValue( type_.additional_properties, '(&it.value())', 'tmp', 'false')).Append( 'out->additional_properties[it.key()] = tmp;' ).Eblock('}')) c.Append('return true;') (c.Eblock('}').Substitute({ 'namespace': cpp_namespace, 'name': classname })) return c
def _GenerateTypeFromValue(self, cpp_namespace, type_): classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() (c.Append('// static').Append( 'std::unique_ptr<%s> %s::FromValue(%s) {' % (classname, cpp_namespace, self._GenerateParams(('const base::Value& value', ))))) if self._generate_error_messages: c.Append('DCHECK(error);') (c.Append(' std::unique_ptr<%s> out(new %s());' % (classname, classname)).Append(' if (!Populate(%s))' % self._GenerateArgs( ('value', 'out.get()'))). Append(' return nullptr;').Append(' return out;').Append('}')) return c
def _CreateValueFromType(self, cpp_namespace, type_, var, is_ptr=False): """Creates a base::Value given a type. Generated code passes ownership to caller. var: variable or variable* E.g for std::string, generate new base::StringValue(var) """ underlying_type = self._type_helper.FollowRef(type_) if (underlying_type.property_type == PropertyType.CHOICES or underlying_type.property_type == PropertyType.OBJECT): if is_ptr: return '(%s)->ToValue().release()' % var else: return '(%s).ToValue().release()' % var elif (underlying_type.property_type == PropertyType.ANY or underlying_type.property_type == PropertyType.FUNCTION): if is_ptr: vardot = '(%s)->' % var else: vardot = '(%s).' % var return '%sDeepCopy()' % vardot elif underlying_type.property_type == PropertyType.ENUM: classname = cpp_util.Classname( schema_util.StripNamespace(underlying_type.name)) return 'new base::StringValue(%sToString(%s))' % (classname, var) elif underlying_type.property_type == PropertyType.BINARY: if is_ptr: vardot = var + '->' else: vardot = var + '.' return ( 'base::BinaryValue::CreateWithCopiedBuffer(%sdata(), %ssize())' % (vardot, vardot)) elif underlying_type.property_type == PropertyType.ARRAY: return '%s.release()' % self._util_cc_helper.CreateValueFromArray( cpp_namespace, underlying_type, var, is_ptr) elif underlying_type.property_type.is_fundamental: if is_ptr: var = '*%s' % var if underlying_type.property_type == PropertyType.STRING: return 'new base::StringValue(%s)' % var else: return 'new base::FundamentalValue(%s)' % var else: raise NotImplementedError('Conversion of %s to base::Value not ' 'implemented' % repr(type_.type_))
def _GenerateType(self, cpp_namespace, type_): """Generates the function definitions for a type. """ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() if type_.functions: # Wrap functions within types in the type's namespace. (c.Append('namespace %s {' % classname).Append()) for function in type_.functions.values(): c.Cblock(self._GenerateFunction(function)) c.Append('} // namespace %s' % classname) elif type_.property_type == PropertyType.ARRAY: c.Cblock(self._GenerateType(cpp_namespace, type_.item_type)) elif (type_.property_type == PropertyType.OBJECT or type_.property_type == PropertyType.CHOICES): if cpp_namespace is None: classname_in_namespace = classname else: classname_in_namespace = '%s::%s' % (cpp_namespace, classname) if type_.property_type == PropertyType.OBJECT: c.Cblock( self._GeneratePropertyFunctions(classname_in_namespace, type_.properties.values())) else: c.Cblock( self._GenerateTypes(classname_in_namespace, type_.choices)) (c.Append('%s::%s()' % (classname_in_namespace, classname)).Cblock( self._GenerateInitializersAndBody(type_)).Append( '%s::~%s() {}' % (classname_in_namespace, classname)).Append()) if type_.origin.from_json: c.Cblock( self._GenerateTypePopulate(classname_in_namespace, type_)) if type_.origin.from_client: c.Cblock( self._GenerateTypeToValue(classname_in_namespace, type_)) elif type_.property_type == PropertyType.ENUM: (c.Cblock(self._GenerateEnumToString(cpp_namespace, type_)).Cblock( self._GenerateEnumFromString(cpp_namespace, type_))) return c
def _GenerateTypePopulate(self, cpp_namespace, type_): """Generates the function for populating a type given a pointer to it. E.g for type "Foo", generates Foo::Populate() """ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() (c.Append('// static') .Append('bool %(namespace)s::Populate(') .Sblock(' %s) {' % self._GenerateParams( ('const base::Value& value', '%(name)s* out')))) if self._generate_error_messages: c.Append('DCHECK(error);') if type_.property_type == PropertyType.CHOICES: for choice in type_.choices: (c.Sblock('if (%s) {' % self._GenerateValueIsTypeExpression('value', choice)) .Concat(self._GeneratePopulateVariableFromValue( choice, '(&value)', 'out->as_%s' % choice.unix_name, 'false', is_ptr=True)) .Append('return true;') .Eblock('}') ) (c.Concat(self._GenerateError( '"expected %s, got " + %s' % (" or ".join(choice.name for choice in type_.choices), self._util_cc_helper.GetValueTypeString('value')))) .Append('return false;')) elif type_.property_type == PropertyType.OBJECT: (c.Sblock('if (!value.IsType(base::Value::TYPE_DICTIONARY)) {') .Concat(self._GenerateError( '"expected dictionary, got " + ' + self._util_cc_helper.GetValueTypeString('value'))) .Append('return false;') .Eblock('}')) if type_.properties or type_.additional_properties is not None: c.Append('const base::DictionaryValue* dict = ' 'static_cast<const base::DictionaryValue*>(&value);') if self._generate_error_messages: c.Append('std::set<std::string> keys;') for prop in type_.properties.itervalues(): c.Concat(self._InitializePropertyToDefault(prop, 'out')) for prop in type_.properties.itervalues(): if self._generate_error_messages: c.Append('keys.insert("%s");' % (prop.name)) c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out')) # Check for extra values. if self._generate_error_messages: (c.Sblock('for (base::DictionaryValue::Iterator it(*dict); ' '!it.IsAtEnd(); it.Advance()) {') .Sblock('if (!keys.count(it.key())) {') .Concat(self._GenerateError('"found unexpected key \'" + ' 'it.key() + "\'"')) .Eblock('}') .Eblock('}') ) if type_.additional_properties is not None: if type_.additional_properties.property_type == PropertyType.ANY: c.Append('out->additional_properties.MergeDictionary(dict);') else: cpp_type = self._type_helper.GetCppType(type_.additional_properties, is_in_container=True) (c.Append('for (base::DictionaryValue::Iterator it(*dict);') .Sblock(' !it.IsAtEnd(); it.Advance()) {') .Append('%s tmp;' % cpp_type) .Concat(self._GeneratePopulateVariableFromValue( type_.additional_properties, '(&it.value())', 'tmp', 'false')) .Append('out->additional_properties[it.key()] = tmp;') .Eblock('}') ) c.Append('return true;') (c.Eblock('}') .Substitute({'namespace': cpp_namespace, 'name': classname})) return c
def _GenerateType(self, type_, is_toplevel=False, generate_typedefs=False): """Generates a struct for |type_|. |is_toplevel| implies that the type was declared in the "types" field of an API schema. This determines the correct function modifier(s). |generate_typedefs| controls whether primitive types should be generated as a typedef. This may not always be desired. If false, primitive types are ignored. """ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() if type_.functions: # Wrap functions within types in the type's namespace. (c.Append('namespace %s {' % classname).Append()) for function in type_.functions.values(): c.Cblock(self._GenerateFunction(function)) c.Append('} // namespace %s' % classname) elif type_.property_type == PropertyType.ARRAY: if generate_typedefs and type_.description: c.Comment(type_.description) c.Cblock( self._GenerateType(type_.item_type, is_toplevel=is_toplevel)) if generate_typedefs: (c.Append('typedef std::vector<%s > %s;' % (self._type_helper.GetCppType( type_.item_type), classname))) elif type_.property_type == PropertyType.STRING: if generate_typedefs: if type_.description: c.Comment(type_.description) c.Append('typedef std::string %(classname)s;') elif type_.property_type == PropertyType.ENUM: if type_.description: c.Comment(type_.description) c.Cblock(self._GenerateEnumDeclaration(classname, type_)) # Top level enums are in a namespace scope so the methods shouldn't be # static. On the other hand, those declared inline (e.g. in an object) do. maybe_static = '' if is_toplevel else 'static ' (c.Append().Append( '%sconst char* ToString(%s as_enum);' % (maybe_static, classname)).Append( '%s%s Parse%s(const std::string& as_string);' % (maybe_static, classname, classname))) elif type_.property_type in (PropertyType.CHOICES, PropertyType.OBJECT): if type_.description: c.Comment(type_.description) (c.Sblock('struct %(classname)s {').Append( '%(classname)s();').Append('~%(classname)s();').Append( '%(classname)s(const %(classname)s&) = delete;'). Append('%(classname)s& operator=(const %(classname)s&) = delete;' ).Append('%(classname)s(%(classname)s&& rhs);').Append( '%(classname)s& operator=(%(classname)s&& rhs);')) if type_.origin.from_manifest_keys: c.Append() c.Comment('Manifest key constants.') c.Concat( self._GenerateManifestKeyConstants( type_.properties.values())) if type_.origin.from_json: (c.Append().Comment( 'Populates a %s object from a base::Value. Returns' ' whether |out| was successfully populated.' % classname).Append('static bool Populate(%s);' % self._GenerateParams( ('const base::Value& value', '%s* out' % classname)))) if is_toplevel: (c.Append().Comment( 'Creates a %s object from a base::Value, or NULL on ' 'failure.' % classname).Append( 'static std::unique_ptr<%s> FromValue(%s);' % (classname, self._GenerateParams( ('const base::Value& value', ))))) if type_.origin.from_client: value_type = ('base::Value' if type_.property_type is PropertyType.CHOICES else 'base::DictionaryValue') (c.Append().Comment( 'Returns a new %s representing the serialized form of this ' '%s object.' % (value_type, classname)).Append( 'std::unique_ptr<%s> ToValue() const;' % value_type)) if type_.origin.from_manifest_keys: c.Cblock(self._GenerateParseFromDictionary(type_, classname)) if type_.property_type == PropertyType.CHOICES: # Choices are modelled with optional fields for each choice. Exactly one # field of the choice is guaranteed to be set by the compiler. c.Cblock(self._GenerateTypes(type_.choices)) c.Append('// Choices:') for choice_type in type_.choices: c.Append('%s as_%s;' % (self._type_helper.GetCppType( choice_type, is_ptr=True), choice_type.unix_name)) else: properties = type_.properties.values() (c.Append().Cblock( self._GenerateTypes(p.type_ for p in properties)).Cblock( self._GenerateFields(properties))) if type_.additional_properties is not None: # Most additionalProperties actually have type "any", which is better # modelled as a DictionaryValue rather than a map of string -> Value. if type_.additional_properties.property_type == PropertyType.ANY: c.Append( 'base::DictionaryValue additional_properties;') else: (c.Cblock( self._GenerateType(type_.additional_properties)). Append( 'std::map<std::string, %s> additional_properties;' % self._type_helper.GetCppType( type_.additional_properties, is_in_container=True))) (c.Eblock('};')) return c.Substitute({'classname': classname})
def _GenerateType(self, type_, is_toplevel=False, generate_typedefs=False): """Generates a struct for |type_|. |is_toplevel| implies that the type was declared in the "types" field of an API schema. This determines the correct function modifier(s). |generate_typedefs| controls whether primitive types should be generated as a typedef. This may not always be desired. If false, primitive types are ignored. """ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() if type_.functions: # Wrap functions within types in the type's namespace. (c.Append('namespace %s {' % classname) .Append() ) for function in type_.functions.values(): c.Cblock(self._GenerateFunction(function)) c.Append('} // namespace %s' % classname) elif type_.property_type == PropertyType.ARRAY: if generate_typedefs and type_.description: c.Comment(type_.description) c.Cblock(self._GenerateType(type_.item_type)) if generate_typedefs: (c.Append('typedef std::vector<%s > %s;' % ( self._type_helper.GetCppType(type_.item_type), classname)) ) elif type_.property_type == PropertyType.STRING: if generate_typedefs: if type_.description: c.Comment(type_.description) c.Append('typedef std::string %(classname)s;') elif type_.property_type == PropertyType.ENUM: if type_.description: c.Comment(type_.description) c.Sblock('enum %(classname)s {') c.Append('%s,' % self._type_helper.GetEnumNoneValue(type_)) for value in type_.enum_values: c.Append('%s,' % self._type_helper.GetEnumValue(type_, value)) # Top level enums are in a namespace scope so the methods shouldn't be # static. On the other hand, those declared inline (e.g. in an object) do. maybe_static = '' if is_toplevel else 'static ' (c.Eblock('};') .Append() .Append('%sstd::string ToString(%s as_enum);' % (maybe_static, classname)) .Append('%s%s Parse%s(const std::string& as_string);' % (maybe_static, classname, classname)) ) elif type_.property_type == PropertyType.OBJECT: if type_.description: c.Comment(type_.description) (c.Sblock('struct %(classname)s {') .Append('%(classname)s();') .Append('~%(classname)s();') ) if type_.origin.from_json: (c.Append() .Comment('Populates a %s object from a base::Value. Returns' ' whether |out| was successfully populated.' % classname) .Append('static bool Populate(const base::Value& value, ' '%(classname)s* out);') ) if type_.origin.from_client: (c.Append() .Comment('Returns a new base::DictionaryValue representing the' ' serialized form of this %s object.' % classname) .Append('scoped_ptr<base::DictionaryValue> ToValue() const;') ) properties = type_.properties.values() (c.Append() .Cblock(self._GenerateTypes(p.type_ for p in properties)) .Cblock(self._GenerateFields(properties))) if type_.additional_properties is not None: # Most additionalProperties actually have type "any", which is better # modelled as a DictionaryValue rather than a map of string -> Value. if type_.additional_properties.property_type == PropertyType.ANY: c.Append('base::DictionaryValue additional_properties;') else: (c.Cblock(self._GenerateType(type_.additional_properties)) .Append('std::map<std::string, %s> additional_properties;' % cpp_util.PadForGenerics( self._type_helper.GetCppType(type_.additional_properties, is_in_container=True))) ) (c.Eblock() .Append() .Sblock(' private:') .Append('DISALLOW_COPY_AND_ASSIGN(%(classname)s);') .Eblock('};') ) elif type_.property_type == PropertyType.CHOICES: if type_.description: c.Comment(type_.description) # Choices are modelled with optional fields for each choice. Exactly one # field of the choice is guaranteed to be set by the compiler. (c.Sblock('struct %(classname)s {') .Append('%(classname)s();') .Append('~%(classname)s();') .Append()) c.Cblock(self._GenerateTypes(type_.choices)) if type_.origin.from_json: (c.Comment('Populates a %s object from a base::Value. Returns' ' whether |out| was successfully populated.' % classname) .Append('static bool Populate(const base::Value& value, ' '%(classname)s* out);') .Append() ) if type_.origin.from_client: (c.Comment('Returns a new base::Value representing the' ' serialized form of this %s object.' % classname) .Append('scoped_ptr<base::Value> ToValue() const;') .Append() ) c.Append('// Choices:') for choice_type in type_.choices: c.Append('%s as_%s;' % ( self._type_helper.GetCppType(choice_type, is_ptr=True), choice_type.unix_name)) c.Eblock('};') c.Substitute({'classname': classname}) return c