Skip to content

Commit

Permalink
Deparse RCall source when re-throwing R errors in JS (#365)
Browse files Browse the repository at this point in the history
* Deparse RCall when re-throwing R errors in JS

* Update NEWS.md
  • Loading branch information
georgestagg authored Feb 28, 2024
1 parent bb317be commit a9396fe
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 4 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

* The `RCall` and `RFunction` classes now have a `capture(options, ...args)` method that captures output during evaluation.

* Errors re-thrown by `evalR` now include information about the source R call, helping to identify the original location of the error.

* JavaScript objects of type `TypedArray`, `ArrayBuffer`, and `ArrayBufferView` (e.g. `Uint8Array`) may now be used with the `RRaw` R object constructor. The generic `RObject` constructor now converts objects of this type to R raw atomic vectors by default.

* Constructing new R objects using `await new RObject(...)` now supports input objects of the form: `{a: [...], b: [...]}` or D3-style data arrays of the form: `[{a: ..., b: ...}, {a: ..., b: ...}, {a: ..., b: ...}, ... ]`. Where possible, lists are constructed of class `data.frame`. Direct construction with `RList()` does not create a `data.frame`.
Expand Down
18 changes: 18 additions & 0 deletions src/webR/robj-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,24 @@ export class RCall extends RObject {
capture(options: EvalROptions = {}) {
return Module.webr.captureR(this, options);
}

deparse(): string {
const prot = { n: 0 };
try {
const call = Module._Rf_lang2(
new RSymbol('deparse1').ptr,
Module._Rf_lang2(new RSymbol('quote').ptr, this.ptr)
);
protectInc(call, prot);

const val = RCharacter.wrap(safeEval(call, objs.baseEnv));
protectInc(val, prot);

return val.toString();
} finally {
unprotect(prot.n);
}
}
}

export class RList extends RObject {
Expand Down
9 changes: 5 additions & 4 deletions src/webR/webr-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { EmPtr, Module } from './emscripten';
import { IN_NODE } from './compat';
import { replaceInObject, throwUnreachable } from './utils';
import { WebRPayloadRaw, WebRPayloadPtr, WebRPayloadWorker, isWebRPayloadPtr } from './payload';
import { RObject, isRObject, REnvironment, RList, getRWorkerClass } from './robj-worker';
import { RObject, isRObject, REnvironment, RList, RCall, getRWorkerClass } from './robj-worker';
import { RCharacter, RString, keep, destroy, purge, shelters } from './robj-worker';
import { RLogical, RInteger, RDouble, initPersistentObjects, objs } from './robj-worker';
import { RPtr, RType, RTypeMap, WebRData, WebRDataRaw } from './robj';
Expand Down Expand Up @@ -711,9 +711,10 @@ function captureR(expr: string | RObject, options: EvalROptions = {}): {
(out) => out.get('type').toString() === 'error'
);
if (error) {
throw new Error(
error.pluck('data', 'message')?.toString() || 'An error occurred evaluating R code.'
);
const call = error.pluck('data','call') as RCall;
const source = call && call.type() === 'call' ? call.deparse() : 'Unknown source';
const message = error.pluck('data', 'message')?.toString() || 'An error occurred evaluating R code.';
throw new Error(`Error in ${source}: ${message}`);
}
}

Expand Down

0 comments on commit a9396fe

Please sign in to comment.