// toPrecision converts a number to a string, takeing an argument specifying a // number of significant figures to round the significand to. For positive // exponent, all values that can be represented using a decimal fraction will // be, e.g. when rounding to 3 s.f. any value up to 999 will be formated as a // decimal, whilst 1000 is converted to the exponential representation 1.00e+3. // For negative exponents values >= 1e-6 are formated as decimal fractions, // with smaller values converted to exponential representation. EncodedTiValue JSC_HOST_CALL numberProtoFuncToPrecision(TiExcState* exec) { // Get x (the double value of this, which should be a Number). TiValue thisValue = exec->hostThisValue(); TiValue v = thisValue.getJSNumber(); if (!v) return throwVMTypeError(exec); double x = v.uncheckedGetNumber(); // Get the argument. int significantFigures; bool isUndefined; if (!getIntegerArgumentInRange(exec, 1, 21, significantFigures, isUndefined)) return throwVMError(exec, createRangeError(exec, "toPrecision() argument must be between 1 and 21")); // To precision called with no argument is treated as ToString. if (isUndefined) return TiValue::encode(jsString(exec, UString::number(x))); // Handle NaN and Infinity. if (isnan(x) || isinf(x)) return TiValue::encode(jsString(exec, UString::number(x))); // Convert to decimal with rounding. DecimalNumber number(x, RoundingSignificantFigures, significantFigures); // If number is in the range 1e-6 <= x < pow(10, significantFigures) then format // as decimal. Otherwise, format the number as an exponential. Decimal format // demands a minimum of (exponent + 1) digits to represent a number, for example // 1234 (1.234e+3) requires 4 digits. (See ECMA-262 15.7.4.7.10.c) NumberToStringBuffer buffer; unsigned length = number.exponent() >= -6 && number.exponent() < significantFigures ? number.toStringDecimal(buffer, WTI::NumberToStringBufferLength) : number.toStringExponential(buffer, WTI::NumberToStringBufferLength); return TiValue::encode(jsString(exec, UString(buffer, length))); }
// toFixed converts a number to a string, always formatting as an a decimal fraction. // This method takes an argument specifying a number of decimal places to round the // significand to. However when converting large values (1e+21 and above) this // method will instead fallback to calling ToString. EncodedTiValue JSC_HOST_CALL numberProtoFuncToFixed(TiExcState* exec) { // Get x (the double value of this, which should be a Number). TiValue thisValue = exec->hostThisValue(); TiValue v = thisValue.getJSNumber(); if (!v) return throwVMTypeError(exec); double x = v.uncheckedGetNumber(); // Get the argument. int decimalPlaces; bool isUndefined; // This is ignored; undefined treated as 0. if (!getIntegerArgumentInRange(exec, 0, 20, decimalPlaces, isUndefined)) return throwVMError(exec, createRangeError(exec, "toFixed() argument must be between 0 and 20")); // 15.7.4.5.7 states "If x >= 10^21, then let m = ToString(x)" // This also covers Ininity, and structure the check so that NaN // values are also handled by numberToString if (!(fabs(x) < 1e+21)) return TiValue::encode(jsString(exec, UString::number(x))); // The check above will return false for NaN or Infinity, these will be // handled by numberToString. ASSERT(!isnan(x) && !isinf(x)); // Convert to decimal with rounding, and format as decimal. NumberToStringBuffer buffer; unsigned length = DecimalNumber(x, RoundingDecimalPlaces, decimalPlaces).toStringDecimal(buffer, WTI::NumberToStringBufferLength); return TiValue::encode(jsString(exec, UString(buffer, length))); }
void TiObject::defineSetter(TiExcState* exec, const Identifier& propertyName, TiObject* setterFunction, unsigned attributes) { TiValue object = getDirect(propertyName); if (object && object.isGetterSetter()) { ASSERT(m_structure->hasGetterSetterProperties()); asGetterSetter(object)->setSetter(setterFunction); return; } PutPropertySlot slot; GetterSetter* getterSetter = new (exec) GetterSetter(exec); putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot); // putDirect will change our Structure if we add a new property. For // getters and setters, though, we also need to change our Structure // if we override an existing non-getter or non-setter. if (slot.type() != PutPropertySlot::NewProperty) { if (!m_structure->isDictionary()) { RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure); setStructure(structure.release()); } } m_structure->setHasGetterSetterProperties(true); getterSetter->setSetter(setterFunction); }
EncodedTiValue JSC_HOST_CALL functionProtoFuncApply(TiExcState* exec) { TiValue thisValue = exec->hostThisValue(); CallData callData; CallType callType = getCallData(thisValue, callData); if (callType == CallTypeNone) return throwVMTypeError(exec); TiValue array = exec->argument(1); MarkedArgumentBuffer applyArgs; if (!array.isUndefinedOrNull()) { if (!array.isObject()) return throwVMTypeError(exec); if (asObject(array)->classInfo() == &Arguments::s_info) asArguments(array)->fillArgList(exec, applyArgs); else if (isTiArray(&exec->globalData(), array)) asArray(array)->fillArgList(exec, applyArgs); else if (asObject(array)->inherits(&TiArray::s_info)) { unsigned length = asArray(array)->get(exec, exec->propertyNames().length).toUInt32(exec); for (unsigned i = 0; i < length; ++i) applyArgs.append(asArray(array)->get(exec, i)); } else return throwVMTypeError(exec); } return TiValue::encode(call(exec, thisValue, callType, callData, exec->argument(0), applyArgs)); }
double TiObject::toNumber(TiExcState* exec) const { TiValue primitive = toPrimitive(exec, PreferNumber); if (exec->hadException()) // should be picked up soon in Nodes.cpp return 0.0; return primitive.toNumber(exec); }
UString TiObject::toString(TiExcState* exec) const { TiValue primitive = toPrimitive(exec, PreferString); if (exec->hadException()) return ""; return primitive.toString(exec); }
static ALWAYS_INLINE bool toThisNumber(TiValue thisValue, double &x) { TiValue v = thisValue.getJSNumber(); if (UNLIKELY(!v)) return false; x = v.uncheckedGetNumber(); return true; }
bool TiValueIsBoolean(TiContextRef ctx, TiValueRef value) { TiExcState* exec = toJS(ctx); APIEntryShim entryShim(exec); TiValue jsValue = toJS(exec, value); return jsValue.isBoolean(); }
bool TiValueIsDate(TiContextRef ctx, TiValueRef value) { TiExcState* exec = toJS(ctx); exec->globalData().heap.registerThread(); TiLock lock(exec); TiValue jsValue = toJS(exec, value); return jsValue.inherits(&DateInstance::info); }
EncodedTiValue JSC_HOST_CALL numberProtoFuncValueOf(TiExcState* exec) { TiValue thisValue = exec->hostThisValue(); TiValue v = thisValue.getJSNumber(); if (!v) return throwVMTypeError(exec); return TiValue::encode(v); }
TiValue JSC_HOST_CALL booleanProtoFuncValueOf(TiExcState* exec, TiObject*, TiValue thisValue, const ArgList&) { if (thisValue.isBoolean()) return thisValue; if (!thisValue.inherits(&BooleanObject::info)) return throwError(exec, TypeError); return asBooleanObject(thisValue)->internalValue(); }
EncodedTiValue JSC_HOST_CALL numberProtoFuncToLocaleString(TiExcState* exec) { TiValue thisValue = exec->hostThisValue(); // FIXME: Not implemented yet. TiValue v = thisValue.getJSNumber(); if (!v) return throwVMTypeError(exec); return TiValue::encode(jsString(exec, v.toString(exec))); }
bool TiObject::getPropertyDescriptor(TiExcState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { TiObject* object = this; while (true) { if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor)) return true; TiValue prototype = object->prototype(); if (!prototype.isObject()) return false; object = asObject(prototype); } }
bool TiValueIsObjectOfClass(TiContextRef ctx, TiValueRef value, TiClassRef jsClass) { TiExcState* exec = toJS(ctx); APIEntryShim entryShim(exec); TiValue jsValue = toJS(exec, value); if (TiObject* o = jsValue.getObject()) { if (o->inherits(&TiCallbackObject<TiGlobalObject>::info)) return static_cast<TiCallbackObject<TiGlobalObject>*>(o)->inherits(jsClass); else if (o->inherits(&TiCallbackObject<TiObject>::info)) return static_cast<TiCallbackObject<TiObject>*>(o)->inherits(jsClass); } return false; }
TiObject* TiFunction::construct(TiExcState* exec, const ArgList& args) { ASSERT(!isHostFunction()); Structure* structure; TiValue prototype = get(exec, exec->propertyNames().prototype); if (prototype.isObject()) structure = asObject(prototype)->inheritorID(); else structure = exec->lexicalGlobalObject()->emptyObjectStructure(); TiObject* thisObj = new (exec) TiObject(structure); TiValue result = exec->interpreter()->execute(jsExecutable(), exec, this, thisObj, args, scopeChain().node(), exec->exceptionSlot()); if (exec->hadException() || !result.isObject()) return thisObj; return asObject(result); }
TiStringRef TiValueToStringCopy(TiContextRef ctx, TiValueRef value, TiValueRef* exception) { TiExcState* exec = toJS(ctx); APIEntryShim entryShim(exec); TiValue jsValue = toJS(exec, value); RefPtr<OpaqueTiString> stringRef(OpaqueTiString::create(jsValue.toString(exec))); if (exec->hadException()) { if (exception) *exception = toRef(exec, exec->exception()); exec->clearException(); stringRef.clear(); } return stringRef.release().releaseRef(); }
double TiValueToNumber(TiContextRef ctx, TiValueRef value, TiValueRef* exception) { TiExcState* exec = toJS(ctx); APIEntryShim entryShim(exec); TiValue jsValue = toJS(exec, value); double number = jsValue.toNumber(exec); if (exec->hadException()) { if (exception) *exception = toRef(exec, exec->exception()); exec->clearException(); number = NaN; } return number; }
TiObjectRef TiValueToObject(TiContextRef ctx, TiValueRef value, TiValueRef* exception) { TiExcState* exec = toJS(ctx); APIEntryShim entryShim(exec); TiValue jsValue = toJS(exec, value); TiObjectRef objectRef = toRef(jsValue.toObject(exec)); if (exec->hadException()) { if (exception) *exception = toRef(exec, exec->exception()); exec->clearException(); objectRef = 0; } return objectRef; }
bool TiObject::hasInstance(TiExcState* exec, TiValue value, TiValue proto) { if (!value.isObject()) return false; if (!proto.isObject()) { throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property."); return false; } TiObject* object = asObject(value); while ((object = object->prototype().getObject())) { if (proto == object) return true; } return false; }
TiValue JSC_HOST_CALL objectProtoFuncDefineSetter(TiExcState* exec, TiObject*, TiValue thisValue, const ArgList& args) { CallData callData; if (args.at(1).getCallData(callData) == CallTypeNone) return throwError(exec, SyntaxError, "invalid setter usage"); thisValue.toThisObject(exec)->defineSetter(exec, Identifier(exec, args.at(0).toString(exec)), asObject(args.at(1))); return jsUndefined(); }
TiValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(TiExcState* exec, TiObject*, TiValue thisValue, const ArgList& args) { TiObject* thisObj = thisValue.toThisObject(exec); if (!args.at(0).isObject()) return jsBoolean(false); TiValue v = asObject(args.at(0))->prototype(); while (true) { if (!v.isObject()) return jsBoolean(false); if (v == thisObj) return jsBoolean(true); v = asObject(v)->prototype(); } }
// ECMA 15.9.3 TiObject* constructDate(TiExcState* exec, const ArgList& args) { int numArgs = args.size(); double value; if (numArgs == 0) // new Date() ECMA 15.9.3.3 value = jsCurrentTime(); else if (numArgs == 1) { if (args.at(0).inherits(&DateInstance::info)) value = asDateInstance(args.at(0))->internalNumber(); else { TiValue primitive = args.at(0).toPrimitive(exec); if (primitive.isString()) value = parseDate(exec, primitive.getString(exec)); else value = primitive.toNumber(exec); } } else { if (isnan(args.at(0).toNumber(exec)) || isnan(args.at(1).toNumber(exec)) || (numArgs >= 3 && isnan(args.at(2).toNumber(exec))) || (numArgs >= 4 && isnan(args.at(3).toNumber(exec))) || (numArgs >= 5 && isnan(args.at(4).toNumber(exec))) || (numArgs >= 6 && isnan(args.at(5).toNumber(exec))) || (numArgs >= 7 && isnan(args.at(6).toNumber(exec)))) value = NaN; else { GregorianDateTime t; int year = args.at(0).toInt32(exec); t.year = (year >= 0 && year <= 99) ? year : year - 1900; t.month = args.at(1).toInt32(exec); t.monthDay = (numArgs >= 3) ? args.at(2).toInt32(exec) : 1; t.hour = args.at(3).toInt32(exec); t.minute = args.at(4).toInt32(exec); t.second = args.at(5).toInt32(exec); t.isDST = -1; double ms = (numArgs >= 7) ? args.at(6).toNumber(exec) : 0; value = gregorianDateTimeToMS(exec, t, ms, false); } } return new (exec) DateInstance(exec, value); }
EncodedTiValue JSC_HOST_CALL functionProtoFuncToString(TiExcState* exec) { TiValue thisValue = exec->hostThisValue(); if (thisValue.inherits(&TiFunction::s_info)) { TiFunction* function = asFunction(thisValue); if (function->isHostFunction()) return TiValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}")); FunctionExecutable* executable = function->jsExecutable(); UString sourceString = executable->source().toString(); insertSemicolonIfNeeded(sourceString); return TiValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "(", executable->paramString(), ") ", sourceString)); } if (thisValue.inherits(&InternalFunction::s_info)) { InternalFunction* function = asInternalFunction(thisValue); return TiValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}")); } return throwVMTypeError(exec); }
bool TiString::getOwnPropertySlot(TiExcState* exec, const Identifier& propertyName, PropertySlot& slot) { // The semantics here are really getPropertySlot, not getOwnPropertySlot. // This function should only be called by TiValue::get. if (getStringPropertySlot(exec, propertyName, slot)) return true; if (propertyName == exec->propertyNames().underscoreProto) { slot.setValue(exec->lexicalGlobalObject()->stringPrototype()); return true; } slot.setBase(this); TiObject* object; for (TiValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) { object = asObject(prototype); if (object->getOwnPropertySlot(exec, propertyName, slot)) return true; } slot.setUndefined(); return true; }
void ProfileGenerator::addParentForConsoleStart(TiExcState* exec) { int lineNumber; intptr_t sourceID; UString sourceURL; TiValue function; exec->interpreter()->retrieveLastCaller(exec, lineNumber, sourceID, sourceURL, function); m_currentNode = ProfileNode::create(Profiler::createCallIdentifier(&exec->globalData(), function ? function.toThisObject(exec) : 0, sourceURL, lineNumber), m_head.get(), m_head.get()); m_head->insertNode(m_currentNode.get()); }
void TiObject::getPropertyNames(TiExcState* exec, PropertyNameArray& propertyNames) { getOwnPropertyNames(exec, propertyNames); if (prototype().isNull()) return; TiObject* prototype = asObject(this->prototype()); while(1) { if (prototype->structure()->typeInfo().overridesGetPropertyNames()) { prototype->getPropertyNames(exec, propertyNames); break; } prototype->getOwnPropertyNames(exec, propertyNames); TiValue nextProto = prototype->prototype(); if (nextProto.isNull()) break; prototype = asObject(nextProto); } }
static ALWAYS_INLINE TiValue callDefaultValueFunction(TiExcState* exec, const TiObject* object, const Identifier& propertyName) { TiValue function = object->get(exec, propertyName); CallData callData; CallType callType = function.getCallData(callData); if (callType == CallTypeNone) return exec->exception(); // Prevent "toString" and "valueOf" from observing execution if an exception // is pending. if (exec->hadException()) return exec->exception(); TiValue result = call(exec, function, callType, callData, const_cast<TiObject*>(object), exec->emptyList()); ASSERT(!result.isGetterSetter()); if (exec->hadException()) return exec->exception(); if (result.isObject()) return TiValue(); return result; }
// Shared implementation used by test and exec. bool RegExpObject::match(TiExcState* exec) { RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); UString input = exec->argument(0).toString(exec); TiGlobalData* globalData = &exec->globalData(); if (!regExp()->global()) { int position; int length; regExpConstructor->performMatch(*globalData, d->regExp.get(), input, 0, position, length); return position >= 0; } TiValue 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; }
EncodedTiValue operationGetByVal(TiExcState* exec, EncodedTiValue encodedBase, EncodedTiValue encodedProperty) { TiValue baseValue = TiValue::decode(encodedBase); TiValue property = TiValue::decode(encodedProperty); if (LIKELY(baseValue.isCell())) { TiCell* base = baseValue.asCell(); if (property.isUInt32()) { TiGlobalData* globalData = &exec->globalData(); uint32_t i = property.asUInt32(); // FIXME: the JIT used to handle these in compiled code! if (isTiArray(globalData, base) && asArray(base)->canGetIndex(i)) return TiValue::encode(asArray(base)->getIndex(i)); // FIXME: the JITstub used to relink this to an optimized form! if (isTiString(globalData, base) && asString(base)->canGetIndex(i)) return TiValue::encode(asString(base)->getIndex(exec, i)); // FIXME: the JITstub used to relink this to an optimized form! if (isTiArrayArray(globalData, base) && asByteArray(base)->canAccessIndex(i)) return TiValue::encode(asByteArray(base)->getIndex(exec, i)); return TiValue::encode(baseValue.get(exec, i)); } if (property.isString()) { Identifier propertyName(exec, asString(property)->value(exec)); PropertySlot slot(base); if (base->fastGetOwnPropertySlot(exec, propertyName, slot)) return TiValue::encode(slot.getValue(exec, propertyName)); } } Identifier ident(exec, property.toString(exec)); return TiValue::encode(baseValue.get(exec, ident)); }
CallIdentifier Profiler::createCallIdentifier(TiExcState* exec, TiValue functionValue, const UString& defaultSourceURL, int defaultLineNumber) { if (!functionValue) return CallIdentifier(GlobalCodeExecution, defaultSourceURL, defaultLineNumber); if (!functionValue.isObject()) return CallIdentifier("(unknown)", defaultSourceURL, defaultLineNumber); if (asObject(functionValue)->inherits(&TiFunction::info)) { TiFunction* function = asFunction(functionValue); if (!function->executable()->isHostFunction()) return createCallIdentifierFromFunctionImp(exec, function); } if (asObject(functionValue)->inherits(&InternalFunction::info)) return CallIdentifier(static_cast<InternalFunction*>(asObject(functionValue))->name(exec), defaultSourceURL, defaultLineNumber); return CallIdentifier(makeString("(", asObject(functionValue)->className(), " object)"), defaultSourceURL, defaultLineNumber); }