[runtime] Properly deal with prototype setup mode during class literal instantiation.

1) Make sure we don't enable prototype setup mode for parent class and its prototype
objects.
2) Make sure we create builtins and their prototypes with completed setup mode.
3) Drive-by-fix: setup typed array classes in bootstrapper.cc instead of typedarray.js,
and drop %FunctionSetPrototype().

Bug: v8:7115, v8:5902
Change-Id: I58ac091d85647abc3307bd47baf48e378e3695c5
Reviewed-on: https://chromium-review.googlesource.com/790992
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49655}
This commit is contained in:
Igor Sheludko 2017-11-28 09:35:49 +01:00 committed by Commit Bot
parent 904c3a1f09
commit 888acb2f3c
12 changed files with 74 additions and 61 deletions

View File

@ -223,8 +223,8 @@ class Genesis BASE_EMBEDDED {
ElementsKind elements_kind);
bool InstallNatives(GlobalContextType context_type);
void InstallTypedArray(const char* name, ElementsKind elements_kind,
Handle<JSFunction>* fun);
Handle<JSFunction> InstallTypedArray(const char* name,
ElementsKind elements_kind);
bool InstallExtraNatives();
bool InstallExperimentalExtraNatives();
bool InstallDebuggerNatives();
@ -403,12 +403,17 @@ V8_NOINLINE Handle<JSFunction> CreateFunction(
builtin_id, IMMUTABLE);
result = isolate->factory()->NewFunction(args);
// Make the JSFunction's prototype object fast.
JSObject::MakePrototypesFast(handle(result->prototype(), isolate),
kStartAtReceiver, isolate);
} else {
NewFunctionArgs args = NewFunctionArgs::ForBuiltinWithoutPrototype(
name, code, builtin_id, LanguageMode::kStrict);
result = isolate->factory()->NewFunction(args);
}
// Make the resulting JSFunction object fast.
JSObject::MakePrototypesFast(result, kStartAtReceiver, isolate);
result->shared()->set_native(true);
return result;
}
@ -3043,8 +3048,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
{ // -- T y p e d A r r a y s
#define INSTALL_TYPED_ARRAY(Type, type, TYPE, ctype, size) \
{ \
Handle<JSFunction> fun; \
InstallTypedArray(#Type "Array", TYPE##_ELEMENTS, &fun); \
Handle<JSFunction> fun = \
InstallTypedArray(#Type "Array", TYPE##_ELEMENTS); \
InstallWithIntrinsicDefaultProto(isolate, fun, \
Context::TYPE##_ARRAY_FUN_INDEX); \
}
@ -3598,8 +3603,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
}
} // NOLINT(readability/fn_size)
void Genesis::InstallTypedArray(const char* name, ElementsKind elements_kind,
Handle<JSFunction>* fun) {
Handle<JSFunction> Genesis::InstallTypedArray(const char* name,
ElementsKind elements_kind) {
Handle<JSObject> global = Handle<JSObject>(native_context()->global_object());
Handle<JSObject> typed_array_prototype =
@ -3607,20 +3612,29 @@ void Genesis::InstallTypedArray(const char* name, ElementsKind elements_kind,
Handle<JSFunction> typed_array_function =
Handle<JSFunction>(isolate()->typed_array_function());
Handle<JSObject> prototype =
factory()->NewJSObject(isolate()->object_function(), TENURED);
Handle<JSFunction> result = InstallFunction(
global, name, JS_TYPED_ARRAY_TYPE, JSTypedArray::kSizeWithEmbedderFields,
0, prototype, Builtins::kIllegal);
0, factory()->the_hole_value(), Builtins::kIllegal);
result->initial_map()->set_elements_kind(elements_kind);
CHECK(JSObject::SetPrototype(result, typed_array_function, false, kDontThrow)
.FromJust());
Handle<Smi> bytes_per_element(
Smi::FromInt(1 << ElementsKindToShiftSize(elements_kind)), isolate());
InstallConstant(isolate(), result, "BYTES_PER_ELEMENT", bytes_per_element);
// Setup prototype object.
DCHECK(result->prototype()->IsJSObject());
Handle<JSObject> prototype(JSObject::cast(result->prototype()), isolate());
CHECK(JSObject::SetPrototype(prototype, typed_array_prototype, false,
kDontThrow)
.FromJust());
*fun = result;
InstallConstant(isolate(), prototype, "BYTES_PER_ELEMENT", bytes_per_element);
return result;
}

View File

@ -453,18 +453,6 @@ function TypedArrayConstructor() {
macro SETUP_TYPED_ARRAY(NAME, ELEMENT_SIZE)
%SetCode(GlobalNAME, NAMEConstructor);
%FunctionSetPrototype(GlobalNAME, new GlobalObject());
%InternalSetPrototype(GlobalNAME, GlobalTypedArray);
%InternalSetPrototype(GlobalNAME.prototype, GlobalTypedArray.prototype);
%AddNamedProperty(GlobalNAME, "BYTES_PER_ELEMENT", ELEMENT_SIZE,
READ_ONLY | DONT_ENUM | DONT_DELETE);
%AddNamedProperty(GlobalNAME.prototype,
"constructor", global.NAME, DONT_ENUM);
%AddNamedProperty(GlobalNAME.prototype,
"BYTES_PER_ELEMENT", ELEMENT_SIZE,
READ_ONLY | DONT_ENUM | DONT_DELETE);
endmacro
TYPED_ARRAYS(SETUP_TYPED_ARRAY)

View File

@ -1411,6 +1411,7 @@ void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) { // NOLINT
os << "\n - registry slot: " << registry_slot();
os << "\n - validity cell: " << Brief(validity_cell());
os << "\n - object create map: " << Brief(object_create_map());
os << "\n - should_be_fast_map: " << should_be_fast_map();
os << "\n";
}

View File

@ -12401,9 +12401,10 @@ void JSObject::MakePrototypesFast(Handle<Object> receiver,
}
// static
void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
bool enable_setup_mode) {
if (object->IsJSGlobalObject()) return;
if (PrototypeBenefitsFromNormalization(object)) {
if (enable_setup_mode && PrototypeBenefitsFromNormalization(object)) {
// First normalize to ensure all JSFunctions are DATA_CONSTANT.
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
"NormalizeAsPrototype");
@ -12657,13 +12658,14 @@ Handle<WeakCell> Map::GetOrCreatePrototypeWeakCell(Handle<JSReceiver> prototype,
}
// static
void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype) {
void Map::SetPrototype(Handle<Map> map, Handle<Object> prototype,
bool enable_prototype_setup_mode) {
RuntimeCallTimerScope stats_scope(*map, &RuntimeCallStats::Map_SetPrototype);
bool is_hidden = false;
if (prototype->IsJSObject()) {
Handle<JSObject> prototype_jsobj = Handle<JSObject>::cast(prototype);
JSObject::OptimizeAsPrototype(prototype_jsobj);
JSObject::OptimizeAsPrototype(prototype_jsobj, enable_prototype_setup_mode);
Object* maybe_constructor = prototype_jsobj->map()->GetConstructor();
if (maybe_constructor->IsJSFunction()) {

View File

@ -2380,7 +2380,8 @@ class JSObject: public JSReceiver {
Handle<Object> value,
PropertyAttributes attributes);
static void OptimizeAsPrototype(Handle<JSObject> object);
static void OptimizeAsPrototype(Handle<JSObject> object,
bool enable_setup_mode = true);
static void ReoptimizeIfPrototype(Handle<JSObject> object);
static void MakePrototypesFast(Handle<Object> receiver,
WhereToStart where_to_start, Isolate* isolate);

View File

@ -498,7 +498,8 @@ class Map : public HeapObject {
// [prototype]: implicit prototype object.
DECL_ACCESSORS(prototype, Object)
// TODO(jkummerow): make set_prototype private.
static void SetPrototype(Handle<Map> map, Handle<Object> prototype);
static void SetPrototype(Handle<Map> map, Handle<Object> prototype,
bool enable_prototype_setup_mode = true);
// [constructor]: points back to the function or FunctionTemplateInfo
// responsible for this map.

View File

@ -450,7 +450,6 @@ bool InitClassPrototype(Isolate* isolate,
Map::SetPrototype(map, prototype_parent);
constructor->set_prototype_or_initial_map(*prototype);
map->SetConstructor(*constructor);
Map::SetShouldBeFastPrototypeMap(map, true, isolate);
Handle<FixedArray> computed_properties(
class_boilerplate->instance_computed_properties(), isolate);
@ -467,7 +466,7 @@ bool InitClassPrototype(Isolate* isolate,
map->set_dictionary_map(true);
map->set_migration_target(false);
map->set_may_have_interesting_symbols(true);
// map->set_construction_counter(Map::kNoSlackTracking);
map->set_construction_counter(Map::kNoSlackTracking);
// We care about name property only for class constructor.
const bool install_name_accessor = false;
@ -496,13 +495,11 @@ bool InitClassConstructor(Isolate* isolate,
Handle<Map> map(constructor->map(), isolate);
map = Map::CopyDropDescriptors(map);
DCHECK(map->is_prototype_map());
Map::SetShouldBeFastPrototypeMap(map, true, isolate);
if (!constructor_parent.is_null()) {
// Set map's prototype without triggering JSObject::OptimizeAsPrototype()
// for parent class.
// map->set_prototype(*constructor_parent);
Map::SetPrototype(map, constructor_parent);
// Set map's prototype without enabling prototype setup mode for superclass
// because it does not make sense.
Map::SetPrototype(map, constructor_parent, false);
}
Handle<NumberDictionary> elements_dictionary_template(

View File

@ -97,18 +97,6 @@ RUNTIME_FUNCTION(Runtime_FunctionSetLength) {
}
RUNTIME_FUNCTION(Runtime_FunctionSetPrototype) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
CHECK(fun->IsConstructor());
JSFunction::SetPrototype(fun, value);
return args[0]; // return TOS
}
RUNTIME_FUNCTION(Runtime_FunctionIsAPIFunction) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());

View File

@ -232,7 +232,6 @@ namespace internal {
F(FunctionGetScriptSourcePosition, 1, 1) \
F(FunctionGetContextData, 1, 1) \
F(FunctionSetLength, 2, 1) \
F(FunctionSetPrototype, 2, 1) \
F(FunctionIsAPIFunction, 1, 1) \
F(SetCode, 2, 1) \
F(SetNativeFlag, 1, 1) \

View File

@ -243,10 +243,6 @@ var migrations = [
name: "set prototype {}",
migr: function(o, i) { o.__proto__ = {}; },
},
{
name: "%FunctionSetPrototype",
migr: function(o, i) { %FunctionSetPrototype(o, null); },
},
{
name: "modify prototype",
migr: function(o, i) { if (i == 0) o.__proto__.__proto1__ = [,,,5,,,]; },

View File

@ -51,12 +51,5 @@ Object.getOwnPropertyNames(global).forEach(function(name) {
`${name}.prototype.constructor`);
});
// This is the current set of dictionary mode objects.
// Remove items as we fix them. See issue 5902.
assertEquals(
[
'Error.prototype',
'EvalError.prototype', 'RangeError.prototype', 'ReferenceError.prototype',
'SyntaxError.prototype', 'TypeError.prototype', 'URIError.prototype'
],
log);
// There should be no dictionary mode builtin objects.
assertEquals([], log);

View File

@ -0,0 +1,33 @@
// Copyright 2017 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.
// Flags: --allow-natives-syntax
function TestBuiltinSubclassing(Builtin) {
assertTrue(%HasFastProperties(Builtin));
assertTrue(%HasFastProperties(Builtin.prototype));
assertTrue(%HasFastProperties(Builtin.prototype.__proto__));
class SubClass extends Builtin {}
assertTrue(%HasFastProperties(Builtin));
assertTrue(%HasFastProperties(Builtin.prototype));
assertTrue(%HasFastProperties(Builtin.prototype.__proto__));
}
let TypedArray = Uint8Array.__proto__;
TestBuiltinSubclassing(RegExp);
TestBuiltinSubclassing(Promise);
TestBuiltinSubclassing(Array);
TestBuiltinSubclassing(TypedArray);
TestBuiltinSubclassing(Uint8Array);
TestBuiltinSubclassing(Int8Array);
TestBuiltinSubclassing(Uint16Array);
TestBuiltinSubclassing(Int16Array);
TestBuiltinSubclassing(Uint32Array);
TestBuiltinSubclassing(Int32Array);
TestBuiltinSubclassing(Float32Array);
TestBuiltinSubclassing(Float64Array);
TestBuiltinSubclassing(Uint8ClampedArray);