зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1609128 - Implement polymorphism in protocol.js r=jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D59333 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
2d3e4c8ba0
Коммит
8631fc037c
|
@ -21,6 +21,8 @@ function simpleHello() {
|
|||
// Predeclaring the actor type so that it can be used in the
|
||||
// implementation of the child actor.
|
||||
types.addActorType("childActor");
|
||||
types.addActorType("otherChildActor");
|
||||
types.addPolymorphicType("polytype", ["childActor", "otherChildActor"]);
|
||||
|
||||
const childSpec = protocol.generateActorSpec({
|
||||
typeName: "childActor",
|
||||
|
@ -200,6 +202,15 @@ class ChildFront extends protocol.FrontClassWithSpec(childSpec) {
|
|||
}
|
||||
protocol.registerFront(ChildFront);
|
||||
|
||||
const otherChildSpec = protocol.generateActorSpec({
|
||||
typeName: "otherChildActor",
|
||||
methods: {},
|
||||
events: {},
|
||||
});
|
||||
const OtherChildActor = protocol.ActorClassWithSpec(otherChildSpec, {});
|
||||
class OtherChildFront extends protocol.FrontClassWithSpec(otherChildSpec) {}
|
||||
protocol.registerFront(OtherChildFront);
|
||||
|
||||
types.addDictType("manyChildrenDict", {
|
||||
child5: "childActor",
|
||||
more: "array:childActor",
|
||||
|
@ -230,6 +241,17 @@ const rootSpec = protocol.generateActorSpec({
|
|||
request: { id: Arg(0) },
|
||||
response: { child: RetVal("temp:childActor") },
|
||||
},
|
||||
getPolymorphism: {
|
||||
request: { id: Arg(0, "number") },
|
||||
response: { child: RetVal("polytype") },
|
||||
},
|
||||
requestPolymorphism: {
|
||||
request: {
|
||||
id: Arg(0, "number"),
|
||||
actor: Arg(1, "polytype"),
|
||||
},
|
||||
response: { child: RetVal("polytype") },
|
||||
},
|
||||
clearTemporaryChildren: {},
|
||||
},
|
||||
});
|
||||
|
@ -296,6 +318,24 @@ const RootActor = protocol.ActorClassWithSpec(rootSpec, {
|
|||
this._temporaryHolder.destroy();
|
||||
delete this._temporaryHolder;
|
||||
},
|
||||
|
||||
getPolymorphism: function(id) {
|
||||
if (id == 0) {
|
||||
return new ChildActor(this.conn, id);
|
||||
} else if (id == 1) {
|
||||
return new OtherChildActor(this.conn);
|
||||
}
|
||||
throw new Error("Unexpected id");
|
||||
},
|
||||
|
||||
requestPolymorphism: function(id, actor) {
|
||||
if (id == 0 && actor instanceof ChildActor) {
|
||||
return actor;
|
||||
} else if (id == 1 && actor instanceof OtherChildActor) {
|
||||
return actor;
|
||||
}
|
||||
throw new Error("Unexpected id or actor");
|
||||
},
|
||||
});
|
||||
|
||||
class RootFront extends protocol.FrontClassWithSpec(rootSpec) {
|
||||
|
@ -363,6 +403,7 @@ add_task(async function() {
|
|||
await testEvents(trace);
|
||||
await testManyChildren(trace);
|
||||
await testGenerator(trace);
|
||||
await testPolymorphism(trace);
|
||||
|
||||
await client.close();
|
||||
});
|
||||
|
@ -641,3 +682,33 @@ async function testGenerator(trace) {
|
|||
Assert.ok(ret[1] !== childFront);
|
||||
Assert.ok(ret[1] instanceof ChildFront);
|
||||
}
|
||||
|
||||
async function testPolymorphism(trace) {
|
||||
// Check polymorphic types returned by an actor
|
||||
const firstChild = await rootFront.getPolymorphism(0);
|
||||
Assert.ok(firstChild instanceof ChildFront);
|
||||
|
||||
// Check polymorphic types passed to a front
|
||||
const sameFirstChild = await rootFront.requestPolymorphism(0, firstChild);
|
||||
Assert.ok(sameFirstChild instanceof ChildFront);
|
||||
Assert.equal(sameFirstChild, firstChild);
|
||||
|
||||
// Same with the second possible type
|
||||
const secondChild = await rootFront.getPolymorphism(1);
|
||||
Assert.ok(secondChild instanceof OtherChildFront);
|
||||
|
||||
const sameSecondChild = await rootFront.requestPolymorphism(1, secondChild);
|
||||
Assert.ok(sameSecondChild instanceof OtherChildFront);
|
||||
Assert.equal(sameSecondChild, secondChild);
|
||||
|
||||
// Check that any other type is rejected
|
||||
Assert.throws(() => {
|
||||
rootFront.requestPolymorphism(0, null);
|
||||
}, /Was expecting one of these actors 'childActor,otherChildActor' but instead got an empty value/);
|
||||
Assert.throws(() => {
|
||||
rootFront.requestPolymorphism(0, 42);
|
||||
}, /Was expecting one of these actors 'childActor,otherChildActor' but instead got value: '42'/);
|
||||
Assert.throws(() => {
|
||||
rootFront.requestPolymorphism(0, rootFront);
|
||||
}, /Was expecting one of these actors 'childActor,otherChildActor' but instead got an actor of type: 'root'/);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
"use strict";
|
||||
|
||||
const { types } = require("devtools/shared/protocol");
|
||||
|
||||
function run_test() {
|
||||
types.addActorType("myActor1");
|
||||
types.addActorType("myActor2");
|
||||
types.addActorType("myActor3");
|
||||
|
||||
types.addPolymorphicType("ptype1", ["myActor1", "myActor2"]);
|
||||
const ptype1 = types.getType("ptype1");
|
||||
Assert.equal(ptype1.name, "ptype1");
|
||||
Assert.equal(ptype1.category, "polymorphic");
|
||||
|
||||
types.addPolymorphicType("ptype2", ["myActor1", "myActor2", "myActor3"]);
|
||||
const ptype2 = types.getType("ptype2");
|
||||
Assert.equal(ptype2.name, "ptype2");
|
||||
Assert.equal(ptype2.category, "polymorphic");
|
||||
|
||||
// Polymorphic types only accept actor types
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["myActor1", "myActor4"]);
|
||||
Assert.ok(false, "getType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(ex.toString(), "Error: Unknown type: myActor4");
|
||||
}
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["myActor1", "string"]);
|
||||
Assert.ok(false, "getType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(
|
||||
ex.toString(),
|
||||
"Error: In polymorphic type 'myActor1,string', the type 'string' isn't an actor"
|
||||
);
|
||||
}
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["myActor1", "boolean"]);
|
||||
Assert.ok(false, "getType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(
|
||||
ex.toString(),
|
||||
"Error: In polymorphic type 'myActor1,boolean', the type 'boolean' isn't an actor"
|
||||
);
|
||||
}
|
||||
|
||||
// Polymorphic types are not compatible with array or nullables
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["array:myActor1", "myActor2"]);
|
||||
Assert.ok(false, "addType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(
|
||||
ex.toString(),
|
||||
"Error: In polymorphic type 'array:myActor1,myActor2', the type 'array:myActor1' isn't an actor"
|
||||
);
|
||||
}
|
||||
try {
|
||||
types.addPolymorphicType("ptype", ["nullable:myActor1", "myActor2"]);
|
||||
Assert.ok(false, "addType should fail");
|
||||
} catch (ex) {
|
||||
Assert.equal(
|
||||
ex.toString(),
|
||||
"Error: In polymorphic type 'nullable:myActor1,myActor2', the type 'nullable:myActor1' isn't an actor"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -12,5 +12,6 @@ support-files =
|
|||
[test_protocol_longstring.js]
|
||||
[test_protocol_simple.js]
|
||||
[test_protocol_stack.js]
|
||||
[test_protocol_types.js]
|
||||
[test_protocol_unregister.js]
|
||||
[test_protocol_watchFronts.js]
|
||||
|
|
|
@ -370,6 +370,68 @@ types.addActorType = function(name) {
|
|||
return type;
|
||||
};
|
||||
|
||||
types.addPolymorphicType = function(name, subtypes) {
|
||||
// Assert that all subtypes are actors, as the marshalling implementation depends on that.
|
||||
for (const subTypeName of subtypes) {
|
||||
const subtype = types.getType(subTypeName);
|
||||
if (subtype.category != "actor") {
|
||||
throw new Error(
|
||||
`In polymorphic type '${subtypes.join(
|
||||
","
|
||||
)}', the type '${subTypeName}' isn't an actor`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return types.addType(name, {
|
||||
category: "polymorphic",
|
||||
read: (value, ctx) => {
|
||||
// `value` is either a string which is an Actor ID or a form object
|
||||
// where `actor` is an actor ID
|
||||
const actorID = typeof value === "string" ? value : value.actor;
|
||||
if (!actorID) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got value: '${value}'`
|
||||
);
|
||||
}
|
||||
|
||||
// Extract the typeName out of the actor ID, which should be composed like this
|
||||
// ${DebuggerServerConnectionPrefix}.${typeName}${Number}
|
||||
const typeName = actorID.match(/\.([a-zA-Z]+)\d+$/)[1];
|
||||
if (!subtypes.includes(typeName)) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got an actor of type: '${typeName}'`
|
||||
);
|
||||
}
|
||||
|
||||
const subtype = types.getType(typeName);
|
||||
return subtype.read(value, ctx);
|
||||
},
|
||||
write: (value, ctx) => {
|
||||
if (!value) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got an empty value.`
|
||||
);
|
||||
}
|
||||
// value is either an `Actor` or a `Front` and both classes exposes a `typeName`
|
||||
const typeName = value.typeName;
|
||||
if (!typeName) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got value: '${value}'. Did you pass a form instead of an Actor?`
|
||||
);
|
||||
}
|
||||
|
||||
if (!subtypes.includes(typeName)) {
|
||||
throw new Error(
|
||||
`Was expecting one of these actors '${subtypes}' but instead got an actor of type: '${typeName}'`
|
||||
);
|
||||
}
|
||||
|
||||
const subtype = types.getType(typeName);
|
||||
return subtype.write(value, ctx);
|
||||
},
|
||||
});
|
||||
};
|
||||
types.addNullableType = function(subtype) {
|
||||
subtype = types.getType(subtype);
|
||||
return types.addType("nullable:" + subtype.name, {
|
||||
|
|
Загрузка…
Ссылка в новой задаче