summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author迷渡 <justjavac@gmail.com>2019-04-19 09:56:33 +0800
committerRyan Dahl <ry@tinyclouds.org>2019-04-18 21:56:33 -0400
commitd0cd7a39a2d05343c5501dc286bb59096659654f (patch)
treeadffb215b15e8ca3411db732f5604158e5ab9640
parent2be7e4440339f616a37b9535cc38936f80efc0e1 (diff)
avoid prototype builtin hasOwnProperty (#2144)
-rw-r--r--js/blob.ts4
-rw-r--r--js/blob_test.ts18
-rw-r--r--js/console_table.ts3
-rw-r--r--js/event_target.ts8
-rw-r--r--js/event_target_test.ts18
-rw-r--r--js/util.ts30
6 files changed, 74 insertions, 7 deletions
diff --git a/js/blob.ts b/js/blob.ts
index c23b74afb..092bfaa08 100644
--- a/js/blob.ts
+++ b/js/blob.ts
@@ -1,6 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import * as domTypes from "./dom_types";
-import { containsOnlyASCII } from "./util";
+import { containsOnlyASCII, hasOwnProperty } from "./util";
import { TextEncoder } from "./text_encoding";
export const bytesSymbol = Symbol("bytes");
@@ -91,7 +91,7 @@ export class DenoBlob implements domTypes.Blob {
options = options || {};
// Set ending property's default value to "transparent".
- if (!options.hasOwnProperty("ending")) {
+ if (!hasOwnProperty(options, "ending")) {
options.ending = "transparent";
}
diff --git a/js/blob_test.ts b/js/blob_test.ts
index 1e8ec8fbd..3c778a0e1 100644
--- a/js/blob_test.ts
+++ b/js/blob_test.ts
@@ -32,4 +32,22 @@ test(function blobSlice() {
assertEquals(b4.size, blob.size);
});
+test(function blobShouldNotThrowError() {
+ let hasThrown = false;
+
+ try {
+ const options1: object = {
+ ending: "utf8",
+ hasOwnProperty: "hasOwnProperty"
+ };
+ const options2: object = Object.create(null);
+ new Blob(["Hello World"], options1);
+ new Blob(["Hello World"], options2);
+ } catch {
+ hasThrown = true;
+ }
+
+ assertEquals(hasThrown, false);
+});
+
// TODO(qti3e) Test the stored data in a Blob after implementing FileReader API.
diff --git a/js/console_table.ts b/js/console_table.ts
index d7cae124c..43819c5fe 100644
--- a/js/console_table.ts
+++ b/js/console_table.ts
@@ -2,6 +2,7 @@
// Forked from Node's lib/internal/cli_table.js
import { TextEncoder } from "./text_encoding";
+import { hasOwnProperty } from "./util";
const encoder = new TextEncoder();
@@ -64,7 +65,7 @@ export function cliTable(head: string[], columns: string[][]): string {
if (rows[j] === undefined) {
rows[j] = [];
}
- const value = (rows[j][i] = column.hasOwnProperty(j) ? column[j] : "");
+ const value = (rows[j][i] = hasOwnProperty(column, j) ? column[j] : "");
const width = columnWidths[i] || 0;
const counted = countBytes(value);
columnWidths[i] = Math.max(width, counted);
diff --git a/js/event_target.ts b/js/event_target.ts
index cc436865e..87268383e 100644
--- a/js/event_target.ts
+++ b/js/event_target.ts
@@ -1,6 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import * as domTypes from "./dom_types";
-import { requiredArguments } from "./util";
+import { requiredArguments, hasOwnProperty } from "./util";
/* TODO: This is an incomplete implementation to provide functionality
* for Event. A proper spec is still required for a proper Web API.
@@ -16,7 +16,7 @@ export class EventTarget implements domTypes.EventTarget {
_options?: boolean | domTypes.AddEventListenerOptions
): void {
requiredArguments("EventTarget.addEventListener", arguments.length, 2);
- if (!this.listeners.hasOwnProperty(type)) {
+ if (!hasOwnProperty(this.listeners, type)) {
this.listeners[type] = [];
}
if (listener !== null) {
@@ -30,7 +30,7 @@ export class EventTarget implements domTypes.EventTarget {
_options?: domTypes.EventListenerOptions | boolean
): void {
requiredArguments("EventTarget.removeEventListener", arguments.length, 2);
- if (this.listeners.hasOwnProperty(type) && callback !== null) {
+ if (hasOwnProperty(this.listeners, type) && callback !== null) {
this.listeners[type] = this.listeners[type].filter(
listener => listener !== callback
);
@@ -39,7 +39,7 @@ export class EventTarget implements domTypes.EventTarget {
public dispatchEvent(event: domTypes.Event): boolean {
requiredArguments("EventTarget.dispatchEvent", arguments.length, 1);
- if (!this.listeners.hasOwnProperty(event.type)) {
+ if (!hasOwnProperty(this.listeners, event.type)) {
return true;
}
const stack = this.listeners[event.type].slice();
diff --git a/js/event_target_test.ts b/js/event_target_test.ts
index 71c872dab..aedbbf72a 100644
--- a/js/event_target_test.ts
+++ b/js/event_target_test.ts
@@ -92,3 +92,21 @@ test(function toStringShouldBeWebCompatibility() {
const target = new EventTarget();
assertEquals(target.toString(), "[object EventTarget]");
});
+
+test(function dispatchEventShouldNotThrowError() {
+ let hasThrown = false;
+
+ try {
+ const target = new EventTarget();
+ const event = new Event("hasOwnProperty", {
+ bubbles: true,
+ cancelable: false
+ });
+ target.addEventListener("hasOwnProperty", () => {});
+ target.dispatchEvent(event);
+ } catch {
+ hasThrown = true;
+ }
+
+ assertEquals(hasThrown, false);
+});
diff --git a/js/util.ts b/js/util.ts
index e6f38c8e1..b81b96aca 100644
--- a/js/util.ts
+++ b/js/util.ts
@@ -142,3 +142,33 @@ export function getPrivateValue<
}
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);
+}