JSValue JSInjectedScriptHost::weakSetEntries(ExecState* exec) { if (exec->argumentCount() < 1) return jsUndefined(); JSValue value = exec->uncheckedArgument(0); JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(value); if (!weakSet) return jsUndefined(); unsigned fetched = 0; unsigned numberToFetch = 100; JSValue numberToFetchArg = exec->argument(1); double fetchDouble = numberToFetchArg.toInteger(exec); if (fetchDouble >= 0) numberToFetch = static_cast<unsigned>(fetchDouble); JSArray* array = constructEmptyArray(exec, nullptr); for (auto it = weakSet->weakMapData()->begin(); it != weakSet->weakMapData()->end(); ++it) { JSObject* entry = constructEmptyObject(exec); entry->putDirect(exec->vm(), Identifier::fromString(exec, "value"), it->key); array->putDirectIndex(exec, fetched++, entry); if (numberToFetch && fetched >= numberToFetch) break; } return array; }
// Shared implementation used by test and exec. MatchResult RegExpObject::match(ExecState* exec, JSString* string) { RegExp* regExp = this->regExp(); RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); String input = string->value(exec); VM& vm = exec->vm(); if (!regExp->global()) return regExpConstructor->performMatch(vm, regExp, string, input, 0); JSValue jsLastIndex = getLastIndex(); unsigned lastIndex; if (LIKELY(jsLastIndex.isUInt32())) { lastIndex = jsLastIndex.asUInt32(); if (lastIndex > input.length()) { setLastIndex(exec, 0); return MatchResult::failed(); } } else { double doubleLastIndex = jsLastIndex.toInteger(exec); if (doubleLastIndex < 0 || doubleLastIndex > input.length()) { setLastIndex(exec, 0); return MatchResult::failed(); } lastIndex = static_cast<unsigned>(doubleLastIndex); } MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex); setLastIndex(exec, result.end); return result; }
JSValue JSInjectedScriptHost::iteratorEntries(ExecState* exec) { if (exec->argumentCount() < 1) return jsUndefined(); VM& vm = exec->vm(); JSValue iterator; JSValue value = exec->uncheckedArgument(0); if (JSMapIterator* mapIterator = jsDynamicCast<JSMapIterator*>(value)) iterator = mapIterator->clone(exec); else if (JSSetIterator* setIterator = jsDynamicCast<JSSetIterator*>(value)) iterator = setIterator->clone(exec); else if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(value)) iterator = stringIterator->clone(exec); else if (JSPropertyNameIterator* propertyNameIterator = jsDynamicCast<JSPropertyNameIterator*>(value)) { iterator = propertyNameIterator->clone(exec); if (UNLIKELY(vm.exception())) return JSValue(); } else { if (JSObject* iteratorObject = jsDynamicCast<JSObject*>(value)) { // Array Iterators are created in JS for performance reasons. Thus the only way to know we have one is to // look for a property that is unique to them. if (JSValue nextIndex = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())) iterator = cloneArrayIteratorObject(exec, vm, iteratorObject, nextIndex); } } if (!iterator) return jsUndefined(); unsigned numberToFetch = 5; JSValue numberToFetchArg = exec->argument(1); double fetchDouble = numberToFetchArg.toInteger(exec); if (fetchDouble >= 0) numberToFetch = static_cast<unsigned>(fetchDouble); JSArray* array = constructEmptyArray(exec, nullptr); if (UNLIKELY(vm.exception())) return jsUndefined(); for (unsigned i = 0; i < numberToFetch; ++i) { JSValue next = iteratorStep(exec, iterator); if (UNLIKELY(vm.exception())) break; if (next.isFalse()) break; JSValue nextValue = iteratorValue(exec, next); if (UNLIKELY(vm.exception())) break; JSObject* entry = constructEmptyObject(exec); entry->putDirect(exec->vm(), Identifier::fromString(exec, "value"), nextValue); array->putDirectIndex(exec, i, entry); } iteratorClose(exec, iterator); return array; }
static inline int32_t extractRadixFromArgs(ExecState* exec) { JSValue radixValue = exec->argument(0); int32_t radix; if (radixValue.isInt32()) radix = radixValue.asInt32(); else if (radixValue.isUndefined()) radix = 10; else radix = static_cast<int32_t>(radixValue.toInteger(exec)); // nan -> 0 return radix; }
JSValue JSInjectedScriptHost::iteratorEntries(ExecState* exec) { if (exec->argumentCount() < 1) return jsUndefined(); JSValue iterator; JSValue value = exec->uncheckedArgument(0); if (JSArrayIterator* arrayIterator = jsDynamicCast<JSArrayIterator*>(value)) iterator = arrayIterator->clone(exec); else if (JSMapIterator* mapIterator = jsDynamicCast<JSMapIterator*>(value)) iterator = mapIterator->clone(exec); else if (JSSetIterator* setIterator = jsDynamicCast<JSSetIterator*>(value)) iterator = setIterator->clone(exec); else if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(value)) iterator = stringIterator->clone(exec); else if (JSPropertyNameIterator* propertyNameIterator = jsDynamicCast<JSPropertyNameIterator*>(value)) iterator = propertyNameIterator->clone(exec); else return jsUndefined(); unsigned numberToFetch = 5; JSValue numberToFetchArg = exec->argument(1); double fetchDouble = numberToFetchArg.toInteger(exec); if (fetchDouble >= 0) numberToFetch = static_cast<unsigned>(fetchDouble); JSArray* array = constructEmptyArray(exec, nullptr); for (unsigned i = 0; i < numberToFetch; ++i) { JSValue next = iteratorStep(exec, iterator); if (exec->hadException()) break; if (next.isFalse()) break; JSValue nextValue = iteratorValue(exec, next); if (exec->hadException()) break; JSObject* entry = constructEmptyObject(exec); entry->putDirect(exec->vm(), Identifier::fromString(exec, "value"), nextValue); array->putDirectIndex(exec, i, entry); } iteratorClose(exec, iterator); return array; }
// Shared implementation used by test and exec. bool RegExpObject::match(ExecState* exec) { RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); UString input = exec->argument(0).toString(exec); JSGlobalData* globalData = &exec->globalData(); if (!regExp()->global()) { int position; int length; regExpConstructor->performMatch(*globalData, d->regExp.get(), input, 0, position, length); return position >= 0; } JSValue jsLastIndex = getLastIndex(); unsigned lastIndex; if (LIKELY(jsLastIndex.isUInt32())) { lastIndex = jsLastIndex.asUInt32(); if (lastIndex > input.length()) { setLastIndex(0); return false; } } else { double doubleLastIndex = jsLastIndex.toInteger(exec); if (doubleLastIndex < 0 || doubleLastIndex > input.length()) { setLastIndex(0); return false; } lastIndex = static_cast<unsigned>(doubleLastIndex); } int position; int length = 0; regExpConstructor->performMatch(*globalData, d->regExp.get(), input, lastIndex, position, length); if (position < 0) { setLastIndex(0); return false; } setLastIndex(position + length); return true; }
//-------------------------------------------------------------------------- // KJSValueToCFTypeInternal //-------------------------------------------------------------------------- // Caller is responsible for releasing the returned CFTypeRef CFTypeRef KJSValueToCFTypeInternal(JSValue inValue, ExecState *exec, ObjectImpList* inImps) { if (!inValue) return 0; CFTypeRef result = 0; JSGlueAPIEntry entry; if (inValue.isBoolean()) { result = inValue.toBoolean(exec) ? kCFBooleanTrue : kCFBooleanFalse; RetainCFType(result); return result; } if (inValue.isString()) { UString uString = inValue.toString(exec); result = UStringToCFString(uString); return result; } if (inValue.isNumber()) { double number1 = inValue.toNumber(exec); double number2 = (double)inValue.toInteger(exec); if (number1 == number2) { int intValue = (int)number2; result = CFNumberCreate(0, kCFNumberIntType, &intValue); } else { result = CFNumberCreate(0, kCFNumberDoubleType, &number1); } return result; } if (inValue.isObject()) { if (inValue.inherits(&UserObjectImp::info)) { UserObjectImp* userObjectImp = static_cast<UserObjectImp *>(asObject(inValue)); JSUserObject* ptr = userObjectImp->GetJSUserObject(); if (ptr) { result = ptr->CopyCFValue(); } } else { JSObject *object = inValue.toObject(exec); UInt8 isArray = false; // if two objects reference each JSObject* imp = object; ObjectImpList* temp = inImps; while (temp) { if (imp == temp->imp) { return CFRetain(GetCFNull()); } temp = temp->next; } ObjectImpList imps; imps.next = inImps; imps.imp = imp; //[...] HACK since we do not have access to the class info we use class name instead #if 0 if (object->inherits(&ArrayInstanceImp::info)) #else if (object->className() == "Array") #endif { isArray = true; JSGlueGlobalObject* globalObject = static_cast<JSGlueGlobalObject*>(exec->dynamicGlobalObject()); if (globalObject && (globalObject->Flags() & kJSFlagConvertAssociativeArray)) { PropertyNameArray propNames(exec); object->getPropertyNames(exec, propNames); PropertyNameArray::const_iterator iter = propNames.begin(); PropertyNameArray::const_iterator end = propNames.end(); while(iter != end && isArray) { Identifier propName = *iter; UString ustr = propName.ustring(); const UniChar* uniChars = (const UniChar*)ustr.characters(); int size = ustr.length(); while (size--) { if (uniChars[size] < '0' || uniChars[size] > '9') { isArray = false; break; } } iter++; } } } if (isArray) { // This is an KJS array unsigned int length = object->get(exec, Identifier(exec, "length")).toUInt32(exec); result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); if (result) { for (unsigned i = 0; i < length; i++) { CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, i), exec, &imps); CFArrayAppendValue((CFMutableArrayRef)result, cfValue); ReleaseCFType(cfValue); } } } else { // Not an array, just treat it like a dictionary which contains (property name, property value) pairs PropertyNameArray propNames(exec); object->getPropertyNames(exec, propNames); { result = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (result) { PropertyNameArray::const_iterator iter = propNames.begin(); PropertyNameArray::const_iterator end = propNames.end(); while(iter != end) { Identifier propName = *iter; if (object->hasProperty(exec, propName)) { CFStringRef cfKey = IdentifierToCFString(propName); CFTypeRef cfValue = KJSValueToCFTypeInternal(object->get(exec, propName), exec, &imps); if (cfKey && cfValue) { CFDictionaryAddValue((CFMutableDictionaryRef)result, cfKey, cfValue); } ReleaseCFType(cfKey); ReleaseCFType(cfValue); } iter++; } } } } } return result; } if (inValue.isUndefinedOrNull()) { result = RetainCFType(GetCFNull()); return result; } ASSERT_NOT_REACHED(); return 0; }
void setRegExpObjectLastIndex(ExecState* exec, JSObject* baseObject, JSValue value) { asRegExpObject(baseObject)->setLastIndex(value.toInteger(exec)); }
EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); JSValue v = thisValue.getJSNumber(); if (!v) return throwVMTypeError(exec); JSValue radixValue = exec->argument(0); int radix; if (radixValue.isInt32()) radix = radixValue.asInt32(); else if (radixValue.isUndefined()) radix = 10; else radix = static_cast<int>(radixValue.toInteger(exec)); // nan -> 0 if (radix == 10) return JSValue::encode(jsString(exec, v.toString(exec))); static const char* const digits = "0123456789abcdefghijklmnopqrstuvwxyz"; // Fast path for number to character conversion. if (radix == 36) { if (v.isInt32()) { int x = v.asInt32(); if (static_cast<unsigned>(x) < 36) { // Exclude negatives JSGlobalData* globalData = &exec->globalData(); return JSValue::encode(globalData->smallStrings.singleCharacterString(globalData, digits[x])); } } } if (radix < 2 || radix > 36) return throwVMError(exec, createRangeError(exec, "toString() radix argument must be between 2 and 36")); // INT_MAX results in 1024 characters left of the dot with radix 2 // give the same space on the right side. safety checks are in place // unless someone finds a precise rule. char s[2048 + 3]; const char* lastCharInString = s + sizeof(s) - 1; double x = v.uncheckedGetNumber(); if (isnan(x) || isinf(x)) return JSValue::encode(jsString(exec, UString::number(x))); bool isNegative = x < 0.0; if (isNegative) x = -x; double integerPart = floor(x); char* decimalPoint = s + sizeof(s) / 2; // convert integer portion char* p = decimalPoint; double d = integerPart; do { int remainderDigit = static_cast<int>(fmod(d, radix)); *--p = digits[remainderDigit]; d /= radix; } while ((d <= -1.0 || d >= 1.0) && s < p); if (isNegative) *--p = '-'; char* startOfResultString = p; ASSERT(s <= startOfResultString); d = x - integerPart; p = decimalPoint; const double epsilon = 0.001; // TODO: guessed. base on radix ? bool hasFractionalPart = (d < -epsilon || d > epsilon); if (hasFractionalPart) { *p++ = '.'; do { d *= radix; const int digit = static_cast<int>(d); *p++ = digits[digit]; d -= digit; } while ((d < -epsilon || d > epsilon) && p < lastCharInString); } *p = '\0'; ASSERT(p < s + sizeof(s)); return JSValue::encode(jsString(exec, startOfResultString)); }