Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions JSTests/stress/poly-proto-typed-array-case.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const typedArray = new Uint8Array();
let key = '0.1';
Object.prototype[key] = undefined;

function bar() {
function Foo() {}

for (let i = 0; i < 100; i++) {
let foo = new Foo();
typedArray.__proto__ = foo;
let {x} = foo;
}
typedArray[key];
}

for (let i = 0; i < 100; i++) {
bar();
}
22 changes: 22 additions & 0 deletions JSTests/stress/typed-array-canonical.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error('bad value: ' + actual);
}

Object.prototype["Infinity"] = 42;
Object.prototype["infinity"] = 42;
Object.prototype["NaN"] = 42;
Object.prototype["nan"] = 42;
Object.prototype["-Infinity"] = 42;
Object.prototype["-infinity"] = 42;
Object.prototype["-NaN"] = 42;
Object.prototype["-nan"] = 42;
var array = new Uint8Array(42);
shouldBe(array["Infinity"], undefined);
shouldBe(array["infinity"], 42);
shouldBe(array["NaN"], undefined);
shouldBe(array["nan"], 42);
shouldBe(array["-Infinity"], undefined);
shouldBe(array["-infinity"], 42);
shouldBe(array["-NaN"], 42);
shouldBe(array["-nan"], 42);
48 changes: 31 additions & 17 deletions Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ ObjectPropertyCondition generateCondition(
}

template<typename Functor>
ObjectPropertyConditionSet generateConditions(JSGlobalObject* globalObject, Structure* structure, JSObject* prototype, const Functor& functor)
ObjectPropertyConditionSet generateConditions(JSGlobalObject* globalObject, Structure* structure, JSObject* prototype, UniquedStringImpl* uid, const Functor& functor)
{
Vector<ObjectPropertyCondition, 8> conditions;

Expand All @@ -286,6 +286,12 @@ ObjectPropertyConditionSet generateConditions(JSGlobalObject* globalObject, Stru
// https://bugs.webkit.org/show_bug.cgi?id=177721
return ObjectPropertyConditionSet::invalid();
}

// TypedArray has an ability to stop [[Prototype]] traversing for numeric index string (e.g. "0.1").
// If we found it, then traverse should stop for Unset case.
// https://262.ecma-international.org/9.0/#_ref_2826
if (!prototype && isTypedArrayType(structure->typeInfo().type()) && uid && isCanonicalNumericIndexString(uid))
break;

JSValue value = structure->prototypeForLookup(globalObject);

Expand Down Expand Up @@ -333,7 +339,7 @@ ObjectPropertyConditionSet generateConditionsForPropertyMiss(
VM& vm, JSCell* owner, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
{
return generateConditions(
globalObject, headStructure, nullptr,
globalObject, headStructure, nullptr, uid,
[&](auto& conditions, JSObject* object, Structure* structure) -> bool {
ObjectPropertyCondition result =
generateCondition(vm, owner, object, structure, uid, PropertyCondition::Absence, Concurrency::MainThread);
Expand All @@ -348,7 +354,7 @@ ObjectPropertyConditionSet generateConditionsForPropertySetterMiss(
VM& vm, JSCell* owner, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
{
return generateConditions(
globalObject, headStructure, nullptr,
globalObject, headStructure, nullptr, uid,
[&](auto& conditions, JSObject* object, Structure* structure) -> bool {
ObjectPropertyCondition result =
generateCondition(vm, owner, object, structure, uid, PropertyCondition::AbsenceOfSetEffect, Concurrency::MainThread);
Expand All @@ -362,7 +368,7 @@ ObjectPropertyConditionSet generateConditionsForPropertySetterMiss(
ObjectPropertyConditionSet generateConditionsForIndexedMiss(VM& vm, JSCell* owner, JSGlobalObject* globalObject, Structure* headStructure)
{
return generateConditions(
globalObject, headStructure, nullptr,
globalObject, headStructure, nullptr, nullptr,
[&](auto& conditions, JSObject* object, Structure* structure) -> bool {
ObjectPropertyCondition result =
generateCondition(vm, owner, object, structure, nullptr, PropertyCondition::AbsenceOfIndexedProperties, Concurrency::MainThread);
Expand All @@ -378,7 +384,7 @@ ObjectPropertyConditionSet generateConditionsForPrototypePropertyHit(
UniquedStringImpl* uid)
{
return generateConditions(
globalObject, headStructure, prototype,
globalObject, headStructure, prototype, uid,
[&](auto& conditions, JSObject* object, Structure* structure) -> bool {
PropertyCondition::Kind kind =
object == prototype ? PropertyCondition::Presence : PropertyCondition::Absence;
Expand All @@ -396,7 +402,7 @@ ObjectPropertyConditionSet generateConditionsForPrototypePropertyHitCustom(
UniquedStringImpl* uid, unsigned attributes)
{
return generateConditions(
globalObject, headStructure, prototype,
globalObject, headStructure, prototype, uid,
[&](auto& conditions, JSObject* object, Structure* structure) -> bool {
auto kind = PropertyCondition::Absence;
if (object == prototype) {
Expand Down Expand Up @@ -446,7 +452,7 @@ ObjectPropertyConditionSet generateConditionsForInstanceOf(
if (ObjectPropertyConditionSetInternal::verbose)
dataLog("Searching for prototype ", JSValue(prototype), " starting with structure ", RawPointer(headStructure), " with shouldHit = ", shouldHit, "\n");
ObjectPropertyConditionSet result = generateConditions(
globalObject, headStructure, shouldHit ? prototype : nullptr,
globalObject, headStructure, shouldHit ? prototype : nullptr, nullptr,
[&](auto& conditions, JSObject* object, Structure* structure) -> bool {
if (ObjectPropertyConditionSetInternal::verbose)
dataLog("Encountered object: ", RawPointer(object), "\n");
Expand Down Expand Up @@ -474,7 +480,7 @@ ObjectPropertyConditionSet generateConditionsForInstanceOf(
ObjectPropertyConditionSet generateConditionsForPrototypeEquivalenceConcurrently(
VM& vm, JSGlobalObject* globalObject, Structure* headStructure, JSObject* prototype, UniquedStringImpl* uid)
{
return generateConditions(globalObject, headStructure, prototype,
return generateConditions(globalObject, headStructure, prototype, uid,
[&](auto& conditions, JSObject* object, Structure* structure) -> bool {
PropertyCondition::Kind kind =
object == prototype ? PropertyCondition::Equivalence : PropertyCondition::Absence;
Expand All @@ -490,7 +496,7 @@ ObjectPropertyConditionSet generateConditionsForPropertyMissConcurrently(
VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
{
return generateConditions(
globalObject, headStructure, nullptr,
globalObject, headStructure, nullptr, uid,
[&](auto& conditions, JSObject* object, Structure* structure) -> bool {
ObjectPropertyCondition result = generateCondition(vm, nullptr, object, structure, uid, PropertyCondition::Absence, Concurrency::ConcurrentThread);
if (!result)
Expand All @@ -504,7 +510,7 @@ ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently(
VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
{
return generateConditions(
globalObject, headStructure, nullptr,
globalObject, headStructure, nullptr, uid,
[&](auto& conditions, JSObject* object, Structure* structure) -> bool {
ObjectPropertyCondition result =
generateCondition(vm, nullptr, object, structure, uid, PropertyCondition::AbsenceOfSetEffect, Concurrency::ConcurrentThread);
Expand All @@ -522,7 +528,7 @@ ObjectPropertyCondition generateConditionForSelfEquivalence(
}

// Current might be null. Structure can't be null.
static std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject* globalObject, JSCell* current, Structure* structure, JSObject* target)
static std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject* globalObject, JSCell* current, Structure* structure, UniquedStringImpl* propertyName, JSObject* target)
{
ASSERT(structure);
VM& vm = globalObject->vm();
Expand Down Expand Up @@ -555,6 +561,14 @@ static std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGloba
break;
}

// TypedArray has an ability to stop [[Prototype]] traversing for numeric index string (e.g. "0.1").
// If we found it, then traverse should stop for Unset case.
// https://262.ecma-international.org/9.0/#_ref_2826
if (!target && propertyName && isTypedArrayType(structure->typeInfo().type()) && isCanonicalNumericIndexString(propertyName)) {
found = true;
break;
}

// We only have poly proto if we need to access our prototype via
// the poly proto protocol. If the slot base is the only poly proto
// thing in the chain, and we have a cache hit on it, then we're not
Expand Down Expand Up @@ -584,20 +598,20 @@ static std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGloba
return result;
}

std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject* globalObject, JSCell* base, JSObject* target)
std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject* globalObject, JSCell* base, UniquedStringImpl* propertyName, JSObject* target)
{
return prepareChainForCaching(globalObject, base, base->structure(), target);
return prepareChainForCaching(globalObject, base, base->structure(), propertyName, target);
}

std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject* globalObject, JSCell* base, const PropertySlot& slot)
std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject* globalObject, JSCell* base, UniquedStringImpl* propertyName, const PropertySlot& slot)
{
JSObject* target = slot.isUnset() ? nullptr : slot.slotBase();
return prepareChainForCaching(globalObject, base, target);
return prepareChainForCaching(globalObject, base, propertyName, target);
}

std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject* globalObject, Structure* baseStructure, JSObject* target)
std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject* globalObject, Structure* baseStructure, UniquedStringImpl* propertyName, JSObject* target)
{
return prepareChainForCaching(globalObject, nullptr, baseStructure, target);
return prepareChainForCaching(globalObject, nullptr, baseStructure, propertyName, target);
}

} // namespace JSC
Expand Down
6 changes: 3 additions & 3 deletions Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ struct PrototypeChainCachingStatus {
bool flattenedDictionary;
};

std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject*, JSCell* base, const PropertySlot&);
std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject*, JSCell* base, JSObject* target);
std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject*, Structure* base, JSObject* target);
std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject*, JSCell* base, UniquedStringImpl*, const PropertySlot&);
std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject*, JSCell* base, UniquedStringImpl*, JSObject* target);
std::optional<PrototypeChainCachingStatus> prepareChainForCaching(JSGlobalObject*, Structure* base, UniquedStringImpl*, JSObject* target);

} // namespace JSC
14 changes: 11 additions & 3 deletions Source/JavaScriptCore/bytecode/PolyProtoAccessChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@

namespace JSC {

RefPtr<PolyProtoAccessChain> PolyProtoAccessChain::tryCreate(JSGlobalObject* globalObject, JSCell* base, const PropertySlot& slot)
RefPtr<PolyProtoAccessChain> PolyProtoAccessChain::tryCreate(JSGlobalObject* globalObject, JSCell* base, CacheableIdentifier propertyName, const PropertySlot& slot)
{
JSObject* target = slot.isUnset() ? nullptr : slot.slotBase();
return tryCreate(globalObject, base, target);
return tryCreate(globalObject, base, propertyName, target);
}

RefPtr<PolyProtoAccessChain> PolyProtoAccessChain::tryCreate(JSGlobalObject* globalObject, JSCell* base, JSObject* target)
RefPtr<PolyProtoAccessChain> PolyProtoAccessChain::tryCreate(JSGlobalObject* globalObject, JSCell* base, CacheableIdentifier propertyName, JSObject* target)
{
JSCell* current = base;

Expand Down Expand Up @@ -67,6 +67,14 @@ RefPtr<PolyProtoAccessChain> PolyProtoAccessChain::tryCreate(JSGlobalObject* glo
break;
}

// TypedArray has an ability to stop [[Prototype]] traversing for numeric index string (e.g. "0.1").
// If we found it, then traverse should stop for Unset case.
// https://262.ecma-international.org/9.0/#_ref_2826
if (!target && isTypedArrayType(structure->typeInfo().type()) && isCanonicalNumericIndexString(propertyName.uid())) {
found = true;
break;
}

JSValue prototype = structure->prototypeForLookup(globalObject, current);
if (prototype.isNull())
break;
Expand Down
5 changes: 3 additions & 2 deletions Source/JavaScriptCore/bytecode/PolyProtoAccessChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#pragma once

#include "CacheableIdentifier.h"
#include "StructureID.h"
#include "VM.h"
#include <wtf/FixedVector.h>
Expand All @@ -41,8 +42,8 @@ class Structure;
class PolyProtoAccessChain final : public ThreadSafeRefCounted<PolyProtoAccessChain> {
public:
// Returns nullptr when invalid.
static RefPtr<PolyProtoAccessChain> tryCreate(JSGlobalObject*, JSCell* base, const PropertySlot&);
static RefPtr<PolyProtoAccessChain> tryCreate(JSGlobalObject*, JSCell* base, JSObject* target);
static RefPtr<PolyProtoAccessChain> tryCreate(JSGlobalObject*, JSCell* base, CacheableIdentifier, const PropertySlot&);
static RefPtr<PolyProtoAccessChain> tryCreate(JSGlobalObject*, JSCell* base, CacheableIdentifier, JSObject* target);

const FixedVector<StructureID>& chain() const { return m_chain; }

Expand Down
Loading