0

Implement ts_import for typemap

This introduces a ts_import path configuration for ts_typemap. When
present, both the convert and webui code will import the necessary
deps.

Updated the nested test case to be a bit more interesting.

Sample output:
webui: gpaste/6232011783274496
converters: gpaste/4842181136482304


Bug: 40615900
Change-Id: I51eb269f4a76b8be629b7e2de159fd397f7d99fe
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5827800
Reviewed-by: Ken Rockot <rockot@google.com>
Reviewed-by: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: Charlie Reis <creis@chromium.org>
Commit-Queue: Fred Shih <ffred@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1352268}
This commit is contained in:
Fred Shih
2024-09-06 20:50:54 +00:00
committed by Chromium LUCI CQ
parent 6cd0f9325b
commit 8275fba70f
13 changed files with 116 additions and 29 deletions

@ -1183,10 +1183,8 @@ mojom("web_ui_ts_test_mojo_bindings") {
},
{
mojom = "content.mojom.NestedMappedType"
# TODO(ffred): we should allow ts_import so we can typemap to user
# defined types.
ts = "Object"
ts = "TestNode"
ts_import = "./web_ui_mojo_ts_test_node.js"
converter = "NestedTypeConverter"
import = "web_ui_mojo_ts_test_converters.js"
},
@ -1200,6 +1198,7 @@ preprocess_if_expr("preprocess_mojo_webui_test") {
out_folder = "$target_gen_dir/data"
in_files = [
"web_ui_mojo_ts_test.ts",
"web_ui_mojo_ts_test_node.ts",
"web_ui_mojo_ts_test_converters.ts",
"web_ui_managed_interface_test.ts",
]
@ -1210,6 +1209,7 @@ webui_ts_library("web_ui_mojo_test_build_ts") {
out_dir = "$target_gen_dir/data/tsc"
in_files = [
"web_ui_mojo_ts_test.ts",
"web_ui_mojo_ts_test_node.ts",
"web_ui_mojo_ts_test_converters.ts",
"web_ui_ts_test.test-mojom-converters.ts",
"web_ui_ts_test.test-mojom-webui.ts",

@ -7956,6 +7956,7 @@ data/web_ui_mojo_test.js
data/web_ui_mojo_ts_test.html
data/web_ui_mojo_ts_test.ts
data/web_ui_mojo_ts_test_converters.ts
data/web_ui_mojo_ts_test_node.ts
data/web_ui_shared_worker.js
data/web_ui_test.test-mojom
data/web_ui_test_types.test-mojom

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {TestNode} from './web_ui_mojo_ts_test_node.js';
import {OptionalNumericsStruct, TestEnum, WebUITsMojoTestCache} from './web_ui_ts_test.test-mojom-webui.js';
const TEST_DATA: Array<{url: string, contents: string}> = [
@ -76,7 +77,7 @@ async function doTest(): Promise<boolean> {
} =
await cache.echo(
true, null, TestEnum.kOne, testStruct, [], [], [], {}, {}, {}, '',
{});
new TestNode());
if (optionalBool !== false) {
return false;
}
@ -130,7 +131,8 @@ async function doTest(): Promise<boolean> {
} =
await cache.echo(
null, 1, null, testStruct, inOptionalBools, inOptionalInts,
inOptionalEnums, inBoolMap, inIntMap, inEnumMap, '', {});
inOptionalEnums, inBoolMap, inIntMap, inEnumMap, '',
new TestNode());
if (optionalBool !== null) {
return false;
}
@ -168,7 +170,7 @@ async function doTest(): Promise<boolean> {
{
const str = 'foobear';
const result = await cache.echo(
null, 1, null, testStruct, [], [], [], {}, {}, {}, str, {});
null, 1, null, testStruct, [], [], [], {}, {}, {}, str, new TestNode());
if (result.simpleMappedType !== str) {
return false;
@ -178,32 +180,30 @@ async function doTest(): Promise<boolean> {
// Tests an empty nested struct to test basic encoding/decoding.
{
const result = await cache.echo(
null, 1, null, testStruct, [], [], [], {}, {}, {}, '', {});
null, 1, null, testStruct, [], [], [], {}, {}, {}, '', new TestNode());
assertObjectEquals(
{}, result.nestedMappedType,
new TestNode(), result.nestedMappedType,
'nested mappped type: got: ' + JSON.stringify(result.nestedMappedType) +
', expected: {}');
', expected: {next: null}');
}
// Tests a nested type where a struct includes itself.
{
const depth = 10;
const testNested: any = {};
let cursor = testNested;
const chain: TestNode = new TestNode();
let cursor = chain;
for (let i = 0; i < depth; ++i) {
cursor.nested = {} as Object;
cursor = cursor.nested;
cursor = cursor!.next = new TestNode();
}
const result = await cache.echo(
null, 1, null, testStruct, [], [], [], {}, {}, {}, '', testNested);
null, 1, null, testStruct, [], [], [], {}, {}, {}, '', chain);
if (JSON.stringify(testNested) !==
JSON.stringify(result.nestedMappedType)) {
if (JSON.stringify(chain) !== JSON.stringify(result.nestedMappedType)) {
throw new Error(
'nested mappped type: got: ' +
JSON.stringify(result.nestedMappedType) +
', expected: ' + JSON.stringify(testNested));
', expected: ' + JSON.stringify(chain));
}
}

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {TestNode} from './web_ui_mojo_ts_test_node.js';
import {NestedMappedTypeDataView, NestedMappedTypeTypeMapper, SimpleMappedTypeDataView, SimpleMappedTypeTypeMapper,} from './web_ui_ts_test.test-mojom-converters.js';
export class SimpleTypeConverter implements SimpleMappedTypeTypeMapper<string> {
@ -14,12 +15,13 @@ export class SimpleTypeConverter implements SimpleMappedTypeTypeMapper<string> {
}
}
export class NestedTypeConverter implements NestedMappedTypeTypeMapper<Object> {
nested(mappedType: any): Object|null {
return mappedType?.nested || null;
export class NestedTypeConverter implements
NestedMappedTypeTypeMapper<TestNode> {
nested(mappedType: TestNode): TestNode|null {
return mappedType.next;
}
convert(dataView: NestedMappedTypeDataView): Object {
return dataView.nested() ? {'nested': dataView.nested()} : {};
convert(dataView: NestedMappedTypeDataView): TestNode {
return new TestNode(dataView.nested());
}
}

@ -0,0 +1,17 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This is effectively the same structure as NestedMappedType mojom,
// except it is implemented purely in ts and has a different field name
// to exercise the conversion logic.
//
// Using a class instead of an interface in order to have a constructor to
// instantiate objects of this type with "new"".
export class TestNode {
next: TestNode|null;
constructor(next?: TestNode|null) {
this.next = next || null;
}
}

@ -32,6 +32,7 @@ This file specifies resources for content_browsertests.
<include name="IDR_WEB_UI_TS_TEST_MOJOM_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_ts_test.test-mojom-webui.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_ts_test.test-mojom-webui.js" />
<include name="IDR_WEB_UI_TS_TEST_MOJOM_CONVERTER_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_ts_test.test-mojom-converters.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_ts_test.test-mojom-converters.js" />
<include name="IDR_WEB_UI_TS_TEST_CONVERTERS_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_mojo_ts_test_converters.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_mojo_ts_test_converters.js" />
<include name="IDR_WEB_UI_TS_TEST_NODE_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_mojo_ts_test_node.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_mojo_ts_test_node.js" />
<include name="IDR_WEB_UI_TS_TEST_TYPES_MOJOM_JS" file="${root_gen_dir}/content/test/data/tsc/web_ui_ts_test_types.test-mojom-webui.js" use_base_dir="false" type="BINDATA" resource_path="web_ui_ts_test_types.test-mojom-webui.js" />
</includes>
</release>

@ -105,6 +105,7 @@ def LoadTsTypemapConfig(path):
for entry in config['types']:
configs[entry['mojom']] = {
'typename': entry['ts'],
'type_import': entry.get('ts_import', None),
'converter_import': entry['import'],
'converter': entry['converter'],
}

@ -245,6 +245,8 @@ class Generator(generator.Generator):
"unions": self.module.unions,
"generate_struct_deserializers": self.js_generate_struct_deserializers,
"typemapped_structs": self._TypeMappedStructs(),
"typemap_imports": self._TypeMapImports(),
"converter_imports": self._ConverterImports(),
}
@staticmethod
@ -685,3 +687,41 @@ class Generator(generator.Generator):
if struct.qualified_name in self.typemap:
mapped_structs[struct] = self.typemap[struct.qualified_name]
return mapped_structs
# Returns a list of imports in the format:
# {
# <import path>: [list of types],
# ...
# }
def _TypeMapImports(self):
imports = {}
typemaps = self._TypeMappedStructs()
for typemap in typemaps.values():
type_import = typemap['type_import']
# The typemapping could just be a native type, in which case there would
# not be any import.
if not type_import:
continue
imports.setdefault(type_import, []).append(typemap['typename'])
return imports
def _ConverterImports(self):
imports = {}
# TODO(ffred): we also need to import types from other *-mojom-webui
# dependencies.
typemapped_structs = self._TypeMappedStructs()
for struct in typemapped_structs:
for field in struct.fields:
if mojom.IsStructKind(field.kind):
qualified = field.kind.qualified_name
if qualified in self.typemap:
typemap = self.typemap[qualified]
typemap_import = typemap['type_import']
if typemap_import:
imports.setdefault(typemap_import, []).append(typemap['typename'])
return imports

@ -8,6 +8,15 @@ import type {
} from './{{module_filename}}';
{%- endfor %}
{% for path, types in converter_imports.items()|sort -%}
import type {
{%- for type in types|sort %}
{{type}}
{%- if not loop.last -%},{% endif -%}
{%- endfor %}
} from '{{path}}';
{% endfor %}
{%- for struct in typemapped_structs %}
export class {{struct.name}}DataView {

@ -27,6 +27,15 @@ import {
{%- endfor %}
{%- endif %}
{% for path, types in typemap_imports.items()|sort -%}
import type {
{%- for type in types|sort %}
{{type}}
{%- if not loop.last -%},{% endif -%}
{%- endfor %}
} from '{{path}}';
{% endfor %}
{% for typemap in typemapped_structs.values() -%}
import { {{typemap.converter}} } from './{{typemap.converter_import}}';
{% endfor -%}

@ -11,7 +11,9 @@ mojom("test_mojom") {
types = [
{
mojom = "bindings.tests.mojom.SimpleStruct"
ts = "string"
ts = "String16"
ts_import =
"//resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js"
converter = "SimpleStructConverter"
import = "test_converter.js"
},

@ -3,18 +3,22 @@
// found in the LICENSE file.
import { SimpleStructDataView, SimpleStructTypeMapper } from './test.test-mojom-converters.js';
import type {
String16
} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
export class SimpleStructConverter implements SimpleStructTypeMapper<string> {
str(_: string): string {
export class SimpleStructConverter implements SimpleStructTypeMapper<String16> {
str(_: String16): string {
return '';
}
number(_: string): number {
number(_: String16): number {
return 888;
}
convert(_: SimpleStructDataView): string {
return 'hihi';
convert(_: SimpleStructDataView): String16 {
return {data: []};
}
}

@ -16,6 +16,7 @@ def CheckTsTypemapConfigs(target_name, config_filename, out_filename):
])
_SUPPORTED_TYPE_KEYS = set([
'ts',
'ts_import',
'converter',
'import',
'mojom',