Add fast path for proxy with isExtensible trap

ObjectIsExtensible is now a Torque builtin (previously CPP) and the Proxy path is implemented completely in Torque while everything else calls into runtime (and is thus a bit slower than previously).

Improvement in micro-benchmark
Before:
IsExtensibleWithoutTrap-Proxies(Score): 2228
IsExtensibleWithTrap-Proxies(Score): 917

After:
IsExtensibleWithoutTrap-Proxies(Score): 3683
IsExtensibleWithTrap-Proxies(Score): 3310

Bug: v8:6664
Change-Id: I1fbe1c51cb724a23d7a59fc8231bb3d1461a6add
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1637444
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62006}
This commit is contained in:
Z Nguyen-Huu 2019-06-05 09:56:31 -07:00 committed by Commit Bot
parent 8dc7da0c07
commit 3167b3b600
12 changed files with 229 additions and 22 deletions

View File

@ -952,9 +952,11 @@ torque_files = [
"src/builtins/iterator.tq",
"src/builtins/math.tq",
"src/builtins/object-fromentries.tq",
"src/builtins/object.tq",
"src/builtins/proxy-constructor.tq",
"src/builtins/proxy-get-property.tq",
"src/builtins/proxy-has-property.tq",
"src/builtins/proxy-is-extensible.tq",
"src/builtins/proxy-revocable.tq",
"src/builtins/proxy-revoke.tq",
"src/builtins/proxy-set-property.tq",

View File

@ -729,7 +729,6 @@ namespace internal {
CPP(ObjectGetPrototypeOf) \
CPP(ObjectSetPrototypeOf) \
TFJ(ObjectIs, 2, kReceiver, kLeft, kRight) \
CPP(ObjectIsExtensible) \
CPP(ObjectIsFrozen) \
CPP(ObjectIsSealed) \
TFJ(ObjectKeys, 1, kReceiver, kObject) \

View File

@ -332,18 +332,6 @@ BUILTIN(ObjectGetOwnPropertySymbols) {
return GetOwnPropertyKeys(isolate, args, SKIP_STRINGS);
}
// ES6 section 19.1.2.11 Object.isExtensible ( O )
BUILTIN(ObjectIsExtensible) {
HandleScope scope(isolate);
Handle<Object> object = args.atOrUndefined(isolate, 1);
Maybe<bool> result =
object->IsJSReceiver()
? JSReceiver::IsExtensible(Handle<JSReceiver>::cast(object))
: Just(false);
MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
return isolate->heap()->ToBoolean(result.FromJust());
}
// ES6 section 19.1.2.12 Object.isFrozen ( O )
BUILTIN(ObjectIsFrozen) {
HandleScope scope(isolate);

26
src/builtins/object.tq Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace runtime {
extern transitioning runtime
ObjectIsExtensible(implicit context: Context)(Object): Object;
} // namespace runtime
namespace object {
transitioning macro
ObjectIsExtensible(implicit context: Context)(object: Object): Object {
const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
otherwise return runtime::ObjectIsExtensible(objectJSReceiver);
return proxy::ProxyIsExtensible(objectJSProxy);
}
} // namespace object
namespace object_isextensible {
// ES6 section 19.1.2.11 Object.isExtensible ( O )
transitioning javascript builtin ObjectIsExtensible(
implicit context: Context)(receiver: Object, object: Object): Object {
return object::ObjectIsExtensible(object);
}
} // namespace object-isextensible

View File

@ -0,0 +1,55 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include 'src/builtins/builtins-proxy-gen.h'
namespace proxy {
// ES #sec-proxy-object-internal-methods-and-internal-slots-isextensible
// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible
transitioning builtin ProxyIsExtensible(implicit context:
Context)(proxy: JSProxy): Object {
PerformStackCheck();
const kTrapName: constexpr string = 'isExtensible';
try {
// 1. Let handler be O.[[ProxyHandler]].
// 2. If handler is null, throw a TypeError exception.
// 3. Assert: Type(handler) is Object.
const handler =
Cast<JSReceiver>(proxy.handler) otherwise ThrowProxyHandlerRevoked;
// 4. Let target be O.[[ProxyTarget]].
const target = proxy.target;
// 5. Let trap be ? GetMethod(handler, "isExtensible").
// 6. If trap is undefined, then (see 6.a below).
const trap: Callable = GetMethod(handler, kTrapName)
otherwise goto TrapUndefined(target);
// 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «
// target»)).
const trapResult = ToBoolean(Call(context, trap, handler, target));
// 8. Let targetResult be ? IsExtensible(target).
const targetResult: bool = ToBoolean(object::ObjectIsExtensible(target));
// 9. If SameValue(booleanTrapResult, targetResult) is false, throw a
// TypeError exception.
if (trapResult != targetResult) {
ThrowTypeError(
kProxyIsExtensibleInconsistent,
SelectBooleanConstant(targetResult));
}
// 10. Return booleanTrapResult.
return SelectBooleanConstant(trapResult);
}
label TrapUndefined(target: Object) {
// 6.a. Return ? IsExtensible(target).
return object::ObjectIsExtensible(target);
}
label ThrowProxyHandlerRevoked deferred {
ThrowTypeError(kProxyRevoked, kTrapName);
}
}
}

View File

@ -37,6 +37,8 @@ namespace proxy {
generates 'MessageTemplate::kProxyTrapReturnedFalsishFor';
const kProxyPrivate: constexpr MessageTemplate
generates 'MessageTemplate::kProxyPrivate';
const kProxyIsExtensibleInconsistent: constexpr MessageTemplate
generates 'MessageTemplate::kProxyIsExtensibleInconsistent';
const kProxyGet: constexpr int31
generates 'JSProxy::AccessKind::kGet';

View File

@ -312,6 +312,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(ObjectValuesSkipFastPath) \
V(ObjectGetOwnPropertyNames) \
V(ObjectGetOwnPropertyNamesTryFast) \
V(ObjectIsExtensible) \
V(RegExpInitializeAndCompile) \
V(StackGuard) \
V(StringAdd) \
@ -959,6 +960,7 @@ static bool TransitivelyCalledBuiltinHasNoSideEffect(Builtins::Name caller,
case Builtins::kOrdinaryToPrimitive_String:
case Builtins::kParseInt:
case Builtins::kProxyHasProperty:
case Builtins::kProxyIsExtensible:
case Builtins::kRecordWrite:
case Builtins::kStringAdd_CheckNone:
case Builtins::kStringEqual:

View File

@ -1452,7 +1452,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kObjectSetPrototypeOf, 2, false);
SimpleInstallFunction(isolate_, object_function, "isExtensible",
Builtins::kObjectIsExtensible, 1, false);
Builtins::kObjectIsExtensible, 1, true);
SimpleInstallFunction(isolate_, object_function, "isFrozen",
Builtins::kObjectIsFrozen, 1, false);

View File

@ -502,6 +502,19 @@ RUNTIME_FUNCTION(Runtime_ObjectEntriesSkipFastPath) {
return *isolate->factory()->NewJSArrayWithElements(entries);
}
RUNTIME_FUNCTION(Runtime_ObjectIsExtensible) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
Maybe<bool> result =
object->IsJSReceiver()
? JSReceiver::IsExtensible(Handle<JSReceiver>::cast(object))
: Just(false);
MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
return isolate->heap()->ToBoolean(result.FromJust());
}
RUNTIME_FUNCTION(Runtime_GetProperty) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());

View File

@ -307,6 +307,7 @@ namespace internal {
F(ObjectKeys, 1, 1) \
F(ObjectGetOwnPropertyNames, 1, 1) \
F(ObjectGetOwnPropertyNamesTryFast, 1, 1) \
F(ObjectIsExtensible, 1, 1) \
F(ObjectValues, 1, 1) \
F(ObjectValuesSkipFastPath, 1, 1) \
F(OptimizeObjectForAddingMultipleProperties, 2, 1) \

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Reflect.
(function () {
// No trap.
@ -17,7 +17,6 @@
assertFalse(Reflect.isExtensible(proxy));
})();
(function () {
// "Undefined" trap.
@ -40,13 +39,13 @@
var handler = { preventExtensions: 42 };
var proxy = new Proxy(target, handler);
assertThrows(() => {Reflect.preventExtensions(proxy)}, TypeError);
assertThrows(() => { Reflect.preventExtensions(proxy) }, TypeError);
})();
(function () {
var target = {};
var handler = { isExtensible() {return "bla"} };
var handler = { isExtensible() { return "bla" } };
var proxy = new Proxy(target, handler);
// Trap returns trueish and target is extensible.
@ -54,7 +53,7 @@
// Trap returns trueish but target is not extensible.
Reflect.preventExtensions(target);
assertThrows(() => {Reflect.isExtensible(proxy)}, TypeError);
assertThrows(() => { Reflect.isExtensible(proxy) }, TypeError);
})();
@ -62,7 +61,7 @@
// Trap returns falsish.
var target = {};
var handler = { preventExtensions() {return 0} };
var handler = { preventExtensions() { return 0 } };
var proxy = new Proxy(target, handler);
assertFalse(Reflect.preventExtensions(proxy));
@ -73,13 +72,131 @@
(function () {
var target = {};
var handler = { preventExtensions() {return Symbol()} };
var handler = { preventExtensions() { return Symbol() } };
var proxy = new Proxy(target, handler);
// Trap returns trueish but target is extensible.
assertThrows(() => {Reflect.preventExtensions(proxy)}, TypeError);
assertThrows(() => { Reflect.preventExtensions(proxy) }, TypeError);
// Trap returns trueish and target is not extensible.
Reflect.preventExtensions(target);
assertTrue(Reflect.preventExtensions(proxy));
})();
(function () {
// Target is proxy
var object = {};
assertTrue(Reflect.preventExtensions(object));
var target = new Proxy(object, {});
var proxy = new Proxy(target, {});
assertFalse(Reflect.isExtensible(object));
assertFalse(Reflect.isExtensible(target));
assertFalse(Reflect.isExtensible(proxy));
})();
// Object.
(function () {
// No trap.
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
assertTrue(Object.isExtensible(target));
assertTrue(Object.isExtensible(proxy));
Object.preventExtensions(proxy);
assertFalse(Object.isExtensible(target));
assertFalse(Object.isExtensible(proxy));
})();
(function () {
// "Undefined" trap.
var target = {};
var handler = { preventExtensions: null };
var proxy = new Proxy(target, handler);
assertTrue(Object.isExtensible(target));
assertTrue(Object.isExtensible(proxy));
Object.preventExtensions(proxy);
assertFalse(Object.isExtensible(target));
assertFalse(Object.isExtensible(proxy));
})();
(function () {
// Invalid trap.
var target = {};
var handler = { preventExtensions: 42 };
var proxy = new Proxy(target, handler);
assertThrows(() => { Object.preventExtensions(proxy) }, TypeError);
})();
(function () {
var target = {};
var handler = { isExtensible() { return "bla" } };
var proxy = new Proxy(target, handler);
// Trap returns trueish and target is extensible.
assertTrue(Object.isExtensible(proxy));
// Trap returns trueish but target is not extensible.
Object.preventExtensions(target);
assertThrows(() => { Object.isExtensible(proxy) }, TypeError);
})();
(function () {
// Trap returns falsish.
var target = {};
var handler = { isExtensible() { return false } };
var proxy = new Proxy(target, handler);
assertThrows(() => { Object.isExtensible(proxy) }, TypeError);
Object.preventExtensions(target);
assertFalse(Object.isExtensible(proxy));
})();
(function () {
// Trap returns falsish.
var target = {};
var handler = { preventExtensions() { return 0 } };
var proxy = new Proxy(target, handler);
assertFalse(Reflect.preventExtensions(proxy));
Object.preventExtensions(target);
assertFalse(Reflect.preventExtensions(proxy));
})();
(function () {
var target = {};
var handler = { preventExtensions() { return Symbol() } };
var proxy = new Proxy(target, handler);
// Trap returns trueish but target is extensible.
assertThrows(() => { Object.preventExtensions(proxy) }, TypeError);
// Trap returns trueish and target is not extensible.
Object.preventExtensions(target);
assertTrue(Reflect.preventExtensions(proxy));
})();
(function () {
// Target is proxy
var object = {};
Object.preventExtensions(object);
var target = new Proxy(object, {});
var proxy = new Proxy(target, {});
assertFalse(Object.isExtensible(object));
assertFalse(Object.isExtensible(target));
assertFalse(Object.isExtensible(proxy));
})();

View File

@ -30,6 +30,8 @@
// Flags: --allow-natives-syntax
assertFalse(Object.isExtensible());
var obj1 = {};
// Extensible defaults to true.
assertTrue(Object.isExtensible(obj1));