Skip to content

Commit

Permalink
Fix a bug with mojom imports where 2 imports with the same namespace …
Browse files Browse the repository at this point in the history
…would

cause a variable name collision in JS.

BUG=325500
R=darin@chromium.org

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@251105 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
mpcomplete@google.com committed Feb 13, 2014
1 parent d42a9d5 commit df7de69
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 53 deletions.
7 changes: 6 additions & 1 deletion mojo/apps/js/bindings/sample_service_unittests.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ define([
expect(full.point.y).toBe(15);

expect(full.shape_masks.length).toBe(1);
expect(full.shape_masks[0]).toBe(1 << imported.SHAPE_RECTANGLE);
// TODO(mpcomplete): This is broken.
// http://crbug.com/320082
// expect(full.shape_masks[0]).toBe(1 << imported.Shape.SHAPE_RECTANGLE);

//expect(full.thing.shape).toBe(imported.Shape.SHAPE_RECTANGLE);
//expect(full.thing.color).toBe(imported.Color.COLOR_RED);
}

function ServiceImpl() {
Expand Down
1 change: 1 addition & 0 deletions mojo/mojo_public.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@
'sources': [
'public/bindings/tests/sample_service.mojom',
'public/bindings/tests/sample_import.mojom',
'public/bindings/tests/sample_import2.mojom',
],
'includes': [ 'public/bindings/mojom_bindings_generator.gypi' ],
'export_dependent_settings': [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ define([
{%- endfor %}
], function(core, codec
{%- for import in imports -%}
, {{import.namespace}}
, {{import.unique_name}}
{%- endfor -%}
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ tmp{{depth}}.{{subfield.name}} = {{result}};
}
{#--- POD types ---#}
{%- else -%}
{{caller(value)}}
{{caller(value|substitute_namespace(imports))}}
{%- endif %}
{%- endmacro %}

Expand Down
29 changes: 7 additions & 22 deletions mojo/public/bindings/generators/mojom_cpp_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ class Generator(mojom_generator.Generator):
"verify_token_type": mojom_generator.VerifyTokenType,
}

@UseJinja("cpp_templates/module.h.tmpl", filters=cpp_filters)
def GenerateModuleHeader(self):
def GetJinjaExports(self):
return {
"module_name": self.module.name,
"namespace": self.module.namespace,
Expand All @@ -144,31 +143,17 @@ def GenerateModuleHeader(self):
"include_prefix": self.GetIncludePrefix(),
}

@UseJinja("cpp_templates/module.h.tmpl", filters=cpp_filters)
def GenerateModuleHeader(self):
return self.GetJinjaExports()

@UseJinja("cpp_templates/module_internal.h.tmpl", filters=cpp_filters)
def GenerateModuleInternalHeader(self):
return {
"module_name": self.module.name,
"namespace": self.module.namespace,
"imports": self.module.imports,
"kinds": self.module.kinds,
"enums": self.module.enums,
"structs": self.GetStructs(),
"interfaces": self.module.interfaces,
"include_prefix": self.GetIncludePrefix(),
}
return self.GetJinjaExports()

@UseJinja("cpp_templates/module.cc.tmpl", filters=cpp_filters)
def GenerateModuleSource(self):
return {
"module_name": self.module.name,
"namespace": self.module.namespace,
"imports": self.module.imports,
"kinds": self.module.kinds,
"enums": self.module.enums,
"structs": self.GetStructs(),
"interfaces": self.module.interfaces,
"include_prefix": self.GetIncludePrefix(),
}
return self.GetJinjaExports()

def GenerateFiles(self):
self.Write(self.GenerateModuleHeader(), "%s.h" % self.module.name)
Expand Down
27 changes: 25 additions & 2 deletions mojo/public/bindings/generators/mojom_js_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,19 @@ def JavaScriptEncodeSnippet(kind):
return JavaScriptEncodeSnippet(mojom.MSGPIPE)


def SubstituteNamespace(value, imports):
for import_item in imports:
value = value.replace(import_item["namespace"] + ".",
import_item["unique_name"] + ".")
return value


def JavascriptType(kind):
if kind.imported_from:
return kind.imported_from["unique_name"] + "." + kind.name
return kind.name


class Generator(mojom_generator.Generator):

js_filters = {
Expand All @@ -159,15 +172,16 @@ class Generator(mojom_generator.Generator):
"is_object_kind": mojom_generator.IsObjectKind,
"is_string_kind": mojom_generator.IsStringKind,
"is_array_kind": lambda kind: isinstance(kind, mojom.Array),
"js_type": lambda kind: kind.GetFullName("."),
"js_type": JavascriptType,
"stylize_method": mojom_generator.StudlyCapsToCamel,
"substitute_namespace": SubstituteNamespace,
"verify_token_type": mojom_generator.VerifyTokenType,
}

@UseJinja("js_templates/module.js.tmpl", filters=js_filters)
def GenerateJsModule(self):
return {
"imports": self.module.imports,
"imports": self.GetImports(),
"kinds": self.module.kinds,
"enums": self.module.enums,
"structs": self.GetStructs() + self.GetStructsFromMethods(),
Expand All @@ -176,3 +190,12 @@ def GenerateJsModule(self):

def GenerateFiles(self):
self.Write(self.GenerateJsModule(), "%s.js" % self.module.name)

def GetImports(self):
# Since each import is assigned a variable in JS, they need to have unique
# names.
counter = 1
for each in self.module.imports:
each["unique_name"] = "import" + str(counter)
counter += 1
return self.module.imports
14 changes: 9 additions & 5 deletions mojo/public/bindings/mojom_bindings_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ def LoadGenerators(generators_string):

def ProcessFile(args, generator_modules, filename, processed_files):
# Ensure we only visit each file once.
processed_files.append(filename)
if filename in processed_files:
if processed_files[filename] is None:
raise Exception("Circular dependency: " + filename)
return processed_files[filename]
processed_files[filename] = None

dirname, name = os.path.split(filename)
name = os.path.splitext(name)[0]
Expand All @@ -61,16 +65,16 @@ def ProcessFile(args, generator_modules, filename, processed_files):
# We use these to generate proper type info.
for import_data in mojom['imports']:
import_filename = os.path.join(dirname, import_data['filename'])
if import_filename not in processed_files:
import_data['module'] = ProcessFile(
args, generator_modules, import_filename, processed_files)
import_data['module'] = ProcessFile(
args, generator_modules, import_filename, processed_files)

module = mojom_data.OrderedModuleFromData(mojom)
for generator_module in generator_modules:
generator = generator_module.Generator(module, args.include_dir,
args.output_dir)
generator.GenerateFiles()

processed_files[filename] = module
return module

def Main():
Expand All @@ -95,7 +99,7 @@ def Main():
os.makedirs(args.output_dir)

for filename in args.filename:
ProcessFile(args, generator_modules, filename, [])
ProcessFile(args, generator_modules, filename, {})

return 0

Expand Down
14 changes: 7 additions & 7 deletions mojo/public/bindings/pylib/generate/mojom.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def __init__(self, name = None, kind = None, ordinal = None, default = None):
class Struct(Kind):
def __init__(self, name = None):
self.name = name
self.imported_from_namespace = None
self.imported_from = None
if name != None:
spec = 'x:' + name
else:
Expand All @@ -78,11 +78,11 @@ def __init__(self, name = None):
self.fields = []

@classmethod
def CreateFromImport(cls, kind, import_namespace):
def CreateFromImport(cls, kind, imported_from):
"""Used with 'import module' - clones the kind imported from the
given module's namespace."""
kind = copy.deepcopy(kind)
kind.imported_from_namespace = import_namespace
kind.imported_from = imported_from
return kind

def AddField(self, name, kind, ordinal = None, default = None):
Expand All @@ -92,16 +92,16 @@ def AddField(self, name, kind, ordinal = None, default = None):

def GetFullName(self, separator):
"""Returns the fully qualified type name, including namespace prefix."""
if self.imported_from_namespace:
return separator.join([self.imported_from_namespace, self.name])
if self.imported_from:
return separator.join([self.imported_from["namespace"], self.name])
return self.name

def GetFullNameInternal(self, separator):
"""Returns the fully qualified type name for an internal data structure,
including namespace prefix."""
if self.imported_from_namespace:
if self.imported_from:
return separator.join(
[self.imported_from_namespace, "internal", self.name])
[self.imported_from["namespace"], "internal", self.name])
return self.name


Expand Down
27 changes: 14 additions & 13 deletions mojo/public/bindings/pylib/generate/mojom_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,19 @@ def KindFromData(kinds, data):
kinds[data] = kind
return kind

def ImportFromData(data):
def ImportFromData(module, data):
import_module = data['module']

import_item = {}
import_item['module_name'] = import_module.name
import_item['namespace'] = import_module.namespace

# Copy the struct kinds from our imports into the current module.
for kind in import_module.kinds.itervalues():
# TODO(mpcomplete): Handle enums
if isinstance(kind, mojom.Struct) and kind.imported_from is None:
kind = mojom.Struct.CreateFromImport(kind, import_item)
module.kinds[kind.spec] = kind
return import_item

def StructToData(struct):
Expand Down Expand Up @@ -182,25 +189,19 @@ def ModuleFromData(data):
module.kinds = {}
for kind in mojom.PRIMITIVES:
module.kinds[kind.spec] = kind
# Copy the struct kinds from our imports into the current module.
for import_data in data['imports']:
import_module = import_data['module']
for kind in import_module.kinds.itervalues():
# TODO(mpcomplete): Handle enums
if isinstance(kind, mojom.Struct):
kind = mojom.Struct.CreateFromImport(
kind, import_module.namespace)
module.kinds[kind.spec] = kind

module.name = data['name']
module.namespace = data['namespace']
# Imports must come first, because they add to module.kinds which is used
# by by the others.
module.imports = map(
lambda import_data: ImportFromData(import_data), data['imports'])
lambda import_data: ImportFromData(module, import_data),
data['imports'])
module.structs = map(
lambda struct: StructFromData(module, struct), data['structs'])
module.interfaces = map(
lambda interface:
InterfaceFromData(module, interface), data['interfaces'])
lambda interface: InterfaceFromData(module, interface),
data['interfaces'])
if data.has_key('enums'):
module.enums = map(
lambda enum: EnumFromData(module.kinds, enum), data['enums'])
Expand Down
2 changes: 1 addition & 1 deletion mojo/public/bindings/tests/sample_import.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module imported {
// sample_service.mojom, to show how import works.

enum Shape {
SHAPE_RECTANGLE,
SHAPE_RECTANGLE = 1,
SHAPE_CIRCLE,
SHAPE_TRIANGLE,
};
Expand Down
29 changes: 29 additions & 0 deletions mojo/public/bindings/tests/sample_import2.mojom
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2014 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 "sample_import.mojom"

module imported {

// This sample adds more types and constants to the "imported" namespace,
// to test a bug with importing multiple modules with the same namespace.

enum Color {
COLOR_RED,
COLOR_BLACK,
};

struct Size {
int32 width;
int32 height;
};

struct Thing {
int32 shape;
int32 color;
Point location = {0, 0};
Size size;
};

} // module imported
2 changes: 2 additions & 0 deletions mojo/public/bindings/tests/sample_service.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

import "sample_import.mojom"
import "sample_import2.mojom"

module sample {

Expand Down Expand Up @@ -47,6 +48,7 @@ struct DefaultsTest {
uint8[] data = [1, 2, 3] @2;
imported.Point point = {7, 15} @3;
int32[] shape_masks = [1 << imported.SHAPE_RECTANGLE] @4;
imported.Thing thing = {imported.SHAPE_RECTANGLE, imported.COLOR_RED};
};

interface Port {
Expand Down
3 changes: 3 additions & 0 deletions mojo/public/bindings/tests/sample_service_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ TEST(BindingsSampleTest, DefaultValues) {

EXPECT_EQ(1u, full.shape_masks().size());
EXPECT_EQ(1 << imported::SHAPE_RECTANGLE, full.shape_masks()[0]);

EXPECT_EQ(imported::SHAPE_RECTANGLE, full.thing().shape());
EXPECT_EQ(imported::COLOR_RED, full.thing().color());
}

} // namespace sample

0 comments on commit df7de69

Please sign in to comment.