Modified: trunk/Source/_javascript_Core/runtime/JSFunction.cpp (283166 => 283167)
--- trunk/Source/_javascript_Core/runtime/JSFunction.cpp 2021-09-28 13:21:15 UTC (rev 283166)
+++ trunk/Source/_javascript_Core/runtime/JSFunction.cpp 2021-09-28 13:37:17 UTC (rev 283167)
@@ -296,6 +296,26 @@
return callData;
}
+static constexpr unsigned prototypeAttributesForNonClass = PropertyAttribute::DontEnum | PropertyAttribute::DontDelete;
+
+static inline JSObject* constructPrototypeObject(JSGlobalObject* globalObject, JSFunction* thisObject)
+{
+ VM& vm = globalObject->vm();
+ JSGlobalObject* scopeGlobalObject = thisObject->scope()->globalObject();
+ // Unlike Function instances, the prototype object of GeneratorFunction instances lacks own "constructor" property.
+ // https://tc39.es/ecma262/#sec-runtime-semantics-instantiategeneratorfunctionobject (step 6)
+ if (isGeneratorWrapperParseMode(thisObject->jsExecutable()->parseMode()))
+ return constructEmptyObject(globalObject, scopeGlobalObject->generatorPrototype());
+ // Unlike Function instances, the prototype object of AsyncGeneratorFunction instances lacks own "constructor" property.
+ // https://tc39.es/ecma262/#sec-runtime-semantics-instantiateasyncgeneratorfunctionobject (step 6)
+ if (isAsyncGeneratorWrapperParseMode(thisObject->jsExecutable()->parseMode()))
+ return constructEmptyObject(globalObject, scopeGlobalObject->asyncGeneratorPrototype());
+
+ JSObject* prototype = constructEmptyObject(globalObject, scopeGlobalObject->objectPrototype());
+ prototype->putDirect(vm, vm.propertyNames->constructor, thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum));
+ return prototype;
+}
+
bool JSFunction::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot)
{
VM& vm = globalObject->vm();
@@ -308,32 +328,13 @@
RELEASE_AND_RETURN(scope, Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot));
}
- if (propertyName == vm.propertyNames->prototype && thisObject->jsExecutable()->hasPrototypeProperty() && !thisObject->jsExecutable()->isClassConstructorFunction()) {
- // NOTE: class constructors define the prototype property in bytecode using
- // defineOwnProperty, which ends up calling into this code (see our defineOwnProperty
- // implementation below). The bytecode will end up doing the proper definition
- // with the property being non-writable/non-configurable. However, we must ignore
- // the initial materialization of the property so that the defineOwnProperty call
- // from bytecode succeeds. Otherwise, the materialization here would prevent the
- // defineOwnProperty from being able to overwrite the property.
+ if (propertyName == vm.propertyNames->prototype && thisObject->jsExecutable()->hasPrototypeProperty()) {
unsigned attributes;
PropertyOffset offset = thisObject->getDirectOffset(vm, propertyName, attributes);
if (!isValidOffset(offset)) {
- JSGlobalObject* scopeGlobalObject = thisObject->scope()->globalObject();
- JSObject* prototype = nullptr;
- if (isGeneratorWrapperParseMode(thisObject->jsExecutable()->parseMode())) {
- // Unlike function instances, the object that is the value of the a GeneratorFunction's prototype
- // property does not have a constructor property whose value is the GeneratorFunction instance.
- // https://tc39.github.io/ecma262/#sec-generatorfunction-instances-prototype
- prototype = constructEmptyObject(globalObject, scopeGlobalObject->generatorPrototype());
- } else if (isAsyncGeneratorWrapperParseMode(thisObject->jsExecutable()->parseMode()))
- prototype = constructEmptyObject(globalObject, scopeGlobalObject->asyncGeneratorPrototype());
- else {
- prototype = constructEmptyObject(globalObject, scopeGlobalObject->objectPrototype());
- prototype->putDirect(vm, vm.propertyNames->constructor, thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum));
- }
-
- thisObject->putDirect(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontDelete | PropertyAttribute::DontEnum);
+ // For class constructors, prototype object is initialized from bytecode via defineOwnProperty().
+ ASSERT(!thisObject->jsExecutable()->isClassConstructorFunction());
+ thisObject->putDirect(vm, propertyName, constructPrototypeObject(globalObject, thisObject), prototypeAttributesForNonClass);
offset = thisObject->getDirectOffset(vm, vm.propertyNames->prototype, attributes);
ASSERT(isValidOffset(offset));
}
@@ -385,16 +386,18 @@
RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, propertyName, value, slot));
}
- if (propertyName == vm.propertyNames->prototype) {
+ if (propertyName == vm.propertyNames->prototype && thisObject->jsExecutable()->hasPrototypeProperty()) {
slot.disableCaching();
- // Make sure prototype has been reified, such that it can only be overwritten
- // following the rules set out in ECMA-262 8.12.9.
- PropertySlot getSlot(thisObject, PropertySlot::InternalMethodType::VMInquiry, &vm);
- thisObject->methodTable(vm)->getOwnPropertySlot(thisObject, globalObject, propertyName, getSlot);
- RETURN_IF_EXCEPTION(scope, false);
if (FunctionRareData* rareData = thisObject->rareData())
rareData->clear("Store to prototype property of a function");
- getSlot.disallowVMEntry.reset();
+ if (!isValidOffset(thisObject->getDirectOffset(vm, propertyName))) {
+ // For class constructors, prototype object is initialized from bytecode via defineOwnProperty().
+ ASSERT(!thisObject->jsExecutable()->isClassConstructorFunction());
+ if (UNLIKELY(slot.thisValue() != thisObject))
+ RELEASE_AND_RETURN(scope, JSObject::definePropertyOnReceiver(globalObject, propertyName, value, slot));
+ thisObject->putDirect(vm, propertyName, value, prototypeAttributesForNonClass);
+ return true;
+ }
RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, propertyName, value, slot));
}
@@ -454,15 +457,18 @@
RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, throwException));
}
- if (propertyName == vm.propertyNames->prototype) {
- // Make sure prototype has been reified, such that it can only be overwritten
- // following the rules set out in ECMA-262 8.12.9.
- PropertySlot slot(thisObject, PropertySlot::InternalMethodType::VMInquiry, &vm);
- thisObject->methodTable(vm)->getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
- RETURN_IF_EXCEPTION(scope, false);
+ if (propertyName == vm.propertyNames->prototype && thisObject->jsExecutable()->hasPrototypeProperty()) {
if (FunctionRareData* rareData = thisObject->rareData())
rareData->clear("Store to prototype property of a function");
- slot.disallowVMEntry.reset();
+ if (!isValidOffset(thisObject->getDirectOffset(vm, propertyName))) {
+ if (thisObject->jsExecutable()->isClassConstructorFunction()) {
+ // Fast path for prototype object initialization from bytecode that avoids calling into getOwnPropertySlot().
+ ASSERT(descriptor.isDataDescriptor());
+ thisObject->putDirect(vm, propertyName, descriptor.value(), descriptor.attributes());
+ return true;
+ }
+ thisObject->putDirect(vm, propertyName, constructPrototypeObject(globalObject, thisObject), prototypeAttributesForNonClass);
+ }
} else {
thisObject->reifyLazyPropertyIfNeeded(vm, globalObject, propertyName);
RETURN_IF_EXCEPTION(scope, false);