mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-29 07:04:09 +08:00
Bind builtin static functions and read Buffer.alloc, fs.realpathSync and String methods as values
- bind/call/apply on builtin function values held as checked-dynamic
functions (String.fromCharCode, JSON.stringify and the forms below).
- Function values for Buffer.byteLength (Node's runtime encoding ladder),
Buffer.compare, Buffer.alloc (size check, then fill), JSON.parse,
fs.realpathSync and fs.realpathSync.native (Node's options handling).
- A JavaScript string method read as a value answers String.prototype's
function (smol-toml's `!!(''.isWellFormed)` probe).
The runtime entries live in a new unit, scr_builtin_values.c, linked only
by programs that read these values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
139542bcf1
commit
be9c18f0b3
@@ -28,6 +28,7 @@ export function executableLinkFeatures(mod: IrModule): NativeLinkFeatures {
|
||||
...(features.dynTier ? { dynTier: true } : {}),
|
||||
...(features.streamDyn ? { streamDyn: true } : {}),
|
||||
...(features.eventsOnceSignal ? { eventsOnceSignal: true } : {}),
|
||||
...(features.builtinValues ? { builtinValues: true } : {}),
|
||||
events: features.processEvents,
|
||||
emitter: features.emitter,
|
||||
symbol: features.symbol,
|
||||
|
||||
@@ -374,6 +374,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"fsp.globStrings": "scr_fsp_glob_strings",
|
||||
"fsp.globError": "scr_fsp_glob_error",
|
||||
"fs.realpathSync": "scr_fs_realpath",
|
||||
"fs.realpathValue": "scr_fs_realpath_value",
|
||||
"fs.realpathNativeSync": "scr_fs_realpath_promise",
|
||||
"fs.unlinkSync": "scr_fs_unlink",
|
||||
"fs.chmodSync": "scr_fs_chmod",
|
||||
@@ -437,6 +438,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"buffer.concatDyn": "scr_buffer_concat_dyn",
|
||||
"buffer.concatLenDyn": "scr_buffer_concat_len_dyn",
|
||||
"buffer.alloc": "scr_buffer_alloc",
|
||||
"buffer.allocValue": "scr_buffer_alloc_value",
|
||||
"buffer.allocUnsafe": "scr_buffer_alloc_unsafe",
|
||||
"buffer.allocUnsafeSlow": "scr_buffer_alloc_unsafe_slow",
|
||||
"buffer.poolFromStr": "scr_buffer_from_str",
|
||||
@@ -447,6 +449,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"buffer.poolSize": "scr_buffer_pool_size",
|
||||
"buffer.setPoolSize": "scr_buffer_set_pool_size",
|
||||
"buffer.byteLenDyn": "scr_buffer_byte_length_dyn",
|
||||
"buffer.byteLenValue": "scr_buffer_byte_length_value",
|
||||
"buffer.byteLenStr": "scr_bytes_byte_length_str",
|
||||
"buffer.isEncoding": "scr_bytes_is_encoding",
|
||||
// The checked-dynamic compare/equals validators (scr_bytes_io.c):
|
||||
|
||||
@@ -49,6 +49,9 @@ export interface NativeLinkFeatures {
|
||||
/** events.once with options: scr_events_once_signal.c. Absent (not
|
||||
* false) for programs without it. */
|
||||
eventsOnceSignal?: boolean;
|
||||
/** Builtin functions read as values (Buffer.byteLength, Buffer.alloc,
|
||||
* fs.realpathSync) link scr_builtin_values.c; absent (false) otherwise. */
|
||||
builtinValues?: boolean;
|
||||
events: boolean;
|
||||
emitter: boolean;
|
||||
symbol: boolean;
|
||||
|
||||
@@ -119,6 +119,8 @@ export interface CcOptions {
|
||||
/** events.once with options (events.onceSignal): compiles
|
||||
* scr_events_once_signal.c. */
|
||||
eventsOnceSignal?: boolean;
|
||||
/** Builtin functions read as values: compiles scr_builtin_values.c. */
|
||||
builtinValues?: boolean;
|
||||
/** The program constructs EventTarget/Event/CustomEvent values or
|
||||
* calls events.addAbortListener (the event.* libCalls), or uses the
|
||||
* native fetch AbortSignal built on the same engine: compiles
|
||||
|
||||
@@ -179,6 +179,8 @@ const RUNTIME_UNITS: readonly RuntimeUnit[] = [
|
||||
unit((u) => u.opts.assert || u.regex || u.opts.symbol, "scr_assert.c"),
|
||||
unit((u) => u.opts.inspect, "scr_inspect.c", "scr_console_native.c"),
|
||||
unit((u) => u.opts.dynInvoke || u.opts.workers || u.nativeFetch, "scr_dyn_invoke.c"),
|
||||
// Builtin functions read as values in JavaScript dependencies.
|
||||
unit((u) => u.opts.builtinValues === true, "scr_builtin_values.c"),
|
||||
unit((u) => u.opts.dc, "scr_dc.c"),
|
||||
unit((u) => u.opts.dynTier, "scr_js_value.c"),
|
||||
unit((u) => u.opts.eventTarget || u.nativeFetch, "scr_event_target.c"),
|
||||
|
||||
@@ -86,6 +86,7 @@ export interface RuntimeFeatureSet extends NativeLinkFeatures {
|
||||
dynTier: boolean;
|
||||
streamDyn: boolean;
|
||||
eventsOnceSignal: boolean;
|
||||
builtinValues: boolean;
|
||||
warningReplace: boolean;
|
||||
stdioExtended: boolean;
|
||||
nativeFetch: boolean;
|
||||
@@ -279,6 +280,7 @@ export function effectiveRuntimeFeatures(
|
||||
dynTier: features.dynTier === true,
|
||||
streamDyn: features.streamDyn === true,
|
||||
eventsOnceSignal: features.eventsOnceSignal === true,
|
||||
builtinValues: features.builtinValues === true,
|
||||
warningReplace: features.warningReplace === true,
|
||||
stdioExtended: features.stdioExtended === true,
|
||||
nativeFetch,
|
||||
|
||||
@@ -68,6 +68,7 @@ export interface EarlyExecutableNativeFeatures {
|
||||
dynTier?: boolean;
|
||||
streamDyn?: boolean;
|
||||
eventsOnceSignal?: boolean;
|
||||
builtinValues?: boolean;
|
||||
events: boolean;
|
||||
emitter: boolean;
|
||||
symbol: boolean;
|
||||
|
||||
@@ -650,5 +650,57 @@ export function lowerJsonProperty(
|
||||
loc,
|
||||
};
|
||||
}
|
||||
if (member === "parse") {
|
||||
// JSON.parse as a value: ToString of the text, then the reviver form
|
||||
// (a reviver that is not a function is ignored, as in Node).
|
||||
const loc = locOf(expr);
|
||||
const name = "%builtin.JSON.parse";
|
||||
if (!lowerer.builtinCallableValueFns.has(name)) {
|
||||
lowerer.builtinCallableValueFns.set(name, name);
|
||||
const params = ["text", "reviver"].map((name) => ({ localId: name, name, type: DYN }));
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
params,
|
||||
returnType: DYN,
|
||||
locals: params.map((p) => ({ id: p.localId, name: p.name, type: DYN, mutable: false })),
|
||||
loc,
|
||||
body: [
|
||||
{
|
||||
kind: "return",
|
||||
value: {
|
||||
kind: "libCall",
|
||||
fn: "json.parseReviver",
|
||||
args: [
|
||||
{
|
||||
kind: "libCall",
|
||||
fn: "dyn.toStringCoerce",
|
||||
args: [varRef("text", DYN, loc)],
|
||||
type: STRING,
|
||||
loc,
|
||||
},
|
||||
varRef("reviver", DYN, loc),
|
||||
],
|
||||
type: DYN,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
return {
|
||||
kind: "dynFrom",
|
||||
value: {
|
||||
kind: "closure",
|
||||
fnName: name,
|
||||
captures: [],
|
||||
type: { kind: "func", params: [DYN, DYN], ret: DYN },
|
||||
loc,
|
||||
},
|
||||
fnName: "parse",
|
||||
type: DYN,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
lowerer.unsupported("SC1090", expr, `JSON methods as values (call '${member}' directly)`);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
import {
|
||||
BOOL,
|
||||
BYTES_ELEMENT_NAME,
|
||||
BYTES_U8,
|
||||
DYN,
|
||||
F64,
|
||||
type IrExpr,
|
||||
@@ -156,7 +157,7 @@ import { narrowStoredClassValue, structuralReadType } from "../class-unions.js";
|
||||
import { InternalCompilerError } from "../../../errors.js";
|
||||
import { lowerUnionFieldRead } from "./union-fields.js";
|
||||
import { errorPropertyRead } from "../error-methods.js";
|
||||
import { isSafeToRepeat } from "./evaluation-safety.js";
|
||||
import { isSafeToDiscard, isSafeToRepeat } from "./evaluation-safety.js";
|
||||
import { isZlibStreamMember, lowerZlibStreamBytesWritten } from "../builtins/compression.js";
|
||||
import { isFsStreamMember, lowerFsStreamProperty } from "../builtins/fs-streams.js";
|
||||
import { keyedFieldRead } from "./keyed-field-targets.js";
|
||||
@@ -1624,6 +1625,38 @@ export function lowerIntrinsicProperty(
|
||||
if (netAddressStatic) return netAddressStatic;
|
||||
if (lowerer.stdlibGlobalMember(expr, "Buffer") === "isBuffer")
|
||||
return lowerCheckedPredicateValue(lowerer, "Buffer.isBuffer", "buffer", locOf(expr));
|
||||
// Buffer statics as function values (`Buffer.byteLength.bind(Buffer)` in
|
||||
// truncate-utf8-bytes): the checked forms take any argument as Node does.
|
||||
if (lowerer.stdlibGlobalMember(expr, "Buffer") === "byteLength")
|
||||
return lowerer.lowerNativeCallableValue(
|
||||
{
|
||||
fn: "buffer.byteLenValue",
|
||||
params: [DYN, DYN],
|
||||
result: F64,
|
||||
valueParams: [
|
||||
{ mode: "required", type: DYN },
|
||||
{ mode: "optional", type: DYN, defaultValue: undefined },
|
||||
],
|
||||
},
|
||||
"Buffer.byteLength",
|
||||
locOf(expr),
|
||||
);
|
||||
if (lowerer.stdlibGlobalMember(expr, "Buffer") === "alloc")
|
||||
return bufferAllocValue(lowerer, locOf(expr));
|
||||
if (lowerer.stdlibGlobalMember(expr, "Buffer") === "compare")
|
||||
return lowerer.lowerNativeCallableValue(
|
||||
{
|
||||
fn: "buffer.compareChk",
|
||||
params: [DYN, DYN],
|
||||
result: F64,
|
||||
valueParams: [
|
||||
{ mode: "required", type: DYN },
|
||||
{ mode: "required", type: DYN },
|
||||
],
|
||||
},
|
||||
"Buffer.compare",
|
||||
locOf(expr),
|
||||
);
|
||||
if (lowerer.stdlibGlobalMember(expr, "Date") === "now")
|
||||
return lowerer.lowerNativeCallableValue(
|
||||
{
|
||||
@@ -2205,11 +2238,165 @@ export function lowerIntrinsicProperty(
|
||||
own(STDLIB_SURFACE.string, name) !== undefined
|
||||
: ARRAY_METHODS.has(name)
|
||||
) {
|
||||
// A JavaScript string method read as a value (smol-toml's
|
||||
// `!!(''.isWellFormed)` feature probe): String.prototype's method, the
|
||||
// runtime's interned function that runs it on its receiver.
|
||||
if (kind === "string" && isJsSourceFile(expr.getSourceFile()) && !expr.questionDotToken) {
|
||||
const receiver = lowerer.lowerExpr(expr.expression);
|
||||
const method: IrExpr = {
|
||||
kind: "libCall",
|
||||
fn: "dyn.builtinMethod",
|
||||
args: [
|
||||
{ kind: "strLit", value: "String", type: STRING, loc: locOf(expr) },
|
||||
{ kind: "strLit", value: name, type: STRING, loc: locOf(expr.name) },
|
||||
],
|
||||
type: DYN,
|
||||
loc: locOf(expr),
|
||||
};
|
||||
return isSafeToDiscard(receiver)
|
||||
? method
|
||||
: {
|
||||
kind: "seqExpr",
|
||||
stmts: [{ kind: "exprStmt", expr: receiver, loc: locOf(expr.expression) }],
|
||||
result: method,
|
||||
type: DYN,
|
||||
loc: locOf(expr),
|
||||
};
|
||||
}
|
||||
lowerer.unsupported("SC1090", expr, `${kind} methods as values (call '${name}' directly)`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The fill step of Buffer.alloc's value form: a number, or a string with
|
||||
* no encoding (utf8), fills the fresh buffer statically; any other fill
|
||||
* takes the checked-dynamic `fill` dispatch. */
|
||||
function bufferAllocFill(loc: SrcLoc): IrStmt {
|
||||
const buf = varRef("buf", BYTES_U8, loc);
|
||||
const fill = varRef("fill", DYN, loc);
|
||||
const encoding = varRef("encoding", DYN, loc);
|
||||
const test = (kind: "number" | "string" | "undefined", value: IrExpr): IrExpr => ({
|
||||
kind: "dynTest",
|
||||
test: kind,
|
||||
value,
|
||||
type: BOOL,
|
||||
loc,
|
||||
});
|
||||
const fillStr: IrExpr = {
|
||||
kind: "bytesIntrinsic",
|
||||
method: "fillStr",
|
||||
receiver: buf,
|
||||
args: [
|
||||
{ kind: "dynCheck", value: fill, type: STRING, loc },
|
||||
{ kind: "strLit", value: "utf8", type: STRING, loc },
|
||||
],
|
||||
type: BYTES_U8,
|
||||
loc,
|
||||
};
|
||||
const fillNum: IrExpr = {
|
||||
kind: "bytesIntrinsic",
|
||||
method: "fillNum",
|
||||
receiver: buf,
|
||||
args: [{ kind: "dynCheck", value: fill, type: F64, loc }],
|
||||
type: BYTES_U8,
|
||||
loc,
|
||||
};
|
||||
const other: IrExpr = {
|
||||
kind: "dynInvoke",
|
||||
recv: { kind: "dynFrom", value: buf, type: DYN, loc },
|
||||
method: "fill",
|
||||
calleeName: "buf.fill",
|
||||
args: [fill, encoding],
|
||||
type: DYN,
|
||||
loc,
|
||||
};
|
||||
return {
|
||||
kind: "if",
|
||||
cond: test("number", fill),
|
||||
then: [{ kind: "exprStmt", expr: fillNum, loc }],
|
||||
else_: [
|
||||
{
|
||||
kind: "if",
|
||||
cond: {
|
||||
kind: "logical",
|
||||
op: "&&",
|
||||
left: test("string", fill),
|
||||
right: test("undefined", encoding),
|
||||
type: BOOL,
|
||||
loc,
|
||||
},
|
||||
then: [{ kind: "exprStmt", expr: fillStr, loc }],
|
||||
else_: [{ kind: "exprStmt", expr: other, loc }],
|
||||
loc,
|
||||
},
|
||||
],
|
||||
loc,
|
||||
};
|
||||
}
|
||||
|
||||
/** Buffer.alloc as a function value (`var alloc = Buffer.alloc` in
|
||||
* tar-stream): the checked allocation, then `fill(fill, encoding)` on the
|
||||
* fresh buffer when a fill is given, which is how Node's alloc fills. */
|
||||
function bufferAllocValue(lowerer: Lowerer, loc: SrcLoc): IrExpr {
|
||||
const name = "%builtin.Buffer.alloc";
|
||||
if (!lowerer.builtinCallableValueFns.has(name)) {
|
||||
lowerer.builtinCallableValueFns.set(name, name);
|
||||
const params = ["size", "fill", "encoding"].map((id) => ({ localId: id, name: id, type: DYN }));
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
params,
|
||||
returnType: BYTES_U8,
|
||||
locals: [
|
||||
...params.map((p) => ({ id: p.localId, name: p.name, type: DYN, mutable: false })),
|
||||
{ id: "buf", name: "buf", type: BYTES_U8, mutable: false },
|
||||
],
|
||||
loc,
|
||||
body: [
|
||||
{
|
||||
kind: "varDecl",
|
||||
localId: "buf",
|
||||
init: {
|
||||
kind: "libCall",
|
||||
fn: "buffer.allocValue",
|
||||
args: [varRef("size", DYN, loc)],
|
||||
type: BYTES_U8,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
},
|
||||
{
|
||||
kind: "if",
|
||||
cond: {
|
||||
kind: "dynTest",
|
||||
test: "undefined",
|
||||
negated: true,
|
||||
value: varRef("fill", DYN, loc),
|
||||
type: BOOL,
|
||||
loc,
|
||||
},
|
||||
then: [bufferAllocFill(loc)],
|
||||
else_: null,
|
||||
loc,
|
||||
},
|
||||
{ kind: "return", value: varRef("buf", BYTES_U8, loc), loc },
|
||||
],
|
||||
});
|
||||
}
|
||||
return {
|
||||
kind: "dynFrom",
|
||||
value: {
|
||||
kind: "closure",
|
||||
fnName: name,
|
||||
captures: [],
|
||||
type: { kind: "func", params: [DYN, DYN, DYN], ret: BYTES_U8 },
|
||||
loc,
|
||||
},
|
||||
fnName: "alloc",
|
||||
type: DYN,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
|
||||
/** A `T | undefined` class field read that the checker narrowed to `T`
|
||||
* where the field was assigned a value that may be missing (an unchecked
|
||||
* indexed read). TypeScript's assignment narrowing is not a runtime fact
|
||||
|
||||
@@ -9338,15 +9338,18 @@ function lowerObjLitGenericMethodCall(
|
||||
lowerer.checker.getCallSignatures(recvT).length > 0
|
||||
) {
|
||||
const value = tryLowerExpression(lowerer, access.expression);
|
||||
// Builtin function values held as checked-dynamic functions
|
||||
// (String.fromCharCode, JSON.stringify) bind the same way.
|
||||
if (
|
||||
value?.type.kind === "func" &&
|
||||
lowerer.dynConvertible(value.type) &&
|
||||
value &&
|
||||
(value.type.kind === "dyn" ||
|
||||
(value.type.kind === "func" && lowerer.dynConvertible(value.type))) &&
|
||||
!call.arguments.nodes.some(ts.isSpreadElement)
|
||||
) {
|
||||
const loc = locOf(call);
|
||||
return {
|
||||
kind: "dynInvoke",
|
||||
recv: { kind: "dynFrom", value, type: DYN, loc },
|
||||
recv: value.type.kind === "dyn" ? value : { kind: "dynFrom", value, type: DYN, loc },
|
||||
method: name,
|
||||
calleeName: ts.getTextOfNode(access),
|
||||
args: call.arguments.nodes.map((arg) => lowerer.lowerExprExpecting(arg, DYN)),
|
||||
|
||||
@@ -12945,6 +12945,8 @@ export class Lowerer {
|
||||
loc,
|
||||
};
|
||||
}
|
||||
if (bi.module === "fs" && (bi.member === "realpathSync" || bi.member === "realpathSync.native"))
|
||||
return fsRealpathValue(this, bi.member === "realpathSync.native", loc);
|
||||
const fn = builtinModuleFnOf(this, bi.module, bi.member);
|
||||
return fn ? this.lowerNativeCallableValue(fn, `${bi.module}.${bi.member}`, loc) : null;
|
||||
}
|
||||
@@ -13657,3 +13659,51 @@ function droppedMembers(lowerer: Lowerer, from: IrType, to: IrType, seen: Set<st
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** fs.realpathSync and its .native as function values (fs.realpath stores
|
||||
* `var origRealpathSync = fs.realpathSync`): path and options pass through
|
||||
* unchecked to the runtime, which applies Node's argument handling. */
|
||||
function fsRealpathValue(lowerer: Lowerer, native: boolean, loc: SrcLoc): IrExpr {
|
||||
const name = native ? "%builtin.fs.realpathSync.native" : "%builtin.fs.realpathSync";
|
||||
if (!lowerer.builtinCallableValueFns.has(name)) {
|
||||
lowerer.builtinCallableValueFns.set(name, name);
|
||||
const params = ["path", "options"].map((id) => ({ localId: id, name: id, type: DYN }));
|
||||
lowerer.liftedFns.push({
|
||||
name,
|
||||
params,
|
||||
returnType: DYN,
|
||||
locals: params.map((p) => ({ id: p.localId, name: p.name, type: DYN, mutable: false })),
|
||||
loc,
|
||||
body: [
|
||||
{
|
||||
kind: "return",
|
||||
value: {
|
||||
kind: "libCall",
|
||||
fn: "fs.realpathValue",
|
||||
args: [
|
||||
{ kind: "varRef", localId: "path", type: DYN, loc },
|
||||
{ kind: "varRef", localId: "options", type: DYN, loc },
|
||||
{ kind: "boolLit", value: native, type: BOOL, loc },
|
||||
],
|
||||
type: DYN,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
return {
|
||||
kind: "dynFrom",
|
||||
value: {
|
||||
kind: "closure",
|
||||
fnName: name,
|
||||
captures: [],
|
||||
type: { kind: "func", params: [DYN, DYN], ret: DYN },
|
||||
loc,
|
||||
},
|
||||
fnName: native ? "native" : "realpathSync",
|
||||
type: DYN,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -547,6 +547,7 @@ async function compileExecutableNative(
|
||||
...(features.dynTier ? { dynTier: true } : {}),
|
||||
...(features.streamDyn ? { streamDyn: true } : {}),
|
||||
...(features.eventsOnceSignal ? { eventsOnceSignal: true } : {}),
|
||||
...(features.builtinValues ? { builtinValues: true } : {}),
|
||||
events: features.events,
|
||||
emitter: features.emitter,
|
||||
symbol: features.symbol,
|
||||
|
||||
@@ -85,6 +85,9 @@ export const MAY_THROW_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"bytes.constructor",
|
||||
"bytes.instanceOf",
|
||||
"buffer.byteLenDyn",
|
||||
"buffer.byteLenValue",
|
||||
"buffer.allocValue",
|
||||
"fs.realpathValue",
|
||||
"bytes.construct",
|
||||
"arrayBuffer.new",
|
||||
"sharedArrayBuffer.new",
|
||||
|
||||
@@ -687,6 +687,10 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
/** realpath(3) with Node's error shape (syscall "lstat" in the message,
|
||||
* Node's own spelling for realpathSync failures). +1 fresh string. */
|
||||
"fs.realpathSync": { argTypes: [STRING], result: STRING },
|
||||
/** fs.realpathSync (or its .native when the flag is true) as a function
|
||||
* value: path and options arrive unchecked; the result is a string or a
|
||||
* Buffer by the options' encoding. */
|
||||
"fs.realpathValue": { argTypes: [DYN, DYN, BOOL], result: DYN },
|
||||
/** Native realpath with syscall "realpath" on failure. +1 fresh string. */
|
||||
"fs.realpathNativeSync": { argTypes: [STRING], result: STRING },
|
||||
/** The os.userInfo() field trio (uv_os_get_passwd's slices): pw_name,
|
||||
@@ -2868,6 +2872,8 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
* allocUnsafeSlow never pool. The size forms validate like Node
|
||||
* (ERR_OUT_OF_RANGE). poolSize reads and writes Buffer.poolSize. */
|
||||
"buffer.alloc": { argTypes: [F64], result: BYTES_U8 },
|
||||
/** Buffer.alloc as a function value: the size arrives unchecked. */
|
||||
"buffer.allocValue": { argTypes: [DYN], result: BYTES_U8 },
|
||||
"buffer.allocUnsafe": { argTypes: [F64], result: BYTES_U8 },
|
||||
"buffer.allocUnsafeSlow": { argTypes: [F64], result: BYTES_U8 },
|
||||
"buffer.poolFromStr": { argTypes: [STRING, STRING], result: BYTES_U8 },
|
||||
@@ -2881,6 +2887,8 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
* fromStr's — and Buffer.isEncoding(name) over a runtime string
|
||||
* (case-insensitive against Node's alias set). Pure; never throw. */
|
||||
"buffer.byteLenDyn": { argTypes: [DYN, STRING], result: F64 },
|
||||
/** Buffer.byteLength as a function value: the encoding arrives at run time. */
|
||||
"buffer.byteLenValue": { argTypes: [DYN, DYN], result: F64 },
|
||||
"buffer.byteLenStr": { argTypes: [STRING, STRING], result: F64 },
|
||||
"buffer.isEncoding": { argTypes: [STRING], result: BOOL },
|
||||
/** Buffer.concat(list, totalLength): the concatenation truncated or
|
||||
|
||||
@@ -569,6 +569,8 @@ const RUNTIME_FEATURE_RULES = {
|
||||
/** Checked-dynamic stream slots test values and pipe into JavaScript
|
||||
* stream objects (scr_stream_dyn.c). */
|
||||
streamDyn: rule({ fns: ["stream.dynNative", "stream.pipeDyn", "stream.unpipeDyn"] }),
|
||||
/** Builtin functions read as values (scr_builtin_values.c). */
|
||||
builtinValues: rule({ fns: ["buffer.byteLenValue", "buffer.allocValue", "fs.realpathValue"] }),
|
||||
/** The stream unit emits through the emitter unit, also when the
|
||||
* program constructs no stream (a pipeline of functions). */
|
||||
emitter: rule({
|
||||
@@ -752,6 +754,7 @@ function noRuntimeFeatures(): RuntimeFeatures {
|
||||
stdioExtended: false,
|
||||
stream: false,
|
||||
streamDyn: false,
|
||||
builtinValues: false,
|
||||
symbol: false,
|
||||
tls: false,
|
||||
tlsCa: false,
|
||||
|
||||
@@ -79,6 +79,7 @@ test.skipIf(process.platform !== "linux" && process.platform !== "darwin")(
|
||||
dynTier: false,
|
||||
streamDyn: false,
|
||||
eventsOnceSignal: false,
|
||||
builtinValues: false,
|
||||
warningReplace: false,
|
||||
stdioExtended: false,
|
||||
regex: true,
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6570-npm-builtin-function-bind/main.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -61,6 +61,8 @@ export const OPTIONAL_RUNTIME_SOURCES = [
|
||||
["scr_inspect.c", "inspect"],
|
||||
["scr_console_native.c", "inspect"],
|
||||
["scr_dyn_invoke.c", any("dynInvoke", "nativeFetch")],
|
||||
// Builtin functions read as values in JavaScript dependencies.
|
||||
["scr_builtin_values.c", "builtinValues"],
|
||||
["scr_dc.c", "dc"],
|
||||
// The dynamic tier's jsval values (two-tier design I3): linked only into
|
||||
// programs whose IR has dynamic-tier operations.
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/* Builtin functions called as function values in JavaScript dependencies
|
||||
* (`Buffer.byteLength.bind(Buffer)`, `var alloc = Buffer.alloc`, a stored
|
||||
* `fs.realpathSync`): the arguments arrive unchecked, so these entries apply
|
||||
* Node's argument handling before the ordinary operation. A separate unit,
|
||||
* linked only by programs that read these values. */
|
||||
#include "scr_runtime.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Buffer.byteLength called as a function value (`Buffer.byteLength.bind(
|
||||
* Buffer)` in truncate-utf8-bytes), with the encoding known only at run
|
||||
* time. Node's ladder: a non-string source answers its byte length or
|
||||
* throws before the encoding is read; an empty string answers 0; a falsy
|
||||
* encoding, or one getEncodingOps does not know after ToString (any ASCII
|
||||
* case), counts UTF-8. */
|
||||
double scr_buffer_byte_length_value(ScrDyn *value, ScrDyn *encoding) {
|
||||
static const struct { const char *from; const char *to; } map[] = {
|
||||
{"utf8", "utf8"}, {"utf-8", "utf8"}, {"hex", "hex"},
|
||||
{"base64", "base64"}, {"base64url", "base64url"},
|
||||
{"latin1", "latin1"}, {"binary", "latin1"}, {"ascii", "ascii"},
|
||||
{"utf16le", "utf16le"}, {"utf-16le", "utf16le"},
|
||||
{"ucs2", "utf16le"}, {"ucs-2", "utf16le"},
|
||||
};
|
||||
const char *name = "utf8";
|
||||
if (value->kind == SCR_DYN_STR && value->v.str->len > 0 && scr_dyn_truthy(encoding)) {
|
||||
ScrStr *raw = scr_dyn_string_coerce_js(encoding);
|
||||
if (!raw) return 0;
|
||||
for (size_t i = 0; i < sizeof map / sizeof map[0]; i++) {
|
||||
size_t n = strlen(map[i].from);
|
||||
bool same = raw->len == n;
|
||||
for (size_t k = 0; same && k < n; k++) {
|
||||
char c = raw->data[k];
|
||||
if (c >= 'A' && c <= 'Z') c = (char)(c + ('a' - 'A'));
|
||||
same = c == map[i].from[k];
|
||||
}
|
||||
if (same) {
|
||||
name = map[i].to;
|
||||
break;
|
||||
}
|
||||
}
|
||||
scr_str_release(raw);
|
||||
}
|
||||
ScrStr *canonical = scr_str_new(name, strlen(name));
|
||||
double length = scr_buffer_byte_length_dyn(value, canonical);
|
||||
scr_str_release(canonical);
|
||||
return length;
|
||||
}
|
||||
|
||||
/* Buffer.alloc called as a function value (`var alloc = Buffer.alloc` in
|
||||
* tar-stream): the size must be a number, ERR_INVALID_ARG_TYPE otherwise,
|
||||
* then Node's allocation and range checks. The fill argument rides the
|
||||
* compiled caller. +1, or NULL with the exception pending. */
|
||||
ScrBytes *scr_buffer_alloc_value(const ScrDyn *size) {
|
||||
if (size->kind != SCR_DYN_NUM) {
|
||||
scr_dyn_arg_type_fail("size", "of type number", size);
|
||||
return NULL;
|
||||
}
|
||||
return scr_buffer_alloc(size->v.num);
|
||||
}
|
||||
|
||||
/* fs.realpathSync and fs.realpathSync.native called as function values
|
||||
* (fs.realpath keeps `var origRealpathSync = fs.realpathSync`). Node's
|
||||
* argument handling: options that are null, undefined or a function mean
|
||||
* none, a string is the encoding, an object supplies `encoding`, anything
|
||||
* else throws ERR_INVALID_ARG_TYPE; a path that is not a string converts
|
||||
* with ToString (a Buffer decodes as UTF-8). The result is a string for
|
||||
* utf8, a Buffer for "buffer", and the UTF-8 bytes decoded in any other
|
||||
* encoding, whose name must be one Node knows. +1 or NULL. */
|
||||
ScrDyn *scr_fs_realpath_value(const ScrDyn *path, const ScrDyn *options, bool native) {
|
||||
const ScrDyn *encoding = NULL;
|
||||
if (options->kind == SCR_DYN_STR) {
|
||||
encoding = options;
|
||||
} else if (options->kind == SCR_DYN_OBJ) {
|
||||
encoding = scr_dyn_obj_get(options, "encoding", 8);
|
||||
} else if (options->kind != SCR_DYN_UNDEF && options->kind != SCR_DYN_NULL &&
|
||||
options->kind != SCR_DYN_FUNC) {
|
||||
scr_dyn_arg_type_fail("options", "of type string or an instance of Object", options);
|
||||
return NULL;
|
||||
}
|
||||
static const struct { const char *from; const char *to; } names[] = {
|
||||
{"utf8", "utf8"}, {"utf-8", "utf8"}, {"hex", "hex"}, {"base64", "base64"},
|
||||
{"base64url", "base64url"}, {"latin1", "latin1"}, {"binary", "latin1"},
|
||||
{"ascii", "ascii"}, {"utf16le", "utf16le"}, {"utf-16le", "utf16le"},
|
||||
{"ucs2", "utf16le"}, {"ucs-2", "utf16le"}, {"buffer", "buffer"},
|
||||
};
|
||||
const char *canonical = "utf8";
|
||||
if (encoding && encoding->kind != SCR_DYN_UNDEF && encoding->kind != SCR_DYN_NULL &&
|
||||
!(encoding->kind == SCR_DYN_STR && encoding->v.str->len == 0)) {
|
||||
const char *found = NULL;
|
||||
if (encoding->kind == SCR_DYN_STR) {
|
||||
for (size_t i = 0; i < sizeof names / sizeof names[0] && !found; i++) {
|
||||
size_t n = strlen(names[i].from);
|
||||
if (encoding->v.str->len != n) continue;
|
||||
bool same = true;
|
||||
for (size_t k = 0; k < n && same; k++) {
|
||||
char c = encoding->v.str->data[k];
|
||||
if (c >= 'A' && c <= 'Z') c = (char)(c + ('a' - 'A'));
|
||||
same = c == names[i].from[k];
|
||||
}
|
||||
if (same) found = names[i].to;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ScrStr *shown = scr_dyn_string_coerce_js(encoding);
|
||||
if (!shown) return NULL;
|
||||
char msg[256];
|
||||
int n = snprintf(msg, sizeof msg, "The argument 'encoding' is invalid encoding. Received '%.*s'",
|
||||
(int)(shown->len > 160 ? 160 : shown->len), shown->data);
|
||||
scr_str_release(shown);
|
||||
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, (size_t)n, "ERR_INVALID_ARG_VALUE");
|
||||
return NULL;
|
||||
}
|
||||
canonical = found;
|
||||
}
|
||||
ScrStr *p = path->kind == SCR_DYN_STR ? scr_str_retain(path->v.str) : scr_dyn_string_coerce_js(path);
|
||||
if (!p) return NULL;
|
||||
ScrStr *resolved = native ? scr_fs_realpath_promise(p) : scr_fs_realpath(p);
|
||||
scr_str_release(p);
|
||||
if (!resolved) return NULL;
|
||||
ScrDyn *out;
|
||||
if (!strcmp(canonical, "utf8")) {
|
||||
out = scr_dyn_new_str(resolved);
|
||||
} else {
|
||||
ScrStr *utf8 = scr_str_new("utf8", 4);
|
||||
ScrBytes *bytes = scr_buffer_from_str(resolved, utf8);
|
||||
scr_str_release(utf8);
|
||||
if (!strcmp(canonical, "buffer")) {
|
||||
out = scr_dyn_new_buffer(bytes);
|
||||
} else {
|
||||
ScrStr *enc = scr_str_new(canonical, strlen(canonical));
|
||||
ScrStr *text = scr_bytes_to_str(bytes, enc);
|
||||
scr_str_release(enc);
|
||||
out = scr_dyn_new_str(text);
|
||||
scr_str_release(text);
|
||||
}
|
||||
scr_bytes_release(bytes);
|
||||
}
|
||||
scr_str_release(resolved);
|
||||
return out;
|
||||
}
|
||||
@@ -394,6 +394,8 @@ ScrBytes *scr_buffer_concat_len(const ScrArr *list, double total);
|
||||
/* The untyped-list forms: Node's ERR_INVALID_ARG_TYPE checks on the list
|
||||
* and each element, then the typed concat. NULL with the exception pending. */
|
||||
ScrBytes *scr_buffer_concat_dyn(const struct ScrDyn *list);
|
||||
ScrBytes *scr_buffer_alloc_value(const struct ScrDyn *size);
|
||||
struct ScrDyn *scr_fs_realpath_value(const struct ScrDyn *path, const struct ScrDyn *options, bool native);
|
||||
ScrBytes *scr_buffer_concat_len_dyn(const struct ScrDyn *list, double total);
|
||||
/* Drops the pool's slab reference and restores Buffer.poolSize (process
|
||||
* exit audits and library session resets). */
|
||||
@@ -408,6 +410,7 @@ ScrBytes *scr_bytes_from_str_checked(const ScrStr *s, const ScrStr *enc);
|
||||
* Borrow; never throw. */
|
||||
double scr_bytes_byte_length_str(ScrStr *s, const ScrStr *enc);
|
||||
double scr_buffer_byte_length_dyn(ScrDyn *value, ScrStr *enc);
|
||||
double scr_buffer_byte_length_value(ScrDyn *value, ScrDyn *encoding);
|
||||
bool scr_bytes_is_encoding(const ScrStr *s);
|
||||
|
||||
/* equals / compare / indexOf-lastIndexOf / fill / copy / swap / write on
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Function.prototype.bind, call and apply on builtin static functions in
|
||||
// JavaScript dependencies. The vendored truncate-utf8-bytes 1.0.2 (WTFPL)
|
||||
// binds Buffer.byteLength at load time and calls the bound function per
|
||||
// character; bound-builtins binds Buffer, Math, String and JSON statics.
|
||||
// fs.realpath 1.0.0 (ISC) and value-forms read builtin functions as values:
|
||||
// fs.realpathSync stored and compared, Buffer.alloc aliased, and a String
|
||||
// method read for a feature probe.
|
||||
import truncate from 'truncate-utf8-bytes';
|
||||
import { report, badLength } from 'bound-builtins';
|
||||
import realpath from 'fs.realpath';
|
||||
import { report as valueForms } from 'value-forms';
|
||||
|
||||
console.log(truncate('hello world', 5), truncate('héllo', 2), truncate('héllo', 3));
|
||||
console.log(JSON.stringify(truncate('a😀b', 3)), JSON.stringify(truncate('a😀b', 5)), truncate('short', 99));
|
||||
try {
|
||||
(truncate as unknown as (value: unknown, n: number) => string)(42, 1);
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
}
|
||||
for (const value of report()) console.log(value);
|
||||
console.log(badLength());
|
||||
|
||||
console.log(realpath.realpathSync('/'), realpath.sync('/'));
|
||||
realpath('/', (error, resolved) => console.log('async', error, resolved));
|
||||
for (const value of valueForms()) console.log(value);
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
export declare function report(): unknown[];
|
||||
export declare function badLength(): string;
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// Function.prototype.bind, call and apply on builtin static functions in
|
||||
// JavaScript: Buffer, Math, String and JSON.
|
||||
'use strict';
|
||||
var byteLength = Buffer.byteLength.bind(Buffer);
|
||||
var max = Math.max.bind(null, 10);
|
||||
var fromCodes = String.fromCharCode.bind(String, 104);
|
||||
var stringify = JSON.stringify.bind(JSON);
|
||||
var parse = JSON.parse.bind(JSON);
|
||||
var compare = Buffer.compare.bind(Buffer);
|
||||
|
||||
exports.report = function report() {
|
||||
return [
|
||||
byteLength('héllo'),
|
||||
byteLength('héllo', 'latin1'),
|
||||
byteLength('abcd', 'hex'),
|
||||
byteLength('héllo', 'not-an-encoding'),
|
||||
byteLength(''),
|
||||
byteLength(Buffer.from([1, 2, 3])),
|
||||
max(3, 42),
|
||||
max(),
|
||||
Math.min.call(null, 4, 2),
|
||||
Math.abs.apply(null, [-7]),
|
||||
fromCodes(105, 33),
|
||||
String.fromCharCode.apply(null, [111, 107]),
|
||||
stringify({ a: [1, 2] }),
|
||||
stringify({ b: 1 }, null, 2),
|
||||
compare(Buffer.from('a'), Buffer.from('b')),
|
||||
parse('{"n":1}').n,
|
||||
JSON.stringify(parse('[1,2]', function (key, value) {
|
||||
return typeof value === 'number' ? value * 10 : value;
|
||||
})),
|
||||
];
|
||||
};
|
||||
|
||||
exports.badLength = function badLength() {
|
||||
try {
|
||||
byteLength(42);
|
||||
return 'no error';
|
||||
} catch (error) {
|
||||
return error.code + ' ' + error.name;
|
||||
}
|
||||
};
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{ "name": "bound-builtins", "version": "1.0.0", "main": "index.js", "types": "index.d.ts" }
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
----
|
||||
|
||||
This library bundles a version of the `fs.realpath` and `fs.realpathSync`
|
||||
methods from Node.js v0.10 under the terms of the Node.js MIT license.
|
||||
|
||||
Node's license follows, also included at the header of `old.js` which contains
|
||||
the licensed code:
|
||||
|
||||
Copyright Joyent, Inc. and other Node contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
declare function realpath(p: string, cb: (error: Error | null, resolved?: string) => void): void;
|
||||
declare namespace realpath {
|
||||
function realpathSync(p: string, cache?: object): string;
|
||||
function sync(p: string, cache?: object): string;
|
||||
}
|
||||
export = realpath;
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
module.exports = realpath
|
||||
realpath.realpath = realpath
|
||||
realpath.sync = realpathSync
|
||||
realpath.realpathSync = realpathSync
|
||||
realpath.monkeypatch = monkeypatch
|
||||
realpath.unmonkeypatch = unmonkeypatch
|
||||
|
||||
var fs = require('fs')
|
||||
var origRealpath = fs.realpath
|
||||
var origRealpathSync = fs.realpathSync
|
||||
|
||||
var version = process.version
|
||||
var ok = /^v[0-5]\./.test(version)
|
||||
var old = require('./old.js')
|
||||
|
||||
function newError (er) {
|
||||
return er && er.syscall === 'realpath' && (
|
||||
er.code === 'ELOOP' ||
|
||||
er.code === 'ENOMEM' ||
|
||||
er.code === 'ENAMETOOLONG'
|
||||
)
|
||||
}
|
||||
|
||||
function realpath (p, cache, cb) {
|
||||
if (ok) {
|
||||
return origRealpath(p, cache, cb)
|
||||
}
|
||||
|
||||
if (typeof cache === 'function') {
|
||||
cb = cache
|
||||
cache = null
|
||||
}
|
||||
origRealpath(p, cache, function (er, result) {
|
||||
if (newError(er)) {
|
||||
old.realpath(p, cache, cb)
|
||||
} else {
|
||||
cb(er, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function realpathSync (p, cache) {
|
||||
if (ok) {
|
||||
return origRealpathSync(p, cache)
|
||||
}
|
||||
|
||||
try {
|
||||
return origRealpathSync(p, cache)
|
||||
} catch (er) {
|
||||
if (newError(er)) {
|
||||
return old.realpathSync(p, cache)
|
||||
} else {
|
||||
throw er
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function monkeypatch () {
|
||||
fs.realpath = realpath
|
||||
fs.realpathSync = realpathSync
|
||||
}
|
||||
|
||||
function unmonkeypatch () {
|
||||
fs.realpath = origRealpath
|
||||
fs.realpathSync = origRealpathSync
|
||||
}
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var pathModule = require('path');
|
||||
var isWindows = process.platform === 'win32';
|
||||
var fs = require('fs');
|
||||
|
||||
// JavaScript implementation of realpath, ported from node pre-v6
|
||||
|
||||
var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG);
|
||||
|
||||
function rethrow() {
|
||||
// Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and
|
||||
// is fairly slow to generate.
|
||||
var callback;
|
||||
if (DEBUG) {
|
||||
var backtrace = new Error;
|
||||
callback = debugCallback;
|
||||
} else
|
||||
callback = missingCallback;
|
||||
|
||||
return callback;
|
||||
|
||||
function debugCallback(err) {
|
||||
if (err) {
|
||||
backtrace.message = err.message;
|
||||
err = backtrace;
|
||||
missingCallback(err);
|
||||
}
|
||||
}
|
||||
|
||||
function missingCallback(err) {
|
||||
if (err) {
|
||||
if (process.throwDeprecation)
|
||||
throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs
|
||||
else if (!process.noDeprecation) {
|
||||
var msg = 'fs: missing callback ' + (err.stack || err.message);
|
||||
if (process.traceDeprecation)
|
||||
console.trace(msg);
|
||||
else
|
||||
console.error(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function maybeCallback(cb) {
|
||||
return typeof cb === 'function' ? cb : rethrow();
|
||||
}
|
||||
|
||||
var normalize = pathModule.normalize;
|
||||
|
||||
// Regexp that finds the next partion of a (partial) path
|
||||
// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
|
||||
if (isWindows) {
|
||||
var nextPartRe = /(.*?)(?:[\/\\]+|$)/g;
|
||||
} else {
|
||||
var nextPartRe = /(.*?)(?:[\/]+|$)/g;
|
||||
}
|
||||
|
||||
// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
|
||||
if (isWindows) {
|
||||
var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/;
|
||||
} else {
|
||||
var splitRootRe = /^[\/]*/;
|
||||
}
|
||||
|
||||
exports.realpathSync = function realpathSync(p, cache) {
|
||||
// make p is absolute
|
||||
p = pathModule.resolve(p);
|
||||
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
|
||||
return cache[p];
|
||||
}
|
||||
|
||||
var original = p,
|
||||
seenLinks = {},
|
||||
knownHard = {};
|
||||
|
||||
// current character position in p
|
||||
var pos;
|
||||
// the partial path so far, including a trailing slash if any
|
||||
var current;
|
||||
// the partial path without a trailing slash (except when pointing at a root)
|
||||
var base;
|
||||
// the partial path scanned in the previous round, with slash
|
||||
var previous;
|
||||
|
||||
start();
|
||||
|
||||
function start() {
|
||||
// Skip over roots
|
||||
var m = splitRootRe.exec(p);
|
||||
pos = m[0].length;
|
||||
current = m[0];
|
||||
base = m[0];
|
||||
previous = '';
|
||||
|
||||
// On windows, check that the root exists. On unix there is no need.
|
||||
if (isWindows && !knownHard[base]) {
|
||||
fs.lstatSync(base);
|
||||
knownHard[base] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// walk down the path, swapping out linked pathparts for their real
|
||||
// values
|
||||
// NB: p.length changes.
|
||||
while (pos < p.length) {
|
||||
// find the next part
|
||||
nextPartRe.lastIndex = pos;
|
||||
var result = nextPartRe.exec(p);
|
||||
previous = current;
|
||||
current += result[0];
|
||||
base = previous + result[1];
|
||||
pos = nextPartRe.lastIndex;
|
||||
|
||||
// continue if not a symlink
|
||||
if (knownHard[base] || (cache && cache[base] === base)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var resolvedLink;
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
|
||||
// some known symbolic link. no need to stat again.
|
||||
resolvedLink = cache[base];
|
||||
} else {
|
||||
var stat = fs.lstatSync(base);
|
||||
if (!stat.isSymbolicLink()) {
|
||||
knownHard[base] = true;
|
||||
if (cache) cache[base] = base;
|
||||
continue;
|
||||
}
|
||||
|
||||
// read the link if it wasn't read before
|
||||
// dev/ino always return 0 on windows, so skip the check.
|
||||
var linkTarget = null;
|
||||
if (!isWindows) {
|
||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
||||
if (seenLinks.hasOwnProperty(id)) {
|
||||
linkTarget = seenLinks[id];
|
||||
}
|
||||
}
|
||||
if (linkTarget === null) {
|
||||
fs.statSync(base);
|
||||
linkTarget = fs.readlinkSync(base);
|
||||
}
|
||||
resolvedLink = pathModule.resolve(previous, linkTarget);
|
||||
// track this, if given a cache.
|
||||
if (cache) cache[base] = resolvedLink;
|
||||
if (!isWindows) seenLinks[id] = linkTarget;
|
||||
}
|
||||
|
||||
// resolve the link, then start over
|
||||
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
||||
start();
|
||||
}
|
||||
|
||||
if (cache) cache[original] = p;
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
|
||||
exports.realpath = function realpath(p, cache, cb) {
|
||||
if (typeof cb !== 'function') {
|
||||
cb = maybeCallback(cache);
|
||||
cache = null;
|
||||
}
|
||||
|
||||
// make p is absolute
|
||||
p = pathModule.resolve(p);
|
||||
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
|
||||
return process.nextTick(cb.bind(null, null, cache[p]));
|
||||
}
|
||||
|
||||
var original = p,
|
||||
seenLinks = {},
|
||||
knownHard = {};
|
||||
|
||||
// current character position in p
|
||||
var pos;
|
||||
// the partial path so far, including a trailing slash if any
|
||||
var current;
|
||||
// the partial path without a trailing slash (except when pointing at a root)
|
||||
var base;
|
||||
// the partial path scanned in the previous round, with slash
|
||||
var previous;
|
||||
|
||||
start();
|
||||
|
||||
function start() {
|
||||
// Skip over roots
|
||||
var m = splitRootRe.exec(p);
|
||||
pos = m[0].length;
|
||||
current = m[0];
|
||||
base = m[0];
|
||||
previous = '';
|
||||
|
||||
// On windows, check that the root exists. On unix there is no need.
|
||||
if (isWindows && !knownHard[base]) {
|
||||
fs.lstat(base, function(err) {
|
||||
if (err) return cb(err);
|
||||
knownHard[base] = true;
|
||||
LOOP();
|
||||
});
|
||||
} else {
|
||||
process.nextTick(LOOP);
|
||||
}
|
||||
}
|
||||
|
||||
// walk down the path, swapping out linked pathparts for their real
|
||||
// values
|
||||
function LOOP() {
|
||||
// stop if scanned past end of path
|
||||
if (pos >= p.length) {
|
||||
if (cache) cache[original] = p;
|
||||
return cb(null, p);
|
||||
}
|
||||
|
||||
// find the next part
|
||||
nextPartRe.lastIndex = pos;
|
||||
var result = nextPartRe.exec(p);
|
||||
previous = current;
|
||||
current += result[0];
|
||||
base = previous + result[1];
|
||||
pos = nextPartRe.lastIndex;
|
||||
|
||||
// continue if not a symlink
|
||||
if (knownHard[base] || (cache && cache[base] === base)) {
|
||||
return process.nextTick(LOOP);
|
||||
}
|
||||
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
|
||||
// known symbolic link. no need to stat again.
|
||||
return gotResolvedLink(cache[base]);
|
||||
}
|
||||
|
||||
return fs.lstat(base, gotStat);
|
||||
}
|
||||
|
||||
function gotStat(err, stat) {
|
||||
if (err) return cb(err);
|
||||
|
||||
// if not a symlink, skip to the next path part
|
||||
if (!stat.isSymbolicLink()) {
|
||||
knownHard[base] = true;
|
||||
if (cache) cache[base] = base;
|
||||
return process.nextTick(LOOP);
|
||||
}
|
||||
|
||||
// stat & read the link if not read before
|
||||
// call gotTarget as soon as the link target is known
|
||||
// dev/ino always return 0 on windows, so skip the check.
|
||||
if (!isWindows) {
|
||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
||||
if (seenLinks.hasOwnProperty(id)) {
|
||||
return gotTarget(null, seenLinks[id], base);
|
||||
}
|
||||
}
|
||||
fs.stat(base, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
fs.readlink(base, function(err, target) {
|
||||
if (!isWindows) seenLinks[id] = target;
|
||||
gotTarget(err, target);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function gotTarget(err, target, base) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var resolvedLink = pathModule.resolve(previous, target);
|
||||
if (cache) cache[base] = resolvedLink;
|
||||
gotResolvedLink(resolvedLink);
|
||||
}
|
||||
|
||||
function gotResolvedLink(resolvedLink) {
|
||||
// resolve the link, then start over
|
||||
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
||||
start();
|
||||
}
|
||||
};
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{ "name": "fs.realpath", "version": "1.0.0", "main": "index.js", "types": "index.d.ts", "license": "ISC" }
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
declare function truncate(string: string, byteLength: number): string;
|
||||
export = truncate;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var truncate = require("./lib/truncate");
|
||||
var getLength = Buffer.byteLength.bind(Buffer);
|
||||
module.exports = truncate.bind(null, getLength);
|
||||
Generated
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
function isHighSurrogate(codePoint) {
|
||||
return codePoint >= 0xd800 && codePoint <= 0xdbff;
|
||||
}
|
||||
|
||||
function isLowSurrogate(codePoint) {
|
||||
return codePoint >= 0xdc00 && codePoint <= 0xdfff;
|
||||
}
|
||||
|
||||
// Truncate string by size in bytes
|
||||
module.exports = function truncate(getLength, string, byteLength) {
|
||||
if (typeof string !== "string") {
|
||||
throw new Error("Input must be string");
|
||||
}
|
||||
|
||||
var charLength = string.length;
|
||||
var curByteLength = 0;
|
||||
var codePoint;
|
||||
var segment;
|
||||
|
||||
for (var i = 0; i < charLength; i += 1) {
|
||||
codePoint = string.charCodeAt(i);
|
||||
segment = string[i];
|
||||
|
||||
if (isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1))) {
|
||||
i += 1;
|
||||
segment += string[i];
|
||||
}
|
||||
|
||||
curByteLength += getLength(segment);
|
||||
|
||||
if (curByteLength === byteLength) {
|
||||
return string.slice(0, i + 1);
|
||||
}
|
||||
else if (curByteLength > byteLength) {
|
||||
return string.slice(0, i - segment.length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
};
|
||||
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "truncate-utf8-bytes",
|
||||
"version": "1.0.2",
|
||||
"description": "Truncate string to given length in bytes",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"license": "WTFPL"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare function report(): unknown[];
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Builtin functions read as values: tar-stream's `var alloc = Buffer.alloc`,
|
||||
// fs.realpathSync stored and compared, and smol-toml's `''.isWellFormed`
|
||||
// feature probe.
|
||||
'use strict';
|
||||
var fs = require('fs');
|
||||
var alloc = Buffer.alloc;
|
||||
var origRealpathSync = fs.realpathSync;
|
||||
var HAS_WELLFORMED = !!''.isWellFormed;
|
||||
|
||||
exports.report = function report() {
|
||||
var header = alloc(8);
|
||||
var filled = alloc(5, 'ab');
|
||||
var numbered = alloc(3, 7);
|
||||
var out = [];
|
||||
out.push(header.length, header[0], filled.toString(), numbered[2], typeof alloc, alloc.length);
|
||||
try {
|
||||
alloc('8');
|
||||
} catch (error) {
|
||||
out.push(error.code);
|
||||
}
|
||||
out.push(origRealpathSync('/'), origRealpathSync === fs.realpathSync, typeof fs.realpathSync.native);
|
||||
out.push(Buffer.isBuffer(origRealpathSync('/', 'buffer')), origRealpathSync('/', { encoding: 'hex' }));
|
||||
out.push(fs.realpathSync.native('/'));
|
||||
try {
|
||||
origRealpathSync('/definitely/missing/path');
|
||||
} catch (error) {
|
||||
out.push(error.code);
|
||||
}
|
||||
out.push(HAS_WELLFORMED, 'ok'.isWellFormed(), typeof ''.toUpperCase);
|
||||
return out;
|
||||
};
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{ "name": "value-forms", "version": "1.0.0", "main": "index.js", "types": "index.d.ts" }
|
||||
@@ -12,11 +12,4 @@ json-dyn.ts:46:7 - error SC2007: values of type '{ (text: string, reviver?: ((th
|
||||
| ^~~~~~~~
|
||||
47 | // The exact `string | undefined` root now lowers: strings serialize while
|
||||
|
||||
hint: annotate the slot with the ONE signature the program actually calls (e.g. '(x: number) => string'), or wrap the overloaded function in a single-signature arrow
|
||||
|
||||
json-dyn.ts:46:18 - error SC1090: JSON methods as values (call 'parse' directly) are not supported yet
|
||||
|
||||
45 | const dynArray: unknown[] = []; // unknown[] IS the dyn array now — no fence (corpus 2585)
|
||||
46 | const parseRef = JSON.parse;
|
||||
| ^~~~~~~~~~
|
||||
47 | // The exact `string | undefined` root now lowers: strings serialize while
|
||||
hint: annotate the slot with the ONE signature the program actually calls (e.g. '(x: number) => string'), or wrap the overloaded function in a single-signature arrow
|
||||
Reference in New Issue
Block a user