EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeGetterFormat(ExecState* state)
{
    // 11.3.3 Intl.NumberFormat.prototype.format (ECMA-402 2.0)
    // 1. Let nf be this NumberFormat object.
    IntlNumberFormat* nf = jsDynamicCast<IntlNumberFormat*>(state->thisValue());

    // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
    if (!nf)
        nf = jsDynamicCast<IntlNumberFormat*>(state->thisValue().get(state, state->vm().propertyNames->intlSubstituteValuePrivateName));

    if (!nf)
        return JSValue::encode(throwTypeError(state, ASCIILiteral("Intl.NumberFormat.prototype.format called on value that's not an object initialized as a NumberFormat")));
    
    JSBoundFunction* boundFormat = nf->boundFormat();
    // 2. If nf.[[boundFormat]] is undefined,
    if (!boundFormat) {
        VM& vm = state->vm();
        JSGlobalObject* globalObject = nf->globalObject();
        // a. Let F be a new built-in function object as defined in 11.3.4.
        // b. The value of F’s length property is 1.
        JSFunction* targetObject = JSFunction::create(vm, globalObject, 1, ASCIILiteral("format"), IntlNumberFormatFuncFormatNumber, NoIntrinsic);
        JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0);
        if (!boundArgs)
            return JSValue::encode(throwOutOfMemoryError(state));

        // c. Let bf be BoundFunctionCreate(F, «this value»).
        boundFormat = JSBoundFunction::create(vm, state, globalObject, targetObject, nf, boundArgs, 1, ASCIILiteral("format"));
        if (vm.exception())
            return JSValue::encode(JSValue());
        // d. Set nf.[[boundFormat]] to bf.
        nf->setBoundFormat(vm, boundFormat);
    }
    // 3. Return nf.[[boundFormat]].
    return JSValue::encode(boundFormat);
}
EncodedJSValue JSC_HOST_CALL IntlCollatorPrototypeGetterCompare(ExecState* exec)
{
    // 10.3.3 Intl.Collator.prototype.compare (ECMA-402 2.0)
    // 1. Let collator be this Collator object.
    IntlCollator* collator = jsDynamicCast<IntlCollator*>(exec->thisValue());
    if (!collator)
        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Intl.Collator.prototype.compare called on value that's not an object initialized as a Collator")));

    JSBoundFunction* boundCompare = collator->boundCompare();
    // 2. If collator.[[boundCompare]] is undefined,
    if (!boundCompare) {
        VM& vm = exec->vm();
        JSGlobalObject* globalObject = collator->globalObject();
        // a. Let F be a new built-in function object as defined in 11.3.4.
        // b. The value of F’s length property is 2.
        JSFunction* targetObject = JSFunction::create(vm, globalObject, 2, ASCIILiteral("compare"), IntlCollatorFuncCompare, NoIntrinsic);
        JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0);
        if (!boundArgs)
            return JSValue::encode(throwOutOfMemoryError(exec));

        // c. Let bc be BoundFunctionCreate(F, «this value»).
        boundCompare = JSBoundFunction::create(vm, globalObject, targetObject, collator, boundArgs, 2, ASCIILiteral("compare"));
        // d. Set collator.[[boundCompare]] to bc.
        collator->setBoundCompare(vm, boundCompare);
    }
    // 3. Return collator.[[boundCompare]].
    return JSValue::encode(boundCompare);
}
Example #3
0
// 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec)
{
    VM& vm = exec->vm();
    auto scope = DECLARE_THROW_SCOPE(vm);
    JSGlobalObject* globalObject = exec->callee()->globalObject();

    // Let Target be the this value.
    JSValue target = exec->thisValue();

    // If IsCallable(Target) is false, throw a TypeError exception.
    CallData callData;
    CallType callType = getCallData(target, callData);
    if (callType == CallType::None)
        return throwVMTypeError(exec, scope);
    // Primitive values are not callable.
    ASSERT(target.isObject());
    JSObject* targetObject = asObject(target);

    // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
    size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0;
    JSArray* boundArgs;
    if (numBoundArgs) {
        boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), numBoundArgs);
        if (!boundArgs)
            return JSValue::encode(throwOutOfMemoryError(exec, scope));
        
        for (size_t i = 0; i < numBoundArgs; ++i)
            boundArgs->initializeIndex(vm, i, exec->argument(i + 1));
    } else
        boundArgs = nullptr;

    // If the [[Class]] internal property of Target is "Function", then ...
    // Else set the length own property of F to 0.
    unsigned length = 0;
    if (targetObject->hasOwnProperty(exec, exec->propertyNames().length)) {
        if (exec->hadException())
            return JSValue::encode(jsUndefined());

        // a. Let L be the length property of Target minus the length of A.
        // b. Set the length own property of F to either 0 or L, whichever is larger.
        JSValue lengthValue = target.get(exec, exec->propertyNames().length);
        if (lengthValue.isNumber()) {
            unsigned targetLength = (unsigned)lengthValue.asNumber();
            if (targetLength > numBoundArgs)
                length = targetLength - numBoundArgs;
        }
    }

    JSValue nameProp = target.get(exec, exec->propertyNames().name);
    JSString* name = nameProp.isString() ? nameProp.toString(exec) : jsEmptyString(exec);
    return JSValue::encode(JSBoundFunction::create(vm, exec, globalObject, targetObject, exec->argument(0), boundArgs, length, name->value(exec)));
}
JSObject* IntlPluralRules::resolvedOptions(ExecState& exec)
{
    VM& vm = exec.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    // 13.4.4 Intl.PluralRules.prototype.resolvedOptions ()
    // https://tc39.github.io/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions
    if (UNLIKELY(!m_initializedPluralRules)) {
        throwTypeError(&exec, scope, "Intl.PluralRules.prototype.resolvedOptions called on value that's not an object initialized as a PluralRules"_s);
        return nullptr;
    }

    JSObject* options = constructEmptyObject(&exec);
    options->putDirect(vm, vm.propertyNames->locale, jsNontrivialString(&exec, m_locale));
    options->putDirect(vm, Identifier::fromString(&vm, "type"), jsNontrivialString(&exec, m_type == UPLURAL_TYPE_ORDINAL ? "ordinal"_s : "cardinal"_s));
    options->putDirect(vm, Identifier::fromString(&vm, "minimumIntegerDigits"), jsNumber(m_minimumIntegerDigits));
    options->putDirect(vm, Identifier::fromString(&vm, "minimumFractionDigits"), jsNumber(m_minimumFractionDigits));
    options->putDirect(vm, Identifier::fromString(&vm, "maximumFractionDigits"), jsNumber(m_maximumFractionDigits));
    if (m_minimumSignificantDigits) {
        options->putDirect(vm, Identifier::fromString(&vm, "minimumSignificantDigits"), jsNumber(m_minimumSignificantDigits.value()));
        options->putDirect(vm, Identifier::fromString(&vm, "maximumSignificantDigits"), jsNumber(m_maximumSignificantDigits.value()));
    }

#if HAVE(ICU_PLURALRULES_KEYWORDS)
    JSGlobalObject* globalObject = exec.jsCallee()->globalObject(vm);
    JSArray* categories = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), 0);
    if (UNLIKELY(!categories)) {
        throwOutOfMemoryError(&exec, scope);
        return nullptr;
    }

    UErrorCode status = U_ZERO_ERROR;
    auto keywords = std::unique_ptr<UEnumeration, UEnumerationDeleter>(uplrules_getKeywords(m_pluralRules.get(), &status));
    ASSERT(U_SUCCESS(status));
    int32_t resultLength;

    // Category names are always ASCII, so use char[].
    unsigned index = 0;
    while (const char* result = uenum_next(keywords.get(), &resultLength, &status)) {
        ASSERT(U_SUCCESS(status));
        categories->putDirectIndex(&exec, index++, jsNontrivialString(&exec, String(result, resultLength)));
        RETURN_IF_EXCEPTION(scope, { });
    }
    options->putDirect(vm, Identifier::fromString(&vm, "pluralCategories"), categories);
#endif

    RELEASE_AND_RETURN(scope, options);
}
// 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec)
{
    JSGlobalObject* globalObject = exec->callee()->globalObject();

    // Let Target be the this value.
    JSValue target = exec->hostThisValue();

    // If IsCallable(Target) is false, throw a TypeError exception.
    CallData callData;
    CallType callType = getCallData(target, callData);
    if (callType == CallTypeNone)
        return throwVMTypeError(exec);
    // Primitive values are not callable.
    ASSERT(target.isObject());
    JSObject* targetObject = asObject(target);

    // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
    size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0;
    JSArray* boundArgs = JSArray::tryCreateUninitialized(exec->vm(), globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), numBoundArgs);
    if (!boundArgs)
        return JSValue::encode(throwOutOfMemoryError(exec));

    for (size_t i = 0; i < numBoundArgs; ++i)
        boundArgs->initializeIndex(exec->vm(), i, exec->argument(i + 1));

    // If the [[Class]] internal property of Target is "Function", then ...
    // Else set the length own property of F to 0.
    unsigned length = 0;
    if (targetObject->inherits(&JSFunction::s_info)) {
        ASSERT(target.get(exec, exec->propertyNames().length).isNumber());
        // a. Let L be the length property of Target minus the length of A.
        // b. Set the length own property of F to either 0 or L, whichever is larger.
        unsigned targetLength = (unsigned)target.get(exec, exec->propertyNames().length).asNumber();
        if (targetLength > numBoundArgs)
            length = targetLength - numBoundArgs;
    }

    JSString* name = target.get(exec, exec->propertyNames().name).toString(exec);
    return JSValue::encode(JSBoundFunction::create(exec, globalObject, targetObject, exec->argument(0), boundArgs, length, name->value(exec)));
}
EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeGetterFormat(ExecState* state)
{
    // 12.3.3 Intl.DateTimeFormat.prototype.format (ECMA-402 2.0)
    // 1. Let dtf be this DateTimeFormat object.
    IntlDateTimeFormat* dtf = jsDynamicCast<IntlDateTimeFormat*>(state->thisValue());

    // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
    // https://bugs.webkit.org/show_bug.cgi?id=153679
    if (!dtf)
        dtf = jsDynamicCast<IntlDateTimeFormat*>(state->thisValue().get(state, state->vm().propertyNames->intlSubstituteValuePrivateName));

    // 2. ReturnIfAbrupt(dtf).
    if (!dtf)
        return JSValue::encode(throwTypeError(state, ASCIILiteral("Intl.DateTimeFormat.prototype.format called on value that's not an object initialized as a DateTimeFormat")));

    JSBoundFunction* boundFormat = dtf->boundFormat();
    // 3. If the [[boundFormat]] internal slot of this DateTimeFormat object is undefined,
    if (!boundFormat) {
        VM& vm = state->vm();
        JSGlobalObject* globalObject = dtf->globalObject();
        // a. Let F be a new built-in function object as defined in 12.3.4.
        // b. The value of F’s length property is 1. (Note: F’s length property was 0 in ECMA-402 1.0)
        JSFunction* targetObject = JSFunction::create(vm, globalObject, 1, ASCIILiteral("format"), IntlDateTimeFormatFuncFormatDateTime, NoIntrinsic);
        JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), 0);
        if (!boundArgs)
            return JSValue::encode(throwOutOfMemoryError(state));

        // c. Let bf be BoundFunctionCreate(F, «this value»).
        boundFormat = JSBoundFunction::create(vm, state, globalObject, targetObject, dtf, boundArgs, 1, ASCIILiteral("format"));
        if (vm.exception())
            return JSValue::encode(JSValue());
        // d. Set dtf.[[boundFormat]] to bf.
        dtf->setBoundFormat(vm, boundFormat);
    }
    // 4. Return dtf.[[boundFormat]].
    return JSValue::encode(boundFormat);
}
JSValue IntlNumberFormat::formatToParts(ExecState& exec, double value)
{
    VM& vm = exec.vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    // FormatNumberToParts (ECMA-402)
    // https://tc39.github.io/ecma402/#sec-formatnumbertoparts
    // https://tc39.github.io/ecma402/#sec-partitionnumberpattern

    if (!m_initializedNumberFormat)
        return throwTypeError(&exec, scope, "Intl.NumberFormat.prototype.formatToParts called on value that's not an object initialized as a NumberFormat"_s);

    UErrorCode status = U_ZERO_ERROR;
    auto fieldItr = std::unique_ptr<UFieldPositionIterator, UFieldPositionIteratorDeleter>(ufieldpositer_open(&status));
    if (U_FAILURE(status))
        return throwTypeError(&exec, scope, "failed to open field position iterator"_s);

    status = U_ZERO_ERROR;
    Vector<UChar, 32> result(32);
    auto resultLength = unum_formatDoubleForFields(m_numberFormat.get(), value, result.data(), result.size(), fieldItr.get(), &status);
    if (status == U_BUFFER_OVERFLOW_ERROR) {
        status = U_ZERO_ERROR;
        result.grow(resultLength);
        unum_formatDoubleForFields(m_numberFormat.get(), value, result.data(), resultLength, fieldItr.get(), &status);
    }
    if (U_FAILURE(status))
        return throwTypeError(&exec, scope, "failed to format a number."_s);

    int32_t literalFieldType = -1;
    auto literalField = IntlNumberFormatField(literalFieldType, resultLength);
    Vector<IntlNumberFormatField> fields(resultLength, literalField);
    int32_t beginIndex = 0;
    int32_t endIndex = 0;
    auto fieldType = ufieldpositer_next(fieldItr.get(), &beginIndex, &endIndex);
    while (fieldType >= 0) {
        auto size = endIndex - beginIndex;
        for (auto i = beginIndex; i < endIndex; ++i) {
            // Only override previous value if new value is more specific.
            if (fields[i].size >= size)
                fields[i] = IntlNumberFormatField(fieldType, size);
        }
        fieldType = ufieldpositer_next(fieldItr.get(), &beginIndex, &endIndex);
    }

    JSGlobalObject* globalObject = exec.jsCallee()->globalObject(vm);
    JSArray* parts = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), 0);
    if (!parts)
        return throwOutOfMemoryError(&exec, scope);
    unsigned index = 0;

    auto resultString = String(result.data(), resultLength);
    auto typePropertyName = Identifier::fromString(&vm, "type");
    auto literalString = jsString(&exec, "literal"_s);

    int32_t currentIndex = 0;
    while (currentIndex < resultLength) {
        auto startIndex = currentIndex;
        auto fieldType = fields[currentIndex].type;
        while (currentIndex < resultLength && fields[currentIndex].type == fieldType)
            ++currentIndex;
        auto partType = fieldType == literalFieldType ? literalString : jsString(&exec, partTypeString(UNumberFormatFields(fieldType), value));
        auto partValue = jsSubstring(&vm, resultString, startIndex, currentIndex - startIndex);
        JSObject* part = constructEmptyObject(&exec);
        part->putDirect(vm, typePropertyName, partType);
        part->putDirect(vm, vm.propertyNames->value, partValue);
        parts->putDirectIndex(&exec, index++, part);
        RETURN_IF_EXCEPTION(scope, { });
    }

    return parts;
}
    bool run()
    {
        // FIXME: This phase shouldn't exist. We should have registered all structures by now, since
        // we may already have done optimizations that rely on structures having been registered.
        // Currently, we still have places where we don't register structures prior to this phase,
        // but structures don't end up being used for optimization prior to this phase. That's a
        // pretty fragile situation and we should fix it eventually.
        // https://bugs.webkit.org/show_bug.cgi?id=147889
        
        // We need to set this before this phase finishes. This phase doesn't do anything
        // conditioned on this field, except for assertIsRegistered() below. We intend for that
        // method to behave as if the phase was already finished. So, we set this up here.
        m_graph.m_structureRegistrationState = AllStructuresAreRegistered;
        
        // These are pretty dumb, but needed to placate subsequent assertions. We don't actually
        // have to watch these because there is no way to transition away from it, but they are
        // watchable and so we will assert if they aren't watched.
        registerStructure(m_graph.m_vm.structureStructure.get());
        registerStructure(m_graph.m_vm.stringStructure.get());
        registerStructure(m_graph.m_vm.symbolStructure.get());
        
        for (FrozenValue* value : m_graph.m_frozenValues)
            assertIsRegistered(value->structure());
        
        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
            BasicBlock* block = m_graph.block(blockIndex);
            if (!block)
                continue;
        
            for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
                Node* node = block->at(nodeIndex);
            
                switch (node->op()) {
                case CheckStructure:
                    assertAreRegistered(node->structureSet());
                    break;
                
                case NewObject:
                case ArrayifyToStructure:
                case NewStringObject:
                    registerStructure(node->structure());
                    break;
                
                case PutStructure:
                case AllocatePropertyStorage:
                case ReallocatePropertyStorage:
                    registerStructure(node->transition()->previous);
                    registerStructure(node->transition()->next);
                    break;

                case GetGetterSetterByOffset:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->getterSetterStructure());
                    break;

                case MultiGetByOffset:
                    for (const MultiGetByOffsetCase& getCase : node->multiGetByOffsetData().cases)
                        registerStructures(getCase.set());
                    break;
                    
                case MultiPutByOffset:
                    for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) {
                        PutByIdVariant& variant = node->multiPutByOffsetData().variants[i];
                        registerStructures(variant.oldStructure());
                        if (variant.kind() == PutByIdVariant::Transition)
                            registerStructure(variant.newStructure());
                    }
                    break;
                    
                case NewArray:
                case NewArrayBuffer:
                case NewArrayWithSize: {
                    JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
                    registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
                    registerStructure(globalObject->originalArrayStructureForIndexingType(ArrayWithSlowPutArrayStorage));
                    break;
                }
                    
                case NewTypedArray:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->typedArrayStructureConcurrently(node->typedArrayType()));
                    break;
                    
                case ToString:
                case CallStringConstructor:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->stringObjectStructure());
                    break;
                    
                case CreateActivation:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->activationStructure());
                    break;
                    
                case CreateDirectArguments:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->directArgumentsStructure());
                    break;
                    
                case CreateScopedArguments:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->scopedArgumentsStructure());
                    break;

                case CreateClonedArguments:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->clonedArgumentsStructure());
                    break;

                case NewRegexp:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->regExpStructure());
                    break;
                case NewFunction:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->functionStructure());
                    break;
                case NewGeneratorFunction:
                    registerStructure(m_graph.globalObjectFor(node->origin.semantic)->generatorFunctionStructure());
                    break;

                default:
                    break;
                }
            }
        }
        
        return true;
    }