Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(node/url): add more node:test parallel cases #16404

Merged
merged 1 commit into from
Jan 17, 2025
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
50 changes: 47 additions & 3 deletions src/bun.js/bindings/BunObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,23 +604,67 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj
url = WTF::URL(arg0.toWTFString(globalObject));
RETURN_IF_EXCEPTION(scope, {});
} else {
throwTypeError(globalObject, scope, "Argument must be a URL"_s);
Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, "url"_s, "string"_s, arg0);
return {};
}
} else {
url = domURL->href();
}

/// cannot turn non-`file://` URLs into file paths
if (UNLIKELY(!url.protocolIsFile())) {
throwTypeError(globalObject, scope, "Argument must be a file URL"_s);
Bun::ERR::INVALID_URL_SCHEME(scope, globalObject, "file"_s);
return {};
}

// NOTE: On Windows, WTF::URL::fileSystemPath will handle UNC paths
// (`file:\\server\share\etc` -> `\\server\share\etc`), so hostname check only
// needs to happen on posix systems
#if !OS(WINDOWS)
// file://host/path is illegal if `host` is not `localhost`.
// Should be `file:///` instead
if (UNLIKELY(url.host().length() > 0 && url.host() != "localhost"_s)) {

#if OS(DARWIN)
Bun::ERR::INVALID_FILE_URL_HOST(scope, globalObject, "darwin"_s);
return {};
#else
Bun::ERR::INVALID_FILE_URL_HOST(scope, globalObject, "linux"_s);
return {};
#endif
}
#endif

// ban url-encoded slashes. '/' on posix, '/' and '\' on windows.
StringView p = url.path();
if (p.length() > 3) {
for (int i = 0; i < p.length() - 2; i++) {
if (p[i] == '%') {
const char second = p[i + 1];
const uint8_t third = p[i + 2] | 0x20;
#if OS(WINDOWS)
if (
(second == '2' && third == 102) || // 2f 2F '/'
(second == '5' && third == 99) // 5c 5C '\'
) {
Bun::ERR::INVALID_FILE_URL_PATH(scope, globalObject, "must not include encoded \\ or / characters"_s);
return {};
}
#else
if (second == '2' && third == 102) {
Bun::ERR::INVALID_FILE_URL_PATH(scope, globalObject, "must not include encoded / characters"_s);
return {};
}
#endif
}
}
}

auto fileSystemPath = url.fileSystemPath();

#if OS(WINDOWS)
if (!isAbsolutePath(fileSystemPath)) {
throwTypeError(globalObject, scope, "File URL path must be absolute"_s);
Bun::ERR::INVALID_FILE_URL_PATH(scope, globalObject, "must be an absolute path"_s);
return {};
}
#endif
Expand Down
28 changes: 27 additions & 1 deletion src/bun.js/bindings/ErrorCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,32 @@ JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobal
return {};
}

JSC::EncodedJSValue INVALID_URL_SCHEME(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& expectedScheme)
{
auto message = makeString("The URL must be of scheme "_s, expectedScheme);
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_URL_SCHEME, message));
return {};
}
JSC::EncodedJSValue INVALID_FILE_URL_HOST(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& platform)
{
auto message = makeString("File URL host must be \"localhost\" or empty on "_s, platform);
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_FILE_URL_HOST, message));
return {};
}
JSC::EncodedJSValue INVALID_FILE_URL_HOST(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const ASCIILiteral platform)
{
auto message = makeString("File URL host must be \"localhost\" or empty on "_s, platform);
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_FILE_URL_HOST, message));
return {};
}
/// `File URL path {suffix}`
JSC::EncodedJSValue INVALID_FILE_URL_PATH(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const ASCIILiteral suffix)
{
auto message = makeString("File URL path "_s, suffix);
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_FILE_URL_PATH, message));
return {};
}

JSC::EncodedJSValue UNKNOWN_ENCODING(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::StringView encoding)
{
auto message = makeString("Unknown encoding: "_s, encoding);
Expand Down Expand Up @@ -655,7 +681,7 @@ JSC::EncodedJSValue CRYPTO_JWK_UNSUPPORTED_CURVE(JSC::ThrowScope& throwScope, JS
return {};
}

}
} // namespace ERR

static JSC::JSValue ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue arg0, JSValue arg1, JSValue arg2)
{
Expand Down
10 changes: 10 additions & 0 deletions src/bun.js/bindings/ErrorCode.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// To add a new error code, put it in ErrorCode.ts
#pragma once

#include "ZigGlobalObject.h"
Expand Down Expand Up @@ -86,6 +87,15 @@ JSC::EncodedJSValue ASSERTION(JSC::ThrowScope& throwScope, JSC::JSGlobalObject*
JSC::EncodedJSValue CRYPTO_INVALID_CURVE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject);
JSC::EncodedJSValue CRYPTO_JWK_UNSUPPORTED_CURVE(JSC::ThrowScope&, JSC::JSGlobalObject*, const WTF::String&);

// URL

/// `URL must be of scheme {expectedScheme}`
JSC::EncodedJSValue INVALID_URL_SCHEME(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& expectedScheme);
/// `File URL host must be "localhost" or empty on {platform}`
JSC::EncodedJSValue INVALID_FILE_URL_HOST(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& platform);
/// `File URL path {suffix}`
JSC::EncodedJSValue INVALID_FILE_URL_PATH(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const ASCIILiteral suffix);

}

void throwBoringSSLError(JSC::VM& vm, JSC::ThrowScope& scope, JSGlobalObject* globalObject, int errorCode);
Expand Down
19 changes: 12 additions & 7 deletions src/bun.js/bindings/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
type ErrorCodeMapping = Array<
[
/** error.code */
string,
code: string,
/** Constructor **/
typeof TypeError | typeof RangeError | typeof Error | typeof SyntaxError,
ctor: typeof TypeError | typeof RangeError | typeof Error | typeof SyntaxError,
/** error.name. Defaults to `Constructor.name` (that is, mapping[1].name */
string?,
name?: string,
(typeof TypeError | typeof RangeError | typeof Error | typeof SyntaxError)?,
(typeof TypeError | typeof RangeError | typeof Error | typeof SyntaxError)?,
]
Expand Down Expand Up @@ -43,7 +43,6 @@ const errors: ErrorCodeMapping = [
["ERR_INVALID_STATE", Error, undefined, TypeError, RangeError],
["ERR_INVALID_THIS", TypeError],
["ERR_INVALID_URI", URIError],
["ERR_INVALID_URL", TypeError],
["ERR_IPC_CHANNEL_CLOSED", Error],
["ERR_IPC_DISCONNECTED", Error],
["ERR_IPC_ONE_PIPE", Error],
Expand All @@ -64,16 +63,16 @@ const errors: ErrorCodeMapping = [
["ERR_SOCKET_DGRAM_IS_CONNECTED", Error],
["ERR_SOCKET_DGRAM_NOT_CONNECTED", Error],
["ERR_SOCKET_DGRAM_NOT_RUNNING", Error],
["ERR_STREAM_PREMATURE_CLOSE", Error],
["ERR_STREAM_ALREADY_FINISHED", Error],
["ERR_STREAM_CANNOT_PIPE", Error],
["ERR_STREAM_DESTROYED", Error],
["ERR_STREAM_NULL_VALUES", TypeError],
["ERR_STREAM_PREMATURE_CLOSE", Error],
["ERR_STREAM_WRITE_AFTER_END", Error],
["ERR_STREAM_PUSH_AFTER_EOF", Error],
["ERR_STREAM_RELEASE_LOCK", Error, "AbortError"],
["ERR_STREAM_UNABLE_TO_PIPE", Error],
["ERR_STREAM_UNSHIFT_AFTER_END_EVENT", Error],
["ERR_STREAM_WRITE_AFTER_END", Error],
["ERR_STRING_TOO_LONG", Error],
["ERR_UNAVAILABLE_DURING_EXIT", Error],
["ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET", Error],
Expand Down Expand Up @@ -176,5 +175,11 @@ const errors: ErrorCodeMapping = [
["ERR_S3_INVALID_ENDPOINT", Error],
["ERR_S3_INVALID_SIGNATURE", Error],
["ERR_S3_INVALID_SESSION_TOKEN", Error],
] as const;

// URL
["ERR_INVALID_URL", TypeError],
["ERR_INVALID_URL_SCHEME", TypeError],
["ERR_INVALID_FILE_URL_HOST", TypeError],
["ERR_INVALID_FILE_URL_PATH", TypeError],
];
export default errors;
16 changes: 12 additions & 4 deletions src/bun.js/bindings/PathInlines.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
#define PLATFORM_SEP POSIX_PATH_SEP
#endif

#define IS_LETTER(byte) \
((byte >= 'a' && byte <= 'z') || (byte >= 'A' && byte <= 'Z'))
#define IS_SLASH(byte) \
(byte == '/' || byte == '\\')

ALWAYS_INLINE bool isAbsolutePath(WTF::String input)
{
#if OS(WINDOWS)
Expand All @@ -22,23 +27,23 @@ ALWAYS_INLINE bool isAbsolutePath(WTF::String input)
if (len < 1)
return false;
const auto bytes = input.span8().data();
if (bytes[0] == '/' || bytes[0] == '\\')
if (IS_SLASH(bytes[0]))
return true;
if (len < 2)
return false;
if (bytes[1] == ':' && (bytes[2] == '/' || bytes[2] == '\\'))
if (IS_LETTER(bytes[0]) && bytes[1] == ':' && IS_SLASH(bytes[2]))
return true;
return false;
} else {
auto len = input.length();
if (len < 1)
return false;
const auto bytes = input.span16().data();
if (bytes[0] == '/' || bytes[0] == '\\')
if (IS_SLASH(bytes[0]))
return true;
if (len < 2)
return false;
if (bytes[1] == ':' && (bytes[2] == '/' || bytes[2] == '\\'))
if (IS_LETTER(bytes[0]) && bytes[1] == ':' && IS_SLASH(bytes[2]))
return true;
return false;
}
Expand All @@ -47,6 +52,9 @@ ALWAYS_INLINE bool isAbsolutePath(WTF::String input)
#endif
}

#undef IS_LETTER
#undef IS_SLASH

extern "C" BunString ResolvePath__joinAbsStringBufCurrentPlatformBunString(JSC::JSGlobalObject*, BunString);

/// CWD is determined by the global object's current cwd.
Expand Down
1 change: 0 additions & 1 deletion src/bun.js/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#include "root.h"

#include "ZigGlobalObject.h"
Expand Down
26 changes: 26 additions & 0 deletions src/bun.js/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6346,3 +6346,29 @@ extern "C" EncodedJSValue Bun__JSObject__getCodePropertyVMInquiry(JSC::JSGlobalO

return JSValue::encode(slot.getPureResult());
}

using StackCodeType = JSC::StackVisitor::Frame::CodeType;
CPP_DECL bool Bun__util__isInsideNodeModules(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)
{
JSC::VM& vm = globalObject->vm();
bool inNodeModules = false;
JSC::StackVisitor::visit(callFrame, vm, [&](JSC::StackVisitor& visitor) -> WTF::IterationStatus {
if (Zig::isImplementationVisibilityPrivate(visitor) || visitor->isNativeCalleeFrame()) {
return WTF::IterationStatus::Continue;
}

if (visitor->hasLineAndColumnInfo()) {
String sourceURL = Zig::sourceURL(visitor);
if (sourceURL.startsWith("node:"_s) || sourceURL.startsWith("bun:"_s))
return WTF::IterationStatus::Continue;
if (sourceURL.contains("node_modules"_s))
inNodeModules = true;

return WTF::IterationStatus::Done;
}

return WTF::IterationStatus::Continue;
});

return inNodeModules;
}
15 changes: 14 additions & 1 deletion src/bun.js/node/node_util_binding.zig
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,20 @@ pub fn extractedSplitNewLinesFastPathStringsOnly(globalThis: *JSC.JSGlobalObject
};
}

fn split(comptime encoding: bun.strings.EncodingNonAscii, globalThis: *JSC.JSGlobalObject, allocator: Allocator, str: *const bun.String) bun.JSError!JSC.JSValue {
extern fn Bun__util__isInsideNodeModules(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bool;
/// Walks the call stack from bottom to top, returning `true` when it finds a
/// frame within a `node_modules` directory.
pub fn isInsideNodeModules(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
const res = Bun__util__isInsideNodeModules(globalObject, callframe);
return JSC.JSValue.jsBoolean(res);
}

fn split(
comptime encoding: bun.strings.EncodingNonAscii,
globalThis: *JSC.JSGlobalObject,
allocator: Allocator,
str: *const bun.String,
) bun.JSError!JSC.JSValue {
var fallback = std.heap.stackFallback(1024, allocator);
const alloc = fallback.get();
const Char = switch (encoding) {
Expand Down
1 change: 1 addition & 0 deletions src/codegen/generate-node-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let zig = ``;
enumHeader = `
// clang-format off
// Generated by: src/codegen/generate-node-errors.ts
// Input: src/bun.js/bindings/ErrorCode.ts
#pragma once

#include <cstdint>
Expand Down
28 changes: 25 additions & 3 deletions src/js/builtins.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,15 @@ declare function $getByIdDirectPrivate<T = any>(obj: any, key: string): T;
declare function $getByValWithThis(target: any, receiver: any, propertyKey: string): void;
/** gets the prototype of an object */
declare function $getPrototypeOf(value: any): any;
/** gets an internal property on a promise
/**
* Gets an internal property on a promise
*
* You can pass
* - $promiseFieldFlags - get a number with flags
* - $promiseFieldReactionsOrResult - get the result (like Bun.peek)
* - {@link $promiseFieldFlags} - get a number with flags
* - {@link $promiseFieldReactionsOrResult} - get the result (like {@link Bun.peek})
*
* @param promise the promise to get the field from
* @param key an internal field id.
*/
declare function $getPromiseInternalField<K extends PromiseFieldType, V>(
promise: Promise<V>,
Expand All @@ -99,6 +103,19 @@ declare function $getMapIteratorInternalField(): TODO;
declare function $getSetIteratorInternalField(): TODO;
declare function $getProxyInternalField(): TODO;
declare function $idWithProfile(): TODO;
/**
* True for object-like `JSCell`s. That is, this is roughly equivalent to this
* JS code:
* ```js
* typeof obj === "object" && obj !== null
* ```
*
* @param obj The object to check
* @returns `true` if `obj` is an object-like `JSCell`
*
* @see [JSCell.h](https://github.com/oven-sh/WebKit/blob/main/Source/JavaScriptCore/runtime/JSCell.h)
* @see [JIT implementation](https://github.com/oven-sh/WebKit/blob/433f7598bf3537a295d0af5ffd83b9a307abec4e/Source/JavaScriptCore/jit/JITOpcodes.cpp#L311)
*/
declare function $isObject(obj: unknown): obj is object;
declare function $isArray(obj: unknown): obj is any[];
declare function $isCallable(fn: unknown): fn is CallableFunction;
Expand Down Expand Up @@ -559,6 +576,11 @@ declare interface Function {
path: string;
}

interface String {
$charCodeAt: String["charCodeAt"];
// add others as needed
}

declare var $Buffer: {
new (a: any, b?: any, c?: any): Buffer;
};
Expand Down
5 changes: 5 additions & 0 deletions src/js/internal/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const isInsideNodeModules: () => boolean = $newZigFunction("node_util_binding.zig", "isInsideNodeModules", 0);
Jarred-Sumner marked this conversation as resolved.
Show resolved Hide resolved

export default {
isInsideNodeModules,
};
Loading
Loading