Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions src/bun.js/bindings/ZigGlobalObject.lut.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@
TextEncoderStream TextEncoderStreamConstructorCallback PropertyCallback
TransformStream TransformStreamConstructorCallback PropertyCallback
TransformStreamDefaultController TransformStreamDefaultControllerConstructorCallback PropertyCallback
URL DOMURLConstructorCallback PropertyCallback
URLSearchParams URLSearchParamsConstructorCallback PropertyCallback
URL DOMURLConstructorCallback DontEnum|PropertyCallback
URLSearchParams URLSearchParamsConstructorCallback DontEnum|PropertyCallback
WebSocket WebSocketConstructorCallback PropertyCallback
Worker WorkerConstructorCallback PropertyCallback
WritableStream WritableStreamConstructorCallback PropertyCallback
Expand Down
25 changes: 0 additions & 25 deletions src/bun.js/bindings/webcore/JSDOMAttribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,31 +101,6 @@ class IDLAttribute {
}
}

// FIXME: This can be merged with `get` if we replace the explicit setter function template parameter with a generic lambda.
template<GetterPassingPropertyName getter, CastedThisErrorBehavior shouldThrow = CastedThisErrorBehavior::Throw>
static JSC::EncodedJSValue getPassingPropertyName(JSC::JSGlobalObject& lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName attributeName)
{
auto throwScope = DECLARE_THROW_SCOPE(JSC::getVM(&lexicalGlobalObject));

if constexpr (shouldThrow == CastedThisErrorBehavior::Assert) {
ASSERT(castThisValue<JSClass>(lexicalGlobalObject, JSC::JSValue::decode(thisValue)));
auto* thisObject = JSC::jsCast<JSClass*>(JSC::JSValue::decode(thisValue));
RELEASE_AND_RETURN(throwScope, (JSC::JSValue::encode(getter(lexicalGlobalObject, *thisObject, attributeName))));
} else {
auto* thisObject = castThisValue<JSClass>(lexicalGlobalObject, JSC::JSValue::decode(thisValue));
if (UNLIKELY(!thisObject)) {
if constexpr (shouldThrow == CastedThisErrorBehavior::Throw)
return JSC::throwVMDOMAttributeGetterTypeError(&lexicalGlobalObject, throwScope, JSClass::info(), attributeName);
else if constexpr (shouldThrow == CastedThisErrorBehavior::RejectPromise)
RELEASE_AND_RETURN(throwScope, rejectPromiseWithGetterTypeError(lexicalGlobalObject, JSClass::info(), attributeName));
else
return JSC::JSValue::encode(JSC::jsUndefined());
}

RELEASE_AND_RETURN(throwScope, (JSC::JSValue::encode(getter(lexicalGlobalObject, *thisObject, attributeName))));
}
}

template<StaticGetter getter, CastedThisErrorBehavior shouldThrow = CastedThisErrorBehavior::Throw>
static JSC::EncodedJSValue getStatic(JSC::JSGlobalObject& lexicalGlobalObject, JSC::EncodedJSValue, JSC::PropertyName)
{
Expand Down
1 change: 1 addition & 0 deletions src/bun.js/bindings/webcore/JSTextEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ static inline JSC::EncodedJSValue jsTextEncoderPrototypeFunction_encodeBody(JSC:
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);

EnsureStillAliveScope argument0 = callFrame->argument(0);
if (argument0.value().isUndefined()) {
auto res = JSC::JSUint8Array::create(lexicalGlobalObject, lexicalGlobalObject->m_typedArrayUint8.get(lexicalGlobalObject), 0);
Expand Down
2 changes: 1 addition & 1 deletion src/js/builtins/ReadableStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ export function getReader(this, options) {
return new ReadableStreamBYOBReader(this);
}

throw new TypeError("Invalid mode is specified");
throw $ERR_INVALID_ARG_VALUE("mode", mode, "byob");
}

export function pipeThrough(this, streams, options) {
Expand Down
4 changes: 2 additions & 2 deletions src/js/builtins/StreamInternals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export function createFulfilledPromise(value) {
}

export function toDictionary(value, defaultValue, errorMessage) {
if (value === undefined || value === null) return defaultValue;
if (!$isObject(value)) throw new TypeError(errorMessage);
if ($isUndefinedOrNull(value)) return defaultValue;
if (!$isObject(value)) throw $ERR_INVALID_ARG_TYPE(errorMessage);
return value;
}
62 changes: 62 additions & 0 deletions test/js/node/test/parallel/test-whatwg-readablebytestreambyob.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict';

const common = require('../common');

const {
open,
} = require('fs/promises');

const {
Buffer,
} = require('buffer');

class Source {
async start(controller) {
this.file = await open(__filename);
this.controller = controller;
}

async pull(controller) {
const byobRequest = controller.byobRequest;
const view = byobRequest.view;

const {
bytesRead,
} = await this.file.read({
buffer: view,
offset: view.byteOffset,
length: view.byteLength
});

if (bytesRead === 0) {
await this.file.close();
this.controller.close();
}

byobRequest.respond(bytesRead);
}

get type() { return 'bytes'; }

get autoAllocateChunkSize() { return 1024; }
}

(async () => {
const source = new Source();
const stream = new ReadableStream(source);

const { emitWarning } = process;

process.emitWarning = common.mustNotCall();

try {
const reader = stream.getReader({ mode: 'byob' });

let result;
do {
result = await reader.read(Buffer.alloc(100));
} while (!result.done);
} finally {
process.emitWarning = emitWarning;
}
})().then(common.mustCall());
43 changes: 43 additions & 0 deletions test/js/node/test/parallel/test-whatwg-url-canparse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Flags: --expose-internals --no-warnings --allow-natives-syntax
'use strict';

const common = require('../common');

const { URL } = require('url');
const assert = require('assert');

// NOTE: This is an internal Node API that Bun does not support.
// const { internalBinding } = require('internal/test/binding');

// One argument is required
assert.throws(() => {
URL.canParse();
}, {
code: 'ERR_MISSING_ARGS',
name: 'TypeError',
});

// It should not throw when called without a base string
assert.strictEqual(URL.canParse('https://example.org'), true);

{
// V8 Fast API
function testFastPaths() {
// `canParse` binding has two overloads.
assert.strictEqual(URL.canParse('https://www.example.com/path/?query=param#hash'), true);
assert.strictEqual(URL.canParse('/', 'http://n'), true);
}

// eval('%PrepareFunctionForOptimization(URL.canParse)');
testFastPaths();
// eval('%OptimizeFunctionOnNextCall(URL.canParse)');
testFastPaths();

if (common.isDebug) {
// const { getV8FastApiCallCount } = internalBinding('debug');
// // TODO: the counts should be 1. The function is optimized, but the fast
// // API is not called.
// assert.strictEqual(getV8FastApiCallCount('url.canParse'), 0);
// assert.strictEqual(getV8FastApiCallCount('url.canParse.withBase'), 0);
}
}
26 changes: 26 additions & 0 deletions test/js/node/test/parallel/test-whatwg-url-custom-global.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';

// Tests below are not from WPT.

require('../common');
const assert = require('assert');

assert.deepStrictEqual(
Object.getOwnPropertyDescriptor(globalThis, 'URL'),
{
value: URL,
writable: true,
configurable: true,
enumerable: false
}
);

assert.deepStrictEqual(
Object.getOwnPropertyDescriptor(globalThis, 'URLSearchParams'),
{
value: URLSearchParams,
writable: true,
configurable: true,
enumerable: false
}
);
146 changes: 146 additions & 0 deletions test/js/node/test/parallel/test-whatwg-url-custom-properties.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Flags: --expose-internals
'use strict';

// Tests below are not from WPT.

require('../common');
const assert = require('assert');

const url = new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test');
const oldParams = url.searchParams; // For test of [SameObject]

// To retrieve enumerable but not necessarily own properties,
// we need to use the for-in loop.
const props = [];
for (const prop in url) {
props.push(prop);
}

// See: https://url.spec.whatwg.org/#api
// https://heycam.github.io/webidl/#es-attributes
// https://heycam.github.io/webidl/#es-stringifier
const expected = ['toString',
'href', 'origin', 'protocol',
'username', 'password', 'host', 'hostname', 'port',
'pathname', 'search', 'searchParams', 'hash', 'toJSON'].sort();

assert.deepStrictEqual(props.sort(), expected);

// `href` is writable (not readonly) and is stringifier
assert.strictEqual(url.toString(), url.href);
url.href = 'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test';
assert.strictEqual(url.href,
'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test');
assert.strictEqual(url.toString(), url.href);
// Return true because it's configurable, but because the properties
// are defined on the prototype per the spec, the deletion has no effect
assert.strictEqual((delete url.href), true);
assert.strictEqual(url.href,
'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test');
assert.strictEqual(url.searchParams, oldParams); // [SameObject]

// searchParams is readonly. Under strict mode setting a
// non-writable property should throw.
// Note: this error message is subject to change in V8 updates
assert.throws(
() => url.origin = 'http://foo.bar.com:22',
/Attempted to assign to readonly property/
);
assert.strictEqual(url.origin, 'http://foo.bar.com:21');
assert.strictEqual(url.toString(),
'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test');
assert.strictEqual((delete url.origin), true);
assert.strictEqual(url.origin, 'http://foo.bar.com:21');

// The following properties should be writable (not readonly)
url.protocol = 'https:';
assert.strictEqual(url.protocol, 'https:');
assert.strictEqual(url.toString(),
'https://user:pass@foo.bar.com:21/aaa/zzz?l=25#test');
assert.strictEqual((delete url.protocol), true);
assert.strictEqual(url.protocol, 'https:');

url.username = 'user2';
assert.strictEqual(url.username, 'user2');
assert.strictEqual(url.toString(),
'https://user2:pass@foo.bar.com:21/aaa/zzz?l=25#test');
assert.strictEqual((delete url.username), true);
assert.strictEqual(url.username, 'user2');

url.password = 'pass2';
assert.strictEqual(url.password, 'pass2');
assert.strictEqual(url.toString(),
'https://user2:pass2@foo.bar.com:21/aaa/zzz?l=25#test');
assert.strictEqual((delete url.password), true);
assert.strictEqual(url.password, 'pass2');

url.host = 'foo.bar.net:22';
assert.strictEqual(url.host, 'foo.bar.net:22');
assert.strictEqual(url.toString(),
'https://user2:pass2@foo.bar.net:22/aaa/zzz?l=25#test');
assert.strictEqual((delete url.host), true);
assert.strictEqual(url.host, 'foo.bar.net:22');

url.hostname = 'foo.bar.org';
assert.strictEqual(url.hostname, 'foo.bar.org');
assert.strictEqual(url.toString(),
'https://user2:pass2@foo.bar.org:22/aaa/zzz?l=25#test');
assert.strictEqual((delete url.hostname), true);
assert.strictEqual(url.hostname, 'foo.bar.org');

url.port = '23';
assert.strictEqual(url.port, '23');
assert.strictEqual(url.toString(),
'https://user2:pass2@foo.bar.org:23/aaa/zzz?l=25#test');
assert.strictEqual((delete url.port), true);
assert.strictEqual(url.port, '23');

url.pathname = '/aaa/bbb';
assert.strictEqual(url.pathname, '/aaa/bbb');
assert.strictEqual(url.toString(),
'https://user2:pass2@foo.bar.org:23/aaa/bbb?l=25#test');
assert.strictEqual((delete url.pathname), true);
assert.strictEqual(url.pathname, '/aaa/bbb');

url.search = '?k=99';
assert.strictEqual(url.search, '?k=99');
assert.strictEqual(url.toString(),
'https://user2:pass2@foo.bar.org:23/aaa/bbb?k=99#test');
assert.strictEqual((delete url.search), true);
assert.strictEqual(url.search, '?k=99');

url.hash = '#abcd';
assert.strictEqual(url.hash, '#abcd');
assert.strictEqual(url.toString(),
'https://user2:pass2@foo.bar.org:23/aaa/bbb?k=99#abcd');
assert.strictEqual((delete url.hash), true);
assert.strictEqual(url.hash, '#abcd');

// searchParams is readonly. Under strict mode setting a
// non-writable property should throw.
// Note: this error message is subject to change in V8 updates
assert.throws(
() => url.searchParams = '?k=88',
/Attempted to assign to readonly property/
);
assert.strictEqual(url.searchParams, oldParams);
assert.strictEqual(url.toString(),
'https://user2:pass2@foo.bar.org:23/aaa/bbb?k=99#abcd');
assert.strictEqual((delete url.searchParams), true);
assert.strictEqual(url.searchParams, oldParams);

// Test special origins
[
{ expected: 'https://whatwg.org',
url: 'blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f' },
{ expected: 'ftp://example.org', url: 'ftp://example.org/foo' },
{ expected: 'http://example.org', url: 'http://example.org/foo' },
{ expected: 'https://example.org', url: 'https://example.org/foo' },
{ expected: 'ws://example.org', url: 'ws://example.org/foo' },
{ expected: 'wss://example.org', url: 'wss://example.org/foo' },
{ expected: 'null', url: 'gopher://gopher.quux.org/1/' },
{ expected: 'null', url: 'file:///tmp/mock/path' },
{ expected: 'null', url: 'npm://nodejs/rules' },
].forEach((test) => {
assert.strictEqual(new URL(test.url).origin, test.expected);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict';

// Tests below are not from WPT.

require('../common');
const assert = require('assert');

{
const params = new URLSearchParams();
assert.throws(() => {
params.append.call(undefined);
}, {
code: 'ERR_INVALID_THIS',
name: 'TypeError',
message: 'Can only call URLSearchParams.append on instances of URLSearchParams',
});
assert.throws(() => {
params.append('a');
}, {
code: 'ERR_MISSING_ARGS',
name: 'TypeError',
message: 'Not enough arguments'
});

const obj = {
toString() { throw new Error('toString'); },
valueOf() { throw new Error('valueOf'); }
};
const sym = Symbol();
assert.throws(() => params.set(obj, 'b'), /^Error: toString$/);
assert.throws(() => params.set('a', obj), /^Error: toString$/);
assert.throws(() => params.set(sym, 'b'),
/^TypeError: Cannot convert a symbol to a string$/);
assert.throws(() => params.set('a', sym),
/^TypeError: Cannot convert a symbol to a string$/);
}
Loading