Var JavascriptStringIterator::EntryNext(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); Assert(!(callInfo.Flags & CallFlags_New)); Var thisObj = args[0]; if (!JavascriptStringIterator::Is(thisObj)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedStringIterator, L"String Iterator.prototype.next"); } JavascriptStringIterator* iterator = JavascriptStringIterator::FromVar(thisObj); JavascriptString* string = iterator->m_string; if (string == nullptr) { return library->CreateIteratorResultObjectUndefinedTrue(); } charcount_t length = string->GetLength(); charcount_t index = iterator->m_nextIndex; if (index >= length) { // Nulling out the m_string field is important so that the iterator // does not keep the string alive after iteration is completed. iterator->m_string = nullptr; return library->CreateIteratorResultObjectUndefinedTrue(); } wchar_t chFirst = string->GetItem(index); Var result; if (index + 1 == string->GetLength() || !NumberUtilities::IsSurrogateLowerPart(chFirst) || !NumberUtilities::IsSurrogateUpperPart(string->GetItem(index + 1))) { result = scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(chFirst); iterator->m_nextIndex += 1; } else { result = JavascriptString::SubstringCore(string, index, 2, scriptContext); iterator->m_nextIndex += 2; } return library->CreateIteratorResultObjectValueFalse(result); }
BOOL JavascriptBoolean::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) { if (this->GetValue()) { JavascriptString* trueDisplayString = GetLibrary()->GetTrueDisplayString(); stringBuilder->Append(trueDisplayString->GetString(), trueDisplayString->GetLength()); } else { JavascriptString* falseDisplayString = GetLibrary()->GetFalseDisplayString(); stringBuilder->Append(falseDisplayString->GetString(), falseDisplayString->GetLength()); } return TRUE; }
int64 ConvertStringToInt64(Var string, ScriptContext* scriptContext) { JavascriptString* str = JavascriptString::FromVar(string); charcount_t length = str->GetLength(); const char16* buf = str->GetString(); int radix = 10; if (length >= 2 && buf[0] == '0' && buf[1] == 'x') { radix = 16; } return (int64)_wcstoui64(buf, nullptr, radix); }
Var ForInObjectEnumerator::GetCurrentAndMoveNext(PropertyId& propertyId) { JavascriptEnumerator *pEnumerator = currentEnumerator; PropertyRecord const * propRecord; PropertyAttributes attributes = PropertyNone; while (true) { propertyId = Constants::NoProperty; currentIndex = pEnumerator->GetCurrentAndMoveNext(propertyId, &attributes); #if ENABLE_COPYONACCESS_ARRAY JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(currentIndex); #endif if (currentIndex) { if (firstPrototype == nullptr) { // We are calculating correct shadowing for non-enumerable properties of the child object, we will receive // both enumerable and non-enumerable properties from GetCurrentAndMoveNext so we need to check before we simply // return here. If this property is non-enumerable we're going to skip it. if (!(attributes & PropertyEnumerable)) { continue; } // There are no prototype that has enumerable properties, // don't need to keep track of the propertyIds we visited. return currentIndex; } // Property Id does not exist. if (propertyId == Constants::NoProperty) { if (!JavascriptString::Is(currentIndex)) //This can be undefined { continue; } JavascriptString *pString = JavascriptString::FromVar(currentIndex); if (VirtualTableInfo<Js::PropertyString>::HasVirtualTable(pString)) { // If we have a property string, it is assumed that the propertyId is being // kept alive with the object PropertyString * propertyString = (PropertyString *)pString; propertyId = propertyString->GetPropertyRecord()->GetPropertyId(); } else { ScriptContext* scriptContext = pString->GetScriptContext(); scriptContext->GetOrAddPropertyRecord(pString->GetString(), pString->GetLength(), &propRecord); propertyId = propRecord->GetPropertyId(); // We keep the track of what is enumerated using a bit vector of propertyID. // so the propertyId can't be collected until the end of the for in enumerator // Keep a list of the property string. newPropertyStrings.Prepend(GetScriptContext()->GetRecycler(), propRecord); } } //check for shadowed property if (TestAndSetEnumerated(propertyId) //checks if the property is already enumerated or not && (attributes & PropertyEnumerable)) { return currentIndex; } } else { if (object == baseObject) { if (firstPrototype == nullptr) { return NULL; } object = firstPrototype; } else { //walk the prototype chain object = object->GetPrototype(); if ((object == NULL) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return NULL; } } do { if (!GetCurrentEnumerator()) { return nullptr; } pEnumerator = currentEnumerator; if (!VirtualTableInfo<Js::NullEnumerator>::HasVirtualTable(pEnumerator)) { break; } //walk the prototype chain object = object->GetPrototype(); if ((object == NULL) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return NULL; } } while (true); } } }
Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterFunction(RecyclableObject* function, CallInfo callInfo, ...) { EngineInterfaceObject_CommonFunctionProlog(function, callInfo); AssertOrFailFast(args.Info.Count >= 3 && JavascriptObject::Is(args.Values[1]) && JavascriptFunction::Is(args.Values[2])); JavascriptLibrary * library = scriptContext->GetLibrary(); // retrieves arguments RecyclableObject* funcInfo = nullptr; if (!JavascriptConversion::ToObject(args.Values[1], scriptContext, &funcInfo)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Object.assign")); } Var classNameProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::className, scriptContext); Var methodNameProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::methodName, scriptContext); Var argumentsCountProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::argumentsCount, scriptContext); Var forceInlineProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::forceInline, scriptContext); Var aliasProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::alias, scriptContext); Assert(JavascriptString::Is(classNameProperty)); Assert(JavascriptString::Is(methodNameProperty)); Assert(TaggedInt::Is(argumentsCountProperty)); JavascriptString* className = JavascriptString::FromVar(classNameProperty); JavascriptString* methodName = JavascriptString::FromVar(methodNameProperty); int argumentsCount = TaggedInt::ToInt32(argumentsCountProperty); BOOL forceInline = JavascriptConversion::ToBoolean(forceInlineProperty, scriptContext); JavascriptFunction* func = JavascriptFunction::FromVar(args.Values[2]); // Set the function's display name, as the function we pass in argument are anonym. func->GetFunctionProxy()->SetIsPublicLibraryCode(); func->GetFunctionProxy()->EnsureDeserialized()->SetDisplayName(methodName->GetString(), methodName->GetLength(), 0); DynamicObject* prototype = GetPrototypeFromName(JavascriptOperators::GetPropertyId(className, scriptContext), scriptContext); PropertyIds functionIdentifier = methodName->BufferEquals(_u("Symbol.iterator"), 15)? PropertyIds::_symbolIterator : JavascriptOperators::GetPropertyId(methodName, scriptContext); // Link the function to the prototype. ScriptFunction* scriptFunction = library->CreateScriptFunction(func->GetFunctionProxy()); scriptFunction->GetFunctionProxy()->SetIsJsBuiltInCode(); if (forceInline) { Assert(scriptFunction->HasFunctionBody()); scriptFunction->GetFunctionBody()->SetJsBuiltInForceInline(); } scriptFunction->SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(argumentsCount), PropertyConfigurable, nullptr); scriptFunction->SetConfigurable(PropertyIds::prototype, true); scriptFunction->DeleteProperty(PropertyIds::prototype, Js::PropertyOperationFlags::PropertyOperation_None); scriptFunction->SetPropertyWithAttributes(PropertyIds::name, methodName, PropertyConfigurable, nullptr); library->AddMember(prototype, functionIdentifier, scriptFunction); RecordCommonNativeInterfaceBuiltIns(functionIdentifier, scriptContext, scriptFunction); if (!JavascriptOperators::IsUndefinedOrNull(aliasProperty)) { JavascriptString * alias = JavascriptConversion::ToString(aliasProperty, scriptContext); // Cannot do a string to property id search here, Symbol.* have different hashing mechanism, so resort to this str compare PropertyIds aliasFunctionIdentifier = alias->BufferEquals(_u("Symbol.iterator"), 15) ? PropertyIds::_symbolIterator : JavascriptOperators::GetPropertyId(alias, scriptContext); library->AddMember(prototype, aliasFunctionIdentifier, scriptFunction); } if (prototype == library->arrayPrototype) { RecordDefaultIteratorFunctions(functionIdentifier, scriptContext, scriptFunction); } //Don't need to return anything return library->GetUndefined(); }
Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterChakraLibraryFunction(RecyclableObject* function, CallInfo callInfo, ...) { EngineInterfaceObject_CommonFunctionProlog(function, callInfo); AssertOrFailFast(args.Info.Count >= 3 && JavascriptString::Is(args.Values[1]) && JavascriptFunction::Is(args.Values[2])); JavascriptLibrary * library = scriptContext->GetLibrary(); // retrieves arguments JavascriptString* methodName = JavascriptString::FromVar(args.Values[1]); JavascriptFunction* func = JavascriptFunction::FromVar(args.Values[2]); // Set the function's display name, as the function we pass in argument are anonym. func->GetFunctionProxy()->SetIsPublicLibraryCode(); func->GetFunctionProxy()->EnsureDeserialized()->SetDisplayName(methodName->GetString(), methodName->GetLength(), 0); DynamicObject* chakraLibraryObject = GetPrototypeFromName(PropertyIds::__chakraLibrary, scriptContext); PropertyIds functionIdentifier = JavascriptOperators::GetPropertyId(methodName, scriptContext); // Link the function to __chakraLibrary. ScriptFunction* scriptFunction = library->CreateScriptFunction(func->GetFunctionProxy()); scriptFunction->GetFunctionProxy()->SetIsJsBuiltInCode(); Assert(scriptFunction->HasFunctionBody()); scriptFunction->GetFunctionBody()->SetJsBuiltInForceInline(); scriptFunction->SetPropertyWithAttributes(PropertyIds::name, methodName, PropertyConfigurable, nullptr); library->AddMember(chakraLibraryObject, functionIdentifier, scriptFunction); //Don't need to return anything return library->GetUndefined(); }
// Symbol.for as described in ES 2015 Var JavascriptSymbol::EntryFor(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); AssertMsg(args.Info.Count, "Should always have implicit 'this'."); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); Assert(!(callInfo.Flags & CallFlags_New)); JavascriptString* key; if (args.Info.Count > 1) { key = JavascriptConversion::ToString(args[1], scriptContext); } else { key = library->GetUndefinedDisplayString(); } // Search the global symbol registration map for a symbol with description equal to the string key. // The map can only have one symbol with that description so if we found a symbol, that is the registered // symbol for the string key. const Js::PropertyRecord* propertyRecord = scriptContext->GetThreadContext()->GetSymbolFromRegistrationMap(key->GetString()); // If we didn't find a PropertyRecord in the map, we'll create a new symbol with description equal to the key string. // This is the only place we add new PropertyRecords to the map, so we should never have multiple PropertyRecords in the // map with the same string key value (since we would return the one we found above instead of creating a new one). if (propertyRecord == nullptr) { propertyRecord = scriptContext->GetThreadContext()->AddSymbolToRegistrationMap(key->GetString(), key->GetLength()); } Assert(propertyRecord != nullptr); return library->CreateSymbol(propertyRecord); }
Var EngineInterfaceObject::Entry_TagPublicLibraryCode(RecyclableObject *function, CallInfo callInfo, ...) { EngineInterfaceObject_CommonFunctionProlog(function, callInfo); if (callInfo.Count >= 2 && JavascriptFunction::Is(args.Values[1])) { JavascriptFunction* func = JavascriptFunction::FromVar(args.Values[1]); func->GetFunctionProxy()->SetIsPublicLibraryCode(); if (callInfo.Count >= 3 && JavascriptString::Is(args.Values[2])) { JavascriptString* customFunctionName = JavascriptString::FromVar(args.Values[2]); // tagPublicFunction("Intl.Collator", Collator); in Intl.js calls TagPublicLibraryCode the expected name is Collator so we need to calculate the offset const wchar_t * shortName = wcsrchr(customFunctionName->GetString(), L'.'); uint shortNameOffset = 0; if (shortName != nullptr) { // JavascriptString length is bounded by uint max shortName++; shortNameOffset = static_cast<uint>(shortName - customFunctionName->GetString()); } func->GetFunctionProxy()->EnsureDeserialized()->SetDisplayName(customFunctionName->GetString(), customFunctionName->GetLength(), shortNameOffset); } return func; } return scriptContext->GetLibrary()->GetUndefined(); }
JavascriptString * ForInObjectEnumerator::MoveAndGetNext(PropertyId& propertyId) { PropertyRecord const * propRecord; PropertyAttributes attributes = PropertyNone; while (true) { propertyId = Constants::NoProperty; JavascriptString * currentIndex = enumerator.MoveAndGetNext(propertyId, &attributes); // The object type may have changed and we may not be able to use Jit fast path anymore. // canUseJitFastPath is determined in ForInObjectEnumerator::Initialize, once we decide we can't use // Jit fast path we will never go back to use fast path so && with current value - if it's already // false we don't call CanUseJITFastPath() this->canUseJitFastPath = this->canUseJitFastPath && enumerator.CanUseJITFastPath(); if (currentIndex) { if (this->shadowData == nullptr) { // There are no prototype that has enumerable properties, // don't need to keep track of the propertyIds we visited. // We have asked for enumerable properties only, so don't need to check the attribute returned. Assert(attributes & PropertyEnumerable); return currentIndex; } // Property Id does not exist. if (propertyId == Constants::NoProperty) { if (VirtualTableInfo<Js::PropertyString>::HasVirtualTable(currentIndex)) { // If we have a property string, it is assumed that the propertyId is being // kept alive with the object PropertyString * propertyString = (PropertyString *)currentIndex; propertyId = propertyString->GetPropertyRecord()->GetPropertyId(); } else { ScriptContext* scriptContext = currentIndex->GetScriptContext(); scriptContext->GetOrAddPropertyRecord(currentIndex->GetString(), currentIndex->GetLength(), &propRecord); propertyId = propRecord->GetPropertyId(); // We keep the track of what is enumerated using a bit vector of propertyID. // so the propertyId can't be collected until the end of the for in enumerator // Keep a list of the property string. this->shadowData->newPropertyStrings.Prepend(GetScriptContext()->GetRecycler(), propRecord); } } if (TestAndSetEnumerated(propertyId) //checks if the property is already enumerated or not && (attributes & PropertyEnumerable)) { return currentIndex; } } else { if (this->shadowData == nullptr) { Assert(!this->enumeratingPrototype); return nullptr; } RecyclableObject * object; if (!this->enumeratingPrototype) { this->enumeratingPrototype = true; object = this->shadowData->firstPrototype; this->shadowData->currentObject = object; } else { //walk the prototype chain object = this->shadowData->currentObject->GetPrototype(); this->shadowData->currentObject = object; if ((object == nullptr) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return nullptr; } } do { if (!InitializeCurrentEnumerator(object)) { return nullptr; } if (!enumerator.IsNullEnumerator()) { break; } //walk the prototype chain object = object->GetPrototype(); this->shadowData->currentObject = object; if ((object == nullptr) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return nullptr; } } while (true); } } }