blob: e8bbdb237531f9ad529ab8a383335b0539462883 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLibraryPch.h"
#include "EngineInterfaceObject.h"
#include "JsBuiltInEngineInterfaceExtensionObject.h"
#include "Types/DeferredTypeHandler.h"
#ifdef ENABLE_JS_BUILTINS
#include "ByteCode/ByteCodeSerializer.h"
#include "errstr.h"
#include "ByteCode/ByteCodeDumper.h"
#pragma warning(push)
#pragma warning(disable:4309) // truncation of constant value
#pragma warning(disable:4838) // conversion from 'int' to 'const char' requires a narrowing conversion
#if DISABLE_JIT
#if TARGET_64
#include "JsBuiltIn/JsBuiltIn.js.nojit.bc.64b.h"
#else
#include "JsBuiltIn/JsBuiltIn.js.nojit.bc.32b.h"
#endif // TARGET_64
#else
#if TARGET_64
#include "JsBuiltIn/JsBuiltIn.js.bc.64b.h"
#else
#include "JsBuiltIn/JsBuiltIn.js.bc.32b.h"
#endif // TARGET_64
#endif // DISABLE_JIT
#pragma warning(pop)
#define IfFailAssertMsgAndThrowHr(op, msg) \
if (FAILED(hr=(op))) \
{ \
AssertMsg(false, msg); \
JavascriptError::MapAndThrowError(scriptContext, hr); \
} \
#endif // ENABLE_JS_BUILTINS
namespace Js
{
#ifdef ENABLE_JS_BUILTINS
JsBuiltInEngineInterfaceExtensionObject::JsBuiltInEngineInterfaceExtensionObject(ScriptContext * scriptContext) :
EngineExtensionObjectBase(EngineInterfaceExtensionKind_JsBuiltIn, scriptContext),
jsBuiltInByteCode(nullptr),
wasInitialized(false)
{
}
NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_RegisterFunction(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterFunction));
NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_RegisterChakraLibraryFunction(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterChakraLibraryFunction));
NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ToLengthFunction(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ToLengthFunction));
NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ToIntegerFunction(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ToIntegerFunction));
void JsBuiltInEngineInterfaceExtensionObject::Initialize()
{
if (wasInitialized)
{
return;
}
JavascriptLibrary* library = scriptContext->GetLibrary();
DynamicObject* commonObject = library->GetEngineInterfaceObject()->GetCommonNativeInterfaces();
if (scriptContext->IsJsBuiltInEnabled())
{
Assert(library->GetEngineInterfaceObject() != nullptr);
builtInNativeInterfaces = DynamicObject::New(library->GetRecycler(),
DynamicType::New(scriptContext, TypeIds_Object, commonObject, nullptr,
DeferredTypeHandler<InitializeJsBuiltInNativeInterfaces>::GetDefaultInstance()));
library->AddMember(library->GetEngineInterfaceObject(), Js::PropertyIds::JsBuiltIn, builtInNativeInterfaces);
}
wasInitialized = true;
}
void JsBuiltInEngineInterfaceExtensionObject::InjectJsBuiltInLibraryCode(ScriptContext * scriptContext)
{
if (jsBuiltInByteCode != nullptr)
{
return;
}
try {
EnsureJsBuiltInByteCode(scriptContext);
Assert(jsBuiltInByteCode != nullptr);
Js::ScriptFunction *function = scriptContext->GetLibrary()->CreateScriptFunction(jsBuiltInByteCode->GetNestedFunctionForExecution(0));
// If we are profiling, we need to register the script to the profiler callback, so the script compiled event will be sent.
if (scriptContext->IsProfiling())
{
scriptContext->RegisterScript(function->GetFunctionProxy());
}
// Mark we are profiling library code already, so that any initialization library code called here won't be reported to profiler
AutoProfilingUserCode autoProfilingUserCode(scriptContext->GetThreadContext(), /*isProfilingUserCode*/false);
Js::Var args[] = { scriptContext->GetLibrary()->GetUndefined(), scriptContext->GetLibrary()->GetEngineInterfaceObject() };
Js::CallInfo callInfo(Js::CallFlags_Value, _countof(args));
// Clear disable implicit call bit as initialization code doesn't have any side effect
Js::ImplicitCallFlags saveImplicitCallFlags = scriptContext->GetThreadContext()->GetImplicitCallFlags();
scriptContext->GetThreadContext()->ClearDisableImplicitFlags();
JavascriptFunction::CallRootFunctionInScript(function, Js::Arguments(callInfo, args));
scriptContext->GetThreadContext()->SetImplicitCallFlags((Js::ImplicitCallFlags)(saveImplicitCallFlags));
#if DBG_DUMP
if (PHASE_DUMP(Js::ByteCodePhase, function->GetFunctionProxy()) && Js::Configuration::Global.flags.Verbose)
{
DumpByteCode();
}
#endif
}
catch (const JavascriptException& err)
{
Js::JavascriptExceptionObject* ex = err.GetAndClear();
ex->GetScriptContext();
JavascriptError::ThrowTypeError(ex->GetScriptContext(), JSERR_BuiltInNotAvailable);
}
}
void JsBuiltInEngineInterfaceExtensionObject::InitializeJsBuiltInNativeInterfaces(DynamicObject * builtInNativeInterfaces, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
{
typeHandler->Convert(builtInNativeInterfaces, mode, 16);
ScriptContext* scriptContext = builtInNativeInterfaces->GetScriptContext();
JavascriptLibrary* library = scriptContext->GetLibrary();
library->AddFunctionToLibraryObject(builtInNativeInterfaces, Js::PropertyIds::registerChakraLibraryFunction, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_RegisterChakraLibraryFunction, 2);
library->AddFunctionToLibraryObject(builtInNativeInterfaces, Js::PropertyIds::registerFunction, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_RegisterFunction, 2);
builtInNativeInterfaces->SetHasNoEnumerableProperties(true);
}
#if DBG
void JsBuiltInEngineInterfaceExtensionObject::DumpByteCode()
{
Output::Print(_u("Dumping JS Built Ins Byte Code:\r"));
EnsureJsBuiltInByteCode(scriptContext);
Js::ByteCodeDumper::DumpRecursively(jsBuiltInByteCode);
}
#endif // DBG
void JsBuiltInEngineInterfaceExtensionObject::EnsureJsBuiltInByteCode(ScriptContext * scriptContext)
{
if (jsBuiltInByteCode == nullptr)
{
SourceContextInfo* sourceContextInfo = RecyclerNewStructZ(scriptContext->GetRecycler(), SourceContextInfo);
sourceContextInfo->dwHostSourceContext = Js::Constants::JsBuiltInSourceContext;
sourceContextInfo->isHostDynamicDocument = true;
sourceContextInfo->sourceContextId = Js::Constants::JsBuiltInSourceContextId;
Assert(sourceContextInfo != nullptr);
SRCINFO si;
memset(&si, 0, sizeof(si));
si.sourceContextInfo = sourceContextInfo;
SRCINFO *hsi = scriptContext->AddHostSrcInfo(&si);
uint32 flags = fscrJsBuiltIn | (CONFIG_FLAG(CreateFunctionProxy) && !scriptContext->IsProfiling() ? fscrAllowFunctionProxy : 0);
HRESULT hr = Js::ByteCodeSerializer::DeserializeFromBuffer(scriptContext, flags, (LPCUTF8)nullptr, hsi, (byte*)Library_Bytecode_jsbuiltin, nullptr, &jsBuiltInByteCode);
IfFailAssertMsgAndThrowHr(hr, "Failed to deserialize JsBuiltIn.js bytecode - very probably the bytecode needs to be rebuilt.");
}
}
DynamicObject* JsBuiltInEngineInterfaceExtensionObject::GetPrototypeFromName(Js::PropertyIds propertyId, ScriptContext* scriptContext)
{
JavascriptLibrary* library = scriptContext->GetLibrary();
switch (propertyId) {
case PropertyIds::Array:
return library->arrayPrototype;
case PropertyIds::String:
return library->stringPrototype;
case PropertyIds::__chakraLibrary:
return library->GetChakraLib();
default:
AssertMsg(false, "Unable to find a prototype that match with this className.");
return nullptr;
}
}
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]));
// 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 = scriptContext->GetLibrary()->CreateScriptFunction(func->GetFunctionProxy());
scriptFunction->SetIsJsBuiltInCode();
scriptFunction->GetFunctionProxy()->SetIsJsBuiltInCode();
if (scriptFunction->GetScriptContext()->GetConfig()->IsES6FunctionNameEnabled())
{
scriptFunction->SetPropertyWithAttributes(PropertyIds::name, methodName, PropertyConfigurable, nullptr);
}
scriptContext->GetLibrary()->AddMember(chakraLibraryObject, functionIdentifier, scriptFunction);
//Don't need to return anything
return scriptContext->GetLibrary()->GetUndefined();
}
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]));
// 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);
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 = JavascriptOperators::GetPropertyId(methodName, scriptContext);
// Link the function to the prototype.
ScriptFunction* scriptFunction = scriptContext->GetLibrary()->CreateScriptFunction(func->GetFunctionProxy());
scriptFunction->SetIsJsBuiltInCode();
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);
if (scriptFunction->GetScriptContext()->GetConfig()->IsES6FunctionNameEnabled())
{
scriptFunction->SetPropertyWithAttributes(PropertyIds::name, methodName, PropertyConfigurable, nullptr);
}
scriptContext->GetLibrary()->AddMember(prototype, functionIdentifier, scriptFunction);
if (functionIdentifier == PropertyIds::indexOf)
{
// Special case for Intl who requires to call the non-overriden (by the user) IndexOf function.
scriptContext->GetLibrary()->AddMember(scriptContext->GetLibrary()->GetEngineInterfaceObject()->GetCommonNativeInterfaces(), Js::PropertyIds::builtInJavascriptArrayEntryIndexOf, scriptFunction);
}
//Don't need to return anything
return scriptContext->GetLibrary()->GetUndefined();
}
Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ToLengthFunction(RecyclableObject * function, CallInfo callInfo, ...)
{
EngineInterfaceObject_CommonFunctionProlog(function, callInfo);
AssertOrFailFast(args.Info.Count >= 2);
return JavascriptNumber::ToVar(JavascriptConversion::ToLength(args.Values[1], scriptContext), scriptContext);
}
Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ToIntegerFunction(RecyclableObject * function, CallInfo callInfo, ...)
{
EngineInterfaceObject_CommonFunctionProlog(function, callInfo);
AssertOrFailFast(args.Info.Count >= 2);
Var value = args.Values[1];
if (JavascriptOperators::IsUndefinedOrNull(value))
{
return TaggedInt::ToVarUnchecked(0);
}
else if (TaggedInt::Is(value))
{
return value;
}
return JavascriptNumber::ToVarNoCheck(JavascriptConversion::ToInteger(value, scriptContext), scriptContext);
}
#endif // ENABLE_JS_BUILTINS
}