Move hasOwnProperty to builtins.cc
This gets rid of the JavaScript wrapper. That way we can more quickly handle non-JSReceivers and indexed properties; and don't need to optimize the JavaScript wrapper either. BUG= Review URL: https://codereview.chromium.org/1742283002 Cr-Commit-Position: refs/heads/master@{#34356}
This commit is contained in:
parent
c7339e6ee4
commit
d5cb0ce4e9
@ -1121,6 +1121,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
Builtins::kObjectPreventExtensions, 1, false);
|
||||
SimpleInstallFunction(object_function, "seal", Builtins::kObjectSeal, 1,
|
||||
false);
|
||||
|
||||
SimpleInstallFunction(isolate->initial_object_prototype(), "hasOwnProperty",
|
||||
Builtins::kObjectHasOwnProperty, 1, false);
|
||||
}
|
||||
|
||||
Handle<JSObject> global(native_context()->global_object());
|
||||
|
@ -352,6 +352,83 @@ BUILTIN(Illegal) {
|
||||
|
||||
BUILTIN(EmptyFunction) { return isolate->heap()->undefined_value(); }
|
||||
|
||||
// ES6 7.3.11
|
||||
BUILTIN(ObjectHasOwnProperty) {
|
||||
HandleScope scope(isolate);
|
||||
|
||||
Handle<Object> property = args.atOrUndefined(isolate, 1);
|
||||
|
||||
Handle<Name> key;
|
||||
uint32_t index;
|
||||
bool key_is_array_index = property->ToArrayIndex(&index);
|
||||
|
||||
if (!key_is_array_index) {
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, key,
|
||||
Object::ToName(isolate, property));
|
||||
key_is_array_index = key->AsArrayIndex(&index);
|
||||
}
|
||||
|
||||
Handle<Object> object = args.receiver();
|
||||
|
||||
if (object->IsJSObject()) {
|
||||
Handle<JSObject> js_obj = Handle<JSObject>::cast(object);
|
||||
// Fast case: either the key is a real named property or it is not
|
||||
// an array index and there are no interceptors or hidden
|
||||
// prototypes.
|
||||
// TODO(jkummerow): Make JSReceiver::HasOwnProperty fast enough to
|
||||
// handle all cases directly (without this custom fast path).
|
||||
{
|
||||
LookupIterator::Configuration c = LookupIterator::OWN_SKIP_INTERCEPTOR;
|
||||
LookupIterator it = key_is_array_index
|
||||
? LookupIterator(isolate, js_obj, index, c)
|
||||
: LookupIterator(js_obj, key, c);
|
||||
Maybe<bool> maybe = JSReceiver::HasProperty(&it);
|
||||
if (maybe.IsNothing()) return isolate->heap()->exception();
|
||||
DCHECK(!isolate->has_pending_exception());
|
||||
if (maybe.FromJust()) return isolate->heap()->true_value();
|
||||
}
|
||||
|
||||
Map* map = js_obj->map();
|
||||
if (!map->has_hidden_prototype() &&
|
||||
(key_is_array_index ? !map->has_indexed_interceptor()
|
||||
: !map->has_named_interceptor())) {
|
||||
return isolate->heap()->false_value();
|
||||
}
|
||||
|
||||
// Slow case.
|
||||
LookupIterator::Configuration c = LookupIterator::HIDDEN;
|
||||
LookupIterator it = key_is_array_index
|
||||
? LookupIterator(isolate, js_obj, index, c)
|
||||
: LookupIterator(js_obj, key, c);
|
||||
|
||||
Maybe<bool> maybe = JSReceiver::HasProperty(&it);
|
||||
if (maybe.IsNothing()) return isolate->heap()->exception();
|
||||
DCHECK(!isolate->has_pending_exception());
|
||||
return isolate->heap()->ToBoolean(maybe.FromJust());
|
||||
|
||||
} else if (object->IsJSProxy()) {
|
||||
if (key.is_null()) {
|
||||
DCHECK(key_is_array_index);
|
||||
key = isolate->factory()->Uint32ToString(index);
|
||||
}
|
||||
|
||||
Maybe<bool> result =
|
||||
JSReceiver::HasOwnProperty(Handle<JSProxy>::cast(object), key);
|
||||
if (!result.IsJust()) return isolate->heap()->exception();
|
||||
return isolate->heap()->ToBoolean(result.FromJust());
|
||||
|
||||
} else if (object->IsString()) {
|
||||
return isolate->heap()->ToBoolean(
|
||||
key_is_array_index
|
||||
? index < static_cast<uint32_t>(String::cast(*object)->length())
|
||||
: key->Equals(isolate->heap()->length_string()));
|
||||
} else if (object->IsNull() || object->IsUndefined()) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kUndefinedOrNullToObject));
|
||||
}
|
||||
|
||||
return isolate->heap()->false_value();
|
||||
}
|
||||
|
||||
BUILTIN(ArrayPush) {
|
||||
HandleScope scope(isolate);
|
||||
|
@ -128,6 +128,7 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
|
||||
V(ObjectGetOwnPropertyDescriptor, kNone) \
|
||||
V(ObjectGetOwnPropertyNames, kNone) \
|
||||
V(ObjectGetOwnPropertySymbols, kNone) \
|
||||
V(ObjectHasOwnProperty, kNone) \
|
||||
V(ObjectIs, kNone) \
|
||||
V(ObjectIsExtensible, kNone) \
|
||||
V(ObjectIsFrozen, kNone) \
|
||||
|
@ -35,6 +35,7 @@ var MakeTypeError;
|
||||
var MathFloor;
|
||||
var ObjectDefineProperties = utils.ImportNow("ObjectDefineProperties");
|
||||
var ObjectDefineProperty = utils.ImportNow("ObjectDefineProperty");
|
||||
var ObjectHasOwnProperty = utils.ImportNow("ObjectHasOwnProperty");
|
||||
var patternSymbol = utils.ImportNow("intl_pattern_symbol");
|
||||
var RegExpTest;
|
||||
var resolvedSymbol = utils.ImportNow("intl_resolved_symbol");
|
||||
@ -545,7 +546,7 @@ function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
|
||||
}
|
||||
|
||||
for (var key in keyValues) {
|
||||
if (%HasOwnProperty(keyValues, key)) {
|
||||
if (HAS_OWN_PROPERTY(keyValues, key)) {
|
||||
var value = UNDEFINED;
|
||||
var map = keyValues[key];
|
||||
if (!IS_UNDEFINED(map.property)) {
|
||||
@ -561,7 +562,7 @@ function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
|
||||
// User options didn't have it, check Unicode extension.
|
||||
// Here we want to convert strings 'true', 'false' into proper Boolean
|
||||
// values (not a user error).
|
||||
if (%HasOwnProperty(extensionMap, key)) {
|
||||
if (HAS_OWN_PROPERTY(extensionMap, key)) {
|
||||
value = extensionMap[key];
|
||||
if (!IS_UNDEFINED(value)) {
|
||||
updateProperty(map.property, map.type, value);
|
||||
@ -637,7 +638,7 @@ function getAvailableLocalesOf(service) {
|
||||
var available = %AvailableLocalesOf(service);
|
||||
|
||||
for (var i in available) {
|
||||
if (%HasOwnProperty(available, i)) {
|
||||
if (HAS_OWN_PROPERTY(available, i)) {
|
||||
var parts =
|
||||
%_Call(StringMatch, i, /^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/);
|
||||
if (parts !== null) {
|
||||
@ -953,7 +954,7 @@ function initializeCollator(collator, locales, options) {
|
||||
|
||||
var collation = 'default';
|
||||
var extension = '';
|
||||
if (%HasOwnProperty(extensionMap, 'co') && internalOptions.usage === 'sort') {
|
||||
if (HAS_OWN_PROPERTY(extensionMap, 'co') && internalOptions.usage === 'sort') {
|
||||
|
||||
/**
|
||||
* Allowed -u-co- values. List taken from:
|
||||
@ -1226,10 +1227,10 @@ function initializeNumberFormat(numberFormat, locales, options) {
|
||||
style: {value: internalOptions.style, writable: true},
|
||||
useGrouping: {writable: true}
|
||||
});
|
||||
if (%HasOwnProperty(internalOptions, 'minimumSignificantDigits')) {
|
||||
if (HAS_OWN_PROPERTY(internalOptions, 'minimumSignificantDigits')) {
|
||||
defineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED);
|
||||
}
|
||||
if (%HasOwnProperty(internalOptions, 'maximumSignificantDigits')) {
|
||||
if (HAS_OWN_PROPERTY(internalOptions, 'maximumSignificantDigits')) {
|
||||
defineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED);
|
||||
}
|
||||
var formatter = %CreateNumberFormat(requestedLocale,
|
||||
@ -1301,12 +1302,12 @@ InstallFunction(Intl.NumberFormat.prototype, 'resolvedOptions', function() {
|
||||
format[resolvedSymbol].currencyDisplay);
|
||||
}
|
||||
|
||||
if (%HasOwnProperty(format[resolvedSymbol], 'minimumSignificantDigits')) {
|
||||
if (HAS_OWN_PROPERTY(format[resolvedSymbol], 'minimumSignificantDigits')) {
|
||||
defineWECProperty(result, 'minimumSignificantDigits',
|
||||
format[resolvedSymbol].minimumSignificantDigits);
|
||||
}
|
||||
|
||||
if (%HasOwnProperty(format[resolvedSymbol], 'maximumSignificantDigits')) {
|
||||
if (HAS_OWN_PROPERTY(format[resolvedSymbol], 'maximumSignificantDigits')) {
|
||||
defineWECProperty(result, 'maximumSignificantDigits',
|
||||
format[resolvedSymbol].maximumSignificantDigits);
|
||||
}
|
||||
@ -1486,7 +1487,7 @@ function fromLDMLString(ldmlString) {
|
||||
|
||||
function appendToDateTimeObject(options, option, match, pairs) {
|
||||
if (IS_NULL(match)) {
|
||||
if (!%HasOwnProperty(options, option)) {
|
||||
if (!HAS_OWN_PROPERTY(options, option)) {
|
||||
defineWEProperty(options, option, UNDEFINED);
|
||||
}
|
||||
return options;
|
||||
|
@ -122,12 +122,12 @@ macro TO_PRIMITIVE_NUMBER(arg) = (%_ToPrimitive_Number(arg));
|
||||
macro TO_PRIMITIVE_STRING(arg) = (%_ToPrimitive_String(arg));
|
||||
macro TO_NAME(arg) = (%_ToName(arg));
|
||||
macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null");
|
||||
macro HAS_OWN_PROPERTY(arg, index) = (%_Call(ObjectHasOwnProperty, arg, index));
|
||||
macro HAS_OWN_PROPERTY(obj, key) = (%_Call(ObjectHasOwnProperty, obj, key));
|
||||
macro HAS_INDEX(array, index, is_array) = ((is_array && %_HasFastPackedElements(%IS_VAR(array)) && (index < array.length)) || (index in array));
|
||||
|
||||
# Private names.
|
||||
macro IS_PRIVATE(sym) = (%SymbolIsPrivate(sym));
|
||||
macro HAS_PRIVATE(obj, sym) = (%HasOwnProperty(obj, sym));
|
||||
macro HAS_PRIVATE(obj, key) = HAS_OWN_PROPERTY(obj, key);
|
||||
macro HAS_DEFINED_PRIVATE(obj, sym) = (!IS_UNDEFINED(obj[sym]));
|
||||
macro GET_PRIVATE(obj, sym) = (obj[sym]);
|
||||
macro SET_PRIVATE(obj, sym, val) = (obj[sym] = val);
|
||||
|
@ -34,6 +34,7 @@ var Int8x16ToString;
|
||||
var InternalArray = utils.InternalArray;
|
||||
var internalErrorSymbol = utils.ImportNow("internal_error_symbol");
|
||||
var ObjectDefineProperty;
|
||||
var ObjectHasOwnProperty;
|
||||
var ObjectToString = utils.ImportNow("object_to_string");
|
||||
var Script = utils.ImportNow("Script");
|
||||
var stackTraceSymbol = utils.ImportNow("stack_trace_symbol");
|
||||
@ -56,6 +57,7 @@ utils.Import(function(from) {
|
||||
Int32x4ToString = from.Int32x4ToString;
|
||||
Int8x16ToString = from.Int8x16ToString;
|
||||
ObjectDefineProperty = from.ObjectDefineProperty;
|
||||
ObjectHasOwnProperty = from.ObjectHasOwnProperty;
|
||||
StringCharAt = from.StringCharAt;
|
||||
StringIndexOf = from.StringIndexOf;
|
||||
StringSubstring = from.StringSubstring;
|
||||
|
@ -134,14 +134,6 @@ function ObjectValueOf() {
|
||||
}
|
||||
|
||||
|
||||
// ES6 7.3.11
|
||||
function ObjectHasOwnProperty(value) {
|
||||
var name = TO_NAME(value);
|
||||
var object = TO_OBJECT(this);
|
||||
return %HasOwnProperty(object, name);
|
||||
}
|
||||
|
||||
|
||||
// ES6 19.1.3.3 Object.prototype.isPrototypeOf(V)
|
||||
function ObjectIsPrototypeOf(V) {
|
||||
if (!IS_RECEIVER(V)) return false;
|
||||
@ -850,7 +842,6 @@ utils.InstallFunctions(GlobalObject.prototype, DONT_ENUM, [
|
||||
"toString", ObjectToString,
|
||||
"toLocaleString", ObjectToLocaleString,
|
||||
"valueOf", ObjectValueOf,
|
||||
"hasOwnProperty", ObjectHasOwnProperty,
|
||||
"isPrototypeOf", ObjectIsPrototypeOf,
|
||||
"propertyIsEnumerable", ObjectPropertyIsEnumerable,
|
||||
"__defineGetter__", ObjectDefineGetter,
|
||||
@ -1108,7 +1099,7 @@ utils.Export(function(to) {
|
||||
to.NumberIsNaN = NumberIsNaN;
|
||||
to.ObjectDefineProperties = ObjectDefineProperties;
|
||||
to.ObjectDefineProperty = ObjectDefineProperty;
|
||||
to.ObjectHasOwnProperty = ObjectHasOwnProperty;
|
||||
to.ObjectHasOwnProperty = GlobalObject.prototype.hasOwnProperty;
|
||||
});
|
||||
|
||||
%InstallToContext([
|
||||
|
@ -532,85 +532,6 @@ RUNTIME_FUNCTION(Runtime_DeleteProperty_Strict) {
|
||||
}
|
||||
|
||||
|
||||
static Object* HasOwnPropertyImplementation(Isolate* isolate,
|
||||
Handle<JSObject> object,
|
||||
Handle<Name> key) {
|
||||
Maybe<bool> maybe = JSReceiver::HasOwnProperty(object, key);
|
||||
if (!maybe.IsJust()) return isolate->heap()->exception();
|
||||
if (maybe.FromJust()) return isolate->heap()->true_value();
|
||||
// Handle hidden prototypes. If there's a hidden prototype above this thing
|
||||
// then we have to check it for properties, because they are supposed to
|
||||
// look like they are on this object.
|
||||
if (object->map()->has_hidden_prototype()) {
|
||||
PrototypeIterator iter(isolate, object);
|
||||
DCHECK(!iter.IsAtEnd());
|
||||
|
||||
// TODO(verwaest): The recursion is not necessary for keys that are array
|
||||
// indices. Removing this.
|
||||
// Casting to JSObject is fine because JSProxies are never used as
|
||||
// hidden prototypes.
|
||||
return HasOwnPropertyImplementation(
|
||||
isolate, PrototypeIterator::GetCurrent<JSObject>(iter), key);
|
||||
}
|
||||
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
|
||||
return isolate->heap()->false_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_HasOwnProperty) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0)
|
||||
CONVERT_ARG_HANDLE_CHECKED(Name, key, 1);
|
||||
|
||||
uint32_t index;
|
||||
const bool key_is_array_index = key->AsArrayIndex(&index);
|
||||
|
||||
// Only JS objects can have properties.
|
||||
if (object->IsJSObject()) {
|
||||
Handle<JSObject> js_obj = Handle<JSObject>::cast(object);
|
||||
// Fast case: either the key is a real named property or it is not
|
||||
// an array index and there are no interceptors or hidden
|
||||
// prototypes.
|
||||
// TODO(jkummerow): Make JSReceiver::HasOwnProperty fast enough to
|
||||
// handle all cases directly (without this custom fast path).
|
||||
Maybe<bool> maybe = Nothing<bool>();
|
||||
if (key_is_array_index) {
|
||||
LookupIterator it(js_obj->GetIsolate(), js_obj, index,
|
||||
LookupIterator::HIDDEN);
|
||||
maybe = JSReceiver::HasProperty(&it);
|
||||
} else {
|
||||
maybe = JSObject::HasRealNamedProperty(js_obj, key);
|
||||
}
|
||||
if (!maybe.IsJust()) return isolate->heap()->exception();
|
||||
DCHECK(!isolate->has_pending_exception());
|
||||
if (maybe.FromJust()) {
|
||||
return isolate->heap()->true_value();
|
||||
}
|
||||
Map* map = js_obj->map();
|
||||
if (!key_is_array_index && !map->has_named_interceptor() &&
|
||||
!map->has_hidden_prototype()) {
|
||||
return isolate->heap()->false_value();
|
||||
}
|
||||
// Slow case.
|
||||
return HasOwnPropertyImplementation(isolate, Handle<JSObject>(js_obj),
|
||||
Handle<Name>(key));
|
||||
} else if (object->IsString() && key_is_array_index) {
|
||||
// Well, there is one exception: Handle [] on strings.
|
||||
Handle<String> string = Handle<String>::cast(object);
|
||||
if (index < static_cast<uint32_t>(string->length())) {
|
||||
return isolate->heap()->true_value();
|
||||
}
|
||||
} else if (object->IsJSProxy()) {
|
||||
Maybe<bool> result =
|
||||
JSReceiver::HasOwnProperty(Handle<JSProxy>::cast(object), key);
|
||||
if (!result.IsJust()) return isolate->heap()->exception();
|
||||
return isolate->heap()->ToBoolean(result.FromJust());
|
||||
}
|
||||
return isolate->heap()->false_value();
|
||||
}
|
||||
|
||||
|
||||
// ES6 section 12.9.3, operator in.
|
||||
RUNTIME_FUNCTION(Runtime_HasProperty) {
|
||||
HandleScope scope(isolate);
|
||||
|
@ -411,7 +411,6 @@ namespace internal {
|
||||
F(AppendElement, 2, 1) \
|
||||
F(DeleteProperty_Sloppy, 2, 1) \
|
||||
F(DeleteProperty_Strict, 2, 1) \
|
||||
F(HasOwnProperty, 2, 1) \
|
||||
F(HasProperty, 2, 1) \
|
||||
F(PropertyIsEnumerable, 2, 1) \
|
||||
F(GetOwnPropertyKeys, 2, 1) \
|
||||
|
@ -22065,7 +22065,7 @@ TEST(AccessCheckThrows) {
|
||||
CheckCorrectThrow("%DeleteProperty_Strict(other, 'x')");
|
||||
CheckCorrectThrow("%DeleteProperty_Sloppy(other, '1')");
|
||||
CheckCorrectThrow("%DeleteProperty_Strict(other, '1')");
|
||||
CheckCorrectThrow("%HasOwnProperty(other, 'x')");
|
||||
CheckCorrectThrow("Object.prototype.hasOwnProperty.call(other, 'x')");
|
||||
CheckCorrectThrow("%HasProperty('x', other)");
|
||||
CheckCorrectThrow("%PropertyIsEnumerable(other, 'x')");
|
||||
// PROPERTY_ATTRIBUTES_NONE = 0
|
||||
|
Loading…
Reference in New Issue
Block a user