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.keyFor as described in ES 2015 Var JavascriptSymbol::EntryKeyFor(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)); if (args.Info.Count < 2 || !JavascriptSymbol::Is(args[1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedSymbol, _u("Symbol.keyFor")); } JavascriptSymbol* sym = JavascriptSymbol::FromVar(args[1]); const char16* key = sym->GetValue()->GetBuffer(); // Search the global symbol registration map for a key equal to the description of the symbol passed into Symbol.keyFor. // Symbol.for creates a new symbol with description equal to the key and uses that key as a mapping to the new symbol. // There will only be one symbol in the map with that string key value. const Js::PropertyRecord* propertyRecord = scriptContext->GetThreadContext()->GetSymbolFromRegistrationMap(key); // If we found a PropertyRecord in the map, make sure it is the same symbol that was passed to Symbol.keyFor. // If the two are different, it means the symbol passed to keyFor has the same description as a symbol registered via // Symbol.for _but_ is not the symbol returned from Symbol.for. if (propertyRecord != nullptr && propertyRecord == sym->GetValue()) { return JavascriptString::NewCopyBuffer(key, sym->GetValue()->GetLength(), scriptContext); } return library->GetUndefined(); }
Var JavascriptGenerator::EntryNext(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, L"Generator.prototype.next"); if (!JavascriptGenerator::Is(args[0])) { JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, L"Generator.prototype.next", L"Generator"); } JavascriptGenerator* generator = JavascriptGenerator::FromVar(args[0]); Var input = args.Info.Count > 1 ? args[1] : library->GetUndefined(); if (generator->IsCompleted()) { return library->CreateIteratorResultObjectUndefinedTrue(); } ResumeYieldData yieldData(input, nullptr); return generator->CallGenerator(&yieldData, L"Generator.prototype.next"); }
Var JavascriptGenerator::EntryThrow(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, L"Generator.prototype.throw"); if (!JavascriptGenerator::Is(args[0])) { JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, L"Generator.prototype.throw", L"Generator"); } JavascriptGenerator* generator = JavascriptGenerator::FromVar(args[0]); Var input = args.Info.Count > 1 ? args[1] : library->GetUndefined(); if (generator->IsSuspendedStart()) { generator->SetState(GeneratorState::Completed); } if (generator->IsCompleted()) { JavascriptExceptionOperators::OP_Throw(input, scriptContext); } ResumeYieldData yieldData(input, RecyclerNew(scriptContext->GetRecycler(), JavascriptExceptionObject, input, scriptContext, nullptr)); return generator->CallGenerator(&yieldData, L"Generator.prototype.throw"); }
Var JavascriptWeakSet::NewInstance(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0]; bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget); Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(WeakSetCount); JavascriptWeakSet* weakSetObject = nullptr; if (callInfo.Flags & CallFlags_New) { weakSetObject = library->CreateWeakSet(); } else { JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakSet"), _u("WeakSet")); } Assert(weakSetObject != nullptr); Var iterable = (args.Info.Count > 1) ? args[1] : library->GetUndefined(); RecyclableObject* iter = nullptr; RecyclableObject* adder = nullptr; if (JavascriptConversion::CheckObjectCoercible(iterable, scriptContext)) { iter = JavascriptOperators::GetIterator(iterable, scriptContext); Var adderVar = JavascriptOperators::GetProperty(weakSetObject, PropertyIds::add, scriptContext); if (!JavascriptConversion::IsCallable(adderVar)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction); } adder = RecyclableObject::FromVar(adderVar); } if (iter != nullptr) { Var nextItem; while (JavascriptOperators::IteratorStepAndValue(iter, scriptContext, &nextItem)) { CALL_FUNCTION(adder, CallInfo(CallFlags_Value, 2), weakSetObject, nextItem); } } return isCtorSuperCall ? JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), weakSetObject, nullptr, scriptContext) : weakSetObject; }
Var JavascriptGeneratorFunction::EntryAsyncFunctionImplementation(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(stackArgs, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); RecyclableObject* prototype = scriptContext->GetLibrary()->GetNull(); // InterpreterStackFrame takes a pointer to the args, so copy them to the recycler heap // and use that buffer for this InterpreterStackFrame. Var* argsHeapCopy = RecyclerNewArray(scriptContext->GetRecycler(), Var, stackArgs.Info.Count); js_memcpy_s(argsHeapCopy, sizeof(Var) * stackArgs.Info.Count, stackArgs.Values, sizeof(Var) * stackArgs.Info.Count); Arguments heapArgs(callInfo, argsHeapCopy); JavascriptExceptionObject* e = nullptr; JavascriptPromiseResolveOrRejectFunction* resolve; JavascriptPromiseResolveOrRejectFunction* reject; JavascriptPromiseAsyncSpawnExecutorFunction* executor = library->CreatePromiseAsyncSpawnExecutorFunction( JavascriptPromise::EntryJavascriptPromiseAsyncSpawnExecutorFunction, scriptContext->GetLibrary()->CreateGenerator(heapArgs, JavascriptAsyncFunction::FromVar(function)->GetGeneratorVirtualScriptFunction(), prototype), stackArgs[0]); JavascriptPromise* promise = library->CreatePromise(); JavascriptPromise::InitializePromise(promise, &resolve, &reject, scriptContext); try { CALL_FUNCTION(executor, CallInfo(CallFlags_Value, 3), library->GetUndefined(), resolve, reject); } catch (JavascriptExceptionObject* ex) { e = ex; } if (e != nullptr) { JavascriptPromise::TryRejectWithExceptionObject(e, reject, scriptContext); } return promise; }
// SharedArrayBuffer.prototype.slice Var SharedArrayBuffer::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (!SharedArrayBuffer::Is(args[0])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } JavascriptLibrary* library = scriptContext->GetLibrary(); SharedArrayBuffer* currentBuffer = SharedArrayBuffer::FromVar(args[0]); int64 currentLen = (int64)currentBuffer->GetByteLength(); int64 start = 0, end = 0; int64 newLen = 0; // If no start or end arguments, use the entire length if (args.Info.Count < 2) { newLen = currentLen; } else { start = JavascriptArray::GetIndexFromVar(args[1], currentLen, scriptContext); // If no end argument, use length as the end if (args.Info.Count < 3 || args[2] == library->GetUndefined()) { end = currentLen; } else { end = JavascriptArray::GetIndexFromVar(args[2], currentLen, scriptContext); } newLen = end > start ? end - start : 0; } // We can't have allocated an SharedArrayBuffer with byteLength > MaxArrayBufferLength. // start and end are clamped to valid indices, so the new length also cannot exceed MaxArrayBufferLength. // Therefore, should be safe to cast down newLen to uint32. Assert(newLen < MaxSharedArrayBufferLength); uint32 newbyteLength = static_cast<uint32>(newLen); SharedArrayBuffer* newBuffer = nullptr; if (scriptContext->GetConfig()->IsES6SpeciesEnabled()) { Var constructorVar = JavascriptOperators::SpeciesConstructor(currentBuffer, scriptContext->GetLibrary()->GetSharedArrayBufferConstructor(), scriptContext); JavascriptFunction* constructor = JavascriptFunction::FromVar(constructorVar); Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newbyteLength, scriptContext) }; Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); Js::Var newVar = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext); if (!SharedArrayBuffer::Is(newVar)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } newBuffer = SharedArrayBuffer::FromVar(newVar); if (newBuffer == currentBuffer) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } if (newBuffer->GetByteLength() < newbyteLength) { JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgumentOutOfRange, _u("SharedArrayBuffer.prototype.slice")); } } else { newBuffer = library->CreateSharedArrayBuffer(newbyteLength); } Assert(newBuffer); Assert(newBuffer->GetByteLength() >= newbyteLength); // Don't bother doing memcpy if we aren't copying any elements if (newbyteLength > 0) { AssertMsg(currentBuffer->GetBuffer() != nullptr, "buffer must not be null when we copy from it"); js_memcpy_s(newBuffer->GetBuffer(), newbyteLength, currentBuffer->GetBuffer() + start, newbyteLength); } return newBuffer; }
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 JavascriptArrayIterator::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 (!JavascriptArrayIterator::Is(thisObj)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedArrayIterator, _u("Array Iterator.prototype.next")); } JavascriptArrayIterator* iterator = JavascriptArrayIterator::FromVar(thisObj); Var iterable = iterator->m_iterableObject; if (iterable == nullptr) { return library->CreateIteratorResultObjectUndefinedTrue(); } int64 length; bool bArray = false; JavascriptArray* pArr = nullptr; if (DynamicObject::IsAnyArray(iterable) && !JavascriptArray::FromAnyArray(iterable)->IsCrossSiteObject()) { #if ENABLE_COPYONACCESS_ARRAY JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(iterable); #endif pArr = JavascriptArray::FromAnyArray(iterable); length = pArr->GetLength(); bArray = true; } else { length = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(iterable, scriptContext), scriptContext); } int64 index = iterator->m_nextIndex; if (index >= length) { // Nulling out the m_iterableObject field is important so that the iterator // does not keep the iterable object alive after iteration is completed. iterator->m_iterableObject = nullptr; return library->CreateIteratorResultObjectUndefinedTrue(); } iterator->m_nextIndex += 1; if (iterator->m_kind == JavascriptArrayIteratorKind::Key) { return library->CreateIteratorResultObjectValueFalse(JavascriptNumber::ToVar(index, scriptContext)); } Var value; if (index <= UINT_MAX) { if (bArray) { if (JavascriptArray::Is(pArr)) { value = pArr->DirectGetItem((uint32)index); } else { if (!JavascriptOperators::GetOwnItem(pArr, (uint32) index, &value, scriptContext)) { value = library->GetUndefined(); } } } else { value = JavascriptOperators::OP_GetElementI_UInt32(iterable, (uint32)index, scriptContext); } } else { value = JavascriptOperators::OP_GetElementI(iterable, JavascriptNumber::ToVar(index, scriptContext), scriptContext); } if (iterator->m_kind == JavascriptArrayIteratorKind::Value) { return library->CreateIteratorResultObjectValueFalse(value); } Assert(iterator->m_kind == JavascriptArrayIteratorKind::KeyAndValue); JavascriptArray* keyValueTuple = library->CreateArray(2); keyValueTuple->SetItem(0, JavascriptNumber::ToVar(index, scriptContext), PropertyOperation_None); keyValueTuple->SetItem(1, value, PropertyOperation_None); return library->CreateIteratorResultObjectValueFalse(keyValueTuple); }
Var JavascriptMap::NewInstance(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Map")); Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0]; bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget); Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(MapCount); JavascriptMap* mapObject = nullptr; if (callInfo.Flags & CallFlags_New) { mapObject = library->CreateMap(); } else { JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map"), _u("Map")); } Assert(mapObject != nullptr); Var iterable = (args.Info.Count > 1) ? args[1] : library->GetUndefined(); RecyclableObject* iter = nullptr; RecyclableObject* adder = nullptr; if (JavascriptConversion::CheckObjectCoercible(iterable, scriptContext)) { iter = JavascriptOperators::GetIterator(iterable, scriptContext); Var adderVar = JavascriptOperators::GetProperty(mapObject, PropertyIds::set, scriptContext); if (!JavascriptConversion::IsCallable(adderVar)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction); } adder = RecyclableObject::FromVar(adderVar); } if (mapObject->map != nullptr) { JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_ObjectIsAlreadyInitialized, _u("Map"), _u("Map")); } mapObject->map = RecyclerNew(scriptContext->GetRecycler(), MapDataMap, scriptContext->GetRecycler()); if (iter != nullptr) { Var undefined = library->GetUndefined(); JavascriptOperators::DoIteratorStepAndValue(iter, scriptContext, [&](Var nextItem) { if (!JavascriptOperators::IsObject(nextItem)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject); } RecyclableObject* obj = RecyclableObject::FromVar(nextItem); Var key, value; if (!JavascriptOperators::GetItem(obj, 0u, &key, scriptContext)) { key = undefined; } if (!JavascriptOperators::GetItem(obj, 1u, &value, scriptContext)) { value = undefined; } // CONSIDER: if adder is the default built-in, fast path it and skip the JS call? CALL_FUNCTION(adder, CallInfo(CallFlags_Value, 3), mapObject, key, value); }); } return isCtorSuperCall ? JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), mapObject, nullptr, scriptContext) : mapObject; }