Skip to content

Commit

Permalink
feat(rpc): remove last union types from the protocol (#3059)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgozman committed Jul 21, 2020
1 parent de9570e commit 3dead4c
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 62 deletions.
25 changes: 13 additions & 12 deletions src/rpc/channels.ts
Expand Up @@ -23,9 +23,10 @@ export type Binary = string;
export interface Channel extends EventEmitter {
}

export type SerializedValue = undefined | boolean | number | string | ComplexSerializedValue;

export type ComplexSerializedValue = {
export type SerializedValue = {
n?: number,
b?: boolean,
s?: string,
v?: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0',
d?: string,
r?: {
Expand Down Expand Up @@ -863,7 +864,7 @@ export type FrameEvalOnSelectorParams = {
arg: SerializedArgument,
};
export type FrameEvalOnSelectorResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type FrameEvalOnSelectorAllParams = {
selector: string,
Expand All @@ -872,7 +873,7 @@ export type FrameEvalOnSelectorAllParams = {
arg: SerializedArgument,
};
export type FrameEvalOnSelectorAllResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type FrameAddScriptTagParams = {
url?: string,
Expand Down Expand Up @@ -941,7 +942,7 @@ export type FrameEvaluateExpressionParams = {
arg: SerializedArgument,
};
export type FrameEvaluateExpressionResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type FrameEvaluateExpressionHandleParams = {
expression: string,
Expand Down Expand Up @@ -1119,7 +1120,7 @@ export type WorkerEvaluateExpressionParams = {
arg: SerializedArgument,
};
export type WorkerEvaluateExpressionResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type WorkerEvaluateExpressionHandleParams = {
expression: string,
Expand Down Expand Up @@ -1154,7 +1155,7 @@ export type JSHandleEvaluateExpressionParams = {
arg: SerializedArgument,
};
export type JSHandleEvaluateExpressionResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type JSHandleEvaluateExpressionHandleParams = {
expression: string,
Expand All @@ -1179,7 +1180,7 @@ export type JSHandleGetPropertyResult = {
};
export type JSHandleJsonValueParams = {};
export type JSHandleJsonValueResult = {
value?: SerializedValue,
value: SerializedValue,
};

// ----------- ElementHandle -----------
Expand Down Expand Up @@ -1219,7 +1220,7 @@ export type ElementHandleEvalOnSelectorParams = {
arg: SerializedArgument,
};
export type ElementHandleEvalOnSelectorResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type ElementHandleEvalOnSelectorAllParams = {
selector: string,
Expand All @@ -1228,7 +1229,7 @@ export type ElementHandleEvalOnSelectorAllParams = {
arg: SerializedArgument,
};
export type ElementHandleEvalOnSelectorAllResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type ElementHandleBoundingBoxParams = {};
export type ElementHandleBoundingBoxResult = {
Expand Down Expand Up @@ -1642,7 +1643,7 @@ export type ElectronApplicationEvaluateExpressionParams = {
arg: SerializedArgument,
};
export type ElectronApplicationEvaluateExpressionResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type ElectronApplicationEvaluateExpressionHandleParams = {
expression: string,
Expand Down
10 changes: 5 additions & 5 deletions src/rpc/client/jsHandle.ts
Expand Up @@ -17,7 +17,7 @@
import { JSHandleChannel, JSHandleInitializer, SerializedArgument, SerializedValue, Channel } from '../channels';
import { ElementHandle } from './elementHandle';
import { ChannelOwner } from './channelOwner';
import { serializeAsCallArgument, parseEvaluationResultValue } from '../../common/utilityScriptSerializers';
import { parseSerializedValue, serializeValue } from '../serializers';

type NoHandles<Arg> = Arg extends JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles<Arg[Key]> } : Arg);
type Unboxed<Arg> =
Expand Down Expand Up @@ -99,14 +99,14 @@ export function serializeArgument(arg: any): SerializedArgument {
handles.push(channel);
return handles.length - 1;
};
const value = serializeAsCallArgument(arg, value => {
const value = serializeValue(arg, value => {
if (value instanceof JSHandle)
return { h: pushHandle(value._channel) };
return { fallThrough: value };
});
}, new Set());
return { value, handles };
}

export function parseResult(arg: SerializedValue): any {
return parseEvaluationResultValue(arg as any, []);
export function parseResult(value: SerializedValue): any {
return parseSerializedValue(value, undefined);
}
36 changes: 19 additions & 17 deletions src/rpc/protocol.pdl
Expand Up @@ -12,31 +12,33 @@
# See the License for the specific language governing permissions and
# limitations under the License.

union SerializedValue
undefined
boolean
number
string
ComplexSerializedValue

type ComplexSerializedValue
# Exactly one of the optional fields must be present.
type SerializedValue
n?: number
b?: boolean
s?: string
v?: enum
null
undefined
NaN
Infinity
-Infinity
-0
# String representation of the Date.
d?: string
# Regular expression pattern and flags.
r?: object
p: string
f: string
a?: SerializedValue[]
# Object with keys and values.
o?: object[]
k: string
v: SerializedValue
# An index in the handles array from SerializedArgument.
h?: number

# Represents a value with handle references.
type SerializedArgument
value: SerializedValue
handles: Channel[]
Expand Down Expand Up @@ -756,7 +758,7 @@ interface Frame
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue

command evalOnSelectorAll
parameters
Expand All @@ -765,7 +767,7 @@ interface Frame
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue

command addScriptTag
parameters
Expand Down Expand Up @@ -846,7 +848,7 @@ interface Frame
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue

command evaluateExpressionHandle
parameters
Expand Down Expand Up @@ -1031,7 +1033,7 @@ interface Worker
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue

command evaluateExpressionHandle
parameters
Expand All @@ -1057,7 +1059,7 @@ interface JSHandle
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue

command evaluateExpressionHandle
parameters
Expand All @@ -1081,7 +1083,7 @@ interface JSHandle

command jsonValue
returns
value?: SerializedValue
value: SerializedValue

interface ElementHandle extends JSHandle
command evalOnSelector
Expand All @@ -1091,7 +1093,7 @@ interface ElementHandle extends JSHandle
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue

command evalOnSelectorAll
parameters
Expand All @@ -1100,7 +1102,7 @@ interface ElementHandle extends JSHandle
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue

command boundingBox
returns
Expand Down Expand Up @@ -1458,7 +1460,7 @@ interface ElectronApplication
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue

command evaluateExpressionHandle
parameters
Expand Down
126 changes: 121 additions & 5 deletions src/rpc/serializers.ts
Expand Up @@ -21,18 +21,20 @@ import * as util from 'util';
import { TimeoutError } from '../errors';
import * as types from '../types';
import { helper, assert } from '../helper';
import { SerializedError, AXNode } from './channels';
import { serializeAsCallArgument, parseEvaluationResultValue } from '../common/utilityScriptSerializers';
import { SerializedError, AXNode, SerializedValue } from './channels';

export function serializeError(e: any): SerializedError {
if (helper.isError(e))
return { error: { message: e.message, stack: e.stack, name: e.name } };
return { value: serializeAsCallArgument(e, value => ({ fallThrough: value })) };
return { value: serializeValue(e, value => ({ fallThrough: value }), new Set()) };
}

export function parseError(error: SerializedError): Error {
if (!error.error)
return parseEvaluationResultValue(error.value as any, []);
if (!error.error) {
if (error.value === undefined)
throw new Error('Serialized error must have either an error or a value');
return parseSerializedValue(error.value, undefined);
}
if (error.error.name === 'TimeoutError') {
const e = new TimeoutError(error.error.message);
e.stack = error.error.stack || '';
Expand Down Expand Up @@ -169,3 +171,117 @@ export function axNodeFromProtocol(axNode: AXNode): types.SerializedAXNode {
delete (result as any).valueString;
return result;
}

export function parseSerializedValue(value: SerializedValue, handles: any[] | undefined): any {
if (value.n !== undefined)
return value.n;
if (value.s !== undefined)
return value.s;
if (value.b !== undefined)
return value.b;
if (value.v !== undefined) {
if (value.v === 'undefined')
return undefined;
if (value.v === 'null')
return null;
if (value.v === 'NaN')
return NaN;
if (value.v === 'Infinity')
return Infinity;
if (value.v === '-Infinity')
return -Infinity;
if (value.v === '-0')
return -0;
}
if (value.d !== undefined)
return new Date(value.d);
if (value.r !== undefined)
return new RegExp(value.r.p, value.r.f);
if (value.a !== undefined)
return value.a.map((a: any) => parseSerializedValue(a, handles));
if (value.o !== undefined) {
const result: any = {};
for (const { k, v } of value.o)
result[k] = parseSerializedValue(v, handles);
return result;
}
if (value.h !== undefined) {
if (handles === undefined)
throw new Error('Unexpected handle');
return handles[value.h];
}
throw new Error('Unexpected value');
}

export type HandleOrValue = { h: number } | { fallThrough: any };
export function serializeValue(value: any, handleSerializer: (value: any) => HandleOrValue, visited: Set<any>): SerializedValue {
const handle = handleSerializer(value);
if ('fallThrough' in handle)
value = handle.fallThrough;
else
return handle;

if (visited.has(value))
throw new Error('Argument is a circular structure');
if (typeof value === 'symbol')
return { v: 'undefined' };
if (Object.is(value, undefined))
return { v: 'undefined' };
if (Object.is(value, null))
return { v: 'null' };
if (Object.is(value, NaN))
return { v: 'NaN' };
if (Object.is(value, Infinity))
return { v: 'Infinity' };
if (Object.is(value, -Infinity))
return { v: '-Infinity' };
if (Object.is(value, -0))
return { v: '-0' };
if (typeof value === 'boolean')
return { b: value };
if (typeof value === 'number')
return { n: value };
if (typeof value === 'string')
return { s: value };
if (isError(value)) {
const error = value;
if ('captureStackTrace' in global.Error) {
// v8
return { s: error.stack || '' };
}
return { s: `${error.name}: ${error.message}\n${error.stack}` };
}
if (isDate(value))
return { d: value.toJSON() };
if (isRegExp(value))
return { r: { p: value.source, f: value.flags } };
if (Array.isArray(value)) {
const a = [];
visited.add(value);
for (let i = 0; i < value.length; ++i)
a.push(serializeValue(value[i], handleSerializer, visited));
visited.delete(value);
return { a };
}
if (typeof value === 'object') {
const o: { k: string, v: SerializedValue }[] = [];
visited.add(value);
for (const name of Object.keys(value))
o.push({ k: name, v: serializeValue(value[name], handleSerializer, visited) });
visited.delete(value);
return { o };
}
throw new Error('Unexpected value');
}

function isRegExp(obj: any): obj is RegExp {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
}

function isDate(obj: any): obj is Date {
return obj instanceof Date || Object.prototype.toString.call(obj) === '[object Date]';
}

function isError(obj: any): obj is Error {
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
}

0 comments on commit 3dead4c

Please sign in to comment.