Skip to content

Commit abca4a9

Browse files
committed
Include code in stringified errors
This is done by returning the entire error stringified instead of just the message. This fixes the issue with the "save as" dialog.
1 parent 33cdaf9 commit abca4a9

File tree

7 files changed

+40
-83
lines changed

7 files changed

+40
-83
lines changed

packages/ide/test/fs.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,8 +496,9 @@ describe("fs", () => {
496496
});
497497

498498
it("should fail to stat nonexistent file", async () => {
499-
await expect(util.promisify(fs.stat)(tmpFile()))
500-
.rejects.toThrow("ENOENT");
499+
const error = await util.promisify(fs.stat)(tmpFile()).catch((e) => e);
500+
expect(error.message).toContain("ENOENT");
501+
expect(error.code).toBe("ENOENT");
501502
});
502503
});
503504

packages/protocol/src/browser/client.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export class Client {
9898
const eventsMsg = new EvalEventMessage();
9999
eventsMsg.setId(doEval.id);
100100
eventsMsg.setEvent(event);
101-
eventsMsg.setArgsList(args.map(stringify));
101+
eventsMsg.setArgsList(args.map((a) => stringify(a)));
102102
const clientMsg = new ClientMessage();
103103
clientMsg.setEvalEvent(eventsMsg);
104104
this.connection.send(clientMsg.serializeBinary());
@@ -140,7 +140,7 @@ export class Client {
140140
const id = this.evalId++;
141141
newEval.setId(id);
142142
newEval.setActive(active);
143-
newEval.setArgsList([a1, a2, a3, a4, a5, a6].map(stringify));
143+
newEval.setArgsList([a1, a2, a3, a4, a5, a6].map((a) => stringify(a)));
144144
newEval.setFunction(func.toString());
145145

146146
const clientMsg = new ClientMessage();
@@ -155,16 +155,15 @@ export class Client {
155155

156156
const d1 = this.evalDoneEmitter.event((doneMsg) => {
157157
if (doneMsg.getId() === id) {
158-
const resp = doneMsg.getResponse();
159158
dispose();
160-
resolve(parse(resp));
159+
resolve(parse(doneMsg.getResponse()));
161160
}
162161
});
163162

164163
const d2 = this.evalFailedEmitter.event((failedMsg) => {
165164
if (failedMsg.getId() === id) {
166165
dispose();
167-
reject(new Error(failedMsg.getMessage()));
166+
reject(parse(failedMsg.getResponse()));
168167
}
169168
});
170169
});

packages/protocol/src/common/util.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,19 @@ export type IEncodingOptions = {
2525
export type IEncodingOptionsCallback = IEncodingOptions | ((err: NodeJS.ErrnoException, ...args: any[]) => void);
2626

2727
/**
28-
* Stringify an event argument.
28+
* Stringify an event argument. isError is because although methods like
29+
* `fs.stat` are supposed to throw Error objects, they currently throw regular
30+
* objects when running tests through Jest.
2931
*/
30-
export const stringify = (arg: any): string => { // tslint:disable-line no-any
31-
if (arg instanceof Error) {
32+
export const stringify = (arg: any, isError?: boolean): string => { // tslint:disable-line no-any
33+
if (arg instanceof Error || isError) {
3234
// Errors don't stringify at all. They just become "{}".
3335
return JSON.stringify({
3436
type: "Error",
3537
data: {
3638
message: arg.message,
37-
name: arg.name,
3839
stack: arg.stack,
40+
code: (arg as NodeJS.ErrnoException).code,
3941
},
4042
});
4143
} else if (arg instanceof Uint8Array) {
@@ -74,7 +76,16 @@ export const parse = (arg: string): any => { // tslint:disable-line no-any
7476
// what happens to buffers and stringify them as regular objects.
7577
case "Error":
7678
if (result.data.message) {
77-
return new Error(result.data.message);
79+
const error = new Error(result.data.message);
80+
// TODO: Can we set the stack? Doing so seems to make it into an
81+
// "invalid object".
82+
if (typeof result.data.code !== "undefined") {
83+
(error as NodeJS.ErrnoException).code = result.data.code;
84+
}
85+
// tslint:disable-next-line no-any
86+
(error as any).originalStack = result.data.stack;
87+
88+
return error;
7889
}
7990
break;
8091
}

packages/protocol/src/node/evaluate.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ export const evaluate = (connection: SendableConnection, message: NewEvalMessage
3636
const sendException = (error: Error): void => {
3737
const evalFailed = new EvalFailedMessage();
3838
evalFailed.setId(message.getId());
39-
evalFailed.setReason(EvalFailedMessage.Reason.EXCEPTION);
40-
evalFailed.setMessage(error.toString() + " " + error.stack);
39+
evalFailed.setResponse(stringify(error, true));
4140

4241
const serverMsg = new ServerMessage();
4342
serverMsg.setEvalFailed(evalFailed);
@@ -58,7 +57,7 @@ export const evaluate = (connection: SendableConnection, message: NewEvalMessage
5857
logger.trace(() => [
5958
`${event}`,
6059
field("id", message.getId()),
61-
field("args", args.map(stringify)),
60+
field("args", args.map((a) => stringify(a))),
6261
]);
6362
cb(...args);
6463
});
@@ -67,11 +66,11 @@ export const evaluate = (connection: SendableConnection, message: NewEvalMessage
6766
logger.trace(() => [
6867
`emit ${event}`,
6968
field("id", message.getId()),
70-
field("args", args.map(stringify)),
69+
field("args", args.map((a) => stringify(a))),
7170
]);
7271
const eventMsg = new EvalEventMessage();
7372
eventMsg.setEvent(event);
74-
eventMsg.setArgsList(args.map(stringify));
73+
eventMsg.setArgsList(args.map((a) => stringify(a)));
7574
eventMsg.setId(message.getId());
7675
const serverMsg = new ServerMessage();
7776
serverMsg.setEvalEvent(eventMsg);

packages/protocol/src/proto/node.proto

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,7 @@ message EvalEventMessage {
1919

2020
message EvalFailedMessage {
2121
uint64 id = 1;
22-
enum Reason {
23-
Timeout = 0;
24-
Exception = 1;
25-
Conflict = 2;
26-
}
27-
Reason reason = 2;
28-
string message = 3;
22+
string response = 2;
2923
}
3024

3125
message EvalDoneMessage {

packages/protocol/src/proto/node_pb.d.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,8 @@ export class EvalFailedMessage extends jspb.Message {
7575
getId(): number;
7676
setId(value: number): void;
7777

78-
getReason(): EvalFailedMessage.Reason;
79-
setReason(value: EvalFailedMessage.Reason): void;
80-
81-
getMessage(): string;
82-
setMessage(value: string): void;
78+
getResponse(): string;
79+
setResponse(value: string): void;
8380

8481
serializeBinary(): Uint8Array;
8582
toObject(includeInstance?: boolean): EvalFailedMessage.AsObject;
@@ -94,14 +91,7 @@ export class EvalFailedMessage extends jspb.Message {
9491
export namespace EvalFailedMessage {
9592
export type AsObject = {
9693
id: number,
97-
reason: EvalFailedMessage.Reason,
98-
message: string,
99-
}
100-
101-
export enum Reason {
102-
TIMEOUT = 0,
103-
EXCEPTION = 1,
104-
CONFLICT = 2,
94+
response: string,
10595
}
10696
}
10797

packages/protocol/src/proto/node_pb.js

Lines changed: 9 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ var global = Function('return this')();
1414
goog.exportSymbol('proto.EvalDoneMessage', null, global);
1515
goog.exportSymbol('proto.EvalEventMessage', null, global);
1616
goog.exportSymbol('proto.EvalFailedMessage', null, global);
17-
goog.exportSymbol('proto.EvalFailedMessage.Reason', null, global);
1817
goog.exportSymbol('proto.NewEvalMessage', null, global);
1918

2019
/**
@@ -554,8 +553,7 @@ proto.EvalFailedMessage.prototype.toObject = function(opt_includeInstance) {
554553
proto.EvalFailedMessage.toObject = function(includeInstance, msg) {
555554
var f, obj = {
556555
id: jspb.Message.getFieldWithDefault(msg, 1, 0),
557-
reason: jspb.Message.getFieldWithDefault(msg, 2, 0),
558-
message: jspb.Message.getFieldWithDefault(msg, 3, "")
556+
response: jspb.Message.getFieldWithDefault(msg, 2, "")
559557
};
560558

561559
if (includeInstance) {
@@ -597,12 +595,8 @@ proto.EvalFailedMessage.deserializeBinaryFromReader = function(msg, reader) {
597595
msg.setId(value);
598596
break;
599597
case 2:
600-
var value = /** @type {!proto.EvalFailedMessage.Reason} */ (reader.readEnum());
601-
msg.setReason(value);
602-
break;
603-
case 3:
604598
var value = /** @type {string} */ (reader.readString());
605-
msg.setMessage(value);
599+
msg.setResponse(value);
606600
break;
607601
default:
608602
reader.skipField();
@@ -640,32 +634,16 @@ proto.EvalFailedMessage.serializeBinaryToWriter = function(message, writer) {
640634
f
641635
);
642636
}
643-
f = message.getReason();
644-
if (f !== 0.0) {
645-
writer.writeEnum(
646-
2,
647-
f
648-
);
649-
}
650-
f = message.getMessage();
637+
f = message.getResponse();
651638
if (f.length > 0) {
652639
writer.writeString(
653-
3,
640+
2,
654641
f
655642
);
656643
}
657644
};
658645

659646

660-
/**
661-
* @enum {number}
662-
*/
663-
proto.EvalFailedMessage.Reason = {
664-
TIMEOUT: 0,
665-
EXCEPTION: 1,
666-
CONFLICT: 2
667-
};
668-
669647
/**
670648
* optional uint64 id = 1;
671649
* @return {number}
@@ -682,32 +660,17 @@ proto.EvalFailedMessage.prototype.setId = function(value) {
682660

683661

684662
/**
685-
* optional Reason reason = 2;
686-
* @return {!proto.EvalFailedMessage.Reason}
687-
*/
688-
proto.EvalFailedMessage.prototype.getReason = function() {
689-
return /** @type {!proto.EvalFailedMessage.Reason} */ (jspb.Message.getFieldWithDefault(this, 2, 0));
690-
};
691-
692-
693-
/** @param {!proto.EvalFailedMessage.Reason} value */
694-
proto.EvalFailedMessage.prototype.setReason = function(value) {
695-
jspb.Message.setProto3EnumField(this, 2, value);
696-
};
697-
698-
699-
/**
700-
* optional string message = 3;
663+
* optional string response = 2;
701664
* @return {string}
702665
*/
703-
proto.EvalFailedMessage.prototype.getMessage = function() {
704-
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
666+
proto.EvalFailedMessage.prototype.getResponse = function() {
667+
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
705668
};
706669

707670

708671
/** @param {string} value */
709-
proto.EvalFailedMessage.prototype.setMessage = function(value) {
710-
jspb.Message.setProto3StringField(this, 3, value);
672+
proto.EvalFailedMessage.prototype.setResponse = function(value) {
673+
jspb.Message.setProto3StringField(this, 2, value);
711674
};
712675

713676

0 commit comments

Comments
 (0)