Skip to content

Commit 3dead4c

Browse files
authored
feat(rpc): remove last union types from the protocol (#3059)
1 parent de9570e commit 3dead4c

File tree

10 files changed

+170
-62
lines changed

10 files changed

+170
-62
lines changed

src/rpc/channels.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ export type Binary = string;
2323
export interface Channel extends EventEmitter {
2424
}
2525

26-
export type SerializedValue = undefined | boolean | number | string | ComplexSerializedValue;
27-
28-
export type ComplexSerializedValue = {
26+
export type SerializedValue = {
27+
n?: number,
28+
b?: boolean,
29+
s?: string,
2930
v?: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0',
3031
d?: string,
3132
r?: {
@@ -863,7 +864,7 @@ export type FrameEvalOnSelectorParams = {
863864
arg: SerializedArgument,
864865
};
865866
export type FrameEvalOnSelectorResult = {
866-
value?: SerializedValue,
867+
value: SerializedValue,
867868
};
868869
export type FrameEvalOnSelectorAllParams = {
869870
selector: string,
@@ -872,7 +873,7 @@ export type FrameEvalOnSelectorAllParams = {
872873
arg: SerializedArgument,
873874
};
874875
export type FrameEvalOnSelectorAllResult = {
875-
value?: SerializedValue,
876+
value: SerializedValue,
876877
};
877878
export type FrameAddScriptTagParams = {
878879
url?: string,
@@ -941,7 +942,7 @@ export type FrameEvaluateExpressionParams = {
941942
arg: SerializedArgument,
942943
};
943944
export type FrameEvaluateExpressionResult = {
944-
value?: SerializedValue,
945+
value: SerializedValue,
945946
};
946947
export type FrameEvaluateExpressionHandleParams = {
947948
expression: string,
@@ -1119,7 +1120,7 @@ export type WorkerEvaluateExpressionParams = {
11191120
arg: SerializedArgument,
11201121
};
11211122
export type WorkerEvaluateExpressionResult = {
1122-
value?: SerializedValue,
1123+
value: SerializedValue,
11231124
};
11241125
export type WorkerEvaluateExpressionHandleParams = {
11251126
expression: string,
@@ -1154,7 +1155,7 @@ export type JSHandleEvaluateExpressionParams = {
11541155
arg: SerializedArgument,
11551156
};
11561157
export type JSHandleEvaluateExpressionResult = {
1157-
value?: SerializedValue,
1158+
value: SerializedValue,
11581159
};
11591160
export type JSHandleEvaluateExpressionHandleParams = {
11601161
expression: string,
@@ -1179,7 +1180,7 @@ export type JSHandleGetPropertyResult = {
11791180
};
11801181
export type JSHandleJsonValueParams = {};
11811182
export type JSHandleJsonValueResult = {
1182-
value?: SerializedValue,
1183+
value: SerializedValue,
11831184
};
11841185

11851186
// ----------- ElementHandle -----------
@@ -1219,7 +1220,7 @@ export type ElementHandleEvalOnSelectorParams = {
12191220
arg: SerializedArgument,
12201221
};
12211222
export type ElementHandleEvalOnSelectorResult = {
1222-
value?: SerializedValue,
1223+
value: SerializedValue,
12231224
};
12241225
export type ElementHandleEvalOnSelectorAllParams = {
12251226
selector: string,
@@ -1228,7 +1229,7 @@ export type ElementHandleEvalOnSelectorAllParams = {
12281229
arg: SerializedArgument,
12291230
};
12301231
export type ElementHandleEvalOnSelectorAllResult = {
1231-
value?: SerializedValue,
1232+
value: SerializedValue,
12321233
};
12331234
export type ElementHandleBoundingBoxParams = {};
12341235
export type ElementHandleBoundingBoxResult = {
@@ -1642,7 +1643,7 @@ export type ElectronApplicationEvaluateExpressionParams = {
16421643
arg: SerializedArgument,
16431644
};
16441645
export type ElectronApplicationEvaluateExpressionResult = {
1645-
value?: SerializedValue,
1646+
value: SerializedValue,
16461647
};
16471648
export type ElectronApplicationEvaluateExpressionHandleParams = {
16481649
expression: string,

src/rpc/client/jsHandle.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import { JSHandleChannel, JSHandleInitializer, SerializedArgument, SerializedValue, Channel } from '../channels';
1818
import { ElementHandle } from './elementHandle';
1919
import { ChannelOwner } from './channelOwner';
20-
import { serializeAsCallArgument, parseEvaluationResultValue } from '../../common/utilityScriptSerializers';
20+
import { parseSerializedValue, serializeValue } from '../serializers';
2121

2222
type NoHandles<Arg> = Arg extends JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles<Arg[Key]> } : Arg);
2323
type Unboxed<Arg> =
@@ -99,14 +99,14 @@ export function serializeArgument(arg: any): SerializedArgument {
9999
handles.push(channel);
100100
return handles.length - 1;
101101
};
102-
const value = serializeAsCallArgument(arg, value => {
102+
const value = serializeValue(arg, value => {
103103
if (value instanceof JSHandle)
104104
return { h: pushHandle(value._channel) };
105105
return { fallThrough: value };
106-
});
106+
}, new Set());
107107
return { value, handles };
108108
}
109109

110-
export function parseResult(arg: SerializedValue): any {
111-
return parseEvaluationResultValue(arg as any, []);
110+
export function parseResult(value: SerializedValue): any {
111+
return parseSerializedValue(value, undefined);
112112
}

src/rpc/protocol.pdl

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,33 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
union SerializedValue
16-
undefined
17-
boolean
18-
number
19-
string
20-
ComplexSerializedValue
21-
22-
type ComplexSerializedValue
15+
# Exactly one of the optional fields must be present.
16+
type SerializedValue
17+
n?: number
18+
b?: boolean
19+
s?: string
2320
v?: enum
2421
null
2522
undefined
2623
NaN
2724
Infinity
2825
-Infinity
2926
-0
27+
# String representation of the Date.
3028
d?: string
29+
# Regular expression pattern and flags.
3130
r?: object
3231
p: string
3332
f: string
3433
a?: SerializedValue[]
34+
# Object with keys and values.
3535
o?: object[]
3636
k: string
3737
v: SerializedValue
38+
# An index in the handles array from SerializedArgument.
3839
h?: number
3940

41+
# Represents a value with handle references.
4042
type SerializedArgument
4143
value: SerializedValue
4244
handles: Channel[]
@@ -756,7 +758,7 @@ interface Frame
756758
isFunction: boolean
757759
arg: SerializedArgument
758760
returns
759-
value?: SerializedValue
761+
value: SerializedValue
760762

761763
command evalOnSelectorAll
762764
parameters
@@ -765,7 +767,7 @@ interface Frame
765767
isFunction: boolean
766768
arg: SerializedArgument
767769
returns
768-
value?: SerializedValue
770+
value: SerializedValue
769771

770772
command addScriptTag
771773
parameters
@@ -846,7 +848,7 @@ interface Frame
846848
isFunction: boolean
847849
arg: SerializedArgument
848850
returns
849-
value?: SerializedValue
851+
value: SerializedValue
850852

851853
command evaluateExpressionHandle
852854
parameters
@@ -1031,7 +1033,7 @@ interface Worker
10311033
isFunction: boolean
10321034
arg: SerializedArgument
10331035
returns
1034-
value?: SerializedValue
1036+
value: SerializedValue
10351037

10361038
command evaluateExpressionHandle
10371039
parameters
@@ -1057,7 +1059,7 @@ interface JSHandle
10571059
isFunction: boolean
10581060
arg: SerializedArgument
10591061
returns
1060-
value?: SerializedValue
1062+
value: SerializedValue
10611063

10621064
command evaluateExpressionHandle
10631065
parameters
@@ -1081,7 +1083,7 @@ interface JSHandle
10811083

10821084
command jsonValue
10831085
returns
1084-
value?: SerializedValue
1086+
value: SerializedValue
10851087

10861088
interface ElementHandle extends JSHandle
10871089
command evalOnSelector
@@ -1091,7 +1093,7 @@ interface ElementHandle extends JSHandle
10911093
isFunction: boolean
10921094
arg: SerializedArgument
10931095
returns
1094-
value?: SerializedValue
1096+
value: SerializedValue
10951097

10961098
command evalOnSelectorAll
10971099
parameters
@@ -1100,7 +1102,7 @@ interface ElementHandle extends JSHandle
11001102
isFunction: boolean
11011103
arg: SerializedArgument
11021104
returns
1103-
value?: SerializedValue
1105+
value: SerializedValue
11041106

11051107
command boundingBox
11061108
returns
@@ -1458,7 +1460,7 @@ interface ElectronApplication
14581460
isFunction: boolean
14591461
arg: SerializedArgument
14601462
returns
1461-
value?: SerializedValue
1463+
value: SerializedValue
14621464

14631465
command evaluateExpressionHandle
14641466
parameters

src/rpc/serializers.ts

Lines changed: 121 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,20 @@ import * as util from 'util';
2121
import { TimeoutError } from '../errors';
2222
import * as types from '../types';
2323
import { helper, assert } from '../helper';
24-
import { SerializedError, AXNode } from './channels';
25-
import { serializeAsCallArgument, parseEvaluationResultValue } from '../common/utilityScriptSerializers';
24+
import { SerializedError, AXNode, SerializedValue } from './channels';
2625

2726
export function serializeError(e: any): SerializedError {
2827
if (helper.isError(e))
2928
return { error: { message: e.message, stack: e.stack, name: e.name } };
30-
return { value: serializeAsCallArgument(e, value => ({ fallThrough: value })) };
29+
return { value: serializeValue(e, value => ({ fallThrough: value }), new Set()) };
3130
}
3231

3332
export function parseError(error: SerializedError): Error {
34-
if (!error.error)
35-
return parseEvaluationResultValue(error.value as any, []);
33+
if (!error.error) {
34+
if (error.value === undefined)
35+
throw new Error('Serialized error must have either an error or a value');
36+
return parseSerializedValue(error.value, undefined);
37+
}
3638
if (error.error.name === 'TimeoutError') {
3739
const e = new TimeoutError(error.error.message);
3840
e.stack = error.error.stack || '';
@@ -169,3 +171,117 @@ export function axNodeFromProtocol(axNode: AXNode): types.SerializedAXNode {
169171
delete (result as any).valueString;
170172
return result;
171173
}
174+
175+
export function parseSerializedValue(value: SerializedValue, handles: any[] | undefined): any {
176+
if (value.n !== undefined)
177+
return value.n;
178+
if (value.s !== undefined)
179+
return value.s;
180+
if (value.b !== undefined)
181+
return value.b;
182+
if (value.v !== undefined) {
183+
if (value.v === 'undefined')
184+
return undefined;
185+
if (value.v === 'null')
186+
return null;
187+
if (value.v === 'NaN')
188+
return NaN;
189+
if (value.v === 'Infinity')
190+
return Infinity;
191+
if (value.v === '-Infinity')
192+
return -Infinity;
193+
if (value.v === '-0')
194+
return -0;
195+
}
196+
if (value.d !== undefined)
197+
return new Date(value.d);
198+
if (value.r !== undefined)
199+
return new RegExp(value.r.p, value.r.f);
200+
if (value.a !== undefined)
201+
return value.a.map((a: any) => parseSerializedValue(a, handles));
202+
if (value.o !== undefined) {
203+
const result: any = {};
204+
for (const { k, v } of value.o)
205+
result[k] = parseSerializedValue(v, handles);
206+
return result;
207+
}
208+
if (value.h !== undefined) {
209+
if (handles === undefined)
210+
throw new Error('Unexpected handle');
211+
return handles[value.h];
212+
}
213+
throw new Error('Unexpected value');
214+
}
215+
216+
export type HandleOrValue = { h: number } | { fallThrough: any };
217+
export function serializeValue(value: any, handleSerializer: (value: any) => HandleOrValue, visited: Set<any>): SerializedValue {
218+
const handle = handleSerializer(value);
219+
if ('fallThrough' in handle)
220+
value = handle.fallThrough;
221+
else
222+
return handle;
223+
224+
if (visited.has(value))
225+
throw new Error('Argument is a circular structure');
226+
if (typeof value === 'symbol')
227+
return { v: 'undefined' };
228+
if (Object.is(value, undefined))
229+
return { v: 'undefined' };
230+
if (Object.is(value, null))
231+
return { v: 'null' };
232+
if (Object.is(value, NaN))
233+
return { v: 'NaN' };
234+
if (Object.is(value, Infinity))
235+
return { v: 'Infinity' };
236+
if (Object.is(value, -Infinity))
237+
return { v: '-Infinity' };
238+
if (Object.is(value, -0))
239+
return { v: '-0' };
240+
if (typeof value === 'boolean')
241+
return { b: value };
242+
if (typeof value === 'number')
243+
return { n: value };
244+
if (typeof value === 'string')
245+
return { s: value };
246+
if (isError(value)) {
247+
const error = value;
248+
if ('captureStackTrace' in global.Error) {
249+
// v8
250+
return { s: error.stack || '' };
251+
}
252+
return { s: `${error.name}: ${error.message}\n${error.stack}` };
253+
}
254+
if (isDate(value))
255+
return { d: value.toJSON() };
256+
if (isRegExp(value))
257+
return { r: { p: value.source, f: value.flags } };
258+
if (Array.isArray(value)) {
259+
const a = [];
260+
visited.add(value);
261+
for (let i = 0; i < value.length; ++i)
262+
a.push(serializeValue(value[i], handleSerializer, visited));
263+
visited.delete(value);
264+
return { a };
265+
}
266+
if (typeof value === 'object') {
267+
const o: { k: string, v: SerializedValue }[] = [];
268+
visited.add(value);
269+
for (const name of Object.keys(value))
270+
o.push({ k: name, v: serializeValue(value[name], handleSerializer, visited) });
271+
visited.delete(value);
272+
return { o };
273+
}
274+
throw new Error('Unexpected value');
275+
}
276+
277+
function isRegExp(obj: any): obj is RegExp {
278+
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
279+
}
280+
281+
function isDate(obj: any): obj is Date {
282+
return obj instanceof Date || Object.prototype.toString.call(obj) === '[object Date]';
283+
}
284+
285+
function isError(obj: any): obj is Error {
286+
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
287+
}

0 commit comments

Comments
 (0)