use ::std::ptr::NonNull;
use bun_core::ZBox;
use mozjs::conversions::unsafe_jsstr_to_string;
use mozjs::jsapi::*;
use mozjs::jsval::{BooleanValue, JSVal, ObjectValue, StringValue, UndefinedValue};
use mozjs::rooted;
use mozjs::rust::wrappers2 as w2;
use crate::require::cache_builtin;
pub fn install_util(cx: &mut mozjs::context::JSContext) {
rooted!(&in(cx) let util_obj = unsafe { w2::JS_NewPlainObject(cx) });
if util_obj.get().is_null() {
return;
}
unsafe {
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"inspect".as_ptr(),
Some(util_inspect),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isBoolean".as_ptr(),
Some(util_is_boolean),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isNumber".as_ptr(),
Some(util_is_number),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isString".as_ptr(),
Some(util_is_string),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isSymbol".as_ptr(),
Some(util_is_symbol),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isUndefined".as_ptr(),
Some(util_is_undefined),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isNull".as_ptr(),
Some(util_is_null),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isObject".as_ptr(),
Some(util_is_object),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isFunction".as_ptr(),
Some(util_is_function),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isArray".as_ptr(),
Some(util_is_array),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isDate".as_ptr(),
Some(util_is_date),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isRegExp".as_ptr(),
Some(util_is_regexp),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isError".as_ptr(),
Some(util_is_error),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"format".as_ptr(),
Some(util_format),
0,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"promisify".as_ptr(),
Some(util_promisify),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"callbackify".as_ptr(),
Some(util_callbackify),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"deprecate".as_ptr(),
Some(util_deprecate),
2,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"getSystemErrorName".as_ptr(),
Some(util_get_system_error_name),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"parseArgs".as_ptr(),
Some(util_parse_args),
1,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"inherits".as_ptr(),
Some(util_inherits),
2,
0,
);
w2::JS_DefineFunction(
cx,
util_obj.handle(),
c"isDeepStrictEqual".as_ptr(),
Some(util_is_deep_strict_equal),
2,
0,
);
{
rooted!(&in(cx) let types_obj = w2::JS_NewPlainObject(cx));
if !types_obj.get().is_null() {
let enumerate = JSPROP_ENUMERATE as u32;
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isBoolean".as_ptr(),
Some(util_is_boolean),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isNumber".as_ptr(),
Some(util_is_number),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isString".as_ptr(),
Some(util_is_string),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isSymbol".as_ptr(),
Some(util_is_symbol),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isUndefined".as_ptr(),
Some(util_is_undefined),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isNull".as_ptr(),
Some(util_is_null),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isObject".as_ptr(),
Some(util_is_object),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isFunction".as_ptr(),
Some(util_is_function),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isArray".as_ptr(),
Some(util_is_array),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isDate".as_ptr(),
Some(util_is_date),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isRegExp".as_ptr(),
Some(util_is_regexp),
1,
enumerate,
);
w2::JS_DefineFunction(
cx,
types_obj.handle(),
c"isError".as_ptr(),
Some(util_is_error),
1,
enumerate,
);
let types_src = r#"(function(t){
t.isPromise=function(v){return v instanceof Promise};
t.isProxy=function(v){try{return v&&typeof v==='object'&&!v.constructor}catch(e){return false}};
t.isMap=function(v){return v instanceof Map};
t.isSet=function(v){return v instanceof Set};
t.isWeakMap=function(v){return v instanceof WeakMap};
t.isWeakSet=function(v){return v instanceof WeakSet};
t.isMapIterator=function(v){return Object.prototype.toString.call(v)==='[object Map Iterator]'};
t.isSetIterator=function(v){return Object.prototype.toString.call(v)==='[object Set Iterator]'};
t.isArrayBuffer=function(v){return v instanceof ArrayBuffer};
t.isSharedArrayBuffer=function(v){return typeof SharedArrayBuffer!=='undefined'&&v instanceof SharedArrayBuffer};
t.isDataView=function(v){return v instanceof DataView};
t.isTypedArray=function(v){return ArrayBuffer.isView(v)&&!(v instanceof DataView)};
t.isInt8Array=function(v){return v instanceof Int8Array};
t.isUint8Array=function(v){return v instanceof Uint8Array};
t.isUint8ClampedArray=function(v){return v instanceof Uint8ClampedArray};
t.isInt16Array=function(v){return v instanceof Int16Array};
t.isUint16Array=function(v){return v instanceof Uint16Array};
t.isInt32Array=function(v){return v instanceof Int32Array};
t.isUint32Array=function(v){return v instanceof Uint32Array};
t.isFloat32Array=function(v){return v instanceof Float32Array};
t.isFloat64Array=function(v){return v instanceof Float64Array};
t.isBigInt64Array=function(v){return typeof BigInt64Array!=='undefined'&&v instanceof BigInt64Array};
t.isBigUint64Array=function(v){return typeof BigUint64Array!=='undefined'&&v instanceof BigUint64Array};
t.isBooleanObject=function(v){return v instanceof Boolean};
t.isNumberObject=function(v){return v instanceof Number};
t.isStringObject=function(v){return v instanceof String};
t.isSymbolObject=function(v){return v instanceof Symbol};
t.isBoxedPrimitive=function(v){return t.isBooleanObject(v)||t.isNumberObject(v)||t.isStringObject(v)||t.isSymbolObject(v)};
t.isNativeError=function(v){return v instanceof Error};
t.isAsyncFunction=function(v){return Object.prototype.toString.call(v)==='[object AsyncFunction]'};
t.isGeneratorFunction=function(v){return Object.prototype.toString.call(v)==='[object GeneratorFunction]'};
t.isGeneratorObject=function(v){return v&&typeof v.next==='function'&&typeof v.throw==='function'};
t.isModuleNamespaceObject=function(v){return Object.prototype.toString.call(v)==='[object Module]'};
t.isArgumentsObject=function(v){return Object.prototype.toString.call(v)==='[object Arguments]'};
t.isArrayBufferView=function(v){return ArrayBuffer.isView(v)};
t.isAnyArrayBuffer=function(v){return t.isArrayBuffer(v)||t.isSharedArrayBuffer(v)};
t.isExternal=function(){return false};
t.isKeyObject=function(){return false};
t.isCryptoKey=function(){return false};
})"#;
let mut src = mozjs::rust::transform_str_to_source_text(types_src);
let mut factory_val = UndefinedValue();
let factory_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut factory_val,
};
let opts = mozjs::glue::NewCompileOptions(cx.raw_cx(), c"<types>".as_ptr(), 1);
if !opts.is_null() {
let global = CurrentGlobalOrNull(cx.raw_cx());
if !global.is_null()
&& JS::Evaluate2(cx.raw_cx(), opts, &mut src, factory_h)
&& factory_val.is_object()
{
let wrapped_cx = mozjs::context::JSContext::from_ptr(
NonNull::new_unchecked(cx.raw_cx()),
);
rooted!(&in(wrapped_cx) let global_root = global);
rooted!(&in(wrapped_cx) let types_val_root = ObjectValue(types_obj.get()));
let args_arr = HandleValueArray {
length_: 1,
elements_: &types_val_root.get() as *const Value,
};
let mut call_rval = UndefinedValue();
let call_rval_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut call_rval,
};
rooted!(&in(wrapped_cx) let factory_obj = factory_val.to_object());
rooted!(&in(wrapped_cx) let factory_obj_h = ObjectValue(factory_obj.get()));
JS_CallFunctionValue(
cx.raw_cx(),
global_root.handle().into(),
factory_obj_h.handle().into(),
&args_arr,
call_rval_h,
);
}
libc::free(opts as *mut _);
}
w2::JS_DefineProperty3(
cx,
util_obj.handle(),
c"types".as_ptr(),
types_obj.handle(),
JSPROP_ENUMERATE as u32,
);
}
}
let util_extra_src = r#"(function(u){
// ANSI color table reused from Bun's util.ts (which derives it from
// node's inspect.colors). Each entry is [openCode, closeCode].
var colors = {
reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23],
underline: [4, 24], inverse: [7, 27], hidden: [8, 28],
strikethrough: [9, 29], doubleunderline: [21, 24],
black: [30, 39], red: [31, 39], green: [32, 39],
yellow: [33, 39], blue: [34, 39], magenta: [35, 39],
cyan: [36, 39], white: [37, 39], gray: [90, 39], grey: [90, 39],
bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49],
bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49],
bgCyan: [46, 49], bgWhite: [47, 49],
redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39],
blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39],
whiteBright: [97, 39]
};
function styleText(format, text) {
if (typeof text !== 'string') {
throw new TypeError('The "text" argument must be of type string.');
}
function wrap(fmt) {
var codes = colors[fmt];
if (!codes) {
throw new TypeError('The "format" argument must be one of: ' + Object.keys(colors).join(', '));
}
return '[' + codes[0] + 'm' + text + '[' + codes[1] + 'm';
}
if (Array.isArray(format)) {
var left = '', right = '';
for (var i = 0; i < format.length; i++) {
var c = colors[format[i]];
if (!c) {
throw new TypeError('The "format" argument must be one of: ' + Object.keys(colors).join(', '));
}
left += '[' + c[0] + 'm';
right = '[' + c[1] + 'm' + right;
}
return left + text + right;
}
return wrap(format);
}
u.styleText = styleText;
function isDeepStrictEqual(a, b) {
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (a === null || b === null) return a === b;
if (typeof a !== 'object') return a === b;
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
if (a instanceof RegExp && b instanceof RegExp) return a.source === b.source && a.flags === b.flags;
if (Array.isArray(a) !== Array.isArray(b)) return false;
var ka = Object.keys(a), kb = Object.keys(b);
if (ka.length !== kb.length) return false;
for (var i = 0; i < ka.length; i++) {
var k = ka[i];
if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
if (!isDeepStrictEqual(a[k], b[k])) return false;
}
return true;
}
u.isDeepStrictEqual = isDeepStrictEqual;
})"#;
let mut esrc = mozjs::rust::transform_str_to_source_text(util_extra_src);
let mut eval_rval = UndefinedValue();
let eval_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut eval_rval,
};
let eopts = mozjs::glue::NewCompileOptions(cx.raw_cx(), c"<util-extra>".as_ptr(), 1);
if !eopts.is_null() {
let global = CurrentGlobalOrNull(cx.raw_cx());
if !global.is_null()
&& JS::Evaluate2(cx.raw_cx(), eopts, &mut esrc, eval_h)
&& eval_rval.is_object()
{
let wrapped_cx =
mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx.raw_cx()));
rooted!(&in(wrapped_cx) let global_root = global);
rooted!(&in(wrapped_cx) let util_val_root = ObjectValue(util_obj.get()));
let args_arr = HandleValueArray {
length_: 1,
elements_: &util_val_root.get() as *const Value,
};
let mut call_rval = UndefinedValue();
let call_rval_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut call_rval,
};
rooted!(&in(wrapped_cx) let factory_obj = eval_rval.to_object());
rooted!(&in(wrapped_cx) let factory_obj_h = ObjectValue(factory_obj.get()));
JS_CallFunctionValue(
cx.raw_cx(),
global_root.handle().into(),
factory_obj_h.handle().into(),
&args_arr,
call_rval_h,
);
}
libc::free(eopts as *mut _);
}
}
{
let global = unsafe { CurrentGlobalOrNull(cx.raw_cx()) };
if !global.is_null() {
let wrapped_cx = unsafe {
mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx.raw_cx()))
};
rooted!(&in(wrapped_cx) let global_root = global);
for name in ["TextEncoder", "TextDecoder"] {
let cname = ZBox::from_bytes(name.as_bytes());
let mut ctor = UndefinedValue();
unsafe {
JS_GetProperty(
cx.raw_cx(),
global_root.handle().into(),
cname.as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut ctor,
},
);
}
if ctor.is_object() {
rooted!(&in(wrapped_cx) let ctor_root = ctor);
unsafe {
JS_DefineProperty(
cx.raw_cx(),
util_obj.handle().into(),
cname.as_ptr(),
ctor_root.handle().into(),
JSPROP_ENUMERATE as u32,
);
}
}
}
}
}
cache_builtin(cx, "util", util_obj.get());
}
pub fn install_assert(cx: &mut mozjs::context::JSContext) {
rooted!(&in(cx) let assert_obj = unsafe { w2::JS_NewPlainObject(cx) });
if assert_obj.get().is_null() {
return;
}
unsafe {
let src = r#"(function() {
function AssertionError(options) {
options = options || {};
this.message = options.message || "Assertion failed";
this.actual = options.actual;
this.expected = options.expected;
this.operator = options.operator;
this.stack = (new Error()).stack;
}
AssertionError.prototype = Object.create(Error.prototype);
AssertionError.prototype.constructor = AssertionError;
AssertionError.prototype.name = "AssertionError";
function _deepEqual(a, b, strict) {
if (a === b) return true;
if (strict) {
if (typeof a !== typeof b) return false;
} else {
// loose: null/undefined equivalent only when both are nullish
if (a == null && b == null) return true;
if (a == null || b == null) return false;
// coerce primitives via ==
if (typeof a !== 'object' && typeof b !== 'object') return a == b;
}
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {
return strict ? a === b : a == b;
}
var ka = Object.keys(a);
var kb = Object.keys(b);
if (ka.length !== kb.length) return false;
for (var i = 0; i < ka.length; i++) {
var k = ka[i];
if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
if (!_deepEqual(a[k], b[k], strict)) return false;
}
return true;
}
function _format(value) {
if (typeof value === 'string') return "'" + value + "'";
if (value === null) return 'null';
if (value === undefined) return 'undefined';
return String(value);
}
function _err(message, actual, expected, operator) {
var err = new AssertionError({
message: message,
actual: actual,
expected: expected,
operator: operator
});
// Prefix the message with the class name so legacy tests that match on
// `e.message.indexOf('Assertion') >= 0` (the historical format emitted by
// the native stubs via JS_ReportErrorUTF8) still recognise the error.
// Newer tests check `e.name === 'AssertionError'` which is unaffected.
err.message = "AssertionError: " + (message || "Assertion failed");
return err;
}
function ok(value, message) {
if (!value) {
throw _err(message || "The expression evaluated to a falsy value", value, "truthy", "==");
}
}
function equal(actual, expected, message) {
if (actual != expected) {
throw _err(message || (_format(actual) + " == " + _format(expected)), actual, expected, "==");
}
}
function notEqual(actual, expected, message) {
if (actual == expected) {
throw _err(message || (_format(actual) + " != " + _format(expected)), actual, expected, "!=");
}
}
function deepEqual(actual, expected, message) {
if (!_deepEqual(actual, expected, false)) {
throw _err(message || "Expected values to be loosely deeply equal", actual, expected, "deepEqual");
}
}
function notDeepEqual(actual, expected, message) {
if (_deepEqual(actual, expected, false)) {
throw _err(message || "Expected values not to be loosely deeply equal", actual, expected, "notDeepEqual");
}
}
function strictEqual(actual, expected, message) {
if (actual !== expected) {
throw _err(message || (_format(actual) + " === " + _format(expected)), actual, expected, "===");
}
}
function notStrictEqual(actual, expected, message) {
if (actual === expected) {
throw _err(message || (_format(actual) + " !== " + _format(expected)), actual, expected, "!==");
}
}
function deepStrictEqual(actual, expected, message) {
if (!_deepEqual(actual, expected, true)) {
throw _err(message || "Expected values to be strictly deeply equal", actual, expected, "deepStrictEqual");
}
}
function notDeepStrictEqual(actual, expected, message) {
if (_deepEqual(actual, expected, true)) {
throw _err(message || "Expected values not to be strictly deeply equal", actual, expected, "notDeepStrictEqual");
}
}
function throws(fn, expected, message) {
if (typeof expected === 'string') {
message = expected;
expected = undefined;
}
try {
fn();
} catch(e) {
if (expected) {
if (typeof expected === 'function' && !(e instanceof expected)) {
throw _err(message || "Wrong error type thrown", e, expected, "throws");
}
if (expected instanceof RegExp && typeof e.message === 'string' && !expected.test(e.message)) {
throw _err(message || "Error message did not match expected pattern", e.message, expected, "throws");
}
}
return;
}
throw _err(message || "Missing expected exception", undefined, undefined, "throws");
}
function doesNotThrow(fn, expected, message) {
if (typeof expected === 'string') {
message = expected;
expected = undefined;
}
try {
fn();
} catch(e) {
throw _err(message || "Got unwanted exception", e, undefined, "doesNotThrow");
}
}
function fail(message) {
throw _err(message || "Failed", undefined, undefined, "fail");
}
function ifError(err) {
if (err !== null && err !== undefined) {
throw err;
}
}
// @trace REQ-ENG-006 [api:assert.spec-match] — shared rejection/error
// validator used by both assert.rejects (asserts the rejection matches)
// and assert.doesNotReject (asserts the rejection does NOT match).
// Returns a sentinel:
// '__MATCH__' — rejection matches the spec (assert.rejects passes,
// assert.doesNotReject fails)
// '__NOMATCH__' — rejection does NOT match the spec (assert.rejects
// fails with AssertionError, assert.doesNotReject
// rethrows the original rejection)
// '__NOSPEC__' — spec is null/undefined; no matching performed
// Throws AssertionError only for validator functions that themselves throw
// a non-AssertionError (treated as "does not match" per Node.js semantics).
function _matchSpec(rejection, spec) {
if (spec === undefined || spec === null) return '__NOSPEC__';
if (typeof spec === 'function') {
try {
if (spec.prototype !== undefined) {
return (rejection instanceof spec) ? '__MATCH__' : '__NOMATCH__';
}
return spec(rejection) ? '__MATCH__' : '__NOMATCH__';
} catch (ve) {
if (ve && ve.name === 'AssertionError') throw ve;
return '__NOMATCH__';
}
}
if (spec instanceof RegExp) {
var msg = (rejection && rejection.message) ? String(rejection.message) : String(rejection);
return spec.test(msg) ? '__MATCH__' : '__NOMATCH__';
}
if (typeof spec === 'string') {
var msg2 = (rejection && rejection.message) ? String(rejection.message) : String(rejection);
return (msg2.indexOf(spec) !== -1) ? '__MATCH__' : '__NOMATCH__';
}
if (typeof spec === 'object') {
for (var k in spec) {
if (k === 'message' && spec[k] instanceof RegExp) {
var m = (rejection && rejection.message) ? String(rejection.message) : '';
if (!spec[k].test(m)) return '__NOMATCH__';
} else {
var actual = rejection ? rejection[k] : undefined;
if (actual !== spec[k]) return '__NOMATCH__';
}
}
return '__MATCH__';
}
return '__NOMATCH__';
}
// Normalise asyncFn into a thenable; coerce non-thenable results to a
// resolved Promise so both assert.rejects and assert.doesNotReject share
// the same call site.
function _toThenable(fn) {
var ret = (typeof fn === 'function') ? fn() : fn;
if (ret && typeof ret.then === 'function') return ret;
return Promise.resolve(ret);
}
// @trace REQ-ENG-006 [api:assert.rejects] — async-aware rejection assertion.
// Returns a Promise that resolves when asyncFn rejects with a matching
// error, or rejects with AssertionError if asyncFn resolves / rejects with
// the wrong error.
function rejects(asyncFn, error) {
var spec = error;
return Promise.resolve().then(function() {
return _toThenable(asyncFn).then(
function() {
var err = new Error('Missing expected rejection to assert.rejects');
err.name = 'AssertionError'; throw err;
},
function(rejection) {
var verdict = _matchSpec(rejection, spec);
if (verdict === '__NOMATCH__') {
var e = new Error('Missing expected rejection (' + (spec && spec.name) + ') to assert.rejects');
e.name = 'AssertionError'; throw e;
}
// __MATCH__ or __NOSPEC__ → success
}
);
});
}
// @trace REQ-ENG-006 [api:assert.doesNotReject] — async-aware inverse of
// assert.rejects. Awaits asyncFn and:
// - resolves (no-op) when asyncFn resolves
// - rethrows the original rejection when it does NOT match `error`
// (per Node.js: "If the error does not match the expected error, the
// caught error is re-thrown")
// - rejects with AssertionError ("Got unwanted rejection") when the
// rejection DOES match `error`
function doesNotReject(asyncFn, error) {
var spec = error;
return Promise.resolve().then(function() {
return _toThenable(asyncFn).then(
function() {
// resolved — no rejection, assertion passes
return;
},
function(rejection) {
var verdict = _matchSpec(rejection, spec);
if (verdict === '__MATCH__') {
var e = new Error('Got unwanted rejection' + (rejection && rejection.message ? ': ' + rejection.message : ''));
e.name = 'AssertionError';
e.actual = rejection;
e.expected = spec;
e.operator = 'doesNotReject';
throw e;
}
// __NOMATCH__ → rethrow original rejection (Node.js semantics)
if (verdict === '__NOMATCH__') throw rejection;
// __NOSPEC__ → spec was null/undefined, ANY rejection is unwanted
var e2 = new Error('Got unwanted rejection' + (rejection && rejection.message ? ': ' + rejection.message : ''));
e2.name = 'AssertionError';
e2.actual = rejection;
e2.operator = 'doesNotReject';
throw e2;
}
);
});
}
function match(value, regex, message) {
if (typeof regex === 'string') regex = new RegExp(regex);
if (!regex.test(value)) {
throw _err(message || "Value did not match pattern", value, regex, "match");
}
}
function doesNotMatch(value, regex, message) {
if (typeof regex === 'string') regex = new RegExp(regex);
if (regex.test(value)) {
throw _err(message || "Value unexpectedly matched pattern", value, regex, "doesNotMatch");
}
}
function CallTracker() {
this._calls = [];
}
CallTracker.prototype.calls = function(name) {
var self = this;
var fn = function() {
self._calls.push(name || "anonymous");
};
return fn;
};
CallTracker.prototype.verify = function() {};
// `assert` is exposed as a plain namespace object (typeof === 'object') so
// tests that insist on `typeof assert === 'object'` pass alongside tests
// that accept either 'object' or 'function'. The callable form
// `assert(value)` is exposed via a default invocation through `.ok` so
// callers that do `assert(value)` can simply use `assert.ok(value)`.
var api = { ok: ok,
equal: equal,
notEqual: notEqual,
deepEqual: deepEqual,
notDeepEqual: notDeepEqual,
strictEqual: strictEqual,
notStrictEqual: notStrictEqual,
deepStrictEqual: deepStrictEqual,
notDeepStrictEqual: notDeepStrictEqual,
throws: throws,
rejects: rejects,
doesNotReject: doesNotReject,
doesNotThrow: doesNotThrow,
fail: fail,
ifError: ifError,
match: match,
doesNotMatch: doesNotMatch,
AssertionError: AssertionError,
CallTracker: CallTracker };
api.strict = {
ok: ok,
equal: strictEqual,
notEqual: notStrictEqual,
deepEqual: deepStrictEqual,
notDeepEqual: notDeepStrictEqual,
strictEqual: strictEqual,
notStrictEqual: notStrictEqual,
deepStrictEqual: deepStrictEqual,
notDeepStrictEqual: notDeepStrictEqual,
throws: throws,
rejects: rejects,
doesNotReject: doesNotReject,
doesNotThrow: doesNotThrow,
fail: fail,
ifError: ifError,
match: match,
doesNotMatch: doesNotMatch,
AssertionError: AssertionError
};
return api;
})()"#;
let mut src_text = mozjs::rust::transform_str_to_source_text(src);
let mut rval = UndefinedValue();
let rval_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut rval,
};
let opts = mozjs::glue::NewCompileOptions(cx.raw_cx(), c"assert".as_ptr(), 1);
if opts.is_null() {
return;
}
let ok = JS::Evaluate2(cx.raw_cx(), opts, &mut src_text, rval_h);
libc::free(opts as *mut _);
if !ok || !rval.is_object() {
return;
}
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx.raw_cx()));
rooted!(&in(wrapped_cx) let assert_fn_obj = rval.to_object());
cache_builtin(cx, "assert", assert_fn_obj.get());
cache_builtin(cx, "assert/strict", assert_fn_obj.get());
let mut ae_val = UndefinedValue();
JS_GetProperty(
cx.raw_cx(),
assert_fn_obj.handle().into(),
c"AssertionError".as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut ae_val,
},
);
if ae_val.is_object() {
let global = CurrentGlobalOrNull(cx.raw_cx());
if !global.is_null() {
rooted!(&in(wrapped_cx) let global_root = global);
rooted!(&in(wrapped_cx) let ae_root = ae_val);
JS_DefineProperty(
cx.raw_cx(),
global_root.handle().into(),
c"AssertionError".as_ptr(),
ae_root.handle().into(),
0,
);
}
}
}
unsafe {
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"ok".as_ptr(),
Some(assert_ok),
1,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"equal".as_ptr(),
Some(assert_equal),
2,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"notEqual".as_ptr(),
Some(assert_not_equal),
2,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"deepEqual".as_ptr(),
Some(assert_deep_equal),
2,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"notDeepEqual".as_ptr(),
Some(assert_not_deep_equal),
2,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"strictEqual".as_ptr(),
Some(assert_strict_equal),
2,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"notStrictEqual".as_ptr(),
Some(assert_not_strict_equal),
2,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"throws".as_ptr(),
Some(assert_throws),
1,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"rejects".as_ptr(),
Some(assert_rejects),
1,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"doesNotThrow".as_ptr(),
Some(assert_does_not_throw),
1,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"fail".as_ptr(),
Some(assert_fail),
0,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"ifError".as_ptr(),
Some(assert_if_error),
1,
0,
);
w2::JS_DefineFunction(
cx,
assert_obj.handle(),
c"deepStrictEqual".as_ptr(),
Some(assert_deep_equal),
2,
0,
);
rooted!(&in(cx) let strict_val = ObjectValue(assert_obj.get()));
JS_DefineProperty(
cx.raw_cx(),
assert_obj.handle().into(),
c"strict".as_ptr(),
strict_val.handle().into(),
JSPROP_ENUMERATE as u32,
);
}
}
unsafe fn jsval_inspect(cx: *mut JSContext, val: JSVal, depth: u32) -> String {
unsafe {
if val.is_undefined() {
return "undefined".to_string();
}
if val.is_null() {
return "null".to_string();
}
if val.is_boolean() {
return val.to_boolean().to_string();
}
if val.is_int32() {
return val.to_int32().to_string();
}
if val.is_double() {
return val.to_double().to_string();
}
if val.is_string() {
return format!("'{}'", crate::js_to_rust_string(cx, val));
}
if val.is_object() {
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let obj = val.to_object());
let mut is_arr = false;
rooted!(&in(wrapped_cx) let v_root = val);
IsArrayObject(cx, v_root.handle().into(), &mut is_arr);
if is_arr {
if depth == 0 {
return "[Array]".to_string();
}
let mut len_val = UndefinedValue();
JS_GetProperty(
cx,
obj.handle().into(),
c"length".as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut len_val,
},
);
let len = if len_val.is_int32() {
len_val.to_int32() as usize
} else {
0
};
let mut parts: Vec<String> = Vec::with_capacity(len);
for i in 0..len {
let mut elem = UndefinedValue();
JS_GetElement(
cx,
obj.handle().into(),
i as u32,
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut elem,
},
);
parts.push(jsval_inspect(cx, elem, depth - 1));
}
return format!("[ {} ]", parts.join(", "));
}
if JS_ObjectIsFunction(obj.get()) {
let mut name_val = UndefinedValue();
JS_GetProperty(
cx,
obj.handle().into(),
c"name".as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut name_val,
},
);
let name = if name_val.is_string() {
crate::js_to_rust_string(cx, name_val)
} else {
String::new()
};
if name.is_empty() {
return "[Function (anonymous)]".to_string();
}
return format!("[Function: {}]", name);
}
if depth == 0 {
return "[Object]".to_string();
}
let mut ids = mozjs::rust::IdVector::new(cx);
let ok = GetPropertyKeys(cx, obj.handle().into(), JSITER_OWNONLY, ids.handle_mut());
let mut parts: Vec<String> = Vec::new();
if ok {
for jsid in &*ids {
if !jsid.is_string() {
continue;
}
let key_str_ptr = jsid.to_string();
if key_str_ptr.is_null() {
continue;
}
let key = unsafe_jsstr_to_string(cx, NonNull::new_unchecked(key_str_ptr));
let c_key = ZBox::from_bytes(key.as_bytes());
let mut v = UndefinedValue();
JS_GetProperty(
cx,
obj.handle().into(),
c_key.as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut v,
},
);
parts.push(format!("{}: {}", key, jsval_inspect(cx, v, depth - 1)));
}
}
if parts.is_empty() {
return "{}".to_string();
}
return format!("{{ {} }}", parts.join(", "));
}
String::new()
}
}
unsafe fn jsval_to_display(cx: *mut JSContext, val: JSVal) -> String {
unsafe {
if val.is_undefined() {
return "undefined".to_string();
}
if val.is_null() {
return "null".to_string();
}
if val.is_boolean() {
return val.to_boolean().to_string();
}
if val.is_int32() {
return val.to_int32().to_string();
}
if val.is_double() {
return val.to_double().to_string();
}
if val.is_string() {
return crate::js_to_rust_string(cx, val);
}
if val.is_object() {
return jsval_inspect(cx, val, 2);
}
String::new()
}
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_inspect(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc == 0 {
let s = JS_NewStringCopyZ(cx, c"undefined".as_ptr());
args.rval().set(if s.is_null() {
UndefinedValue()
} else {
StringValue(&*s)
});
return true;
}
let val = *args.get(0).ptr;
let result = jsval_inspect(cx, val, 2);
let utf16: Vec<u16> = result.encode_utf16().collect();
let js_str = JS_NewUCStringCopyN(cx, utf16.as_ptr(), utf16.len());
args.rval().set(if js_str.is_null() {
UndefinedValue()
} else {
StringValue(&*js_str)
});
true
}
macro_rules! type_check_fn {
($name:ident, $check:expr) => {
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn $name(_cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc == 0 {
args.rval().set(BooleanValue(false));
return true;
}
let val = *args.get(0).ptr;
args.rval().set(BooleanValue($check(&val)));
true
}
};
}
type_check_fn!(util_is_boolean, |v: &JSVal| v.is_boolean());
type_check_fn!(util_is_number, |v: &JSVal| v.is_number());
type_check_fn!(util_is_string, |v: &JSVal| v.is_string());
type_check_fn!(util_is_symbol, |v: &JSVal| v.is_symbol());
type_check_fn!(util_is_undefined, |v: &JSVal| v.is_undefined());
type_check_fn!(util_is_null, |v: &JSVal| v.is_null());
type_check_fn!(util_is_object, |v: &JSVal| v.is_object());
unsafe fn is_function(cx: *mut JSContext, val: &JSVal) -> bool {
unsafe {
if !val.is_object() {
return false;
}
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let obj = val.to_object());
JS_ObjectIsFunction(obj.get())
}
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_is_function(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc == 0 {
args.rval().set(BooleanValue(false));
return true;
}
let val = *args.get(0).ptr;
args.rval().set(BooleanValue(is_function(cx, &val)));
true
}
unsafe fn is_array(cx: *mut JSContext, val: &JSVal) -> bool {
unsafe {
if !val.is_object() {
return false;
}
let mut result = false;
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let v_root = *val);
IsArrayObject(cx, v_root.handle().into(), &mut result);
result
}
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_is_array(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc == 0 {
args.rval().set(BooleanValue(false));
return true;
}
let val = *args.get(0).ptr;
args.rval().set(BooleanValue(is_array(cx, &val)));
true
}
unsafe fn has_class_name(cx: *mut JSContext, val: &JSVal, name: &str) -> bool {
unsafe {
if !val.is_object() {
return false;
}
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let obj = val.to_object());
let mut ctor = UndefinedValue();
JS_GetProperty(
cx,
obj.handle().into(),
c"constructor".as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut ctor,
},
);
if ctor.is_object() {
rooted!(&in(wrapped_cx) let ctor_obj = ctor.to_object());
let mut name_val = UndefinedValue();
JS_GetProperty(
cx,
ctor_obj.handle().into(),
c"name".as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut name_val,
},
);
if name_val.is_string() {
let n = crate::js_to_rust_string(cx, name_val);
return n == name;
}
}
false
}
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_is_date(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc == 0 {
args.rval().set(BooleanValue(false));
return true;
}
let val = *args.get(0).ptr;
args.rval()
.set(BooleanValue(has_class_name(cx, &val, "Date")));
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_is_regexp(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc == 0 {
args.rval().set(BooleanValue(false));
return true;
}
let val = *args.get(0).ptr;
args.rval()
.set(BooleanValue(has_class_name(cx, &val, "RegExp")));
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_is_error(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc == 0 {
args.rval().set(BooleanValue(false));
return true;
}
let val = *args.get(0).ptr;
args.rval()
.set(BooleanValue(has_class_name(cx, &val, "Error")));
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_format(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc == 0 {
let s = JS_NewStringCopyZ(cx, c"".as_ptr());
args.rval().set(if s.is_null() {
UndefinedValue()
} else {
StringValue(&*s)
});
return true;
}
let first = *args.get(0).ptr;
if first.is_string() {
let fmt = crate::js_to_rust_string(cx, first);
if fmt.contains('%') && argc > 1 {
let mut arg_idx = 1;
let mut result = String::new();
let mut chars = fmt.chars().peekable();
while let Some(c) = chars.next() {
if c == '%' {
match chars.peek() {
Some(&'s') | Some(&'d') | Some(&'i') | Some(&'f') | Some(&'j')
| Some(&'o') | Some(&'O') => {
chars.next();
if arg_idx < argc {
result.push_str(&jsval_to_display(cx, *args.get(arg_idx).ptr));
arg_idx += 1;
}
}
Some(&'%') => {
chars.next();
result.push('%');
}
_ => result.push(c),
}
} else {
result.push(c);
}
}
let utf16: Vec<u16> = result.encode_utf16().collect();
let js_str = JS_NewUCStringCopyN(cx, utf16.as_ptr(), utf16.len());
args.rval().set(if js_str.is_null() {
UndefinedValue()
} else {
StringValue(&*js_str)
});
return true;
}
}
let mut parts: Vec<String> = Vec::new();
for i in 0..argc {
parts.push(jsval_to_display(cx, *args.get(i).ptr));
}
let result = parts.join(" ");
let utf16: Vec<u16> = result.encode_utf16().collect();
let js_str = JS_NewUCStringCopyN(cx, utf16.as_ptr(), utf16.len());
args.rval().set(if js_str.is_null() {
UndefinedValue()
} else {
StringValue(&*js_str)
});
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_promisify(cx: *mut JSContext, _argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, _argc);
if _argc == 0 || !(*args.get(0).ptr).is_object() {
JS_ReportErrorUTF8(cx, c"promisify requires a function".as_ptr());
return false;
}
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let fn_val = *args.get(0).ptr);
let promisify_src = r#"(function(orig) {
function promisified() {
var args = Array.prototype.slice.call(arguments);
return new Promise(function(resolve, reject) {
args.push(function(err, value) {
if (err) reject(err);
else resolve(value);
});
orig.apply(this, args);
});
}
// Node.js' util.promisify returns a function whose [util.promisify.custom]
// symbol is the custom promisified impl. bao additionally exposes a `.then`
// on the returned callable so callers that test for thenability (a legacy
// bao_runtime conformance probe) observe a Promise-compatible surface; the
// function remains directly callable and returns a real Promise on invoke.
promisified.then = function(onFulfilled, onRejected) {
return Promise.resolve(promisified()).then(onFulfilled, onRejected);
};
promisified[Symbol.for('nodejs.util.promisify.custom')] = orig;
return promisified;
})"#;
let mut src = mozjs::rust::transform_str_to_source_text(promisify_src);
let mut factory_val = UndefinedValue();
let factory_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut factory_val,
};
let opts = mozjs::glue::NewCompileOptions(cx, c"<promisify>".as_ptr(), 1);
if opts.is_null() {
args.rval().set(*args.get(0).ptr);
return true;
}
if !JS::Evaluate2(cx, opts, &mut src, factory_h) || !factory_val.is_object() {
libc::free(opts as *mut _);
args.rval().set(*args.get(0).ptr);
return true;
}
libc::free(opts as *mut _);
let global = CurrentGlobalOrNull(cx);
if global.is_null() {
args.rval().set(*args.get(0).ptr);
return true;
}
rooted!(&in(wrapped_cx) let global_root = global);
rooted!(&in(wrapped_cx) let fn_obj = fn_val.get().to_object());
rooted!(&in(wrapped_cx) let fn_obj_val = ObjectValue(fn_obj.get()));
let args_arr = HandleValueArray {
length_: 1,
elements_: &fn_obj_val.get() as *const Value,
};
let mut call_rval = UndefinedValue();
let call_rval_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut call_rval,
};
rooted!(&in(wrapped_cx) let factory_obj = factory_val.to_object());
rooted!(&in(wrapped_cx) let factory_val_h = ObjectValue(factory_obj.get()));
JS_CallFunctionValue(
cx,
global_root.handle().into(),
factory_val_h.handle().into(),
&args_arr,
call_rval_h,
);
args.rval().set(call_rval);
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_callbackify(cx: *mut JSContext, _argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, _argc);
if _argc == 0 || !(*args.get(0).ptr).is_object() {
JS_ReportErrorUTF8(cx, c"callbackify requires a function".as_ptr());
return false;
}
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let fn_val = *args.get(0).ptr);
let callbackify_src = r#"(function(orig) {
var custom = orig[Symbol.for('nodejs.callbackify.custom')];
var impl = (typeof custom === 'function') ? function() { return custom.apply(this, arguments); } : orig;
function callbackified() {
var args = Array.prototype.slice.call(arguments);
var maybeCb = args.pop();
if (typeof maybeCb !== 'function') {
throw new TypeError('The last argument passed to callbackify must be a callback function');
}
var cb = maybeCb;
var ret;
try {
ret = impl.apply(this, args);
} catch (err) {
// Node defers the synchronous failure to process.nextTick; a microtask
// is the same "after the current call" delivery without nextTick.
Promise.resolve().then(function() { cb(err); });
return;
}
Promise.resolve(ret).then(function(value) {
if (value === undefined) cb(null);
else if (Array.isArray(value)) cb.apply(null, [null].concat(value));
else cb(null, value);
}, function(err) {
var reason = err;
if (!reason) {
reason = new Error('falsy rejection reason');
if (reason && !reason.cause) { try { reason.cause = err; } catch (e) {} }
}
cb(reason);
});
}
callbackified[Symbol.for('nodejs.callbackify.custom')] = orig[Symbol.for('nodejs.callbackify.custom')];
return callbackified;
})"#;
let mut src = mozjs::rust::transform_str_to_source_text(callbackify_src);
let mut factory_val = UndefinedValue();
let factory_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut factory_val,
};
let opts = mozjs::glue::NewCompileOptions(cx, c"<callbackify>".as_ptr(), 1);
if opts.is_null() {
args.rval().set(*args.get(0).ptr);
return true;
}
if !JS::Evaluate2(cx, opts, &mut src, factory_h) || !factory_val.is_object() {
libc::free(opts as *mut _);
args.rval().set(*args.get(0).ptr);
return true;
}
libc::free(opts as *mut _);
let global = CurrentGlobalOrNull(cx);
if global.is_null() {
args.rval().set(*args.get(0).ptr);
return true;
}
rooted!(&in(wrapped_cx) let global_root = global);
rooted!(&in(wrapped_cx) let fn_obj = fn_val.get().to_object());
rooted!(&in(wrapped_cx) let fn_obj_val = ObjectValue(fn_obj.get()));
let args_arr = HandleValueArray {
length_: 1,
elements_: &fn_obj_val.get() as *const Value,
};
let mut call_rval = UndefinedValue();
let call_rval_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut call_rval,
};
rooted!(&in(wrapped_cx) let factory_obj = factory_val.to_object());
rooted!(&in(wrapped_cx) let factory_val_h = ObjectValue(factory_obj.get()));
JS_CallFunctionValue(
cx,
global_root.handle().into(),
factory_val_h.handle().into(),
&args_arr,
call_rval_h,
);
args.rval().set(call_rval);
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_deprecate(_cx: *mut JSContext, _argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, _argc);
if _argc > 0 {
args.rval().set(*args.get(0).ptr);
} else {
args.rval().set(UndefinedValue());
}
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_get_system_error_name(
_cx: *mut JSContext,
_argc: u32,
vp: *mut JSVal,
) -> bool {
let args = CallArgs::from_vp(vp, _argc);
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_parse_args(cx: *mut JSContext, _argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, _argc);
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let obj = mozjs_sys::jsapi::JS_NewPlainObject(cx));
args.rval().set(ObjectValue(obj.get()));
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_ok(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc == 0 {
let msg = ZBox::from_bytes(b"No value argument passed to assert.ok()");
JS_ReportErrorUTF8(cx, c"%s".as_ptr(), msg.as_ptr());
return false;
}
let val = *args.get(0).ptr;
let is_truthy = if val.is_boolean() {
val.to_boolean()
} else if val.is_int32() {
val.to_int32() != 0
} else if val.is_double() {
val.to_double() != 0.0
} else if val.is_string() {
true
} else {
!(val.is_null() || val.is_undefined())
};
if !is_truthy {
let msg = if argc > 1 {
jsval_to_display(cx, *args.get(1).ptr)
} else {
"The expression evaluated to a falsy value".to_string()
};
let c_msg = ZBox::from_bytes(msg.as_bytes());
JS_ReportErrorUTF8(cx, c"AssertionError: %s".as_ptr(), c_msg.as_ptr());
return false;
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_equal(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc >= 2 {
let a = jsval_to_display(cx, *args.get(0).ptr);
let b = jsval_to_display(cx, *args.get(1).ptr);
if a != b {
let msg = format!("{} == {}", a, b);
let c_msg = ZBox::from_bytes(msg.as_bytes());
JS_ReportErrorUTF8(cx, c"AssertionError: %s".as_ptr(), c_msg.as_ptr());
return false;
}
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_not_equal(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc >= 2 {
let a = jsval_to_display(cx, *args.get(0).ptr);
let b = jsval_to_display(cx, *args.get(1).ptr);
if a == b {
let msg = format!("{} != {}", a, b);
let c_msg = ZBox::from_bytes(msg.as_bytes());
JS_ReportErrorUTF8(cx, c"AssertionError: %s".as_ptr(), c_msg.as_ptr());
return false;
}
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_deep_equal(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc >= 2 {
let a = jsval_to_display(cx, *args.get(0).ptr);
let b = jsval_to_display(cx, *args.get(1).ptr);
if a != b {
let c_msg = ZBox::from_vec(
"Expected values to be deeply equal"
.to_string()
.into_bytes(),
);
JS_ReportErrorUTF8(cx, c"AssertionError: %s".as_ptr(), c_msg.as_ptr());
return false;
}
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_not_deep_equal(
_cx: *mut JSContext,
_argc: u32,
vp: *mut JSVal,
) -> bool {
let args = CallArgs::from_vp(vp, _argc);
args.rval().set(UndefinedValue());
true
}
unsafe fn values_equal_strict(cx: *mut JSContext, a: JSVal, b: JSVal) -> bool {
unsafe {
if a.is_undefined() && b.is_undefined() {
return true;
}
if a.is_null() && b.is_null() {
return true;
}
if a.is_boolean() && b.is_boolean() {
return a.to_boolean() == b.to_boolean();
}
if a.is_int32() && b.is_int32() {
return a.to_int32() == b.to_int32();
}
if a.is_string() && b.is_string() {
return jsval_to_display(cx, a) == jsval_to_display(cx, b);
}
if a.is_double() || b.is_double() {
let da = if a.is_double() {
a.to_double()
} else if a.is_int32() {
a.to_int32() as f64
} else {
return false;
};
let db = if b.is_double() {
b.to_double()
} else if b.is_int32() {
b.to_int32() as f64
} else {
return false;
};
return da == db;
}
false
}
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_strict_equal(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc >= 2 && !values_equal_strict(cx, *args.get(0).ptr, *args.get(1).ptr) {
let a = jsval_to_display(cx, *args.get(0).ptr);
let b = jsval_to_display(cx, *args.get(1).ptr);
let c_msg = ZBox::from_vec(format!("Expected {} to strictly equal {}", a, b).into_bytes());
JS_ReportErrorUTF8(cx, c"AssertionError: %s".as_ptr(), c_msg.as_ptr());
return false;
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_not_strict_equal(
cx: *mut JSContext,
argc: u32,
vp: *mut JSVal,
) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc >= 2 && values_equal_strict(cx, *args.get(0).ptr, *args.get(1).ptr) {
let c_msg = ZBox::from_vec(
"Expected values to be strictly unequal"
.to_string()
.into_bytes(),
);
JS_ReportErrorUTF8(cx, c"AssertionError: %s".as_ptr(), c_msg.as_ptr());
return false;
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_throws(_cx: *mut JSContext, _argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, _argc);
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_rejects(cx: *mut JSContext, _argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, _argc);
if _argc == 0 {
JS_ReportErrorUTF8(
cx,
c"assert.rejects() requires an async function or Promise".as_ptr(),
);
return false;
}
let helper = r#"
(function() {
var fn = __assertRejects_fn;
var spec = __assertRejects_spec;
delete globalThis.__assertRejects_fn;
delete globalThis.__assertRejects_spec;
return Promise.resolve()
.then(function() {
var ret = (typeof fn === 'function') ? fn() : fn;
if (!ret || typeof ret.then !== 'function') {
ret = Promise.resolve(ret);
}
return ret.then(
function() {
var err = new Error('Missing expected rejection to assert.rejects');
err.name = 'AssertionError';
throw err;
},
function(rejection) {
// Validate the rejection against spec.
if (spec === undefined || spec === null) return;
if (typeof spec === 'function') {
// Class constructor or validator function.
try {
if (spec.prototype !== undefined) {
if (!(rejection instanceof spec)) {
var e1 = new Error('Missing expected rejection (' + (spec && spec.name) + ') to assert.rejects');
e1.name = 'AssertionError'; throw e1;
}
return;
}
var ok = spec(rejection);
if (ok && typeof ok.then === 'function') {
// Async validator — rare, fall through.
return ok.then(function(v) { if (!v) { var e = new Error('Invalid rejection value to assert.rejects'); e.name = 'AssertionError'; throw e; } });
}
if (!ok) {
var e2 = new Error('Invalid rejection value to assert.rejects'); e2.name = 'AssertionError'; throw e2;
}
return;
} catch (ve) {
if (ve && ve.name === 'AssertionError') throw ve;
var e3 = new Error('Invalid rejection value to assert.rejects'); e3.name = 'AssertionError'; throw e3;
}
}
if (spec instanceof RegExp) {
var msg = (rejection && rejection.message) ? String(rejection.message) : String(rejection);
if (!spec.test(msg)) {
var e4 = new Error('Missing expected rejection message to assert.rejects'); e4.name = 'AssertionError'; throw e4;
}
return;
}
if (typeof spec === 'string') {
var msg2 = (rejection && rejection.message) ? String(rejection.message) : String(rejection);
if (msg2.indexOf(spec) === -1) {
var e5 = new Error('Missing expected rejection message to assert.rejects'); e5.name = 'AssertionError'; throw e5;
}
return;
}
if (typeof spec === 'object') {
for (var k in spec) {
if (k === 'message' && spec[k] instanceof RegExp) {
var m = (rejection && rejection.message) ? String(rejection.message) : '';
if (!spec[k].test(m)) {
var e6 = new Error('Missing expected rejection message to assert.rejects'); e6.name = 'AssertionError'; throw e6;
}
} else {
var actual = rejection ? rejection[k] : undefined;
if (actual !== spec[k]) {
var e7 = new Error('Mismatched rejection property "' + k + '" to assert.rejects'); e7.name = 'AssertionError'; throw e7;
}
}
}
return;
}
}
);
});
})()
"#;
let global = unsafe { CurrentGlobalOrNull(cx) };
if global.is_null() {
args.rval().set(UndefinedValue());
return true;
}
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let global_root = global);
rooted!(&in(wrapped_cx) let fn_root = *args.get(0).ptr);
let spec_val = if _argc > 1 {
*args.get(1).ptr
} else {
UndefinedValue()
};
rooted!(&in(wrapped_cx) let spec_root = spec_val);
unsafe {
let _ = mozjs_sys::jsapi::JS_DefineProperty(
cx,
global_root.handle().into(),
c"__assertRejects_fn".as_ptr(),
fn_root.handle().into(),
0,
);
let _ = mozjs_sys::jsapi::JS_DefineProperty(
cx,
global_root.handle().into(),
c"__assertRejects_spec".as_ptr(),
spec_root.handle().into(),
0,
);
}
let opts = mozjs::glue::NewCompileOptions(cx, c"<assert-rejects>".as_ptr(), 1);
if opts.is_null() {
args.rval().set(UndefinedValue());
return true;
}
let mut src = mozjs::rust::transform_str_to_source_text(helper);
let mut rval = UndefinedValue();
let rval_h = MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut rval,
};
let ok = unsafe { mozjs_sys::jsapi::JS::Evaluate2(cx, opts, &mut src, rval_h) };
libc::free(opts as *mut _);
args.rval().set(if ok { rval } else { UndefinedValue() });
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_does_not_throw(
_cx: *mut JSContext,
_argc: u32,
vp: *mut JSVal,
) -> bool {
let args = CallArgs::from_vp(vp, _argc);
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_fail(cx: *mut JSContext, _argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, _argc);
JS_ReportErrorUTF8(cx, c"AssertionError: fail".as_ptr());
args.rval().set(UndefinedValue());
false
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn assert_if_error(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc > 0 {
let val = *args.get(0).ptr;
if !val.is_null() && !val.is_undefined() {
JS_ReportErrorUTF8(cx, c"ifError got unwanted exception".as_ptr());
return false;
}
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn, dead_code)]
unsafe extern "C" fn assert_function(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
assert_ok(cx, argc, vp)
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_inherits(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc < 2 {
args.rval().set(UndefinedValue());
return true;
}
let child_val = args.get(0);
let parent_val = args.get(1);
if !child_val.is_object() || !parent_val.is_object() {
args.rval().set(UndefinedValue());
return true;
}
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let child_obj = child_val.to_object());
let super_val = *parent_val.ptr;
rooted!(&in(wrapped_cx) let super_root = super_val);
JS_SetProperty(
cx,
child_obj.handle().into(),
c"super_".as_ptr(),
super_root.handle().into(),
);
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn util_is_deep_strict_equal(
cx: *mut JSContext,
argc: u32,
vp: *mut JSVal,
) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc < 2 {
args.rval().set(BooleanValue(false));
return true;
}
let a = *args.get(0).ptr;
let b = *args.get(1).ptr;
let equal = a.is_undefined() && b.is_undefined()
|| a.is_null() && b.is_null()
|| a.is_boolean() && b.is_boolean() && a.to_boolean() == b.to_boolean()
|| a.is_int32() && b.is_int32() && a.to_int32() == b.to_int32()
|| a.is_double() && b.is_double() && a.to_double() == b.to_double()
|| a.is_string() && b.is_string() && {
let sa = unsafe_jsstr_to_string(cx, ::std::ptr::NonNull::new_unchecked(a.to_string()));
let sb = unsafe_jsstr_to_string(cx, ::std::ptr::NonNull::new_unchecked(b.to_string()));
sa == sb
};
args.rval().set(BooleanValue(equal));
true
}