Changeset 275544 in webkit


Ignore:
Timestamp:
Apr 6, 2021 1:20:41 PM (3 years ago)
Author:
Alexey Shvayka
Message:

Array's toString() is incorrect if join() is non-callable
https://bugs.webkit.org/show_bug.cgi?id=224215

Reviewed by Yusuke Suzuki.

JSTests:

  • stress/array-toString-non-callable-join.js: Added.

Source/JavaScriptCore:

This patch exposes objectPrototypeToString() to be used by Array.prototype.toString
if "join" lookup doesn't return a callable value [1].

Fixes Array's toString() to return the correct tag instead of internal className,
perform Symbol.toStringTag lookup, and throw for revoked Proxy objects.
Aligns JSC with V8 and SpiderMonkey.

Also, a few objectPrototypeToString() tweaks: a bit nicer undefined / null
checks and simpler toObject() exception handling.

[1]: https://tc39.es/ecma262/#sec-array.prototype.tostring (step 3)

  • runtime/ArrayPrototype.cpp:

(JSC::JSC_DEFINE_HOST_FUNCTION):

  • runtime/ObjectPrototype.cpp:

(JSC::objectPrototypeToString):
(JSC::JSC_DEFINE_HOST_FUNCTION):

  • runtime/ObjectPrototype.h:
Location:
trunk
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r275472 r275544  
     12021-04-06  Alexey Shvayka  <shvaikalesh@gmail.com>
     2
     3        Array's toString() is incorrect if join() is non-callable
     4        https://bugs.webkit.org/show_bug.cgi?id=224215
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        * stress/array-toString-non-callable-join.js: Added.
     9
    1102021-04-05  Keith Miller  <keith_miller@apple.com>
    211
  • trunk/Source/JavaScriptCore/ChangeLog

    r275542 r275544  
     12021-04-06  Alexey Shvayka  <shvaikalesh@gmail.com>
     2
     3        Array's toString() is incorrect if join() is non-callable
     4        https://bugs.webkit.org/show_bug.cgi?id=224215
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        This patch exposes objectPrototypeToString() to be used by Array.prototype.toString
     9        if "join" lookup doesn't return a callable value [1].
     10
     11        Fixes Array's toString() to return the correct tag instead of internal `className`,
     12        perform Symbol.toStringTag lookup, and throw for revoked Proxy objects.
     13        Aligns JSC with V8 and SpiderMonkey.
     14
     15        Also, a few objectPrototypeToString() tweaks: a bit nicer `undefined` / `null`
     16        checks and simpler toObject() exception handling.
     17
     18        [1]: https://tc39.es/ecma262/#sec-array.prototype.tostring (step 3)
     19
     20        * runtime/ArrayPrototype.cpp:
     21        (JSC::JSC_DEFINE_HOST_FUNCTION):
     22        * runtime/ObjectPrototype.cpp:
     23        (JSC::objectPrototypeToString):
     24        (JSC::JSC_DEFINE_HOST_FUNCTION):
     25        * runtime/ObjectPrototype.h:
     26
    1272021-04-06  Yusuke Suzuki  <ysuzuki@apple.com>
    228
  • trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp

    r272471 r275544  
    3535#include "JSStringJoiner.h"
    3636#include "ObjectConstructor.h"
     37#include "ObjectPrototype.h"
    3738#include "StringRecursionChecker.h"
    3839#include <algorithm>
     
    613614    if (!canUseDefaultArrayJoinForToString(vm, thisObject)) {
    614615        // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join".
    615         JSValue function = JSValue(thisObject).get(globalObject, vm.propertyNames->join);
     616        JSValue function = thisObject->get(globalObject, vm.propertyNames->join);
    616617        RETURN_IF_EXCEPTION(scope, encodedJSValue());
    617618
    618619        // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
    619         bool customJoinCase = false;
    620         if (!function.isCell())
    621             customJoinCase = true;
    622620        auto callData = getCallData(vm, function);
    623         if (callData.type == CallData::Type::None)
    624             customJoinCase = true;
    625 
    626         if (UNLIKELY(customJoinCase))
    627             RELEASE_AND_RETURN(scope, JSValue::encode(jsMakeNontrivialString(globalObject, "[object ", thisObject->methodTable(vm)->className(thisObject, vm), "]")));
     621        if (UNLIKELY(callData.type == CallData::Type::None))
     622            RELEASE_AND_RETURN(scope, JSValue::encode(objectPrototypeToString(globalObject, thisObject)));
    628623
    629624        // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
  • trunk/Source/JavaScriptCore/runtime/ObjectPrototype.cpp

    r267594 r275544  
    312312}
    313313
    314 JSC_DEFINE_HOST_FUNCTION(objectProtoFuncToString, (JSGlobalObject* globalObject, CallFrame* callFrame))
    315 {
    316     VM& vm = globalObject->vm();
    317     auto scope = DECLARE_THROW_SCOPE(vm);
    318 
    319     JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict());
    320     if (thisValue.isUndefinedOrNull())
    321         return JSValue::encode(thisValue.isUndefined() ? vm.smallStrings.undefinedObjectString() : vm.smallStrings.nullObjectString());
     314JSString* objectPrototypeToString(JSGlobalObject* globalObject, JSValue thisValue)
     315{
     316    VM& vm = globalObject->vm();
     317    auto scope = DECLARE_THROW_SCOPE(vm);
     318
     319    if (thisValue.isUndefined())
     320        return vm.smallStrings.undefinedObjectString();
     321    if (thisValue.isNull())
     322        return vm.smallStrings.nullObjectString();
    322323    JSObject* thisObject = thisValue.toObject(globalObject);
    323     EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
    324     if (!thisObject)
    325         return JSValue::encode(jsUndefined());
     324    scope.assertNoException();
    326325
    327326    Integrity::auditStructureID(vm, thisObject->structureID());
    328327    auto result = thisObject->structure(vm)->cachedSpecialProperty(CachedSpecialPropertyKey::ToStringTag);
    329328    if (result)
    330         return JSValue::encode(result);
     329        return asString(result);
    331330
    332331    String tag = thisObject->methodTable(vm)->toStringName(thisObject, globalObject);
    333     RETURN_IF_EXCEPTION(scope, { });
     332    RETURN_IF_EXCEPTION(scope, nullptr);
    334333    JSString* jsTag = nullptr;
    335334
     
    339338    if (hasProperty) {
    340339        JSValue tagValue = slot.getValue(globalObject, vm.propertyNames->toStringTagSymbol);
    341         RETURN_IF_EXCEPTION(scope, { });
     340        RETURN_IF_EXCEPTION(scope, nullptr);
    342341        if (tagValue.isString())
    343342            jsTag = asString(tagValue);
     
    350349
    351350    JSString* jsResult = jsString(globalObject, vm.smallStrings.objectStringStart(), jsTag, vm.smallStrings.singleCharacterString(']'));
    352     RETURN_IF_EXCEPTION(scope, { });
     351    RETURN_IF_EXCEPTION(scope, nullptr);
    353352    thisObject->structure(vm)->cacheSpecialProperty(globalObject, vm, jsResult, CachedSpecialPropertyKey::ToStringTag, slot);
    354     return JSValue::encode(jsResult);
     353    return jsResult;
     354}
     355
     356JSC_DEFINE_HOST_FUNCTION(objectProtoFuncToString, (JSGlobalObject* globalObject, CallFrame* callFrame))
     357{
     358    JSValue thisValue = callFrame->thisValue().toThis(globalObject, ECMAMode::strict());
     359    return JSValue::encode(objectPrototypeToString(globalObject, thisValue));
    355360}
    356361
  • trunk/Source/JavaScriptCore/runtime/ObjectPrototype.h

    r267594 r275544  
    5252
    5353JS_EXPORT_PRIVATE JSC_DECLARE_HOST_FUNCTION(objectProtoFuncToString);
     54JSString* objectPrototypeToString(JSGlobalObject*, JSValue thisValue);
    5455bool objectPrototypeHasOwnProperty(JSGlobalObject*, JSValue base, const Identifier& property);
    5556
Note: See TracChangeset for help on using the changeset viewer.