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); }
// 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; }