Mojo: Add support for constants to the IDL compiler.

Only supports POD-types and strings currently.

BUG=320091

Review URL: https://codereview.chromium.org/268363003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269659 0039d316-1c4b-4281-b951-d872f2087c98
parent 6ba165ef
......@@ -8,6 +8,8 @@ import "sample_import2.mojom"
[JavaPackage="org.chromium.mojo.bindings.test"]
module sample {
const uint8 kThree = 3;
struct Bar {
enum Type {
TYPE_VERTICAL = 1 << 0,
......@@ -23,7 +25,8 @@ struct Bar {
[RequiredFields=7]
struct Foo {
string name = "Fooby" @8;
const string kFooby = "Fooby";
string name = kFooby @8;
int32 x @0;
int32 y @1;
bool a = true @2;
......@@ -31,7 +34,7 @@ struct Foo {
bool c @4;
Bar bar @5;
Bar[] extra_bars @7;
uint8[] data = [1,2,3] @6;
uint8[] data = [1,2,kThree] @6;
handle<message_pipe> source @9;
handle<data_pipe_consumer>[] input_streams @10;
handle<data_pipe_producer>[] output_streams @11;
......@@ -59,6 +62,7 @@ interface Service {
BAZ_REGULAR = 0,
BAZ_EXTRA
};
const uint8 kFavoriteBaz = 1;
Frobinate@0(Foo foo @0, BazOptions baz @1, Port port @2);
};
......
......@@ -15,6 +15,11 @@ class {{interface.name}} {
typedef mojo::NoInterface Client_;
{% endif %}
{#--- Constants #}
{%- for constant in interface.constants %}
static const {{constant.kind|cpp_pod_type}} {{constant.name}};
{%- endfor %}
{#--- Enums #}
{%- for enum in interface.enums %}
{% macro enum_def() %}{% include "enum_declaration.tmpl" %}{% endmacro %}
......
......@@ -59,6 +59,13 @@ params->{{param.name}}()
builder.Finish(&message);
{%- endmacro %}
{#--- Begin #}
{#--- Constants #}
{% for constant in interface.constants %}
const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}} = {{constant.value|expression_to_text(module)}};
{%- endfor %}
{#--- ForwardToCallback definition #}
{%- for method in interface.methods -%}
{%- if method.response_parameters != None %}
......
......@@ -15,6 +15,12 @@
{%- for namespace in namespaces_as_array %}
namespace {{namespace}} {
{%- endfor %}
{#--- Constants #}
{% for constant in module.constants %}
const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant.value|expression_to_text(module)}};
{%- endfor %}
namespace internal {
namespace {
......@@ -50,6 +56,13 @@ const uint32_t {{method_name}} = {{method.ordinal}};
} // namespace internal
{#--- Struct Constants #}
{%- for struct in structs %}
{% for constant in struct.constants %}
const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}} = {{constant.value|expression_to_text(module)}};
{%- endfor %}
{%- endfor %}
{#--- Struct builder definitions #}
{%- for struct in structs %}
{%- include "struct_builder_definition.tmpl" %}
......
......@@ -22,6 +22,11 @@
namespace {{namespace}} {
{%- endfor %}
{#--- Constants #}
{% for constant in module.constants %}
extern const {{constant.kind|cpp_pod_type}} {{constant.name}};
{%- endfor %}
{#--- Enums #}
{% for enum in enums %}
{% include "enum_declaration.tmpl" %}
......
{%- import "struct_macros.tmpl" as struct_macros %}
{%- set class_name = struct.name ~ "_Data" %}
// static
{{class_name}}* {{class_name}}::New(mojo::Buffer* buf, mojo::Buffer::Destructor dtor) {
return new (buf->Allocate(sizeof({{class_name}}), dtor)) {{class_name}}();
......
......@@ -2,6 +2,11 @@ class {{struct.name}} {
public:
typedef internal::{{struct.name}}_Data Data;
{#--- Constants #}
{% for constant in struct.constants %}
static const {{constant.kind|cpp_pod_type}} {{constant.name}};
{%- endfor %}
{#--- Enums #}
{%- for enum in struct.enums -%}
{% macro enum_def() %}{% include "enum_declaration.tmpl" %}{% endmacro %}
......
......@@ -109,3 +109,9 @@ params.{{parameter.name}}{% if not loop.last %}, {% endif -%}
{{enum_def("%sProxy.%s"|format(interface.name, enum.name), enum, module)}}
{{interface.name}}Stub.{{enum.name}} = {{interface.name}}Proxy.{{enum.name}};
{%- endfor %}
{#--- Constants. #}
{% for constant in interface.constants %}
{{interface.name}}Proxy.{{constant.name}} = {{constant.value|expression_to_text(module)}};
{{interface.name}}Stub.{{constant.name}} = {{interface.name}}Proxy.{{constant.name}};
{% endfor %}
......@@ -14,9 +14,14 @@ define("{{module.path}}", [
{%- endfor -%}
) {
{#--- Constants #}
{% for constant in module.constants %}
var {{constant.name}} = {{constant.value|expression_to_text(module)}};
{%- endfor %}
{#--- Enums #}
{% from "enum_definition.tmpl" import enum_def -%}
{% for enum in enums %}
{%- from "enum_definition.tmpl" import enum_def %}
{%- for enum in enums %}
var {{ enum_def(enum.name, enum, module) }}
{%- endfor %}
......@@ -31,6 +36,9 @@ define("{{module.path}}", [
{%- endfor %}
var exports = {};
{% for constant in module.constants %}
exports.{{constant.name}} = {{constant.name}};
{%- endfor %}
{%- for enum in enums %}
exports.{{enum.name}} = {{enum.name}};
{%- endfor %}
......
......@@ -40,15 +40,23 @@ tmp{{depth}}.{{subfield.name}} = {{result}};
{%- endmacro %}
{#--- Begin #}
{#--- Enums. We must define these before the constructor because they
may be used there. Later, we alias these to be class static variables. #}
function {{struct.name}}() {
this.initDefaults_();
}
{#--- Enums #}
{%- from "enum_definition.tmpl" import enum_def %}
{% for enum in struct.enums %}
var {{enum_def("%s_%s"|format(struct.name, enum.name), enum, module)}}
{{enum_def("%s.%s"|format(struct.name, enum.name), enum, module)}}
{% endfor %}
{#--- Constructor #}
function {{struct.name}}() {
{#--- Constants #}
{% for constant in struct.constants %}
{{struct.name}}.{{constant.name}} = {{constant.value|expression_to_text(module)}};
{% endfor %}
{#--- Set up defaults #}
{{struct.name}}.prototype.initDefaults_ = function() {
{%- for packed_field in struct.packed.packed_fields %}
{%- if packed_field.field.default %}
{%- filter indent(4) %}
......@@ -60,12 +68,7 @@ this.{{packed_field.field.name}} = {{result}};
this.{{packed_field.field.name}} = {{packed_field.field|default_value}};
{%- endif %}
{%- endfor %}
}
{#--- Alias any Struct_Enum enums to Struct.Enum #}
{% for enum in struct.enums %}
{{struct.name}}.{{enum.name}} = {{struct.name}}_{{enum.name}};
{%- endfor %}
};
{#--- Encoding and decoding #}
......
......@@ -57,6 +57,11 @@ def GetCppType(kind):
return "mojo::internal::String_Data*"
return _kind_to_cpp_type[kind]
def GetCppPodType(kind):
if kind.spec == 's':
return "char*"
return _kind_to_cpp_type[kind]
def GetCppArrayArgWrapperType(kind):
if isinstance(kind, (mojom.Struct, mojom.Enum)):
return GetNameForKind(kind)
......@@ -148,15 +153,15 @@ def IsStructWithHandles(struct):
return False
def TranslateConstants(token, module):
if isinstance(token, mojom.Constant):
# Enum constants are constructed like:
# Namespace::Struct::FIELD_NAME
if isinstance(token, (mojom.NamedValue, mojom.EnumValue)):
# Both variable and enum constants are constructed like:
# Namespace::Struct::CONSTANT_NAME
name = []
if token.imported_from:
name.extend(NamespaceToArray(token.namespace))
if token.parent_kind:
name.append(token.parent_kind.name)
name.append(token.name[1])
name.append(token.name)
return "::".join(name)
return token
......@@ -173,8 +178,9 @@ class Generator(generator.Generator):
cpp_filters = {
"cpp_const_wrapper_type": GetCppConstWrapperType,
"cpp_field_type": GetCppFieldType,
"cpp_type": GetCppType,
"cpp_pod_type": GetCppPodType,
"cpp_result_type": GetCppResultWrapperType,
"cpp_type": GetCppType,
"cpp_wrapper_type": GetCppWrapperType,
"expression_to_text": ExpressionToText,
"get_pad": pack.GetPad,
......
......@@ -36,9 +36,9 @@ def JavaScriptDefaultValue(field):
if field.kind in mojom.PRIMITIVES:
return _kind_to_javascript_default_value[field.kind]
if isinstance(field.kind, mojom.Struct):
return "null";
return "null"
if isinstance(field.kind, mojom.Array):
return "[]";
return "[]"
if isinstance(field.kind, mojom.Interface):
return _kind_to_javascript_default_value[mojom.MSGPIPE]
if isinstance(field.kind, mojom.Enum):
......@@ -48,11 +48,11 @@ def JavaScriptDefaultValue(field):
def JavaScriptPayloadSize(packed):
packed_fields = packed.packed_fields
if not packed_fields:
return 0;
return 0
last_field = packed_fields[-1]
offset = last_field.offset + last_field.size
pad = pack.GetPad(offset, 8)
return offset + pad;
return offset + pad
_kind_to_codec_type = {
......@@ -92,11 +92,11 @@ def CodecType(kind):
def JavaScriptDecodeSnippet(kind):
if kind in mojom.PRIMITIVES:
return "decodeStruct(%s)" % CodecType(kind);
return "decodeStruct(%s)" % CodecType(kind)
if isinstance(kind, mojom.Struct):
return "decodeStructPointer(%s)" % CodecType(kind.name);
return "decodeStructPointer(%s)" % CodecType(kind.name)
if isinstance(kind, mojom.Array):
return "decodeArrayPointer(%s)" % CodecType(kind.kind);
return "decodeArrayPointer(%s)" % CodecType(kind.kind)
if isinstance(kind, mojom.Interface):
return JavaScriptDecodeSnippet(mojom.MSGPIPE)
if isinstance(kind, mojom.Enum):
......@@ -105,28 +105,29 @@ def JavaScriptDecodeSnippet(kind):
def JavaScriptEncodeSnippet(kind):
if kind in mojom.PRIMITIVES:
return "encodeStruct(%s, " % CodecType(kind);
return "encodeStruct(%s, " % CodecType(kind)
if isinstance(kind, mojom.Struct):
return "encodeStructPointer(%s, " % CodecType(kind.name);
return "encodeStructPointer(%s, " % CodecType(kind.name)
if isinstance(kind, mojom.Array):
return "encodeArrayPointer(%s, " % CodecType(kind.kind);
return "encodeArrayPointer(%s, " % CodecType(kind.kind)
if isinstance(kind, mojom.Interface):
return JavaScriptEncodeSnippet(mojom.MSGPIPE)
if isinstance(kind, mojom.Enum):
return JavaScriptEncodeSnippet(mojom.INT32)
def TranslateConstants(token, module):
if isinstance(token, mojom.Constant):
# Enum constants are constructed like:
# NamespaceUid.Struct_Enum.FIELD_NAME
if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
# Both variable and enum constants are constructed like:
# NamespaceUid.Struct[.Enum].CONSTANT_NAME
name = []
if token.imported_from:
name.append(token.imported_from["unique_name"])
if token.parent_kind:
name.append(token.parent_kind.name + "_" + token.name[0])
else:
name.append(token.name[0])
name.append(token.name[1])
name.append(token.parent_kind.name)
if isinstance(token, mojom.EnumValue):
name.append(token.enum_name)
name.append(token.name)
return ".".join(name)
return token
......
......@@ -65,17 +65,17 @@ def LookupKind(kinds, spec, scope):
return kinds.get(spec)
def LookupConstant(constants, name, scope):
"""Like LookupKind, but for constants."""
def LookupValue(values, name, scope):
"""Like LookupKind, but for constant values."""
for i in xrange(len(scope), -1, -1):
if i > 0:
test_spec = '.'.join(scope[:i]) + '.'
test_spec += name
constant = constants.get(test_spec)
if constant:
return constant
value = values.get(test_spec)
if value:
return value
return constants.get(name)
return values.get(name)
def KindToData(kind):
return kind.spec
......@@ -114,12 +114,12 @@ def ImportFromData(module, data):
kind.imported_from is None):
kind = KindFromImport(kind, import_item)
module.kinds[kind.spec] = kind
# Ditto for constants.
for constant in import_module.constants.itervalues():
if constant.imported_from is None:
constant = copy.deepcopy(constant)
constant.imported_from = import_item
module.constants[constant.GetSpec()] = constant
# Ditto for values.
for value in import_module.values.itervalues():
if value.imported_from is None:
value = copy.deepcopy(value)
value.imported_from = import_item
module.values[value.GetSpec()] = value
return import_item
......@@ -137,6 +137,8 @@ def StructFromData(module, data):
module.kinds[struct.spec] = struct
struct.enums = map(lambda enum:
EnumFromData(module, enum, struct), data['enums'])
struct.constants = map(lambda constant:
ConstantFromData(module, constant, struct), data['constants'])
# Stash fields data here temporarily.
struct.fields_data = data['fields']
return struct
......@@ -160,9 +162,9 @@ def FixupExpression(module, value, scope):
else:
value[i] = FixupExpression(module, value[i], scope)
elif value:
constant = LookupConstant(module.constants, value, scope)
if constant:
return constant
result = LookupValue(module.values, value, scope)
if result:
return result
return value
def FieldFromData(module, data, struct):
......@@ -233,6 +235,8 @@ def InterfaceFromData(module, data):
module.kinds[interface.spec] = interface
interface.enums = map(lambda enum:
EnumFromData(module, enum, interface), data['enums'])
interface.constants = map(lambda constant:
ConstantFromData(module, constant, interface), data['constants'])
# Stash methods data here temporarily.
interface.methods_data = data['methods']
return interface
......@@ -240,14 +244,18 @@ def InterfaceFromData(module, data):
def EnumFieldFromData(module, enum, data, parent_kind):
field = mojom.EnumField()
field.name = data['name']
# TODO(mpcomplete): FixupExpression should be done in the second pass,
# so constants and enums can refer to each other.
# TODO(mpcomplete): But then, what if constants are initialized to an enum? Or
# vice versa?
if parent_kind:
field.value = FixupExpression(
module, data['value'], (module.namespace, parent_kind.name))
else:
field.value = FixupExpression(
module, data['value'], (module.namespace, ))
constant = mojom.Constant(module, enum, field)
module.constants[constant.GetSpec()] = constant
value = mojom.EnumValue(module, enum, field)
module.values[value.GetSpec()] = value
return field
def EnumFromData(module, data, parent_kind):
......@@ -265,6 +273,21 @@ def EnumFromData(module, data, parent_kind):
module.kinds[enum.spec] = enum
return enum
def ConstantFromData(module, data, parent_kind):
constant = mojom.Constant()
constant.name = data['name']
if parent_kind:
scope = (module.namespace, parent_kind.name)
else:
scope = (module.namespace, )
# TODO(mpcomplete): maybe we should only support POD kinds.
constant.kind = KindFromData(module.kinds, data['kind'], scope)
constant.value = FixupExpression(module, data.get('value'), scope)
value = mojom.NamedValue(module, parent_kind, constant.name)
module.values[value.GetSpec()] = value
return constant
def ModuleToData(module):
return {
istr(0, 'name'): module.name,
......@@ -279,7 +302,7 @@ def ModuleFromData(data):
for kind in mojom.PRIMITIVES:
module.kinds[kind.spec] = kind
module.constants = {}
module.values = {}
module.name = data['name']
module.namespace = data['namespace']
......@@ -298,6 +321,9 @@ def ModuleFromData(data):
module.interfaces = map(
lambda interface: InterfaceFromData(module, interface),
data['interfaces'])
module.constants = map(
lambda constant: ConstantFromData(module, constant, None),
data['constants'])
# Second pass expands fields and methods. This allows fields and parameters
# to refer to kinds defined anywhere in the mojom.
......
......@@ -60,18 +60,31 @@ PRIMITIVES = (
)
class Constant(object):
def __init__(self, module, enum, field):
class NamedValue(object):
def __init__(self, module, parent_kind, name):
self.module = module
self.namespace = module.namespace
self.parent_kind = enum.parent_kind
self.name = [enum.name, field.name]
self.parent_kind = parent_kind
self.name = name
self.imported_from = None
def GetSpec(self):
return (self.namespace + '.' +
(self.parent_kind and (self.parent_kind.name + '.') or "") + \
self.name[1])
(self.parent_kind and (self.parent_kind.name + '.') or "") +
self.name)
class EnumValue(NamedValue):
def __init__(self, module, enum, field):
NamedValue.__init__(self, module, enum.parent_kind, field.name)
self.enum_name = enum.name
class Constant(object):
def __init__(self, name=None, kind=None, value=None):
self.name = name
self.kind = kind
self.value = value
class Field(object):
......
......@@ -67,6 +67,7 @@ class Lexer(object):
'STRUCT',
'INTERFACE',
'ENUM',
'CONST',
)
keyword_map = {}
......
......@@ -101,7 +101,8 @@ class Parser(object):
def p_definition(self, p):
"""definition : struct
| interface
| enum"""
| enum
| const"""
p[0] = p[1]
def p_attribute_section(self, p):
......@@ -131,6 +132,7 @@ class Parser(object):
def p_struct_body(self, p):
"""struct_body : field struct_body
| enum struct_body
| const struct_body
| """
if len(p) > 1:
p[0] = _ListFromConcat(p[1], p[2])
......@@ -154,6 +156,7 @@ class Parser(object):
def p_interface_body(self, p):
"""interface_body : method interface_body
| enum interface_body
| const interface_body
| """
if len(p) > 1:
p[0] = _ListFromConcat(p[1], p[2])
......@@ -243,6 +246,10 @@ class Parser(object):
else:
p[0] = ('ENUM_FIELD', p[1], p[3])
def p_const(self, p):
"""const : CONST typename NAME EQUALS expression SEMI"""
p[0] = ('CONST', p[2], p[3], p[5])
### Expressions ###
def p_expression_object(self, p):
......
......@@ -83,6 +83,7 @@ def _MapStruct(tree):
struct['attributes'] = _MapAttributes(tree[2])
struct['fields'] = _MapTree(_MapField, tree[3], 'FIELD')
struct['enums'] = _MapTree(_MapEnum, tree[3], 'ENUM')
struct['constants'] = _MapTree(_MapConstant, tree[3], 'CONST')
return struct
def _MapInterface(tree):
......@@ -91,6 +92,7 @@ def _MapInterface(tree):
interface['client'] = _GetAttribute(tree[2], 'Client')
interface['methods'] = _MapTree(_MapMethod, tree[3], 'METHOD')
interface['enums'] = _MapTree(_MapEnum, tree[3], 'ENUM')
interface['constants'] = _MapTree(_MapConstant, tree[3], 'CONST')
return interface
def _MapEnum(tree):
......@@ -99,6 +101,13 @@ def _MapEnum(tree):
enum['fields'] = _MapTree(_MapEnumField, tree[2], 'ENUM_FIELD')
return enum
def _MapConstant(tree):
constant = {}
constant['name'] = tree[2]
constant['kind'] = _MapKind(tree[1])
constant['value'] = tree[3]
return constant
def _MapModule(tree, name):
mojom = {}
mojom['name'] = name
......@@ -107,6 +116,7 @@ def _MapModule(tree, name):
mojom['structs'] = _MapTree(_MapStruct, tree[3], 'STRUCT')
mojom['interfaces'] = _MapTree(_MapInterface, tree[3], 'INTERFACE')
mojom['enums'] = _MapTree(_MapEnum, tree[3], 'ENUM')
mojom['constants'] = _MapTree(_MapConstant, tree[3], 'CONST')
return mojom
def _MapImport(tree):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment