Skip to content

Commit

Permalink
avoid prototype builtin hasOwnProperty (denoland#2144)
Browse files Browse the repository at this point in the history
  • Loading branch information
justjavac authored and ry committed Apr 19, 2019
1 parent 2be7e44 commit d0cd7a3
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 7 deletions.
4 changes: 2 additions & 2 deletions 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");
Expand Down Expand Up @@ -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";
}

Expand Down
18 changes: 18 additions & 0 deletions js/blob_test.ts
Expand Up @@ -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.
3 changes: 2 additions & 1 deletion js/console_table.ts
Expand Up @@ -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();

Expand Down Expand Up @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions 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.
Expand All @@ -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) {
Expand All @@ -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
);
Expand All @@ -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();
Expand Down
18 changes: 18 additions & 0 deletions js/event_target_test.ts
Expand Up @@ -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);
});
30 changes: 30 additions & 0 deletions js/util.ts
Expand Up @@ -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);
}

0 comments on commit d0cd7a3

Please sign in to comment.