Skip to content

Commit 29504c0

Browse files
authored
feat(rpc): make SerializedValue format pdl-friendly (#3007)
This avoids sum types and instead uses different fields for different types.
1 parent 79d5991 commit 29504c0

File tree

7 files changed

+34
-33
lines changed

7 files changed

+34
-33
lines changed

src/common/utilityScriptSerializers.ts

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ export type SerializedValue =
1818
undefined | boolean | number | string |
1919
{ v: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0' } |
2020
{ d: string } |
21-
{ r: [string, string] } |
21+
{ r: { p: string, f: string} } |
2222
{ a: SerializedValue[] } |
23-
{ o: { [key: string]: SerializedValue } } |
23+
{ o: { k: string, v: SerializedValue }[] } |
2424
{ h: number };
2525

2626
function isRegExp(obj: any): obj is RegExp {
@@ -36,9 +36,9 @@ function isError(obj: any): obj is Error {
3636
}
3737

3838
export function parseEvaluationResultValue(value: SerializedValue, handles: any[] = []): any {
39-
if (value === undefined)
39+
if (Object.is(value, undefined))
4040
return undefined;
41-
if (typeof value === 'object') {
41+
if (typeof value === 'object' && value) {
4242
if ('v' in value) {
4343
if (value.v === 'undefined')
4444
return undefined;
@@ -52,17 +52,18 @@ export function parseEvaluationResultValue(value: SerializedValue, handles: any[
5252
return -Infinity;
5353
if (value.v === '-0')
5454
return -0;
55+
return undefined;
5556
}
5657
if ('d' in value)
5758
return new Date(value.d);
5859
if ('r' in value)
59-
return new RegExp(value.r[0], value.r[1]);
60+
return new RegExp(value.r.p, value.r.f);
6061
if ('a' in value)
6162
return value.a.map((a: any) => parseEvaluationResultValue(a, handles));
6263
if ('o' in value) {
6364
const result: any = {};
64-
for (const name of Object.keys(value.o))
65-
result[name] = parseEvaluationResultValue(value.o[name], handles);
65+
for (const { k, v } of value.o)
66+
result[k] = parseEvaluationResultValue(v, handles);
6667
return result;
6768
}
6869
if ('h' in value)
@@ -72,12 +73,12 @@ export function parseEvaluationResultValue(value: SerializedValue, handles: any[
7273
}
7374

7475
export type HandleOrValue = { h: number } | { fallThrough: any };
75-
export function serializeAsCallArgument(value: any, jsHandleSerializer: (value: any) => HandleOrValue): SerializedValue {
76-
return serialize(value, jsHandleSerializer, new Set());
76+
export function serializeAsCallArgument(value: any, handleSerializer: (value: any) => HandleOrValue): SerializedValue {
77+
return serialize(value, handleSerializer, new Set());
7778
}
7879

79-
function serialize(value: any, jsHandleSerializer: (value: any) => HandleOrValue, visited: Set<any>): SerializedValue {
80-
const result = jsHandleSerializer(value);
80+
function serialize(value: any, handleSerializer: (value: any) => HandleOrValue, visited: Set<any>): SerializedValue {
81+
const result = handleSerializer(value);
8182
if ('fallThrough' in result)
8283
value = result.fallThrough;
8384
else
@@ -111,26 +112,26 @@ function serialize(value: any, jsHandleSerializer: (value: any) => HandleOrValue
111112
const error = value;
112113
if ('captureStackTrace' in global.Error) {
113114
// v8
114-
return error.stack;
115+
return error.stack || '';
115116
}
116117
return `${error.name}: ${error.message}\n${error.stack}`;
117118
}
118119
if (isDate(value))
119120
return { d: value.toJSON() };
120121
if (isRegExp(value))
121-
return { r: [ value.source, value.flags ] };
122+
return { r: { p: value.source, f: value.flags } };
122123

123124
if (Array.isArray(value)) {
124-
const result = [];
125+
const a = [];
125126
visited.add(value);
126127
for (let i = 0; i < value.length; ++i)
127-
result.push(serialize(value[i], jsHandleSerializer, visited));
128+
a.push(serialize(value[i], handleSerializer, visited));
128129
visited.delete(value);
129-
return { a: result };
130+
return { a };
130131
}
131132

132133
if (typeof value === 'object') {
133-
const result: any = {};
134+
const o: { k: string, v: SerializedValue }[] = [];
134135
visited.add(value);
135136
for (const name of Object.keys(value)) {
136137
let item;
@@ -140,11 +141,11 @@ function serialize(value: any, jsHandleSerializer: (value: any) => HandleOrValue
140141
continue; // native bindings will throw sometimes
141142
}
142143
if (name === 'toJSON' && typeof item === 'function')
143-
result[name] = {};
144+
o.push({ k: name, v: { o: [] } });
144145
else
145-
result[name] = serialize(item, jsHandleSerializer, visited);
146+
o.push({ k: name, v: serialize(item, handleSerializer, visited) });
146147
}
147148
visited.delete(value);
148-
return { o: result };
149+
return { o };
149150
}
150151
}

src/injected/utilityScript.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ export default class UtilityScript {
2222
return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result;
2323
}
2424

25-
callFunction(returnByValue: boolean, functionText: string, ...args: any[]) {
26-
const argCount = args[0] as number;
27-
const handles = args.slice(argCount + 1);
28-
const parameters = args.slice(1, argCount + 1).map(a => parseEvaluationResultValue(a, handles));
25+
callFunction(returnByValue: boolean, functionText: string, argCount: number, ...argsAndHandles: any[]) {
26+
const args = argsAndHandles.slice(0, argCount);
27+
const handles = argsAndHandles.slice(argCount);
28+
const parameters = args.map(a => parseEvaluationResultValue(a, handles));
2929
const func = global.eval('(' + functionText + ')');
3030
const result = func(...parameters);
3131
return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result;

src/rpc/channels.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,7 @@ export interface BindingCallChannel extends Channel {
367367
export type BindingCallInitializer = {
368368
frame: FrameChannel,
369369
name: string,
370-
// TODO: migrate this to SerializedArgument.
371-
args: any[]
370+
args: SerializedValue[],
372371
};
373372

374373

src/rpc/client/page.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { ElementHandle } from './elementHandle';
3232
import { Worker } from './worker';
3333
import { Frame, FunctionWithSource, GotoOptions } from './frame';
3434
import { Keyboard, Mouse } from './input';
35-
import { Func1, FuncOn, SmartHandle, serializeArgument } from './jsHandle';
35+
import { Func1, FuncOn, SmartHandle, serializeArgument, parseResult } from './jsHandle';
3636
import { Request, Response, Route, RouteHandler } from './network';
3737
import { FileChooser } from './fileChooser';
3838
import { Buffer } from 'buffer';
@@ -557,7 +557,7 @@ export class BindingCall extends ChannelOwner<BindingCallChannel, BindingCallIni
557557
page: frame._page!,
558558
frame
559559
};
560-
const result = await func(source, ...this._initializer.args);
560+
const result = await func(source, ...this._initializer.args.map(parseResult));
561561
this._channel.resolve({ result: serializeArgument(result) });
562562
} catch (e) {
563563
this._channel.reject({ error: serializeError(e) });

src/rpc/server/electronDispatcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { ElectronApplicationChannel, ElectronApplicationInitializer, PageChannel
2020
import { BrowserContextDispatcher } from './browserContextDispatcher';
2121
import { BrowserContextBase } from '../../browserContext';
2222
import { PageDispatcher } from './pageDispatcher';
23-
import { parseArgument } from './jsHandleDispatcher';
23+
import { parseArgument, serializeResult } from './jsHandleDispatcher';
2424
import { createHandle } from './elementHandlerDispatcher';
2525
import { SerializedValue } from '../../common/utilityScriptSerializers';
2626

@@ -57,7 +57,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
5757

5858
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ value: SerializedValue }> {
5959
const handle = this._object._nodeElectronHandle!;
60-
return { value: await handle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg)) };
60+
return { value: serializeResult(await handle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) };
6161
}
6262

6363
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ handle: JSHandleChannel }> {

src/rpc/server/pageDispatcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ export class BindingCallDispatcher extends Dispatcher<{}, BindingCallInitializer
251251
super(scope, {}, 'bindingCall', {
252252
frame: lookupDispatcher<FrameDispatcher>(source.frame),
253253
name,
254-
args
254+
args: args.map(serializeResult),
255255
});
256256
this._promise = new Promise((resolve, reject) => {
257257
this._resolve = resolve;

test/evaluation.jest.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
const utils = require('./utils');
1919
const path = require('path');
20-
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS, CHANNEL} = testOptions;
20+
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS} = testOptions;
2121

2222
describe('Page.evaluate', function() {
2323
it('should work', async({page, server}) => {
@@ -373,7 +373,8 @@ describe('Page.evaluate', function() {
373373
});
374374
expect(result).toBe(undefined);
375375
});
376-
it.skip(CHANNEL)('should transfer 100Mb of data from page to node.js', async({page}) => {
376+
it.fail(USES_HOOKS)('should transfer 100Mb of data from page to node.js', async({page}) => {
377+
// This does not use hooks, but is slow in wire channel.
377378
const a = await page.evaluate(() => Array(100 * 1024 * 1024 + 1).join('a'));
378379
expect(a.length).toBe(100 * 1024 * 1024);
379380
});

0 commit comments

Comments
 (0)