mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 23:34:47 -05:00
reorg: remove dispatch.ts, move signals, factor out web utils (#4316)
- moves signal definition from "cli/js/process.ts" to "cli/js/signals.ts" - removes "cli/js/dispatch.ts" - removes "cli/js/types.ts" - moves web specific utilities to "cli/js/web/util.ts"
This commit is contained in:
parent
99a0c6df79
commit
2d1b39bef3
20 changed files with 188 additions and 199 deletions
|
@ -99,7 +99,7 @@ export {
|
|||
} from "./permissions.ts";
|
||||
export { openPlugin } from "./plugins.ts";
|
||||
export { kill } from "./ops/process.ts";
|
||||
export { run, RunOptions, Process, ProcessStatus, Signal } from "./process.ts";
|
||||
export { run, RunOptions, Process, ProcessStatus } from "./process.ts";
|
||||
export { readdirSync, readdir } from "./ops/fs/read_dir.ts";
|
||||
export { readFileSync, readFile } from "./read_file.ts";
|
||||
export { readlinkSync, readlink } from "./ops/fs/read_link.ts";
|
||||
|
@ -107,7 +107,7 @@ export { realpathSync, realpath } from "./ops/fs/realpath.ts";
|
|||
export { removeSync, remove, RemoveOptions } from "./ops/fs/remove.ts";
|
||||
export { renameSync, rename } from "./ops/fs/rename.ts";
|
||||
export { resources, close } from "./ops/resources.ts";
|
||||
export { signal, signals, SignalStream } from "./signals.ts";
|
||||
export { signal, signals, Signal, SignalStream } from "./signals.ts";
|
||||
export { statSync, lstatSync, stat, lstat } from "./ops/fs/stat.ts";
|
||||
export { symlinkSync, symlink } from "./ops/fs/symlink.ts";
|
||||
export { connectTLS, listenTLS } from "./tls.ts";
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import * as minimal from "./ops/dispatch_minimal.ts";
|
||||
import * as json from "./ops/dispatch_json.ts";
|
||||
import { AsyncHandler } from "./plugins.ts";
|
||||
|
||||
const PLUGIN_ASYNC_HANDLER_MAP: Map<number, AsyncHandler> = new Map();
|
||||
|
||||
export function setPluginAsyncHandler(
|
||||
opId: number,
|
||||
handler: AsyncHandler
|
||||
): void {
|
||||
PLUGIN_ASYNC_HANDLER_MAP.set(opId, handler);
|
||||
}
|
||||
|
||||
export function getAsyncHandler(opName: string): (msg: Uint8Array) => void {
|
||||
switch (opName) {
|
||||
case "op_write":
|
||||
case "op_read":
|
||||
return minimal.asyncMsgFromRust;
|
||||
default:
|
||||
return json.asyncMsgFromRust;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
import { errors } from "../errors.ts";
|
||||
import * as util from "../util.ts";
|
||||
|
||||
/** Get the loadavg.
|
||||
* Requires the `--allow-env` flag.
|
||||
|
@ -33,7 +32,7 @@ export function osRelease(): string {
|
|||
/** Exit the Deno process with optional exit code. */
|
||||
export function exit(code = 0): never {
|
||||
sendSync("op_exit", { code });
|
||||
return util.unreachable();
|
||||
throw new Error("Code not reachable");
|
||||
}
|
||||
|
||||
function setEnv(key: string, value: string): void {
|
||||
|
|
|
@ -3,7 +3,6 @@ import { File } from "./files.ts";
|
|||
import { close } from "./ops/resources.ts";
|
||||
import { ReadCloser, WriteCloser } from "./io.ts";
|
||||
import { readAll } from "./buffer.ts";
|
||||
import { build } from "./build.ts";
|
||||
import { kill, runStatus as runStatusOp, run as runOp } from "./ops/process.ts";
|
||||
|
||||
/** How to handle subprocess stdio.
|
||||
|
@ -161,85 +160,3 @@ export function run({
|
|||
}) as RunResponse;
|
||||
return new Process(res);
|
||||
}
|
||||
|
||||
// From `kill -l`
|
||||
enum LinuxSignal {
|
||||
SIGHUP = 1,
|
||||
SIGINT = 2,
|
||||
SIGQUIT = 3,
|
||||
SIGILL = 4,
|
||||
SIGTRAP = 5,
|
||||
SIGABRT = 6,
|
||||
SIGBUS = 7,
|
||||
SIGFPE = 8,
|
||||
SIGKILL = 9,
|
||||
SIGUSR1 = 10,
|
||||
SIGSEGV = 11,
|
||||
SIGUSR2 = 12,
|
||||
SIGPIPE = 13,
|
||||
SIGALRM = 14,
|
||||
SIGTERM = 15,
|
||||
SIGSTKFLT = 16,
|
||||
SIGCHLD = 17,
|
||||
SIGCONT = 18,
|
||||
SIGSTOP = 19,
|
||||
SIGTSTP = 20,
|
||||
SIGTTIN = 21,
|
||||
SIGTTOU = 22,
|
||||
SIGURG = 23,
|
||||
SIGXCPU = 24,
|
||||
SIGXFSZ = 25,
|
||||
SIGVTALRM = 26,
|
||||
SIGPROF = 27,
|
||||
SIGWINCH = 28,
|
||||
SIGIO = 29,
|
||||
SIGPWR = 30,
|
||||
SIGSYS = 31
|
||||
}
|
||||
|
||||
// From `kill -l`
|
||||
enum MacOSSignal {
|
||||
SIGHUP = 1,
|
||||
SIGINT = 2,
|
||||
SIGQUIT = 3,
|
||||
SIGILL = 4,
|
||||
SIGTRAP = 5,
|
||||
SIGABRT = 6,
|
||||
SIGEMT = 7,
|
||||
SIGFPE = 8,
|
||||
SIGKILL = 9,
|
||||
SIGBUS = 10,
|
||||
SIGSEGV = 11,
|
||||
SIGSYS = 12,
|
||||
SIGPIPE = 13,
|
||||
SIGALRM = 14,
|
||||
SIGTERM = 15,
|
||||
SIGURG = 16,
|
||||
SIGSTOP = 17,
|
||||
SIGTSTP = 18,
|
||||
SIGCONT = 19,
|
||||
SIGCHLD = 20,
|
||||
SIGTTIN = 21,
|
||||
SIGTTOU = 22,
|
||||
SIGIO = 23,
|
||||
SIGXCPU = 24,
|
||||
SIGXFSZ = 25,
|
||||
SIGVTALRM = 26,
|
||||
SIGPROF = 27,
|
||||
SIGWINCH = 28,
|
||||
SIGINFO = 29,
|
||||
SIGUSR1 = 30,
|
||||
SIGUSR2 = 31
|
||||
}
|
||||
|
||||
/** Signals numbers. This is platform dependent.
|
||||
*/
|
||||
export const Signal: { [key: string]: number } = {};
|
||||
|
||||
export function setSignals(): void {
|
||||
if (build.os === "mac") {
|
||||
Object.assign(Signal, MacOSSignal);
|
||||
} else {
|
||||
Object.assign(Signal, LinuxSignal);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { core } from "./core.ts";
|
||||
import * as dispatch from "./dispatch.ts";
|
||||
import * as dispatchMinimal from "./ops/dispatch_minimal.ts";
|
||||
import * as dispatchJson from "./ops/dispatch_json.ts";
|
||||
import { assert } from "./util.ts";
|
||||
import * as util from "./util.ts";
|
||||
import { setBuildInfo } from "./build.ts";
|
||||
|
@ -11,12 +12,22 @@ import { Start, start as startOp } from "./ops/runtime.ts";
|
|||
|
||||
export let OPS_CACHE: { [name: string]: number };
|
||||
|
||||
function getAsyncHandler(opName: string): (msg: Uint8Array) => void {
|
||||
switch (opName) {
|
||||
case "op_write":
|
||||
case "op_read":
|
||||
return dispatchMinimal.asyncMsgFromRust;
|
||||
default:
|
||||
return dispatchJson.asyncMsgFromRust;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bartlomieju): temporary solution, must be fixed when moving
|
||||
// dispatches to separate crates
|
||||
export function initOps(): void {
|
||||
OPS_CACHE = core.ops();
|
||||
for (const [name, opId] of Object.entries(OPS_CACHE)) {
|
||||
core.setAsyncHandler(opId, dispatch.getAsyncHandler(name));
|
||||
core.setAsyncHandler(opId, getAsyncHandler(name));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
eventTargetProperties
|
||||
} from "./globals.ts";
|
||||
import { internalObject } from "./internals.ts";
|
||||
import { setSignals } from "./process.ts";
|
||||
import { setSignals } from "./signals.ts";
|
||||
import { replLoop } from "./repl.ts";
|
||||
import * as runtime from "./runtime.ts";
|
||||
import { symbols } from "./symbols.ts";
|
||||
|
|
|
@ -1,8 +1,89 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { Signal } from "./process.ts";
|
||||
import { bindSignal, pollSignal, unbindSignal } from "./ops/signal.ts";
|
||||
import { build } from "./build.ts";
|
||||
|
||||
// From `kill -l`
|
||||
enum LinuxSignal {
|
||||
SIGHUP = 1,
|
||||
SIGINT = 2,
|
||||
SIGQUIT = 3,
|
||||
SIGILL = 4,
|
||||
SIGTRAP = 5,
|
||||
SIGABRT = 6,
|
||||
SIGBUS = 7,
|
||||
SIGFPE = 8,
|
||||
SIGKILL = 9,
|
||||
SIGUSR1 = 10,
|
||||
SIGSEGV = 11,
|
||||
SIGUSR2 = 12,
|
||||
SIGPIPE = 13,
|
||||
SIGALRM = 14,
|
||||
SIGTERM = 15,
|
||||
SIGSTKFLT = 16,
|
||||
SIGCHLD = 17,
|
||||
SIGCONT = 18,
|
||||
SIGSTOP = 19,
|
||||
SIGTSTP = 20,
|
||||
SIGTTIN = 21,
|
||||
SIGTTOU = 22,
|
||||
SIGURG = 23,
|
||||
SIGXCPU = 24,
|
||||
SIGXFSZ = 25,
|
||||
SIGVTALRM = 26,
|
||||
SIGPROF = 27,
|
||||
SIGWINCH = 28,
|
||||
SIGIO = 29,
|
||||
SIGPWR = 30,
|
||||
SIGSYS = 31
|
||||
}
|
||||
|
||||
// From `kill -l`
|
||||
enum MacOSSignal {
|
||||
SIGHUP = 1,
|
||||
SIGINT = 2,
|
||||
SIGQUIT = 3,
|
||||
SIGILL = 4,
|
||||
SIGTRAP = 5,
|
||||
SIGABRT = 6,
|
||||
SIGEMT = 7,
|
||||
SIGFPE = 8,
|
||||
SIGKILL = 9,
|
||||
SIGBUS = 10,
|
||||
SIGSEGV = 11,
|
||||
SIGSYS = 12,
|
||||
SIGPIPE = 13,
|
||||
SIGALRM = 14,
|
||||
SIGTERM = 15,
|
||||
SIGURG = 16,
|
||||
SIGSTOP = 17,
|
||||
SIGTSTP = 18,
|
||||
SIGCONT = 19,
|
||||
SIGCHLD = 20,
|
||||
SIGTTIN = 21,
|
||||
SIGTTOU = 22,
|
||||
SIGIO = 23,
|
||||
SIGXCPU = 24,
|
||||
SIGXFSZ = 25,
|
||||
SIGVTALRM = 26,
|
||||
SIGPROF = 27,
|
||||
SIGWINCH = 28,
|
||||
SIGINFO = 29,
|
||||
SIGUSR1 = 30,
|
||||
SIGUSR2 = 31
|
||||
}
|
||||
|
||||
/** Signals numbers. This is platform dependent.
|
||||
*/
|
||||
export const Signal: { [key: string]: number } = {};
|
||||
|
||||
export function setSignals(): void {
|
||||
if (build.os === "mac") {
|
||||
Object.assign(Signal, MacOSSignal);
|
||||
} else {
|
||||
Object.assign(Signal, LinuxSignal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stream of the given signal number. You can use it as an async
|
||||
* iterator.
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
export type TypedArray = Uint8Array | Float32Array | Int32Array;
|
|
@ -1,5 +1,4 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { TypedArray } from "./types.ts";
|
||||
|
||||
let logDebug = false;
|
||||
let logSource = "JS";
|
||||
|
@ -80,30 +79,6 @@ export function notImplemented(): never {
|
|||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function unreachable(): never {
|
||||
throw new Error("Code not reachable");
|
||||
}
|
||||
|
||||
const TypedArrayConstructor = Object.getPrototypeOf(Uint8Array);
|
||||
export function isTypedArray(x: unknown): x is TypedArray {
|
||||
return x instanceof TypedArrayConstructor;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function requiredArguments(
|
||||
name: string,
|
||||
length: number,
|
||||
required: number
|
||||
): void {
|
||||
if (length < required) {
|
||||
const errMsg = `${name} requires at least ${required} argument${
|
||||
required === 1 ? "" : "s"
|
||||
}, but only ${length} present`;
|
||||
throw new TypeError(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function immutableDefine(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -118,45 +93,3 @@ export function immutableDefine(
|
|||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
// Returns values from a WeakMap to emulate private properties in JavaScript
|
||||
export function getPrivateValue<
|
||||
K extends object,
|
||||
V extends object,
|
||||
W extends keyof V
|
||||
>(instance: K, weakMap: WeakMap<K, V>, key: W): V[W] {
|
||||
if (weakMap.has(instance)) {
|
||||
return weakMap.get(instance)![key];
|
||||
}
|
||||
throw new TypeError("Illegal invocation");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an object has a property with the specified name.
|
||||
* Avoid calling prototype builtin `hasOwnProperty` for two reasons:
|
||||
*
|
||||
* 1. `hasOwnProperty` is defined on the object as something else:
|
||||
*
|
||||
* const options = {
|
||||
* ending: 'utf8',
|
||||
* hasOwnProperty: 'foo'
|
||||
* };
|
||||
* options.hasOwnProperty('ending') // throws a TypeError
|
||||
*
|
||||
* 2. The object doesn't inherit from `Object.prototype`:
|
||||
*
|
||||
* const options = Object.create(null);
|
||||
* options.ending = 'utf8';
|
||||
* options.hasOwnProperty('ending'); // throws a TypeError
|
||||
*
|
||||
* @param obj A Object.
|
||||
* @param v A property name.
|
||||
* @see https://eslint.org/docs/rules/no-prototype-builtins
|
||||
* @internal
|
||||
*/
|
||||
export function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
return Object.prototype.hasOwnProperty.call(obj, v);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { isTypedArray } from "../util.ts";
|
||||
import { TypedArray } from "../types.ts";
|
||||
import { isTypedArray, TypedArray } from "./util.ts";
|
||||
import { TextEncoder } from "./text_encoding.ts";
|
||||
import { File, stdout } from "../files.ts";
|
||||
import { cliTable } from "./console_table.ts";
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Forked from Node's lib/internal/cli_table.js
|
||||
|
||||
import { TextEncoder } from "./text_encoding.ts";
|
||||
import { hasOwnProperty } from "../util.ts";
|
||||
import { hasOwnProperty } from "./util.ts";
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import * as domTypes from "./dom_types.ts";
|
||||
import * as event from "./event.ts";
|
||||
import { getPrivateValue, requiredArguments } from "../util.ts";
|
||||
import { getPrivateValue, requiredArguments } from "./util.ts";
|
||||
|
||||
// WeakMaps are recommended for private attributes (see MDN link below)
|
||||
// https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Add-on_SDK/Guides/Contributor_s_Guide/Private_Properties#Using_WeakMaps
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { DomIterable } from "./dom_types.ts";
|
||||
import { requiredArguments } from "../util.ts";
|
||||
import { requiredArguments } from "./util.ts";
|
||||
import { exposeForTest } from "../internals.ts";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import * as domTypes from "./dom_types.ts";
|
||||
import { getPrivateValue, requiredArguments } from "../util.ts";
|
||||
import { getPrivateValue, requiredArguments } from "./util.ts";
|
||||
|
||||
// WeakMaps are recommended for private attributes (see MDN link below)
|
||||
// https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Add-on_SDK/Guides/Contributor_s_Guide/Private_Properties#Using_WeakMaps
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import * as domTypes from "./dom_types.ts";
|
||||
import { hasOwnProperty, requiredArguments } from "../util.ts";
|
||||
import { hasOwnProperty, requiredArguments } from "./util.ts";
|
||||
import {
|
||||
getRoot,
|
||||
isNode,
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import {
|
||||
assert,
|
||||
createResolvable,
|
||||
notImplemented,
|
||||
isTypedArray
|
||||
} from "../util.ts";
|
||||
import { assert, createResolvable, notImplemented } from "../util.ts";
|
||||
import { isTypedArray } from "./util.ts";
|
||||
import * as domTypes from "./dom_types.ts";
|
||||
import { TextDecoder, TextEncoder } from "./text_encoding.ts";
|
||||
import { DenoBlob, bytesSymbol as blobBytesSymbol } from "./blob.ts";
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as domTypes from "./dom_types.ts";
|
|||
import * as blob from "./blob.ts";
|
||||
import * as domFile from "./dom_file.ts";
|
||||
import { DomIterableMixin } from "./dom_iterable.ts";
|
||||
import { requiredArguments } from "../util.ts";
|
||||
import { requiredArguments } from "./util.ts";
|
||||
|
||||
const dataSymbol = Symbol("data");
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import * as domTypes from "./dom_types.ts";
|
||||
import { DomIterableMixin } from "./dom_iterable.ts";
|
||||
import { requiredArguments } from "../util.ts";
|
||||
import { requiredArguments } from "./util.ts";
|
||||
import { customInspect } from "./console.ts";
|
||||
|
||||
// From node-fetch
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { URL } from "./url.ts";
|
||||
import { requiredArguments } from "../util.ts";
|
||||
import { requiredArguments } from "./util.ts";
|
||||
|
||||
// Returns whether o is iterable.
|
||||
// @internal
|
||||
|
|
78
cli/js/web/util.ts
Normal file
78
cli/js/web/util.ts
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
export type TypedArray = Uint8Array | Float32Array | Int32Array;
|
||||
const TypedArrayConstructor = Object.getPrototypeOf(Uint8Array);
|
||||
export function isTypedArray(x: unknown): x is TypedArray {
|
||||
return x instanceof TypedArrayConstructor;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function requiredArguments(
|
||||
name: string,
|
||||
length: number,
|
||||
required: number
|
||||
): void {
|
||||
if (length < required) {
|
||||
const errMsg = `${name} requires at least ${required} argument${
|
||||
required === 1 ? "" : "s"
|
||||
}, but only ${length} present`;
|
||||
throw new TypeError(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function immutableDefine(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
o: any,
|
||||
p: string | number | symbol,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any
|
||||
): void {
|
||||
Object.defineProperty(o, p, {
|
||||
value,
|
||||
configurable: false,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
// Returns values from a WeakMap to emulate private properties in JavaScript
|
||||
export function getPrivateValue<
|
||||
K extends object,
|
||||
V extends object,
|
||||
W extends keyof V
|
||||
>(instance: K, weakMap: WeakMap<K, V>, key: W): V[W] {
|
||||
if (weakMap.has(instance)) {
|
||||
return weakMap.get(instance)![key];
|
||||
}
|
||||
throw new TypeError("Illegal invocation");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an object has a property with the specified name.
|
||||
* Avoid calling prototype builtin `hasOwnProperty` for two reasons:
|
||||
*
|
||||
* 1. `hasOwnProperty` is defined on the object as something else:
|
||||
*
|
||||
* const options = {
|
||||
* ending: 'utf8',
|
||||
* hasOwnProperty: 'foo'
|
||||
* };
|
||||
* options.hasOwnProperty('ending') // throws a TypeError
|
||||
*
|
||||
* 2. The object doesn't inherit from `Object.prototype`:
|
||||
*
|
||||
* const options = Object.create(null);
|
||||
* options.ending = 'utf8';
|
||||
* options.hasOwnProperty('ending'); // throws a TypeError
|
||||
*
|
||||
* @param obj A Object.
|
||||
* @param v A property name.
|
||||
* @see https://eslint.org/docs/rules/no-prototype-builtins
|
||||
* @internal
|
||||
*/
|
||||
export function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
return Object.prototype.hasOwnProperty.call(obj, v);
|
||||
}
|
Loading…
Reference in a new issue