Skip to content

Commit

Permalink
add Object.hasOwn (#1052) (#1157)
Browse files Browse the repository at this point in the history
This is a new, experimental JavaScript feature.
  • Loading branch information
naijun0403 authored Jan 27, 2022
1 parent 743d21c commit f7b16e9
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 12 deletions.
26 changes: 26 additions & 0 deletions src/org/mozilla/javascript/AbstractEcmaObjectOperations.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,32 @@ enum INTEGRITY_LEVEL {
SEALED
}

/**
* Implementation of Abstract Object operation HasOwnProperty as defined by EcmaScript
*
* @param cx
* @param o
* @param property
* @return boolean
* @see <a href="https://262.ecma-international.org/12.0/#sec-hasownproperty"></a>
*/
static boolean hasOwnProperty(Context cx, Object o, Object property) {
ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);
boolean result;
if (property instanceof Symbol) {
result = ScriptableObject.ensureSymbolScriptable(o).has((Symbol) property, obj);
} else {
ScriptRuntime.StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, property);
if (s.stringId == null) {
result = obj.has(s.index, obj);
} else {
result = obj.has(s.stringId, obj);
}
}

return result;
}

/**
* Implementation of Abstract Object operation testIntegrityLevel as defined by EcmaScript
*
Expand Down
23 changes: 11 additions & 12 deletions src/org/mozilla/javascript/NativeObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ protected void fillConstructorProperties(IdFunctionObject ctor) {
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_entries, "entries", 1);
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_fromEntries, "fromEntries", 1);
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_values, "values", 1);
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_hasOwn, "hasOwn", 1);
}
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_keys, "keys", 1);
addIdFunctionProperty(
Expand Down Expand Up @@ -203,19 +204,10 @@ public Object execIdCall(
throw ScriptRuntime.typeErrorById(
"msg." + (thisObj == null ? "null" : "undef") + ".to.object");
}
boolean result;

Object arg = args.length < 1 ? Undefined.instance : args[0];
if (arg instanceof Symbol) {
result = ensureSymbolScriptable(thisObj).has((Symbol) arg, thisObj);
} else {
StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg);
if (s.stringId == null) {
result = thisObj.has(s.index, thisObj);
} else {
result = thisObj.has(s.stringId, thisObj);
}
}
return ScriptRuntime.wrapBoolean(result);

return AbstractEcmaObjectOperations.hasOwnProperty(cx, thisObj, arg);
}

case Id_propertyIsEnumerable:
Expand Down Expand Up @@ -472,6 +464,12 @@ public Object execIdCall(
}
return cx.newArray(scope, ids);
}
case ConstructorId_hasOwn:
{
Object arg = args.length < 1 ? Undefined.instance : args[0];
Object propertyName = args.length < 2 ? Undefined.instance : args[1];
return AbstractEcmaObjectOperations.hasOwnProperty(cx, arg, propertyName);
}
case ConstructorId_getOwnPropertyNames:
{
Object arg = args.length < 1 ? Undefined.instance : args[0];
Expand Down Expand Up @@ -998,6 +996,7 @@ protected int findPrototypeId(String s) {
ConstructorId_entries = -18,
ConstructorId_fromEntries = -19,
ConstructorId_values = -20,
ConstructorId_hasOwn = -21,
Id_constructor = 1,
Id_toString = 2,
Id_toLocaleString = 3,
Expand Down
150 changes: 150 additions & 0 deletions testsrc/org/mozilla/javascript/tests/es2022/NativeObjectTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/** Test for the Object.hasOwn */
package org.mozilla.javascript.tests.es2022;

import static org.junit.Assert.assertEquals;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptableObject;

public class NativeObjectTest {

private Context cx;
private ScriptableObject scope;

@Before
public void setUp() {
cx = Context.enter();
cx.setLanguageVersion(Context.VERSION_ES6);
scope = cx.initStandardObjects();
}

@After
public void tearDown() {
Context.exit();
}

@Test
public void testHasStringOwn() {
Object result =
cx.evaluateString(
scope,
"let result = Object.hasOwn({ test: '123' }, 'test');\n"
+ "'result = ' + result",
"test",
1,
null);

assertEquals("result = true", result);
}

@Test
public void testHasUndefinedOwn() {
Object result =
cx.evaluateString(
scope,
"let result = Object.hasOwn({ test: undefined }, 'test');\n"
+ "'result = ' + result;",
"test",
1,
null);

assertEquals("result = true", result);
}

@Test
public void testHasNullOwn() {
Object result =
cx.evaluateString(
scope,
"let result = Object.hasOwn({ test: null }, 'test');\n"
+ "'result = ' + result;",
"test",
1,
null);

assertEquals("result = true", result);
}

@Test
public void testHasArrayPropertyOwn() {
Object result =
cx.evaluateString(
scope,
"let dessert = [\"cake\", \"coffee\", \"chocolate\"];\n"
+ "let result = Object.hasOwn(dessert, 2);\n"
+ "'result = ' + result;",
"test",
1,
null);

assertEquals("result = true", result);
}

@Test
public void testHasNoOwn() {
Object result =
cx.evaluateString(
scope,
"let result = Object.hasOwn({ cake: 123 }, 'test');\n"
+ "'result = ' + result",
"test",
1,
null);

assertEquals("result = false", result);
}

@Test
public void testCreateHasOwn() {
Object result =
cx.evaluateString(
scope,
"var foo = Object.create(null);\n"
+ "foo.prop = 'test';\n"
+ "var result = Object.hasOwn(foo, 'prop');\n"
+ "'result = ' + result;",
"test",
1,
null);

assertEquals("result = true", result);
}

@Test
public void testCreateNoHasOwn() {
Object result =
cx.evaluateString(
scope,
"var result = Object.hasOwn(Object.create({ q: 321 }), 'q');\n"
+ "'result = ' + result; ",
"test",
1,
null);

assertEquals("result = false", result);
}

@Test
public void testCalledTest() {
Object result =
cx.evaluateString(
scope,
"var called = false;\n"
+ "try {\n"
+ " Object.hasOwn(null, { toString() { called = true } });\n"
+ "} catch (e) {}\n"
+ "'called = ' + called;",
"test",
1,
null);

assertEquals("called = false", result);
}
}

0 comments on commit f7b16e9

Please sign in to comment.