Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/js/internal/assert/assertion_error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const { inspect } = require("internal/util/inspect");
const colors = require("internal/util/colors");
const { validateObject } = require("internal/validators");
const { isErrorStackTraceLimitWritable } = require("internal/shared");
const { myersDiff, printMyersDiff, printSimpleMyersDiff } = require("internal/assert/myers_diff") as typeof Internal;

const ErrorCaptureStackTrace = Error.captureStackTrace;
Expand Down Expand Up @@ -274,9 +275,9 @@ class AssertionError extends Error {
} = options;
let { actual, expected } = options;

// NOTE: stack trace is always writable.
const stackTraceLimitWritable = isErrorStackTraceLimitWritable();
const limit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
if (stackTraceLimitWritable) Error.stackTraceLimit = 0;

if (message != null) {
if (operator === "deepStrictEqual" || operator === "strictEqual") {
Expand Down Expand Up @@ -370,7 +371,7 @@ class AssertionError extends Error {
}
}

Error.stackTraceLimit = limit;
if (stackTraceLimitWritable) Error.stackTraceLimit = limit;

this.generatedMessage = !message;
ObjectDefineProperty(this, "name", {
Expand Down
347 changes: 347 additions & 0 deletions src/js/internal/freeze_intrinsics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
// Adapted from SES/Caja - Copyright (C) 2011 Google Inc.
// Copyright (C) 2018 Agoric
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
//
// Port of Node.js lib/internal/freeze_intrinsics.js. Runs before user code.
Comment thread
robobun marked this conversation as resolved.

const _String = String;
const _TypeError = TypeError;
const ObjectDefineProperty = Object.defineProperty;
const ObjectFreeze = Object.freeze;
const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
const ObjectGetOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
const ObjectGetOwnPropertyNames = Object.getOwnPropertyNames;
const ObjectGetOwnPropertySymbols = Object.getOwnPropertySymbols;
const ObjectGetPrototypeOf = Object.getPrototypeOf;
const ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty;
const ReflectOwnKeys = Reflect.ownKeys;
const SymbolIterator = Symbol.iterator;
const SymbolMatchAll = Symbol.matchAll;
const TypedArray = ObjectGetPrototypeOf(Uint8Array);

process.emitWarning("Frozen intristics is an experimental feature and might change at any time", "ExperimentalWarning");

{
const intrinsicPrototypes: unknown[] = [
// 20 Fundamental Objects
Object.prototype, // 20.1
Function.prototype, // 20.2
Boolean.prototype, // 20.3
Symbol.prototype, // 20.4

Error.prototype, // 20.5
AggregateError.prototype,
EvalError.prototype,
RangeError.prototype,
ReferenceError.prototype,
SuppressedError.prototype,
SyntaxError.prototype,
TypeError.prototype,
URIError.prototype,
Comment thread
robobun marked this conversation as resolved.

// 21 Numbers and Dates
Number.prototype, // 21.1
BigInt.prototype, // 21.2
Date.prototype, // 21.4

// 22 Text Processing
String.prototype, // 22.1
ObjectGetPrototypeOf(String.prototype[SymbolIterator]()), // 22.1.5 StringIteratorPrototype
RegExp.prototype, // 22.2
ObjectGetPrototypeOf(new RegExp("e")[SymbolMatchAll]("")), // 22.2.7 RegExpStringIteratorPrototype

// 23 Indexed Collections
Array.prototype, // 23.1
ObjectGetPrototypeOf(Array.prototype[SymbolIterator]()), // 23.1.5 ArrayIteratorPrototype
TypedArray.prototype, // 23.2
Int8Array.prototype,
Uint8Array.prototype,
Uint8ClampedArray.prototype,
Int16Array.prototype,
Uint16Array.prototype,
Int32Array.prototype,
Uint32Array.prototype,
Float16Array.prototype,
Float32Array.prototype,
Float64Array.prototype,
BigInt64Array.prototype,
BigUint64Array.prototype,
Comment thread
claude[bot] marked this conversation as resolved.

// 24 Keyed Collections
Map.prototype, // 24.1
ObjectGetPrototypeOf(new Map()[SymbolIterator]()), // 24.1.5 MapIteratorPrototype
Set.prototype, // 24.2
ObjectGetPrototypeOf(new Set()[SymbolIterator]()), // 24.2.5 SetIteratorPrototype
WeakMap.prototype, // 24.3
WeakSet.prototype, // 24.4

// 25 Structured Data
ArrayBuffer.prototype, // 25.1
DataView.prototype, // 25.3

// 26 Managing Memory
WeakRef.prototype, // 26.1
FinalizationRegistry.prototype, // 26.2

// 27 Control Abstraction Objects
ObjectGetPrototypeOf(ObjectGetPrototypeOf(Array.prototype[SymbolIterator]())), // 27.1.2 IteratorPrototype
ObjectGetPrototypeOf(ObjectGetPrototypeOf(ObjectGetPrototypeOf((async function* () {})()))), // 27.1.3 AsyncIteratorPrototype
Promise.prototype, // 27.2
DisposableStack.prototype,
AsyncDisposableStack.prototype,

// 28 Reflection
ShadowRealm.prototype,

// Other APIs / Web Compatibility
(console as { Console?: { prototype: object } }).Console?.prototype,
];

const intrinsics: unknown[] = [
// 10.2.4.1 ThrowTypeError
ObjectGetOwnPropertyDescriptor(Function.prototype, "caller")?.get,

// 19 The Global Object
// 19.2 Function Properties of the Global Object
Comment thread
robobun marked this conversation as resolved.
eval,
isFinite,
isNaN,
parseFloat,
parseInt,
decodeURI,
decodeURIComponent,
encodeURI,
encodeURIComponent,

// 20 Fundamental Objects
Object,
Function,
Boolean,
Symbol,
Comment thread
robobun marked this conversation as resolved.
Error,
Comment thread
robobun marked this conversation as resolved.
AggregateError,
EvalError,
RangeError,
ReferenceError,
SuppressedError,
SyntaxError,
TypeError,
URIError,

// 21 Numbers and Dates
Number,
BigInt,
Math,
Date,

// 22 Text Processing
String,
ObjectGetPrototypeOf(String.prototype[SymbolIterator]()),
RegExp,
ObjectGetPrototypeOf(new RegExp("e")[SymbolMatchAll]("")),

// 23 Indexed Collections
Array,
ObjectGetPrototypeOf(Array.prototype[SymbolIterator]()),
TypedArray,
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float16Array,
Float32Array,
Float64Array,
BigInt64Array,
BigUint64Array,

// 24 Keyed Collections
Map,
ObjectGetPrototypeOf(new Map()[SymbolIterator]()),
Set,
ObjectGetPrototypeOf(new Set()[SymbolIterator]()),
WeakMap,
WeakSet,

// 25 Structured Data
ArrayBuffer,
DataView,
Atomics,
JSON,

// 26 Managing Memory
WeakRef,
FinalizationRegistry,

// 27 Control Abstraction Objects
Iterator,
ObjectGetPrototypeOf(ObjectGetPrototypeOf(Array.prototype[SymbolIterator]())), // IteratorPrototype
ObjectGetPrototypeOf(ObjectGetPrototypeOf(ObjectGetPrototypeOf((async function* () {})()))), // AsyncIteratorPrototype
Promise,
Comment thread
robobun marked this conversation as resolved.
ObjectGetPrototypeOf(function* () {}), // GeneratorFunction
ObjectGetPrototypeOf(async function* () {}), // AsyncGeneratorFunction
ObjectGetPrototypeOf(async function () {}), // AsyncFunction
DisposableStack,
AsyncDisposableStack,

// 28 Reflection
Reflect,
Proxy,
ShadowRealm,

// B.2.1
escape,
unescape,

// Other APIs / Web Compatibility
clearImmediate,
clearInterval,
clearTimeout,
setImmediate,
setInterval,
setTimeout,
console,
];

if (typeof SharedArrayBuffer !== "undefined") {
intrinsicPrototypes.push(SharedArrayBuffer.prototype);
intrinsics.push(SharedArrayBuffer);
}
if (typeof WebAssembly !== "undefined") {
intrinsicPrototypes.push(
WebAssembly.Module.prototype,
WebAssembly.Instance.prototype,
WebAssembly.Table.prototype,
WebAssembly.Memory.prototype,
WebAssembly.CompileError.prototype,
WebAssembly.LinkError.prototype,
WebAssembly.RuntimeError.prototype,
);
intrinsics.push(WebAssembly);
}
if (typeof Intl !== "undefined") {
intrinsicPrototypes.push(
Intl.Collator.prototype,
Intl.DateTimeFormat.prototype,
Intl.ListFormat.prototype,
Intl.NumberFormat.prototype,
Intl.PluralRules.prototype,
Intl.RelativeTimeFormat.prototype,
);
intrinsics.push(Intl);
}

for (let i = 0; i < intrinsicPrototypes.length; i++) enableDerivedOverrides(intrinsicPrototypes[i]);

const frozenSet = new WeakSet<object>();
// In Node.js `console._stdout`/`_stderr` are getters; in Bun they are data
// properties, so seed them as visited to keep stream prototypes unfrozen.
Comment thread
robobun marked this conversation as resolved.
const { _stdout, _stderr } = console as { _stdout?: object; _stderr?: object };
if (_stdout) frozenSet.add(_stdout);
if (_stderr) frozenSet.add(_stderr);
for (let i = 0; i < intrinsics.length; i++) deepFreeze(intrinsics[i]);

// 19.1 Value Properties of the Global Object
ObjectDefineProperty(globalThis, "globalThis", {
__proto__: null,
configurable: false,
writable: false,
value: globalThis,
} as PropertyDescriptor);

function deepFreeze(root: unknown): void {
const freezingSet = new Set<object>();

function enqueue(val: unknown): void {
if (Object(val) !== val) return;
if (frozenSet.has(val as object) || freezingSet.has(val as object)) return;
freezingSet.add(val as object);
}

function doFreeze(obj: object): void {
ObjectFreeze(obj);
const proto = ObjectGetPrototypeOf(obj);
const descs = ObjectGetOwnPropertyDescriptors(obj);
enqueue(proto);
const keys = ReflectOwnKeys(descs);
for (let i = 0; i < keys.length; i++) {
const desc = descs[keys[i] as string];
if (ObjectPrototypeHasOwnProperty.$call(desc, "value")) {
enqueue(desc.value);
} else {
enqueue(desc.get);
enqueue(desc.set);
}
}
}

enqueue(root);
// New values added before forEach() has finished will be visited.
freezingSet.forEach(doFreeze);
freezingSet.forEach(frozenSet.add, frozenSet);
Comment thread
robobun marked this conversation as resolved.
Outdated
}

// Convert data properties to accessors so `derived.prop = x` still defines
// an own property after the inherited slot is frozen (ES5 override mistake).
Comment thread
robobun marked this conversation as resolved.
function enableDerivedOverride(obj: object, prop: PropertyKey, desc: PropertyDescriptor): void {
if (!ObjectPrototypeHasOwnProperty.$call(desc, "value") || !desc.configurable) return;
const value = desc.value;

function getter(this: unknown) {
return value;
}
(getter as { value?: unknown }).value = value;
Comment thread
claude[bot] marked this conversation as resolved.

function setter(this: unknown, newValue: unknown) {
if (obj === this) {
throw new _TypeError(`Cannot assign to read only property '${_String(prop)}' of object '${obj}'`);
}
if (ObjectPrototypeHasOwnProperty.$call(this, prop)) {
(this as Record<PropertyKey, unknown>)[prop as string] = newValue;
} else {
ObjectDefineProperty(this as object, prop, {
__proto__: null,
value: newValue,
writable: true,
enumerable: true,
configurable: true,
} as PropertyDescriptor);
}
}

ObjectDefineProperty(obj, prop, {
__proto__: null,
get: getter,
set: setter,
enumerable: desc.enumerable,
configurable: desc.configurable,
} as PropertyDescriptor);
}

function enableDerivedOverrides(obj: unknown): void {
if (!obj) return;
const descs = ObjectGetOwnPropertyDescriptors(obj);
if (!descs) return;
const names = ObjectGetOwnPropertyNames(obj);
for (let i = 0; i < names.length; i++) enableDerivedOverride(obj as object, names[i], descs[names[i]]);
const syms = ObjectGetOwnPropertySymbols(obj);
for (let i = 0; i < syms.length; i++)
enableDerivedOverride(obj as object, syms[i], descs[syms[i] as unknown as string]);
}
}

export default {};
7 changes: 7 additions & 0 deletions src/js/internal/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ function once(callback, { preserveReturnValue = false } = kEmptyObject) {

const kEmptyObject = ObjectFreeze(Object.create(null));

function isErrorStackTraceLimitWritable(): boolean {
const desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit");
if (desc === undefined) return Object.isExtensible(Error);
return Object.prototype.hasOwnProperty.$call(desc, "writable") ? desc.writable! : desc.set !== undefined;
}
Comment thread
robobun marked this conversation as resolved.
Comment thread
robobun marked this conversation as resolved.

// Marks an addEventListener() options object so that dispatch still invokes the
// listener after an unrelated listener called event.stopImmediatePropagation().
// `$kResistStopPropagation` is a private symbol the native EventTarget reads, so
Expand Down Expand Up @@ -341,6 +347,7 @@ export default {
once,
getLazy,
resistStopPropagation,
isErrorStackTraceLimitWritable,

hasObserver,
startPerf,
Expand Down
Loading
Loading