summaryrefslogtreecommitdiff
path: root/ext/console/01_console.js
diff options
context:
space:
mode:
authorKenta Moriuchi <moriken@kimamass.com>2024-01-04 13:12:38 +0900
committerGitHub <noreply@github.com>2024-01-04 09:42:38 +0530
commitb2cd254c35b6b1b128beea0eacdb8e814d91e003 (patch)
treed55fa5910e32d8a664aff5b680e07debea93181e /ext/console/01_console.js
parent48556748577ba46db5f9212d14a0fcaa90d632f6 (diff)
fix: strict type check for cross realms (#21669)
Deno v1.39 introduces `vm.runInNewContext`. This may cause problems when using `Object.prototype.isPrototypeOf` to check built-in types. ```js import vm from "node:vm"; const err = new Error(); const crossErr = vm.runInNewContext(`new Error()`); console.assert( !(crossErr instanceof Error) ); console.assert( Object.getPrototypeOf(err) !== Object.getPrototypeOf(crossErr) ); ``` This PR changes to check using internal slots solves them. --- current: ``` > import vm from "node:vm"; undefined > vm.runInNewContext(`new Error("message")`) Error {} > vm.runInNewContext(`new Date("2018-12-10T02:26:59.002Z")`) Date {} ``` this PR: ``` > import vm from "node:vm"; undefined > vm.runInNewContext(`new Error("message")`) Error: message at <anonymous>:1:1 > vm.runInNewContext(`new Date("2018-12-10T02:26:59.002Z")`) 2018-12-10T02:26:59.002Z ``` --------- Co-authored-by: Bartek IwaƄczuk <biwanczuk@gmail.com>
Diffstat (limited to 'ext/console/01_console.js')
-rw-r--r--ext/console/01_console.js338
1 files changed, 112 insertions, 226 deletions
diff --git a/ext/console/01_console.js b/ext/console/01_console.js
index 83c759d57..0ca8b04ff 100644
--- a/ext/console/01_console.js
+++ b/ext/console/01_console.js
@@ -4,9 +4,7 @@
import { core, internals, primordials } from "ext:core/mod.js";
const {
- AggregateErrorPrototype,
Array,
- ArrayBufferIsView,
ArrayBufferPrototypeGetByteLength,
ArrayIsArray,
ArrayPrototypeFill,
@@ -29,18 +27,16 @@ const {
Boolean,
BooleanPrototypeValueOf,
DateNow,
- DatePrototype,
DatePrototypeGetTime,
DatePrototypeToISOString,
Error,
- ErrorCaptureStackTrace,
ErrorPrototype,
+ ErrorCaptureStackTrace,
ErrorPrototypeToString,
FunctionPrototypeBind,
FunctionPrototypeCall,
FunctionPrototypeToString,
NumberIsNaN,
- MapPrototype,
MapPrototypeDelete,
MapPrototypeEntries,
MapPrototypeForEach,
@@ -93,7 +89,6 @@ const {
SafeSet,
SafeSetIterator,
SafeStringIterator,
- SetPrototype,
SetPrototypeAdd,
SetPrototypeHas,
SetPrototypeGetSize,
@@ -128,12 +123,38 @@ const {
SymbolToStringTag,
TypedArrayPrototypeGetByteLength,
TypedArrayPrototypeGetLength,
- TypedArrayPrototypeGetSymbolToStringTag,
Uint8Array,
- WeakMapPrototypeHas,
- WeakSetPrototypeHas,
} = primordials;
-const ops = core.ops;
+const {
+ isAnyArrayBuffer,
+ isArgumentsObject,
+ isArrayBuffer,
+ isAsyncFunction,
+ isBigIntObject,
+ isBooleanObject,
+ isBoxedPrimitive,
+ isDataView,
+ isDate,
+ isGeneratorFunction,
+ isMap,
+ isMapIterator,
+ isModuleNamespaceObject,
+ isNativeError,
+ isNumberObject,
+ isPromise,
+ isRegExp,
+ isSet,
+ isSetIterator,
+ isStringObject,
+ isTypedArray,
+ isWeakSet,
+ isWeakMap,
+} = core;
+const {
+ op_get_non_index_property_names,
+ op_get_constructor_name,
+ op_preview_entries,
+} = core.ensureFastOps(true);
let noColor = () => false;
@@ -263,187 +284,17 @@ function getSharedArrayBufferByteLength(value) {
return FunctionPrototypeCall(_getSharedArrayBufferByteLength, value);
}
-function isObjectLike(value) {
- return value !== null && typeof value === "object";
-}
-
-function isAnyArrayBuffer(value) {
- return ops.op_is_any_arraybuffer(value);
-}
-
-function isArgumentsObject(value) {
- return core.isArgumentsObject(value);
-}
-
-function isArrayBuffer(value) {
- try {
- ArrayBufferPrototypeGetByteLength(value);
- return true;
- } catch {
- return false;
- }
-}
-
-function isAsyncFunction(value) {
- return core.isAsyncFunction(value);
-}
-
-function isBooleanObject(value) {
- if (!isObjectLike(value)) {
- return false;
- }
-
- try {
- BooleanPrototypeValueOf(value);
- return true;
- } catch {
- return false;
- }
-}
-
-function isBoxedPrimitive(
- value,
-) {
+// The name property is used to allow cross realms to make a determination
+// This is the same as WHATWG's structuredClone algorithm
+// https://github.com/whatwg/html/pull/5150
+function isAggregateError(value) {
return (
- isBooleanObject(value) ||
- isStringObject(value) ||
- isNumberObject(value) ||
- isSymbolObject(value) ||
- isBigIntObject(value)
+ isNativeError(value) &&
+ value.name === "AggregateError" &&
+ ArrayIsArray(value.errors)
);
}
-function isDataView(value) {
- return (
- ArrayBufferIsView(value) &&
- TypedArrayPrototypeGetSymbolToStringTag(value) === undefined
- );
-}
-
-function isTypedArray(value) {
- return TypedArrayPrototypeGetSymbolToStringTag(value) !== undefined;
-}
-
-function isGeneratorFunction(value) {
- return core.isGeneratorFunction(value);
-}
-
-function isMap(value) {
- try {
- MapPrototypeGetSize(value);
- return true;
- } catch {
- return false;
- }
-}
-
-function isMapIterator(value) {
- return core.isMapIterator(value);
-}
-
-function isModuleNamespaceObject(value) {
- return core.isModuleNamespaceObject(value);
-}
-
-function isNativeError(value) {
- return core.isNativeError(value);
-}
-
-function isNumberObject(value) {
- if (!isObjectLike(value)) {
- return false;
- }
-
- try {
- NumberPrototypeValueOf(value);
- return true;
- } catch {
- return false;
- }
-}
-
-function isBigIntObject(value) {
- if (!isObjectLike(value)) {
- return false;
- }
-
- try {
- BigIntPrototypeValueOf(value);
- return true;
- } catch {
- return false;
- }
-}
-
-function isPromise(value) {
- return core.isPromise(value);
-}
-
-function isRegExp(value) {
- return core.isRegExp(value);
-}
-
-function isSet(value) {
- try {
- SetPrototypeGetSize(value);
- return true;
- } catch {
- return false;
- }
-}
-
-function isSetIterator(value) {
- return core.isSetIterator(value);
-}
-
-function isStringObject(value) {
- if (!isObjectLike(value)) {
- return false;
- }
-
- try {
- StringPrototypeValueOf(value);
- return true;
- } catch {
- return false;
- }
-}
-
-function isSymbolObject(value) {
- if (!isObjectLike(value)) {
- return false;
- }
-
- try {
- SymbolPrototypeValueOf(value);
- return true;
- } catch {
- return false;
- }
-}
-
-function isWeakMap(
- value,
-) {
- try {
- WeakMapPrototypeHas(value, null);
- return true;
- } catch {
- return false;
- }
-}
-
-function isWeakSet(
- value,
-) {
- try {
- WeakSetPrototypeHas(value, null);
- return true;
- } catch {
- return false;
- }
-}
-
const kObjectType = 0;
const kArrayType = 1;
const kArrayExtrasType = 2;
@@ -778,7 +629,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray, proxyDetails) {
let extrasType = kObjectType;
- if (proxyDetails != null && ctx.showProxy) {
+ if (proxyDetails !== null && ctx.showProxy) {
return `Proxy ` + formatValue(ctx, proxyDetails, recurseTimes);
} else {
// Iterators and the rest are split to reduce checks.
@@ -791,7 +642,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray, proxyDetails) {
const prefix = (constructor !== "Array" || tag !== "")
? getPrefix(constructor, tag, "Array", `(${value.length})`)
: "";
- keys = ops.op_get_non_index_property_names(value, filter);
+ keys = op_get_non_index_property_names(value, filter);
braces = [`${prefix}[`, "]"];
if (
value.length === 0 && keys.length === 0 && protoProps === undefined
@@ -800,31 +651,43 @@ function formatRaw(ctx, value, recurseTimes, typedArray, proxyDetails) {
}
extrasType = kArrayExtrasType;
formatter = formatArray;
- } else if (isSet(value)) {
- const size = SetPrototypeGetSize(value);
+ } else if (
+ (proxyDetails === null && isSet(value)) ||
+ (proxyDetails !== null && isSet(proxyDetails[0]))
+ ) {
+ const set = proxyDetails?.[0] ?? value;
+ const size = SetPrototypeGetSize(set);
const prefix = getPrefix(constructor, tag, "Set", `(${size})`);
- keys = getKeys(value, ctx.showHidden);
+ keys = getKeys(set, ctx.showHidden);
formatter = constructor !== null
- ? FunctionPrototypeBind(formatSet, null, value)
- : FunctionPrototypeBind(formatSet, null, SetPrototypeValues(value));
+ ? FunctionPrototypeBind(formatSet, null, set)
+ : FunctionPrototypeBind(formatSet, null, SetPrototypeValues(set));
if (size === 0 && keys.length === 0 && protoProps === undefined) {
return `${prefix}{}`;
}
braces = [`${prefix}{`, "}"];
- } else if (isMap(value)) {
- const size = MapPrototypeGetSize(value);
+ } else if (
+ (proxyDetails === null && isMap(value)) ||
+ (proxyDetails !== null && isMap(proxyDetails[0]))
+ ) {
+ const map = proxyDetails?.[0] ?? value;
+ const size = MapPrototypeGetSize(map);
const prefix = getPrefix(constructor, tag, "Map", `(${size})`);
- keys = getKeys(value, ctx.showHidden);
+ keys = getKeys(map, ctx.showHidden);
formatter = constructor !== null
- ? FunctionPrototypeBind(formatMap, null, value)
- : FunctionPrototypeBind(formatMap, null, MapPrototypeEntries(value));
+ ? FunctionPrototypeBind(formatMap, null, map)
+ : FunctionPrototypeBind(formatMap, null, MapPrototypeEntries(map));
if (size === 0 && keys.length === 0 && protoProps === undefined) {
return `${prefix}{}`;
}
braces = [`${prefix}{`, "}"];
- } else if (isTypedArray(value)) {
- keys = core.ops.op_get_non_index_property_names(value, filter);
- const bound = value;
+ } else if (
+ (proxyDetails === null && isTypedArray(value)) ||
+ (proxyDetails !== null && isTypedArray(proxyDetails[0]))
+ ) {
+ const typedArray = proxyDetails?.[0] ?? value;
+ keys = op_get_non_index_property_names(typedArray, filter);
+ const bound = typedArray;
const fallback = "";
if (constructor === null) {
// TODO(wafuwafu13): Implement
@@ -832,23 +695,31 @@ function formatRaw(ctx, value, recurseTimes, typedArray, proxyDetails) {
// // Reconstruct the array information.
// bound = new primordials[fallback](value);
}
- const size = TypedArrayPrototypeGetLength(value);
+ const size = TypedArrayPrototypeGetLength(typedArray);
const prefix = getPrefix(constructor, tag, fallback, `(${size})`);
braces = [`${prefix}[`, "]"];
- if (value.length === 0 && keys.length === 0 && !ctx.showHidden) {
+ if (typedArray.length === 0 && keys.length === 0 && !ctx.showHidden) {
return `${braces[0]}]`;
}
// Special handle the value. The original value is required below. The
// bound function is required to reconstruct missing information.
formatter = FunctionPrototypeBind(formatTypedArray, null, bound, size);
extrasType = kArrayExtrasType;
- } else if (isMapIterator(value)) {
- keys = getKeys(value, ctx.showHidden);
+ } else if (
+ (proxyDetails === null && isMapIterator(value)) ||
+ (proxyDetails !== null && isMapIterator(proxyDetails[0]))
+ ) {
+ const mapIterator = proxyDetails?.[0] ?? value;
+ keys = getKeys(mapIterator, ctx.showHidden);
braces = getIteratorBraces("Map", tag);
// Add braces to the formatter parameters.
formatter = FunctionPrototypeBind(formatIterator, null, braces);
- } else if (isSetIterator(value)) {
- keys = getKeys(value, ctx.showHidden);
+ } else if (
+ (proxyDetails === null && isSetIterator(value)) ||
+ (proxyDetails !== null && isSetIterator(proxyDetails[0]))
+ ) {
+ const setIterator = proxyDetails?.[0] ?? value;
+ keys = getKeys(setIterator, ctx.showHidden);
braces = getIteratorBraces("Set", tag);
// Add braces to the formatter parameters.
formatter = FunctionPrototypeBind(formatIterator, null, braces);
@@ -873,10 +744,14 @@ function formatRaw(ctx, value, recurseTimes, typedArray, proxyDetails) {
if (keys.length === 0 && protoProps === undefined) {
return ctx.stylize(base, "special");
}
- } else if (isRegExp(value)) {
+ } else if (
+ (proxyDetails === null && isRegExp(value)) ||
+ (proxyDetails !== null && isRegExp(proxyDetails[0]))
+ ) {
+ const regExp = proxyDetails?.[0] ?? value;
// Make RegExps say that they are RegExps
base = RegExpPrototypeToString(
- constructor !== null ? value : new SafeRegExp(value),
+ constructor !== null ? regExp : new SafeRegExp(regExp),
);
const prefix = getPrefix(constructor, tag, "RegExp");
if (prefix !== "RegExp ") {
@@ -888,8 +763,11 @@ function formatRaw(ctx, value, recurseTimes, typedArray, proxyDetails) {
) {
return ctx.stylize(base, "regexp");
}
- } else if (ObjectPrototypeIsPrototypeOf(DatePrototype, value)) {
- const date = proxyDetails ? proxyDetails[0] : value;
+ } else if (
+ (proxyDetails === null && isDate(value)) ||
+ (proxyDetails !== null && isDate(proxyDetails[0]))
+ ) {
+ const date = proxyDetails?.[0] ?? value;
if (NumberIsNaN(DatePrototypeGetTime(date))) {
return ctx.stylize("Invalid Date", "date");
} else {
@@ -898,8 +776,16 @@ function formatRaw(ctx, value, recurseTimes, typedArray, proxyDetails) {
return ctx.stylize(base, "date");
}
}
- } else if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, value)) {
- base = inspectError(value, ctx);
+ } else if (
+ (proxyDetails === null &&
+ (isNativeError(value) ||
+ ObjectPrototypeIsPrototypeOf(ErrorPrototype, value))) ||
+ (proxyDetails !== null &&
+ (isNativeError(proxyDetails[0]) ||
+ ObjectPrototypeIsPrototypeOf(ErrorPrototype, proxyDetails[0])))
+ ) {
+ const error = proxyDetails?.[0] ?? value;
+ base = inspectError(error, ctx);
if (keys.length === 0 && protoProps === undefined) {
return base;
}
@@ -1180,7 +1066,7 @@ function getConstructorName(obj, ctx, recurseTimes, protoProps) {
return null;
}
- const res = core.ops.op_get_constructor_name(tmp);
+ const res = op_get_constructor_name(tmp);
if (recurseTimes > ctx.depth && ctx.depth !== null) {
return `${res} <Complex prototype>`;
@@ -1290,7 +1176,7 @@ function formatArray(ctx, value, recurseTimes) {
function getCtxStyle(value, constructor, tag) {
let fallback = "";
if (constructor === null) {
- fallback = core.ops.op_get_constructor_name(value);
+ fallback = op_get_constructor_name(value);
if (fallback === tag) {
fallback = "Object";
}
@@ -1433,7 +1319,7 @@ function getIteratorBraces(type, tag) {
const iteratorRegExp = new SafeRegExp(" Iterator] {$");
function formatIterator(braces, ctx, value, recurseTimes) {
- const { 0: entries, 1: isKeyValue } = ops.op_preview_entries(value, true);
+ const { 0: entries, 1: isKeyValue } = op_preview_entries(value, true);
if (isKeyValue) {
// Mark entry iterators as such.
braces[0] = StringPrototypeReplace(
@@ -1498,7 +1384,7 @@ function inspectError(value, ctx) {
let finalMessage = MapPrototypeGet(refMap, value) ?? "";
- if (ObjectPrototypeIsPrototypeOf(AggregateErrorPrototype, value)) {
+ if (isAggregateError(value)) {
const stackLines = StringPrototypeSplit(value.stack, "\n");
while (true) {
const line = ArrayPrototypeShift(stackLines);
@@ -1635,12 +1521,12 @@ function formatWeakCollection(ctx) {
}
function formatWeakSet(ctx, value, recurseTimes) {
- const entries = ops.op_preview_entries(value, false);
+ const entries = op_preview_entries(value, false);
return formatSetIterInner(ctx, recurseTimes, entries, kWeak);
}
function formatWeakMap(ctx, value, recurseTimes) {
- const entries = ops.op_preview_entries(value, false);
+ const entries = op_preview_entries(value, false);
return formatMapIterInner(ctx, recurseTimes, entries, kWeak);
}
@@ -3278,14 +3164,14 @@ class Console {
const toTable = (header, body) => this.log(cliTable(header, body));
let resultData;
- const isSet = ObjectPrototypeIsPrototypeOf(SetPrototype, data);
- const isMap = ObjectPrototypeIsPrototypeOf(MapPrototype, data);
+ const isSetObject = isSet(data);
+ const isMapObject = isMap(data);
const valuesKey = "Values";
- const indexKey = isSet || isMap ? "(iter idx)" : "(idx)";
+ const indexKey = isSetObject || isMapObject ? "(iter idx)" : "(idx)";
- if (isSet) {
+ if (isSetObject) {
resultData = [...new SafeSetIterator(data)];
- } else if (isMap) {
+ } else if (isMapObject) {
let idx = 0;
resultData = {};
@@ -3342,7 +3228,7 @@ class Console {
const headerProps = properties ||
[
...new SafeArrayIterator(headerKeys),
- !isMap && hasPrimitives && valuesKey,
+ !isMapObject && hasPrimitives && valuesKey,
];
const header = ArrayPrototypeFilter([
indexKey,