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:
parent
8dc7da0c07
commit
3167b3b600
2
BUILD.gn
2
BUILD.gn
@ -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",
|
||||
|
@ -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) \
|
||||
|
@ -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
26
src/builtins/object.tq
Normal 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
|
55
src/builtins/proxy-is-extensible.tq
Normal file
55
src/builtins/proxy-is-extensible.tq
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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';
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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) \
|
||||
|
@ -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));
|
||||
})();
|
||||
|
@ -30,6 +30,8 @@
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
|
||||
assertFalse(Object.isExtensible());
|
||||
|
||||
var obj1 = {};
|
||||
// Extensible defaults to true.
|
||||
assertTrue(Object.isExtensible(obj1));
|
||||
|
Loading…
Reference in New Issue
Block a user