[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:
parent
904c3a1f09
commit
888acb2f3c
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -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());
|
||||
|
@ -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) \
|
||||
|
@ -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,,,]; },
|
||||
|
@ -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);
|
||||
|
33
test/mjsunit/regress/regress-7115.js
Normal file
33
test/mjsunit/regress/regress-7115.js
Normal 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);
|
Loading…
Reference in New Issue
Block a user