Ignore:
Timestamp:
Oct 26, 2018, 11:30:13 AM (7 years ago)
Author:
[email protected]
Message:

Fix missing edge cases with JSGlobalObjects having a bad time.
https://bugs.webkit.org/show_bug.cgi?id=189028
<rdar://problem/45204939>

Reviewed by Saam Barati.

JSTests:

  • stress/regress-189028.js: Added.

Source/JavaScriptCore:

Consider the following scenario:

let object O1 (of global G1) have an indexing type that is not SlowPut.
let global G2 have a bad time.
let object O2 (of global G2) be set as the prototype of O1.
let object O3 (of global G2) have indexed accessors.

In the existing code, if we set O3 as O2's prototype, we'll have a bug where
O1 will not be made aware that that there are indexed accessors in its prototype
chain.

In this patch, we solve this issue by introducing a new invariant:

A prototype chain is considered to possibly have indexed accessors if any
object in the chain belongs to a global object that is having a bad time.

We apply this invariant as follows:

  1. Enhance JSGlobalObject::haveABadTime() to also check if other global objects are affected by it having a bad time. If so, it also ensures that those affected global objects have a bad time.

The original code for JSGlobalObject::haveABadTime() uses a ObjectsWithBrokenIndexingFinder
to find all objects affected by the global object having a bad time. We enhance
ObjectsWithBrokenIndexingFinder to also check for the possibility that any global
objects may be affected by other global objects having a bad time i.e.

let g1 = global1
let g2 = global2
let o1 = an object in g1
let o2 = an object in g2

let g1 have a bad time
g2 is affected if

o1 is in the prototype chain of o2,
and o2 may be a prototype.

If the ObjectsWithBrokenIndexingFinder does find the possibility of other global
objects being affected, it will abort its heap scan and let haveABadTime() take
a slow path to do a more complete multi global object scan.

The slow path works as follows:

  1. Iterate the heap and record the graph of all global object dependencies.

For each global object, record the list of other global objects that are
affected by it.

  1. Compute a list of global objects that need to have a bad time using the current global object dependency graph.
  1. For each global object in the list of affected global objects, fire their HaveABadTime watchpoint and convert all their array structures to the SlowPut alternatives.
  1. Re-run ObjectsWithBrokenIndexingFinder to find all objects that are affected by any of the globals in the list from (2).
  1. Enhance Structure::mayInterceptIndexedAccesses() to also return true if the structure's global object is having a bad time.

Note: there are 3 scenarios that we need to consider:

let g1 = global1
let g2 = global2
let o1 = an object in g1
let o2 = an object in g2

Scenario 1: o2 is a prototype, and

g1 has a bad time after o1 is inserted into the o2's prototype chain.

Scenario 2: o2 is a prototype, and

o1 is inserted into the o2's prototype chain after g1 has a bad time.

Scenario 3: o2 is NOT a prototype, and

o1 is inserted into the o2's prototype chain after g1 has a bad time.

For scenario 1, when g1 has a bad time, we need to also make sure g2 has
a bad time. This is handled by enhancement 1 above.

For scenario 2, when o1 is inserted into o2's prototype chain, we need to check
if o1's global object has a bad time. If so, then we need to make sure o2's
global also has a bad time (because o2 is a prototype) and convert o2's
storage type to SlowPut. This is handled by enhancement 2 above in conjunction
with JSObject::setPrototypeDirect().

For scenario 3, when o1 is inserted into o2's prototype chain, we need to check
if o1's global object has a bad time. If so, then we only need to convert o2's
storage type to SlowPut (because o2 is NOT a prototype). This is handled by
enhancement 2 above.

  1. Also add $vm.isHavingABadTime(), $vm.createGlobalObject() to enable us to write some tests for this issue.
  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::fireWatchpointAndMakeAllArrayStructuresSlowPut):
(JSC::JSGlobalObject::haveABadTime):

  • runtime/JSGlobalObject.h:
  • runtime/JSObject.h:

(JSC::JSObject::mayInterceptIndexedAccesses): Deleted.

  • runtime/JSObjectInlines.h:

(JSC::JSObject::mayInterceptIndexedAccesses):

  • runtime/Structure.h:
  • runtime/StructureInlines.h:

(JSC::Structure::mayInterceptIndexedAccesses const):

  • tools/JSDollarVM.cpp:

(JSC::functionHaveABadTime):
(JSC::functionIsHavingABadTime):
(JSC::functionCreateGlobalObject):
(JSC::JSDollarVM::finishCreation):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp

    r237080 r237469  
    16521652}
    16531653
     1654// Make the globalObject have a bad time. Does nothing if the object is not a JSGlobalObject.
     1655// Usage: $vm.haveABadTime(globalObject)
     1656static EncodedJSValue JSC_HOST_CALL functionHaveABadTime(ExecState* exec)
     1657{
     1658    VM& vm = exec->vm();
     1659    JSLockHolder lock(vm);
     1660    JSValue objValue = exec->argument(0);
     1661    if (!objValue.isObject())
     1662        return JSValue::encode(jsBoolean(false));
     1663
     1664    JSObject* obj = asObject(objValue.asCell());
     1665    JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(vm, obj);
     1666    if (!globalObject)
     1667        JSValue::encode(jsBoolean(false));
     1668
     1669    globalObject->haveABadTime(vm);
     1670    return JSValue::encode(jsBoolean(true));
     1671}
     1672
     1673// Checks if the object (or its global if the object is not a global) is having a bad time.
     1674// Usage: $vm.isHavingABadTime(obj)
     1675static EncodedJSValue JSC_HOST_CALL functionIsHavingABadTime(ExecState* exec)
     1676{
     1677    VM& vm = exec->vm();
     1678    JSLockHolder lock(vm);
     1679    JSValue objValue = exec->argument(0);
     1680    if (!objValue.isObject())
     1681        return JSValue::encode(jsUndefined());
     1682
     1683    JSObject* obj = asObject(objValue.asCell());
     1684    JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(vm, obj);
     1685    if (globalObject)
     1686        JSValue::encode(jsBoolean(globalObject->isHavingABadTime()));
     1687
     1688    globalObject = obj->globalObject();
     1689    if (!globalObject)
     1690        return JSValue::encode(jsUndefined());
     1691
     1692    return JSValue::encode(jsBoolean(globalObject->isHavingABadTime()));
     1693}
     1694
     1695// Creates a new global object.
     1696// Usage: $vm.createGlobalObject()
     1697static EncodedJSValue JSC_HOST_CALL functionCreateGlobalObject(ExecState* exec)
     1698{
     1699    VM& vm = exec->vm();
     1700    JSLockHolder lock(vm);
     1701    JSGlobalObject* globalObject = JSGlobalObject::create(vm, JSGlobalObject::createStructure(vm, jsNull()));
     1702    return JSValue::encode(globalObject);
     1703}
     1704
    16541705static EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState* exec)
    16551706{
     
    21452196    addFunction(vm, "getpid", functionGetPID, 0);
    21462197
     2198    addFunction(vm, "haveABadTime", functionHaveABadTime, 1);
     2199    addFunction(vm, "isHavingABadTime", functionIsHavingABadTime, 1);
     2200
     2201    addFunction(vm, "createGlobalObject", functionCreateGlobalObject, 0);
    21472202    addFunction(vm, "createProxy", functionCreateProxy, 1);
    21482203    addFunction(vm, "createRuntimeArray", functionCreateRuntimeArray, 0);
Note: See TracChangeset for help on using the changeset viewer.