Modified: trunk/Source/_javascript_Core/API/glib/JSCValue.cpp (230703 => 230704)
--- trunk/Source/_javascript_Core/API/glib/JSCValue.cpp 2018-04-17 07:33:57 UTC (rev 230703)
+++ trunk/Source/_javascript_Core/API/glib/JSCValue.cpp 2018-04-17 08:15:28 UTC (rev 230704)
@@ -673,7 +673,6 @@
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
-
JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
JSValueRef result = JSObjectGetProperty(jsContext, object, propertyName.get(), &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
@@ -726,7 +725,6 @@
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
-
JSValueRef result = JSObjectGetPropertyAtIndex(jsContext, object, index, &exception);
if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
return jsc_value_new_undefined(priv->context.get());
@@ -734,6 +732,105 @@
return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
}
+/**
+ * jsc_value_object_has_property:
+ * @value: a #JSCValue
+ * @name: the property name
+ *
+ * Get whether @value has property with @name.
+ *
+ * Returns: %TRUE if @value has a property with @name, or %FALSE otherwise
+ */
+gboolean jsc_value_object_has_property(JSCValue* value, const char* name)
+{
+ g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
+ g_return_val_if_fail(name, FALSE);
+
+ JSCValuePrivate* priv = value->priv;
+ auto* jsContext = jscContextGetJSContext(priv->context.get());
+ JSValueRef exception = nullptr;
+ JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
+ if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+ return FALSE;
+
+ JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
+ return JSObjectHasProperty(jsContext, object, propertyName.get());
+}
+
+/**
+ * jsc_value_object_delete_property:
+ * @value: a #JSCValue
+ * @name: the property name
+ *
+ * Try to delete property with @name from @value. This function will return %FALSE if
+ * the property was defined without %JSC_VALUE_PROPERTY_CONFIGURABLE flag.
+ *
+ * Returns: %TRUE if the property was deleted, or %FALSE otherwise.
+ */
+gboolean jsc_value_object_delete_property(JSCValue* value, const char* name)
+{
+ g_return_val_if_fail(JSC_IS_VALUE(value), FALSE);
+ g_return_val_if_fail(name, FALSE);
+
+ JSCValuePrivate* priv = value->priv;
+ auto* jsContext = jscContextGetJSContext(priv->context.get());
+ JSValueRef exception = nullptr;
+ JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
+ if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+ return FALSE;
+
+ JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString(name));
+ gboolean result = JSObjectDeleteProperty(jsContext, object, propertyName.get(), &exception);
+ if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+ return FALSE;
+
+ return result;
+}
+
+/**
+ * jsc_value_object_enumerate_properties:
+ * @value: a #JSCValue
+ *
+ * Get the list of property names of @value. Only properties defined with %JSC_VALUE_PROPERTY_ENUMERABLE
+ * flag will be collected.
+ *
+ * Returns: (array zero-terminated=1) (transfer full) (nullable): a %NULL-terminated array of strings containing the
+ * property names, or %NULL if @value doesn't have enumerable properties. Use g_strfreev() to free.
+ */
+char** jsc_value_object_enumerate_properties(JSCValue* value)
+{
+ g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
+
+ JSCValuePrivate* priv = value->priv;
+ auto* jsContext = jscContextGetJSContext(priv->context.get());
+ JSValueRef exception = nullptr;
+ JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
+ if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+ return nullptr;
+
+ auto* propertiesArray = JSObjectCopyPropertyNames(jsContext, object);
+ if (!propertiesArray)
+ return nullptr;
+
+ auto propertiesArraySize = JSPropertyNameArrayGetCount(propertiesArray);
+ if (!propertiesArraySize) {
+ JSPropertyNameArrayRelease(propertiesArray);
+ return nullptr;
+ }
+
+ auto* result = static_cast<char**>(g_new0(char*, propertiesArraySize + 1));
+ for (unsigned i = 0; i < propertiesArraySize; ++i) {
+ auto* jsString = JSPropertyNameArrayGetNameAtIndex(propertiesArray, i);
+ size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString);
+ auto* string = static_cast<char*>(g_malloc(maxSize));
+ JSStringGetUTF8CString(jsString, string, maxSize);
+ result[i] = string;
+ }
+ JSPropertyNameArrayRelease(propertiesArray);
+
+ return result;
+}
+
static GRefPtr<JSCValue> jscValueCallFunction(JSCValue* value, JSObjectRef function, JSC::JSCCallbackFunction::Type functionType, JSObjectRef thisObject, GType firstParameterType, va_list args)
{
JSCValuePrivate* priv = value->priv;
Modified: trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp (230703 => 230704)
--- trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp 2018-04-17 07:33:57 UTC (rev 230703)
+++ trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp 2018-04-17 08:15:28 UTC (rev 230704)
@@ -705,12 +705,17 @@
checker.watch(foo.get());
g_assert_true(jsc_value_is_object(foo.get()));
g_assert_true(jsc_value_object_is_instance_of(foo.get(), "Foo"));
+ g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
+ GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
+
GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "foo", G_TYPE_INT, 200, G_TYPE_NONE));
checker.watch(result.get());
g_assert_true(jsc_value_is_number(result.get()));
g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
+ g_assert_false(jsc_value_object_has_property(foo.get(), "bar"));
bool didThrow = false;
g_assert_throw_begin(exceptionHandler, didThrow);
result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "bar", G_TYPE_INT, 200, G_TYPE_NONE));
@@ -755,10 +760,16 @@
g_assert_true(jsc_value_is_object(object.get()));
g_assert_true(jsc_value_object_is_instance_of(object.get(), "Object"));
+ GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(object.get()));
+ g_assert_null(properties.get());
+
GRefPtr<JSCValue> property = adoptGRef(jsc_value_new_number(context.get(), 25));
checker.watch(property.get());
-
+ g_assert_false(jsc_value_object_has_property(object.get(), "val"));
jsc_value_object_define_property_data(object.get(), "val", static_cast<JSCValuePropertyFlags>(0), property.get());
+ g_assert_true(jsc_value_object_has_property(object.get(), "val"));
+ properties.reset(jsc_value_object_enumerate_properties(object.get()));
+ g_assert_null(properties.get());
jsc_context_set_value(context.get(), "f", object.get());
GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f.val;", -1));
@@ -780,11 +791,15 @@
value = adoptGRef(jsc_context_evaluate(context.get(), "delete f.val;", -1));
checker.watch(value.get());
+ g_assert_true(jsc_value_object_has_property(object.get(), "val"));
value = adoptGRef(jsc_context_evaluate(context.get(), "f.val;", -1));
checker.watch(value.get());
g_assert_true(jsc_value_is_number(value.get()));
g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25);
+ g_assert_false(jsc_value_object_delete_property(object.get(), "val"));
+ g_assert_true(jsc_value_object_has_property(object.get(), "val"));
+
property = adoptGRef(jsc_value_new_number(context.get(), 52));
checker.watch(property.get());
g_assert_throw_begin(exceptionHandler, didThrow);
@@ -793,12 +808,19 @@
property = adoptGRef(jsc_value_new_number(context.get(), 32));
checker.watch(property.get());
+ g_assert_false(jsc_value_object_has_property(object.get(), "val2"));
jsc_value_object_define_property_data(object.get(), "val2", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_ENUMERABLE | JSC_VALUE_PROPERTY_WRITABLE), property.get());
+ g_assert_true(jsc_value_object_has_property(object.get(), "val2"));
value = adoptGRef(jsc_context_evaluate(context.get(), "f.val2;", -1));
checker.watch(value.get());
g_assert_true(jsc_value_is_number(value.get()));
g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 32);
+ properties.reset(jsc_value_object_enumerate_properties(object.get()));
+ g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
+ g_assert_cmpstr(properties.get()[0], ==, "val2");
+ g_assert_null(properties.get()[1]);
+
value = adoptGRef(jsc_context_evaluate(context.get(), "'use strict'; f.val2 = 45;", -1));
checker.watch(value.get());
value = adoptGRef(jsc_context_evaluate(context.get(), "f.val2;", -1));
@@ -811,33 +833,81 @@
g_assert_true(jsc_value_is_boolean(value.get()));
g_assert_true(jsc_value_to_boolean(value.get()) == TRUE);
+ g_assert_false(jsc_value_object_delete_property(object.get(), "val2"));
+ g_assert_true(jsc_value_object_has_property(object.get(), "val2"));
+
property = adoptGRef(jsc_value_new_number(context.get(), 125));
checker.watch(property.get());
+ g_assert_false(jsc_value_object_has_property(object.get(), "val3"));
jsc_value_object_define_property_data(object.get(), "val3", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_WRITABLE), property.get());
+ g_assert_true(jsc_value_object_has_property(object.get(), "val3"));
value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;", -1));
checker.watch(value.get());
g_assert_true(jsc_value_is_number(value.get()));
g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 125);
+ properties.reset(jsc_value_object_enumerate_properties(object.get()));
+ g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
+ g_assert_cmpstr(properties.get()[0], ==, "val2");
+ g_assert_null(properties.get()[1]);
+
property = adoptGRef(jsc_value_new_number(context.get(), 150));
checker.watch(property.get());
jsc_value_object_define_property_data(object.get(), "val3", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE), property.get());
+ g_assert_true(jsc_value_object_has_property(object.get(), "val3"));
value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;", -1));
checker.watch(value.get());
g_assert_true(jsc_value_is_number(value.get()));
g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 150);
+ properties.reset(jsc_value_object_enumerate_properties(object.get()));
+ g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
+ g_assert_cmpstr(properties.get()[0], ==, "val2");
+ g_assert_null(properties.get()[1]);
+
value = adoptGRef(jsc_context_evaluate(context.get(), "delete f.val3;", -1));
checker.watch(value.get());
+ g_assert_false(jsc_value_object_has_property(object.get(), "val3"));
value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;", -1));
checker.watch(value.get());
g_assert_true(jsc_value_is_undefined(value.get()));
+ property = adoptGRef(jsc_value_new_number(context.get(), 250));
+ checker.watch(property.get());
+ g_assert_false(jsc_value_object_has_property(object.get(), "val4"));
+ jsc_value_object_define_property_data(object.get(), "val4", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_ENUMERABLE), property.get());
+ g_assert_true(jsc_value_object_has_property(object.get(), "val4"));
+ value = adoptGRef(jsc_context_evaluate(context.get(), "f.val4;", -1));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_number(value.get()));
+ g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 250);
+
+ properties.reset(jsc_value_object_enumerate_properties(object.get()));
+ g_assert_cmpuint(g_strv_length(properties.get()), ==, 2);
+ g_assert_cmpstr(properties.get()[0], ==, "val2");
+ g_assert_cmpstr(properties.get()[1], ==, "val4");
+ g_assert_null(properties.get()[2]);
+
+ g_assert_true(jsc_value_object_delete_property(object.get(), "val4"));
+ g_assert_false(jsc_value_object_has_property(object.get(), "val4"));
+
+ properties.reset(jsc_value_object_enumerate_properties(object.get()));
+ g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
+ g_assert_cmpstr(properties.get()[0], ==, "val2");
+ g_assert_null(properties.get()[1]);
+
GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "foo", G_CALLBACK(foo), nullptr, nullptr, G_TYPE_INT, 1, G_TYPE_INT));
checker.watch(function.get());
+ g_assert_false(jsc_value_object_has_property(object.get(), "foo"));
jsc_value_object_define_property_data(object.get(), "foo", static_cast<JSCValuePropertyFlags>(0), function.get());
+ g_assert_true(jsc_value_object_has_property(object.get(), "foo"));
+ properties.reset(jsc_value_object_enumerate_properties(object.get()));
+ g_assert_cmpuint(g_strv_length(properties.get()), ==, 1);
+ g_assert_cmpstr(properties.get()[0], ==, "val2");
+ g_assert_null(properties.get()[1]);
+
GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(object.get(), "foo", G_TYPE_INT, 200, G_TYPE_NONE));
checker.watch(result.get());
g_assert_true(jsc_value_is_number(result.get()));
@@ -996,7 +1066,14 @@
g_assert_true(jsc_value_is_boolean(result.get()));
g_assert_true(jsc_value_to_boolean(result.get()));
+ GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
+
+ g_assert_false(jsc_value_object_has_property(foo.get(), "getFoo"));
jsc_class_add_method(jscClass, "getFoo", G_CALLBACK(getFoo), nullptr, nullptr, G_TYPE_INT, 0, G_TYPE_NONE);
+ g_assert_true(jsc_value_object_has_property(foo.get(), "getFoo"));
+ properties.reset(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE));
checker.watch(value.get());
g_assert_true(jsc_value_is_number(value.get()));
@@ -1006,7 +1083,11 @@
checker.watch(value2.get());
g_assert_true(value.get() == value2.get());
+ g_assert_false(jsc_value_object_has_property(foo.get(), "setFoo"));
jsc_class_add_method(jscClass, "setFoo", G_CALLBACK(setFoo), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_INT);
+ g_assert_true(jsc_value_object_has_property(foo.get(), "setFoo"));
+ properties.reset(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "setFoo", G_TYPE_INT, 25, G_TYPE_NONE));
checker.watch(result.get());
value = adoptGRef(jsc_context_evaluate(context.get(), "f.getFoo()", -1));
@@ -1040,7 +1121,13 @@
g_assert_true(jsc_value_is_boolean(result.get()));
g_assert_true(jsc_value_to_boolean(result.get()));
+ g_assert_false(jsc_value_object_has_property(foo.get(), "foo"));
+ g_assert_false(jsc_value_object_has_property(foo2.get(), "foo"));
jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr);
+ g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
+ g_assert_true(jsc_value_object_has_property(foo2.get(), "foo"));
+ properties.reset(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
value = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo", -1));
checker.watch(value.get());
g_assert_true(jsc_value_is_number(value.get()));
@@ -1092,9 +1179,15 @@
GRefPtr<JSCValue> f1 = adoptGRef(jsc_context_get_value(context.get(), "f1"));
checker.watch(f1.get());
g_assert_true(jsc_value_is_object(f1.get()));
+ g_assert_true(jsc_value_object_has_property(f1.get(), "sibling"));
+ GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(f1.get()));
+ g_assert_null(properties.get());
GRefPtr<JSCValue> f2 = adoptGRef(jsc_context_get_value(context.get(), "f2"));
checker.watch(f2.get());
g_assert_true(jsc_value_is_object(f2.get()));
+ g_assert_true(jsc_value_object_has_property(f2.get(), "sibling"));
+ properties.reset(jsc_value_object_enumerate_properties(f2.get()));
+ g_assert_null(properties.get());
GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f2.sibling", -1));
checker.watch(value.get());
@@ -1126,6 +1219,9 @@
checker.watch(foo.get());
g_assert_true(jsc_value_is_object(foo.get()));
g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass)));
+ g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
+ GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
jsc_context_set_value(context.get(), "f", foo.get());
GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Foo;", -1));
@@ -1159,6 +1255,7 @@
g_assert_true(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(bazClass)));
g_assert_false(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(jscClass)));
g_assert_false(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(bazClass)));
+ g_assert_false(jsc_value_object_has_property(baz.get(), "foo"));
jsc_context_set_value(context.get(), "bz", baz.get());
result = adoptGRef(jsc_context_evaluate(context.get(), "bz instanceof Baz;", -1));
@@ -1202,6 +1299,9 @@
checker.watch(foo.get());
g_assert_true(jsc_value_is_object(foo.get()));
g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass)));
+ g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
+ GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
jsc_context_set_value(context.get(), "f1", foo.get());
jsc_context_set_value(context.get(), "f2", foo.get());
@@ -1212,7 +1312,9 @@
GRefPtr<JSCValue> property = adoptGRef(jsc_value_new_number(context.get(), 50));
checker.watch(property.get());
+ g_assert_false(jsc_value_object_has_property(foo.get(), "n"));
jsc_value_object_set_property(foo.get(), "n", property.get());
+ g_assert_true(jsc_value_object_has_property(foo.get(), "n"));
result = adoptGRef(jsc_context_evaluate(context.get(), "f1.n", -1));
checker.watch(result.get());
g_assert_true(jsc_value_is_number(result.get()));
@@ -1238,6 +1340,9 @@
checker.watch(foo.get());
g_assert_true(jsc_value_is_object(foo.get()));
g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass)));
+ g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
+ GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_number(context.get(), 125));
checker.watch(value.get());
@@ -1308,6 +1413,9 @@
foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new wk.Foo();", -1));
g_assert_true(jsc_value_is_object(foo.get()));
g_assert_true(jsc_value_object_is_instance_of(foo.get(), "wk.Foo"));
+ g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
+ GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof wk.Foo;", -1));
checker.watch(result.get());
g_assert_true(jsc_value_is_boolean(result.get()));
@@ -1446,6 +1554,10 @@
checker.watch(result.get());
g_assert_true(jsc_value_is_boolean(result.get()));
g_assert_false(jsc_value_to_boolean(result.get()));
+ g_assert_true(jsc_value_object_has_property(foo.get(), "foo"));
+ g_assert_false(jsc_value_object_has_property(foo.get(), "bar"));
+ GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get()));
+ g_assert_null(properties.get());
GRefPtr<JSCValue> bar = adoptGRef(jsc_context_get_value(context.get(), "b"));
checker.watch(bar.get());
@@ -1461,6 +1573,10 @@
checker.watch(result.get());
g_assert_true(jsc_value_is_boolean(result.get()));
g_assert_true(jsc_value_to_boolean(result.get()));
+ g_assert_true(jsc_value_object_has_property(bar.get(), "bar"));
+ g_assert_true(jsc_value_object_has_property(bar.get(), "foo"));
+ properties.reset(jsc_value_object_enumerate_properties(bar.get()));
+ g_assert_null(properties.get());
result = adoptGRef(jsc_context_evaluate(context.get(), "b.bar = 25; b.foo = 42;", -1));
checker.watch(result.get());