Optimize GetPrototype

This introduces Hydrogen for %_GetPrototype. The code falls back on
runtime if the object needs access checks or if its prototype is a
hidden prototype.

BUG=None
LOG=Y
R=dslomov@chromium.org

Review URL: https://codereview.chromium.org/756423006

Cr-Commit-Position: refs/heads/master@{#25694}
This commit is contained in:
arv 2014-12-05 12:36:39 -08:00 committed by Commit bot
parent 761891d201
commit c8c7395644
7 changed files with 114 additions and 9 deletions

View File

@ -984,7 +984,7 @@ function ArraySort(comparefn) {
// of a prototype property.
var CopyFromPrototype = function CopyFromPrototype(obj, length) {
var max = 0;
for (var proto = %GetPrototype(obj); proto; proto = %GetPrototype(proto)) {
for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
var indices = %GetArrayKeys(proto, length);
if (IS_NUMBER(indices)) {
// It's an interval.
@ -1013,7 +1013,7 @@ function ArraySort(comparefn) {
// where a prototype of obj has an element. I.e., shadow all prototype
// elements in that range.
var ShadowPrototypeElements = function(obj, from, to) {
for (var proto = %GetPrototype(obj); proto; proto = %GetPrototype(proto)) {
for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
var indices = %GetArrayKeys(proto, to);
if (IS_NUMBER(indices)) {
// It's an interval.
@ -1081,7 +1081,7 @@ function ArraySort(comparefn) {
}
for (i = length - num_holes; i < length; i++) {
// For compatability with Webkit, do not expose elements in the prototype.
if (i in %GetPrototype(obj)) {
if (i in %_GetPrototype(obj)) {
obj[i] = UNDEFINED;
} else {
delete obj[i];

View File

@ -6179,6 +6179,10 @@ class HObjectAccess FINAL {
return HObjectAccess(kMaps, JSObject::kMapOffset);
}
static HObjectAccess ForPrototype() {
return HObjectAccess(kMaps, Map::kPrototypeOffset);
}
static HObjectAccess ForMapAsInteger32() {
return HObjectAccess(kMaps, JSObject::kMapOffset,
Representation::Integer32());

View File

@ -12390,6 +12390,54 @@ void HOptimizedGraphBuilder::GenerateDebugIsActive(CallRuntime* call) {
}
void HOptimizedGraphBuilder::GenerateGetPrototype(CallRuntime* call) {
DCHECK(call->arguments()->length() == 1);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* object = Pop();
NoObservableSideEffectsScope no_effects(this);
HValue* map = Add<HLoadNamedField>(object, static_cast<HValue*>(NULL),
HObjectAccess::ForMap());
HValue* bit_field = Add<HLoadNamedField>(map, static_cast<HValue*>(NULL),
HObjectAccess::ForMapBitField());
HValue* is_access_check_needed_mask =
Add<HConstant>(1 << Map::kIsAccessCheckNeeded);
HValue* is_access_check_needed_test = AddUncasted<HBitwise>(
Token::BIT_AND, bit_field, is_access_check_needed_mask);
HValue* proto = Add<HLoadNamedField>(map, static_cast<HValue*>(NULL),
HObjectAccess::ForPrototype());
HValue* proto_map = Add<HLoadNamedField>(proto, static_cast<HValue*>(NULL),
HObjectAccess::ForMap());
HValue* proto_bit_field = Add<HLoadNamedField>(
proto_map, static_cast<HValue*>(NULL), HObjectAccess::ForMapBitField());
HValue* is_hidden_prototype_mask =
Add<HConstant>(1 << Map::kIsHiddenPrototype);
HValue* is_hidden_prototype_test = AddUncasted<HBitwise>(
Token::BIT_AND, proto_bit_field, is_hidden_prototype_mask);
{
IfBuilder needs_runtime(this);
needs_runtime.If<HCompareNumericAndBranch>(
is_access_check_needed_test, graph()->GetConstant0(), Token::NE);
needs_runtime.OrIf<HCompareNumericAndBranch>(
is_hidden_prototype_test, graph()->GetConstant0(), Token::NE);
needs_runtime.Then();
{
Add<HPushArguments>(object);
Push(Add<HCallRuntime>(
call->name(), Runtime::FunctionForId(Runtime::kGetPrototype), 1));
}
needs_runtime.Else();
Push(proto);
}
return ast_context()->ReturnValue(Pop());
}
#undef CHECK_BAILOUT
#undef CHECK_ALIVE

View File

@ -1151,7 +1151,7 @@ var StackTraceGetter = function() {
if (IS_UNDEFINED(stack_trace)) {
// Neither formatted nor structured stack trace available.
// Look further up the prototype chain.
holder = %GetPrototype(holder);
holder = %_GetPrototype(holder);
continue;
}
formatted_stack_trace = FormatStackTrace(holder, stack_trace);
@ -1255,7 +1255,7 @@ function GetPropertyWithoutInvokingMonkeyGetters(error, name) {
var current = error;
// Climb the prototype chain until we find the holder.
while (current && !%HasOwnProperty(current, name)) {
current = %GetPrototype(current);
current = %_GetPrototype(current);
}
if (IS_NULL(current)) return UNDEFINED;
if (!IS_OBJECT(current)) return error[name];

View File

@ -45,7 +45,6 @@ namespace internal {
F(IsSloppyModeFunction, 1, 1) \
F(GetDefaultReceiver, 1, 1) \
\
F(GetPrototype, 1, 1) \
F(SetPrototype, 2, 1) \
F(InternalSetPrototype, 2, 1) \
F(IsInPrototypeChain, 2, 1) \
@ -733,7 +732,8 @@ namespace internal {
F(SetGetSize, 1, 1) \
F(SetHas, 2, 1) \
/* Arrays */ \
F(HasFastPackedElements, 1, 1)
F(HasFastPackedElements, 1, 1) \
F(GetPrototype, 1, 1)
//---------------------------------------------------------------------------

View File

@ -973,7 +973,7 @@ function ObjectGetPrototypeOf(obj) {
if (!IS_SPEC_OBJECT(obj)) {
throw MakeTypeError("called_on_non_object", ["Object.getPrototypeOf"]);
}
return %GetPrototype(obj);
return %_GetPrototype(obj);
}
// ES6 section 19.1.2.19.
@ -1361,7 +1361,7 @@ function ObjectIs(obj1, obj2) {
// ECMA-262, Edition 6, section B.2.2.1.1
function ObjectGetProto() {
return %GetPrototype(ToObject(this));
return %_GetPrototype(ToObject(this));
}

View File

@ -24518,3 +24518,56 @@ TEST(TurboAsmDisablesNeuter) {
result = CompileRun(store).As<v8::ArrayBuffer>();
CHECK_EQ(should_be_neuterable, result->IsNeuterable());
}
TEST(GetPrototypeAccessControl) {
i::FLAG_allow_natives_syntax = true;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
LocalContext env;
v8::Handle<v8::ObjectTemplate> obj_template =
v8::ObjectTemplate::New(isolate);
obj_template->SetAccessCheckCallbacks(BlockEverythingNamed,
BlockEverythingIndexed);
env->Global()->Set(v8_str("prohibited"), obj_template->NewInstance());
{
v8::TryCatch try_catch;
CompileRun(
"function f() { %_GetPrototype(prohibited); }"
"%OptimizeFunctionOnNextCall(f);"
"f();");
CHECK(try_catch.HasCaught());
}
}
TEST(GetPrototypeHidden) {
i::FLAG_allow_natives_syntax = true;
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
LocalContext env;
Handle<FunctionTemplate> t = FunctionTemplate::New(isolate);
t->SetHiddenPrototype(true);
Handle<Object> proto = t->GetFunction()->NewInstance();
Handle<Object> object = Object::New(isolate);
Handle<Object> proto2 = Object::New(isolate);
object->SetPrototype(proto);
proto->SetPrototype(proto2);
env->Global()->Set(v8_str("object"), object);
env->Global()->Set(v8_str("proto"), proto);
env->Global()->Set(v8_str("proto2"), proto2);
v8::Handle<v8::Value> result = CompileRun("%_GetPrototype(object)");
CHECK(result->Equals(proto2));
result = CompileRun(
"function f() { return %_GetPrototype(object); }"
"%OptimizeFunctionOnNextCall(f);"
"f()");
CHECK(result->Equals(proto2));
}